@@ -12,53 +12,25 @@ contract Passage {
12
12
/// @notice The address that is allowed to withdraw funds from the contract.
13
13
address public immutable withdrawalAdmin;
14
14
15
- /// @notice Thrown when attempting to fulfill an exit order with a deadline that has passed.
16
- error OrderExpired ();
17
-
18
15
/// @notice Thrown when attempting to withdraw funds if not withdrawal admin.
19
16
error OnlyWithdrawalAdmin ();
20
17
21
18
/// @notice Emitted when tokens enter the rollup.
22
19
/// @param token - The address of the token entering the rollup.
23
20
/// @param rollupRecipient - The recipient of the token on the rollup.
24
21
/// @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 );
26
26
27
27
/// @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.
29
29
/// @param hostRecipient - The recipient of the token on host.
30
30
/// @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
+ );
62
34
63
35
/// @param _defaultRollupChainId - the chainId of the rollup that Ether will be sent to by default
64
36
/// when entering the rollup via fallback() or receive() fns.
@@ -93,64 +65,42 @@ contract Passage {
93
65
/// @param token - The address of the ERC20 token on the Host.
94
66
/// @param amount - The amount of the ERC20 token to transfer to the rollup.
95
67
/// @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 {
97
69
IERC20 (token).transferFrom (msg .sender , address (this ), amount);
98
70
emit Enter (rollupChainId, token, rollupRecipient, amount);
99
71
}
100
72
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);
135
81
}
82
+ emit Withdrawal (token, recipient, amount);
136
83
}
137
84
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);
153
102
}
103
+ emit ExitFulfilled (rollupChainId, token, recipient, amount);
154
104
}
155
105
}
156
106
@@ -159,8 +109,8 @@ contract RollupPassage {
159
109
/// @notice Thrown when an exit transaction is submitted with a deadline that has passed.
160
110
error OrderExpired ();
161
111
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.
164
114
event Exit (
165
115
address indexed tokenIn_RU ,
166
116
address indexed tokenOut_H ,
@@ -173,9 +123,9 @@ contract RollupPassage {
173
123
/// @notice Emitted when tokens or native Ether is swept from the contract.
174
124
/// @dev Intended to improve visibility for Builders to ensure Sweep isn't called unexpectedly.
175
125
/// 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 );
177
127
178
- /// @notice Expresses an intent to exit the rollup with ERC20s.
128
+ /// @notice Request to exit the rollup with ERC20s.
179
129
/// @dev Exits are modeled as a swap between two tokens.
180
130
/// tokenIn_RU is provided on the rollup; in exchange,
181
131
/// tokenOut_H is expected to be received on host.
@@ -195,65 +145,42 @@ contract RollupPassage {
195
145
/// @param amountOutMinimum_H - The minimum amount of tokenOut_H the user expects to receive on host.
196
146
/// @custom:reverts Expired if the deadline has passed.
197
147
/// @custom:emits Exit if the exit transaction succeeds.
198
- function submitExit (
148
+ function exit (
199
149
address tokenIn_RU ,
200
150
address tokenOut_H ,
201
151
address recipient_H ,
202
152
uint256 deadline ,
203
153
uint256 amountIn_RU ,
204
154
uint256 amountOutMinimum_H
205
- ) external {
155
+ ) external payable {
206
156
// check that the deadline hasn't passed
207
157
if (block .timestamp >= deadline) revert OrderExpired ();
208
158
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
+ }
211
164
212
165
// emit the exit event
213
166
emit Exit (tokenIn_RU, tokenOut_H, recipient_H, deadline, amountIn_RU, amountOutMinimum_H);
214
167
}
215
168
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`.
239
172
/// @dev Builder MUST ensure that no other account calls `sweep` before them.
173
+ /// @param token - The token to transfer.
240
174
/// @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);
247
183
}
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);
258
185
}
259
186
}
0 commit comments