Skip to content

Commit 3695fba

Browse files
committed
refactor: split Passage enter/exit from Orders
1 parent 72cac64 commit 3695fba

File tree

4 files changed

+187
-114
lines changed

4 files changed

+187
-114
lines changed

.gas-snapshot

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
ZenithTest:test_badSequence() (gas: 60049)
2-
ZenithTest:test_badSignature() (gas: 66665)
3-
ZenithTest:test_blockExpired() (gas: 55344)
4-
ZenithTest:test_notSequencer() (gas: 58385)
5-
ZenithTest:test_onePerBlock() (gas: 105869)
6-
ZenithTest:test_submitBlock() (gas: 90173)
1+
ZenithTest:test_badSequence() (gas: 60262)
2+
ZenithTest:test_badSignature() (gas: 66878)
3+
ZenithTest:test_blockExpired() (gas: 55493)
4+
ZenithTest:test_notSequencer() (gas: 58598)
5+
ZenithTest:test_onePerBlock() (gas: 106062)
6+
ZenithTest:test_submitBlock() (gas: 90194)

script/Zenith.s.sol

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,27 @@ pragma solidity ^0.8.24;
33

44
import {Script} from "forge-std/Script.sol";
55
import {Zenith} from "../src/Zenith.sol";
6+
import {HostMarket, RollupMarket} from "../src/Market.sol";
7+
import {RollupPassage} from "../src/Passage.sol";
68

79
contract ZenithScript is Script {
810
// deploy:
911
// forge script ZenithScript --sig "deploy(uint256,address,address)" --rpc-url $RPC_URL --etherscan-api-key $ETHERSCAN_API_KEY --private-key $PRIVATE_KEY --broadcast --verify $ROLLUP_CHAIN_ID $WITHDRAWAL_ADMIN_ADDRESS $SEQUENCER_ADMIN_ADDRESS
1012
function deploy(uint256 defaultRollupChainId, address withdrawalAdmin, address sequencerAdmin)
1113
public
12-
returns (Zenith z)
14+
returns (Zenith z, HostMarket m)
1315
{
1416
vm.startBroadcast();
1517
z = new Zenith(defaultRollupChainId, withdrawalAdmin, sequencerAdmin);
18+
m = new HostMarket();
19+
}
20+
21+
// deploy:
22+
// forge script ZenithScript --sig "deployL2()" --rpc-url $L2_RPC_URL --private-key $PRIVATE_KEY --broadcast $ZENITH_ADDRESS
23+
function deployL2(address zenith) public returns (RollupPassage p, RollupMarket m) {
24+
vm.startBroadcast();
25+
p = new RollupPassage(zenith);
26+
m = new RollupMarket();
1627
}
1728

1829
// NOTE: script must be run using SequencerAdmin key

src/Market.sol

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.24;
3+
4+
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
5+
6+
/// @notice Contract capable of processing fulfillment of intent-based Orders.
7+
contract OrderFulfiller {
8+
/// @notice Emitted when an swap order is fulfilled by the Builder.
9+
/// @param originChainId - The chainId on which the swap order was submitted.
10+
/// @param token - The address of the token transferred to the recipient. address(0) corresponds to native Ether.
11+
/// @param recipient - The recipient of the token.
12+
/// @param amount - The amount of the token transferred to the recipient.
13+
event SwapFulfilled(
14+
uint256 indexed originChainId, address indexed token, address indexed recipient, uint256 amount
15+
);
16+
17+
/// @notice Fulfill a rollup Swap order.
18+
/// The user calls `swap` on a rollup; the Builder calls `fulfillSwap` on the target chain.
19+
/// @custom:emits SwapFulfilled
20+
/// @param originChainId - The chainId of the rollup on which `swap` was called.
21+
/// @param token - The address of the token to be transferred to the recipient.
22+
/// address(0) corresponds to native Ether.
23+
/// @param recipient - The recipient of the token.
24+
/// @param amount - The amount of the token to be transferred to the recipient.
25+
function fulfillSwap(uint256 originChainId, address token, address recipient, uint256 amount) external payable {
26+
if (token == address(0)) {
27+
require(amount == msg.value);
28+
payable(recipient).transfer(msg.value);
29+
} else {
30+
IERC20(token).transferFrom(msg.sender, recipient, amount);
31+
}
32+
emit SwapFulfilled(originChainId, token, recipient, amount);
33+
}
34+
}
35+
36+
/// @notice Contract capable of registering initiation of intent-based Orders.
37+
contract OrderInitiator {
38+
/// @notice Thrown when an swap transaction is submitted with a deadline that has passed.
39+
error OrderExpired();
40+
41+
/// @notice Emitted when an swap order is successfully processed, indicating it was also fulfilled on the target chain.
42+
/// @dev See `swap` for parameter docs.
43+
event Swap(
44+
uint256 indexed targetChainId,
45+
address indexed tokenIn,
46+
address indexed tokenOut,
47+
address recipient,
48+
uint256 deadline,
49+
uint256 amountIn,
50+
uint256 amountOut
51+
);
52+
53+
/// @notice Emitted when tokens or native Ether is swept from the contract.
54+
/// @dev Intended to improve visibility for Builders to ensure Sweep isn't called unexpectedly.
55+
/// Intentionally does not bother to emit which token(s) were swept, nor their amounts.
56+
event Sweep(address indexed token, address indexed recipient, uint256 amount);
57+
58+
/// @notice Request to swap ERC20s.
59+
/// @dev tokenIn is provided on the rollup; in exchange,
60+
/// tokenOut is expected to be received on targetChainId.
61+
/// @dev targetChainId may be the current chainId, the Host chainId, or..
62+
/// @dev Fees paid to the Builders for fulfilling the swap orders
63+
/// can be included within the "exchange rate" between tokenIn and tokenOut.
64+
/// @dev The Builder claims the tokenIn from the contract by submitting a transaction to `sweep` the tokens within the same block.
65+
/// @dev The Rollup STF MUST NOT apply `swap` transactions to the rollup state
66+
/// UNLESS a sufficient SwapFulfilled event is emitted on the target chain within the same block.
67+
/// @param targetChainId - The chain on which tokens should be output.
68+
/// @param tokenIn - The address of the token the user supplies as the input on the rollup for the trade.
69+
/// @param tokenOut - The address of the token the user expects to receive on the target chain.
70+
/// @param recipient - The address of the recipient of tokenOut on the target chain.
71+
/// @param deadline - The deadline by which the swap order must be fulfilled.
72+
/// @param amountIn - The amount of tokenIn the user supplies as the input on the rollup for the trade.
73+
/// @param amountOut - The minimum amount of tokenOut the user expects to receive on the target chain.
74+
/// @custom:reverts Expired if the deadline has passed.
75+
/// @custom:emits Swap if the swap transaction succeeds.
76+
function swap(
77+
uint256 targetChainId,
78+
address tokenIn,
79+
address tokenOut,
80+
address recipient,
81+
uint256 deadline,
82+
uint256 amountIn,
83+
uint256 amountOut
84+
) external payable {
85+
// check that the deadline hasn't passed
86+
if (block.timestamp >= deadline) revert OrderExpired();
87+
88+
if (tokenIn == address(0)) {
89+
require(amountIn == msg.value);
90+
} else {
91+
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
92+
}
93+
94+
// emit the swap event
95+
emit Swap(targetChainId, tokenIn, tokenOut, recipient, deadline, amountIn, amountOut);
96+
}
97+
98+
/// @notice Transfer the entire balance of ERC20 tokens to the recipient.
99+
/// @dev Called by the Builder within the same block as users' `swap` transactions
100+
/// to claim the amounts of `tokenIn`.
101+
/// @dev Builder MUST ensure that no other account calls `sweep` before them.
102+
/// @param token - The token to transfer.
103+
/// @param recipient - The address to receive the tokens.
104+
function sweep(address token, address recipient) public {
105+
uint256 balance;
106+
if (token == address(0)) {
107+
balance = address(this).balance;
108+
payable(recipient).transfer(balance);
109+
} else {
110+
balance = IERC20(token).balanceOf(address(this));
111+
IERC20(token).transfer(recipient, balance);
112+
}
113+
emit Sweep(token, recipient, balance);
114+
}
115+
}
116+
117+
// TODO: i don't like this. i will do something else.
118+
contract HostMarket is OrderFulfiller {}
119+
120+
contract RollupMarket is OrderInitiator, OrderFulfiller {}

src/Passage.sol

Lines changed: 49 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,9 @@ pragma solidity ^0.8.24;
33

44
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
55

6-
contract BasePassage {
7-
/// @notice Emitted when an swap order is fulfilled by the Builder.
8-
/// @param originChainId - The chainId on which the swap order was submitted.
9-
/// @param token - The address of the token transferred to the recipient. address(0) corresponds to native Ether.
10-
/// @param recipient - The recipient of the token.
11-
/// @param amount - The amount of the token transferred to the recipient.
12-
event SwapFulfilled(
13-
uint256 indexed originChainId, address indexed token, address indexed recipient, uint256 amount
14-
);
15-
16-
/// @notice Fulfill a rollup Swap order.
17-
/// The user calls `swap` on a rollup; the Builder calls `fulfillSwap` on the target chain.
18-
/// @custom:emits SwapFulfilled
19-
/// @param originChainId - The chainId of the rollup on which `swap` was called.
20-
/// @param token - The address of the token to be transferred to the recipient.
21-
/// address(0) corresponds to native Ether.
22-
/// @param recipient - The recipient of the token.
23-
/// @param amount - The amount of the token to be transferred to the recipient.
24-
function fulfillSwap(uint256 originChainId, address token, address recipient, uint256 amount) external payable {
25-
if (token == address(0)) {
26-
require(amount == msg.value);
27-
payable(recipient).transfer(msg.value);
28-
} else {
29-
IERC20(token).transferFrom(msg.sender, recipient, amount);
30-
}
31-
emit SwapFulfilled(originChainId, token, recipient, amount);
32-
}
33-
}
34-
356
/// @notice A contract deployed to Host chain that allows tokens to enter the rollup,
367
/// and enables Builders to fulfill requests to exchange tokens on the Rollup for tokens on the Host.
37-
contract Passage is BasePassage {
8+
contract Passage {
389
/// @notice The chainId of rollup that Ether will be sent to by default when entering the rollup via fallback() or receive().
3910
uint256 public immutable defaultRollupChainId;
4011

@@ -71,7 +42,6 @@ contract Passage is BasePassage {
7142
}
7243

7344
/// @notice Allows native Ether to enter the rollup.
74-
/// @dev Permanently burns the entire msg.value by locking it in this contract.
7545
/// @param rollupChainId - The rollup chain to enter.
7646
/// @param rollupRecipient - The recipient of the Ether on the rollup.
7747
/// @custom:emits Enter indicating the amount of Ether to mint on the rollup & its recipient.
@@ -80,7 +50,6 @@ contract Passage is BasePassage {
8050
}
8151

8252
/// @notice Allows ERC20s to enter the rollup.
83-
/// @dev Permanently burns the token amount by locking it in this contract.
8453
/// @param rollupChainId - The rollup chain to enter.
8554
/// @param rollupRecipient - The recipient of the Ether on the rollup.
8655
/// @param token - The address of the ERC20 token on the Host.
@@ -104,83 +73,56 @@ contract Passage is BasePassage {
10473
}
10574
}
10675

107-
/// @notice A contract deployed to the Rollup that allows users to atomically exchange tokens on the Rollup for tokens on the Host.
108-
contract RollupPassage is BasePassage {
109-
/// @notice Thrown when an swap transaction is submitted with a deadline that has passed.
110-
error OrderExpired();
111-
112-
/// @notice Emitted when an swap order is successfully processed, indicating it was also fulfilled on the target chain.
113-
/// @dev See `swap` for parameter docs.
114-
event Swap(
115-
uint256 indexed targetChainId,
116-
address indexed tokenIn,
117-
address indexed tokenOut,
118-
address recipient,
119-
uint256 deadline,
120-
uint256 amountIn,
121-
uint256 amountOut
122-
);
123-
124-
/// @notice Emitted when tokens or native Ether is swept from the contract.
125-
/// @dev Intended to improve visibility for Builders to ensure Sweep isn't called unexpectedly.
126-
/// Intentionally does not bother to emit which token(s) were swept, nor their amounts.
127-
event Sweep(address indexed token, address indexed recipient, uint256 amount);
128-
129-
/// @notice Request to swap ERC20s.
130-
/// @dev tokenIn is provided on the rollup; in exchange,
131-
/// tokenOut is expected to be received on targetChainId.
132-
/// @dev targetChainId may be the current chainId, the Host chainId, or..
133-
/// @dev Fees paid to the Builders for fulfilling the swap orders
134-
/// can be included within the "exchange rate" between tokenIn and tokenOut.
135-
/// @dev The Builder claims the tokenIn from the contract by submitting a transaction to `sweep` the tokens within the same block.
136-
/// @dev The Rollup STF MUST NOT apply `swap` transactions to the rollup state
137-
/// UNLESS a sufficient SwapFulfilled event is emitted on the target chain within the same block.
138-
/// @param targetChainId - The chain on which tokens should be output.
139-
/// @param tokenIn - The address of the token the user supplies as the input on the rollup for the trade.
140-
/// @param tokenOut - The address of the token the user expects to receive on the target chain.
141-
/// @param recipient - The address of the recipient of tokenOut on the target chain.
142-
/// @param deadline - The deadline by which the swap order must be fulfilled.
143-
/// @param amountIn - The amount of tokenIn the user supplies as the input on the rollup for the trade.
144-
/// @param amountOut - The minimum amount of tokenOut the user expects to receive on the target chain.
145-
/// @custom:reverts Expired if the deadline has passed.
146-
/// @custom:emits Swap if the swap transaction succeeds.
147-
function swap(
148-
uint256 targetChainId,
149-
address tokenIn,
150-
address tokenOut,
151-
address recipient,
152-
uint256 deadline,
153-
uint256 amountIn,
154-
uint256 amountOut
155-
) external payable {
156-
// check that the deadline hasn't passed
157-
if (block.timestamp >= deadline) revert OrderExpired();
158-
159-
if (tokenIn == address(0)) {
160-
require(amountIn == msg.value);
161-
} else {
162-
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
163-
}
76+
/// @notice A contract deployed to the Rollup that allows users to remove tokens from the Rollup TVL back to the Host.
77+
contract RollupPassage {
78+
address public immutable hostPassage;
79+
80+
/// @notice Thrown when attempting to mint ERC20s if not the host passage contract.
81+
error OnlyHostPassage();
82+
83+
/// @notice Emitted when tokens exit the rollup.
84+
/// @param token - The address of the token exiting the rollup.
85+
/// @param recipient - The desired recipient of the token on the host chain.
86+
/// @param amount - The amount of the token entering the rollup.
87+
event Exit(address indexed token, address indexed recipient, uint256 amount);
88+
89+
/// @notice Emitted when ERC20 tokens are minted on the rollup.
90+
/// @param token - The address of the ERC20 token entering the rollup.
91+
/// @param rollupRecipient - The recipient of the ERC20 token on the rollup.
92+
/// @param amount - The amount of the ERC20 token entering the rollup.
93+
event Enter(address indexed token, address indexed rollupRecipient, uint256 amount);
16494

165-
// emit the swap event
166-
emit Swap(targetChainId, tokenIn, tokenOut, recipient, deadline, amountIn, amountOut);
95+
constructor(address _hostPassage) {
96+
hostPassage = _hostPassage;
16797
}
16898

169-
/// @notice Transfer the entire balance of ERC20 tokens to the recipient.
170-
/// @dev Called by the Builder within the same block as users' `swap` transactions
171-
/// to claim the amounts of `tokenIn`.
172-
/// @dev Builder MUST ensure that no other account calls `sweep` before them.
173-
/// @param token - The token to transfer.
174-
/// @param recipient - The address to receive the tokens.
175-
function sweep(address token, address recipient) public {
176-
uint256 balance;
177-
if (token == address(0)) {
178-
balance = address(this).balance;
179-
payable(recipient).transfer(balance);
180-
} else {
181-
balance = IERC20(token).balanceOf(address(this));
182-
IERC20(token).transfer(recipient, balance);
183-
}
184-
emit Sweep(token, recipient, balance);
99+
/// @notice Allows native Ether to exit the rollup.
100+
/// @dev Rollup node will burn the msg.value.
101+
/// @param recipient - The desired recipient of the Ether on the host chain.
102+
/// @custom:emits Exit indicating the amount of Ether to burn on the rollup & the recipient on the host chain.
103+
function exit(address recipient) public payable {
104+
emit Exit(address(0), recipient, msg.value);
105+
}
106+
107+
/// @notice Allows ERC20s to exit the rollup.
108+
/// @param recipient - The desired recipient of the ERC20s on the host chain.
109+
/// @param token - The address of the ERC20 token on the Rollup.
110+
/// @param amount - The amount of the ERC20 token to burn on the Rollup.
111+
/// @custom:emits Exit indicating the the desired recipient on the host chain.
112+
function exit(address token, address recipient, uint256 amount) external payable {
113+
IERC20(token).transferFrom(msg.sender, address(this), amount);
114+
// TODO: IERC20(token).burn(msg.sender, amount);
115+
emit Exit(token, recipient, amount);
116+
}
117+
118+
/// @notice Allows ERC20s to enter the rollup from L1.
119+
/// @param token - The address of the L1 ERC20 token to mint a representation for.
120+
/// @param rollupRecipient - The recipient of the ERC20 tokens on the rollup, specified by the sender on L1.
121+
/// @param amount - The amount of the ERC20 token to mint on the Rollup, corresponding to the amount locked on L1.
122+
/// @custom:emits Exit indicating the the desired recipient on the host chain.
123+
function enter(address token, address rollupRecipient, uint256 amount) external {
124+
if (msg.sender != hostPassage) revert OnlyHostPassage();
125+
// TODO: IERC20(token).mint(recipient, amount);
126+
emit Enter(token, rollupRecipient, amount);
185127
}
186128
}

0 commit comments

Comments
 (0)