@@ -12,53 +12,25 @@ contract Passage {
1212 /// @notice The address that is allowed to withdraw funds from the contract.
1313 address public immutable withdrawalAdmin;
1414
15- /// @notice Thrown when attempting to fulfill an exit order with a deadline that has passed.
16- error OrderExpired ();
17-
1815 /// @notice Thrown when attempting to withdraw funds if not withdrawal admin.
1916 error OnlyWithdrawalAdmin ();
2017
2118 /// @notice Emitted when tokens enter the rollup.
2219 /// @param token - The address of the token entering the rollup.
2320 /// @param rollupRecipient - The recipient of the token on the rollup.
2421 /// @param amount - The amount of the token entering the rollup.
25- event Enter (uint256 rollupChainId , address indexed token , address indexed rollupRecipient , uint256 amount );
22+ event Enter (uint256 indexed rollupChainId , address indexed token , address indexed rollupRecipient , uint256 amount );
23+
24+ /// @notice Emitted when the admin withdraws tokens from the contract.
25+ event Withdrawal (address indexed token , address indexed recipient , uint256 amount );
2626
2727 /// @notice Emitted when an exit order is fulfilled by the Builder.
28- /// @param token - The address of the token transferred to the recipient.
28+ /// @param token - The address of the token transferred to the recipient. address(0) corresponds to native host Ether.
2929 /// @param hostRecipient - The recipient of the token on host.
3030 /// @param amount - The amount of the token transferred to the recipient.
31- event ExitFilled (uint256 rollupChainId , address indexed token , address indexed hostRecipient , uint256 amount );
32-
33- /// @notice Emitted when the admin withdraws tokens from the contract.
34- event Withdraw (Withdrawal withdrawal );
35-
36- /// @notice A bundled withdrawal of Ether and ERC20 tokens.
37- /// @param recipient - The address to receive the Ether and ERC20 tokens.
38- /// @param ethAmount - The amount of Ether to transfer to the recipient. Zero if no Ether to transfer.
39- /// @param tokens - The addresses of the ERC20 tokens to transfer to the recipient.
40- /// @param tokenAmounts - The amounts of the ERC20 tokens to transfer to the recipient.
41- struct Withdrawal {
42- address recipient;
43- uint256 ethAmount;
44- address [] tokens;
45- uint256 [] tokenAmounts;
46- }
47-
48- /// @notice Details of an exit order to be fulfilled by the Builder.
49- /// @param token - The address of the token to be transferred to the recipient.
50- /// If token is the zero address, the amount is native Ether.
51- /// Corresponds to tokenOut_H in the RollupPassage contract.
52- /// @param recipient - The recipient of the token on host.
53- /// Corresponds to recipient_H in the RollupPassage contract.
54- /// @param amount - The amount of the token to be transferred to the recipient.
55- /// Corresponds to one or more amountOutMinimum_H in the RollupPassage contract.
56- struct ExitOrder {
57- uint256 rollupChainId;
58- address token;
59- address recipient;
60- uint256 amount;
61- }
31+ event ExitFulfilled (
32+ uint256 indexed rollupChainId , address indexed token , address indexed hostRecipient , uint256 amount
33+ );
6234
6335 /// @param _defaultRollupChainId - the chainId of the rollup that Ether will be sent to by default
6436 /// when entering the rollup via fallback() or receive() fns.
@@ -93,64 +65,42 @@ contract Passage {
9365 /// @param token - The address of the ERC20 token on the Host.
9466 /// @param amount - The amount of the ERC20 token to transfer to the rollup.
9567 /// @custom:emits Enter indicating the amount of tokens to mint on the rollup & its recipient.
96- function enter (uint256 rollupChainId , address rollupRecipient , address token , uint256 amount ) public payable {
68+ function enter (uint256 rollupChainId , address token , address rollupRecipient , uint256 amount ) external payable {
9769 IERC20 (token).transferFrom (msg .sender , address (this ), amount);
9870 emit Enter (rollupChainId, token, rollupRecipient, amount);
9971 }
10072
101- /// @notice Fulfills exit orders by transferring tokenOut to the recipient
102- /// @param orders The exit orders to fulfill
103- /// @custom:emits ExitFilled for each exit order fulfilled.
104- /// @dev Builder SHOULD call `fulfillExits` atomically with `submitBlock`.
105- /// Builder SHOULD set a block expiration time that is AT MOST the minimum of all exit order deadlines;
106- /// this way, `fulfillExits` + `submitBlock` will revert atomically on mainnet if any exit orders have expired.
107- /// Otherwise, `fulfillExits` may mine on mainnet, while `submitExit` reverts on the rollup,
108- /// and the Builder can't collect the corresponding value on the rollup.
109- /// @dev Called by the Builder atomically with a transaction calling `submitBlock`.
110- /// The user-submitted transactions initiating the ExitOrders on the rollup
111- /// must be included by the Builder in the rollup block submitted via `submitBlock`.
112- /// @dev The user transfers tokenIn on the rollup, and receives tokenOut on host.
113- /// @dev The Builder receives tokenIn on the rollup, and transfers tokenOut to the user on host.
114- /// @dev The rollup STF MUST NOT apply `submitExit` transactions to the rollup state
115- /// UNLESS a corresponding ExitFilled event is emitted on host in the same block.
116- /// @dev If the user submits multiple exit transactions for the same token in the same rollup block,
117- /// the Builder may transfer the cumulative tokenOut to the user in a single ExitFilled event.
118- /// The rollup STF will apply the user's exit transactions on the rollup up to the point that sum(tokenOut) is lte the ExitFilled amount.
119- /// TODO: add option to fulfill ExitOrders with native ETH? or is it sufficient to only allow users to exit via WETH?
120- function fulfillExits (ExitOrder[] calldata orders ) external payable {
121- uint256 ethRemaining = msg .value ;
122- for (uint256 i = 0 ; i < orders.length ; i++ ) {
123- // transfer value
124- if (orders[i].token == address (0 )) {
125- // transfer native Ether to the recipient
126- payable (orders[i].recipient).transfer (orders[i].amount);
127- // NOTE: this will underflow if sender attempts to transfer more Ether than they sent to the contract
128- ethRemaining -= orders[i].amount;
129- } else {
130- // transfer tokens to the recipient
131- IERC20 (orders[i].token).transferFrom (msg .sender , orders[i].recipient, orders[i].amount);
132- }
133- // emit
134- emit ExitFilled (orders[i].rollupChainId, orders[i].token, orders[i].recipient, orders[i].amount);
73+ /// @notice Allows the admin to withdraw ETH or ERC20 tokens from the contract.
74+ /// @dev Only the admin can call this function.
75+ function withdraw (address token , address recipient , uint256 amount ) external {
76+ if (msg .sender != withdrawalAdmin) revert OnlyWithdrawalAdmin ();
77+ if (token == address (0 )) {
78+ payable (recipient).transfer (amount);
79+ } else {
80+ IERC20 (token).transfer (recipient, amount);
13581 }
82+ emit Withdrawal (token, recipient, amount);
13683 }
13784
138- /// @notice Allows the admin to withdraw tokens from the contract.
139- /// @dev Only the admin can call this function.
140- /// @param withdrawals - The withdrawals to process. See Withdrawal struct docs for details.
141- function withdraw (Withdrawal[] calldata withdrawals ) external {
142- if (msg .sender != withdrawalAdmin) revert OnlyWithdrawalAdmin ();
143- for (uint256 i = 0 ; i < withdrawals.length ; i++ ) {
144- // transfer ether
145- if (withdrawals[i].ethAmount > 0 ) {
146- payable (withdrawals[i].recipient).transfer (withdrawals[i].ethAmount);
147- }
148- // transfer ERC20 tokens
149- for (uint256 j = 0 ; j < withdrawals[i].tokens.length ; j++ ) {
150- IERC20 (withdrawals[i].tokens[j]).transfer (withdrawals[i].recipient, withdrawals[i].tokenAmounts[j]);
151- }
152- emit Withdraw (withdrawals[i]);
85+ /// @notice Fulfill a rollup Exit order.
86+ /// The user calls `exit` on Rollup; the Builder calls `fulfillExit` on Host.
87+ /// @custom:emits ExitFilled
88+ /// @param rollupChainId - The chainId of the rollup on which the `submitExit` was called.
89+ /// @param token - The address of the token to be transferred to the recipient.
90+ /// If token is the zero address, the amount is native Ether.
91+ /// Corresponds to tokenOut_H in the RollupPassage contract.
92+ /// @param recipient - The recipient of the token on host.
93+ /// Corresponds to recipient_H in the RollupPassage contract.
94+ /// @param amount - The amount of the token to be transferred to the recipient.
95+ /// Corresponds to one or more amountOutMinimum_H in the RollupPassage contract.
96+ function fulfillExit (uint256 rollupChainId , address token , address recipient , uint256 amount ) external payable {
97+ if (token == address (0 )) {
98+ require (amount == msg .value );
99+ payable (recipient).transfer (msg .value );
100+ } else {
101+ IERC20 (token).transferFrom (msg .sender , recipient, amount);
153102 }
103+ emit ExitFulfilled (rollupChainId, token, recipient, amount);
154104 }
155105}
156106
@@ -159,8 +109,8 @@ contract RollupPassage {
159109 /// @notice Thrown when an exit transaction is submitted with a deadline that has passed.
160110 error OrderExpired ();
161111
162- /// @notice Emitted when an exit order is submitted & successfully processed, indicating it was also fulfilled on host.
163- /// @dev See `submitExit ` for parameter docs.
112+ /// @notice Emitted when an exit order is successfully processed, indicating it was also fulfilled on host.
113+ /// @dev See `exit ` for parameter docs.
164114 event Exit (
165115 address indexed tokenIn_RU ,
166116 address indexed tokenOut_H ,
@@ -173,9 +123,9 @@ contract RollupPassage {
173123 /// @notice Emitted when tokens or native Ether is swept from the contract.
174124 /// @dev Intended to improve visibility for Builders to ensure Sweep isn't called unexpectedly.
175125 /// Intentionally does not bother to emit which token(s) were swept, nor their amounts.
176- event Sweep (address indexed recipient );
126+ event Sweep (address indexed token , address indexed recipient , uint256 amount );
177127
178- /// @notice Expresses an intent to exit the rollup with ERC20s.
128+ /// @notice Request to exit the rollup with ERC20s.
179129 /// @dev Exits are modeled as a swap between two tokens.
180130 /// tokenIn_RU is provided on the rollup; in exchange,
181131 /// tokenOut_H is expected to be received on host.
@@ -195,65 +145,42 @@ contract RollupPassage {
195145 /// @param amountOutMinimum_H - The minimum amount of tokenOut_H the user expects to receive on host.
196146 /// @custom:reverts Expired if the deadline has passed.
197147 /// @custom:emits Exit if the exit transaction succeeds.
198- function submitExit (
148+ function exit (
199149 address tokenIn_RU ,
200150 address tokenOut_H ,
201151 address recipient_H ,
202152 uint256 deadline ,
203153 uint256 amountIn_RU ,
204154 uint256 amountOutMinimum_H
205- ) external {
155+ ) external payable {
206156 // check that the deadline hasn't passed
207157 if (block .timestamp >= deadline) revert OrderExpired ();
208158
209- // transfer the tokens from the user to the contract
210- IERC20 (tokenIn_RU).transferFrom (msg .sender , address (this ), amountIn_RU);
159+ if (tokenIn_RU == address (0 )) {
160+ require (amountIn_RU == msg .value );
161+ } else {
162+ IERC20 (tokenIn_RU).transferFrom (msg .sender , address (this ), amountIn_RU);
163+ }
211164
212165 // emit the exit event
213166 emit Exit (tokenIn_RU, tokenOut_H, recipient_H, deadline, amountIn_RU, amountOutMinimum_H);
214167 }
215168
216- /// @notice Expresses an intent to exit the rollup with native Ether.
217- /// @dev See `submitExit` above for dev details on how exits work.
218- /// @dev tokenIn_RU is set to address(0), native rollup Ether.
219- /// amountIn_RU is set to msg.value.
220- /// @param tokenOut_H - The address of the token the user expects to receive on host.
221- /// @param recipient_H - The address of the recipient of tokenOut_H on host.
222- /// @param deadline - The deadline by which the exit order must be fulfilled.
223- /// @param amountOutMinimum_H - The minimum amount of tokenOut_H the user expects to receive on host.
224- /// @custom:reverts Expired if the deadline has passed.
225- /// @custom:emits Exit if the exit transaction succeeds.
226- function submitEthExit (address tokenOut_H , address recipient_H , uint256 deadline , uint256 amountOutMinimum_H )
227- external
228- payable
229- {
230- // check that the deadline hasn't passed
231- if (block .timestamp >= deadline) revert OrderExpired ();
232-
233- // emit the exit event
234- emit Exit (address (0 ), tokenOut_H, recipient_H, deadline, msg .value , amountOutMinimum_H);
235- }
236-
237- /// @notice Transfer the entire balance of tokens to the recipient.
238- /// @dev Called by the Builder within the same block as `submitExit` transactions to claim the amounts of `tokenIn`.
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`.
239172 /// @dev Builder MUST ensure that no other account calls `sweep` before them.
173+ /// @param token - The token to transfer.
240174 /// @param recipient - The address to receive the tokens.
241- /// @param tokens - The addresses of the tokens to transfer.
242- /// TODO: should there be more granular control for the builder to specify a different recipient for each token?
243- function sweep (address recipient , address [] calldata tokens ) public {
244- for (uint256 i = 0 ; i < tokens.length ; i++ ) {
245- IERC20 token = IERC20 (tokens[i]);
246- token.transfer (recipient, token.balanceOf (address (this )));
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);
247183 }
248- emit Sweep (recipient);
249- }
250-
251- /// @notice Transfer the entire balance of native Ether to the recipient.
252- /// @dev Called by the Builder within the same block as `submitExit` transactions to claim the amounts of native Ether.
253- /// @dev Builder MUST ensure that no other account calls `sweepETH` before them.
254- /// @param recipient - The address to receive the native Ether.
255- function sweepEth (address payable recipient ) public {
256- recipient.transfer (address (this ).balance);
257- emit Sweep (recipient);
184+ emit Sweep (token, recipient, balance);
258185 }
259186}
0 commit comments