Skip to content

Commit cf9d430

Browse files
committed
refactor: remove loops
1 parent d89de06 commit cf9d430

File tree

1 file changed

+66
-108
lines changed

1 file changed

+66
-108
lines changed

src/Passage.sol

Lines changed: 66 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -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,51 @@ 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);
135-
}
73+
/// @notice Allows the admin to withdraw ETH from the contract.
74+
/// @dev Only the admin can call this function.
75+
function withdrawEth(address recipient, uint256 amount) external {
76+
if (msg.sender != withdrawalAdmin) revert OnlyWithdrawalAdmin();
77+
payable(recipient).transfer(amount);
78+
emit Withdrawal(address(0), recipient, amount);
13679
}
13780

138-
/// @notice Allows the admin to withdraw tokens from the contract.
81+
/// @notice Allows the admin to withdraw ERC20 tokens from the contract.
13982
/// @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 {
83+
function withdraw(address token, address recipient, uint256 amount) external {
14284
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]);
153-
}
85+
IERC20(token).transfer(recipient, amount);
86+
emit Withdrawal(token, recipient, amount);
87+
}
88+
89+
/// @notice Fulfill a rollup Exit order.
90+
/// The user calls `exit` on Rollup; the Builder calls `fulfillExit` on Host.
91+
/// @custom:emits ExitFilled
92+
/// @param rollupChainId - The chainId of the rollup on which the `submitExit` was called.
93+
/// @param token - The address of the token to be transferred to the recipient.
94+
/// If token is the zero address, the amount is native Ether.
95+
/// Corresponds to tokenOut_H in the RollupPassage contract.
96+
/// @param recipient - The recipient of the token on host.
97+
/// Corresponds to recipient_H in the RollupPassage contract.
98+
/// @param amount - The amount of the token to be transferred to the recipient.
99+
/// Corresponds to one or more amountOutMinimum_H in the RollupPassage contract.
100+
function fulfillExit(uint256 rollupChainId, address token, address recipient, uint256 amount) external {
101+
IERC20(token).transferFrom(msg.sender, recipient, amount);
102+
emit ExitFulfilled(rollupChainId, token, recipient, amount);
103+
}
104+
105+
/// @notice Fulfill a rollup Exit order
106+
/// The user calls `exit` on Rollup; the Builder calls `fulfillExit` on Host.
107+
/// @custom:emits ExitFilled
108+
/// @param recipient - The recipient of the token on host.
109+
/// Corresponds to recipient_H in the RollupPassage contract.
110+
function fulfillExitEth(uint256 rollupChainId, address recipient) external payable {
111+
payable(recipient).transfer(msg.value);
112+
emit ExitFulfilled(rollupChainId, address(0), recipient, msg.value);
154113
}
155114
}
156115

@@ -159,8 +118,8 @@ contract RollupPassage {
159118
/// @notice Thrown when an exit transaction is submitted with a deadline that has passed.
160119
error OrderExpired();
161120

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.
121+
/// @notice Emitted when an exit order is successfully processed, indicating it was also fulfilled on host.
122+
/// @dev See `exit` for parameter docs.
164123
event Exit(
165124
address indexed tokenIn_RU,
166125
address indexed tokenOut_H,
@@ -173,9 +132,9 @@ contract RollupPassage {
173132
/// @notice Emitted when tokens or native Ether is swept from the contract.
174133
/// @dev Intended to improve visibility for Builders to ensure Sweep isn't called unexpectedly.
175134
/// Intentionally does not bother to emit which token(s) were swept, nor their amounts.
176-
event Sweep(address indexed recipient);
135+
event Sweep(address indexed token, address indexed recipient, uint256 amount);
177136

178-
/// @notice Expresses an intent to exit the rollup with ERC20s.
137+
/// @notice Request to exit the rollup with ERC20s.
179138
/// @dev Exits are modeled as a swap between two tokens.
180139
/// tokenIn_RU is provided on the rollup; in exchange,
181140
/// tokenOut_H is expected to be received on host.
@@ -195,7 +154,7 @@ contract RollupPassage {
195154
/// @param amountOutMinimum_H - The minimum amount of tokenOut_H the user expects to receive on host.
196155
/// @custom:reverts Expired if the deadline has passed.
197156
/// @custom:emits Exit if the exit transaction succeeds.
198-
function submitExit(
157+
function exit(
199158
address tokenIn_RU,
200159
address tokenOut_H,
201160
address recipient_H,
@@ -206,15 +165,14 @@ contract RollupPassage {
206165
// check that the deadline hasn't passed
207166
if (block.timestamp >= deadline) revert OrderExpired();
208167

209-
// transfer the tokens from the user to the contract
210168
IERC20(tokenIn_RU).transferFrom(msg.sender, address(this), amountIn_RU);
211169

212170
// emit the exit event
213171
emit Exit(tokenIn_RU, tokenOut_H, recipient_H, deadline, amountIn_RU, amountOutMinimum_H);
214172
}
215173

216-
/// @notice Expresses an intent to exit the rollup with native Ether.
217-
/// @dev See `submitExit` above for dev details on how exits work.
174+
/// @notice Request exit the rollup with native Ether.
175+
/// @dev See `exit` docs above for dev details on exits.
218176
/// @dev tokenIn_RU is set to address(0), native rollup Ether.
219177
/// amountIn_RU is set to msg.value.
220178
/// @param tokenOut_H - The address of the token the user expects to receive on host.
@@ -223,7 +181,7 @@ contract RollupPassage {
223181
/// @param amountOutMinimum_H - The minimum amount of tokenOut_H the user expects to receive on host.
224182
/// @custom:reverts Expired if the deadline has passed.
225183
/// @custom:emits Exit if the exit transaction succeeds.
226-
function submitEthExit(address tokenOut_H, address recipient_H, uint256 deadline, uint256 amountOutMinimum_H)
184+
function exitEth(address tokenOut_H, address recipient_H, uint256 deadline, uint256 amountOutMinimum_H)
227185
external
228186
payable
229187
{
@@ -234,26 +192,26 @@ contract RollupPassage {
234192
emit Exit(address(0), tokenOut_H, recipient_H, deadline, msg.value, amountOutMinimum_H);
235193
}
236194

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`.
195+
/// @notice Transfer the entire balance of ERC20 tokens to the recipient.
196+
/// @dev Called by the Builder within the same block as users' `exit` transactions
197+
/// to claim the amounts of `tokenIn`.
239198
/// @dev Builder MUST ensure that no other account calls `sweep` before them.
199+
/// @param token - The token to transfer.
240200
/// @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)));
247-
}
248-
emit Sweep(recipient);
201+
function sweep(address token, address recipient) public {
202+
uint256 balance = IERC20(token).balanceOf(address(this));
203+
IERC20(token).transfer(recipient, balance);
204+
emit Sweep(token, recipient, balance);
249205
}
250206

251207
/// @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.
208+
/// @dev Called by the Builder within the same block as users' `exit` transactions
209+
/// to claim the amounts of native Ether.
253210
/// @dev Builder MUST ensure that no other account calls `sweepETH` before them.
254211
/// @param recipient - The address to receive the native Ether.
255212
function sweepEth(address payable recipient) public {
256-
recipient.transfer(address(this).balance);
257-
emit Sweep(recipient);
213+
uint256 balance = address(this).balance;
214+
recipient.transfer(balance);
215+
emit Sweep(address(0), recipient, balance);
258216
}
259217
}

0 commit comments

Comments
 (0)