@@ -5,103 +5,120 @@ import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
5
5
6
6
/// @notice Contract capable of processing fulfillment of intent-based Orders.
7
7
abstract contract OrderDestination {
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 .
8
+ /// @notice Emitted when an Order's Output is sent to the recipient .
9
+ /// @dev There may be multiple Outputs per Order .
10
+ /// @param originChainId - The chainId on which the Order was initiated .
11
11
/// @param recipient - The recipient of the token.
12
+ /// @param token - The address of the token transferred to the recipient. address(0) corresponds to native Ether.
12
13
/// @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.
14
+ event OutputFilled (uint256 indexed originChainId , address indexed recipient , address indexed token , uint256 amount );
15
+
16
+ /// @notice Send the Output(s) of an Order to fulfill it.
17
+ /// The user calls `initiate` on a rollup; the Builder calls `fill` on the target chain for each Output.
18
+ /// @custom:emits OutputFilled
19
+ /// @param originChainId - The chainId on which the Order was initiated.
20
+ /// @param recipient - The recipient of the token.
21
21
/// @param token - The address of the token to be transferred to the recipient.
22
22
/// address(0) corresponds to native Ether.
23
- /// @param recipient - The recipient of the token.
24
23
/// @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 {
24
+ function fill (uint256 originChainId , address recipient , address token , uint256 amount ) external payable {
26
25
if (token == address (0 )) {
27
26
require (amount == msg .value );
28
27
payable (recipient).transfer (msg .value );
29
28
} else {
30
29
IERC20 (token).transferFrom (msg .sender , recipient, amount);
31
30
}
32
- emit SwapFulfilled (originChainId, token, recipient , amount);
31
+ emit OutputFilled (originChainId, recipient, token , amount);
33
32
}
34
33
}
35
34
36
35
/// @notice Contract capable of registering initiation of intent-based Orders.
37
36
abstract contract OrderOrigin {
38
- /// @notice Thrown when an swap transaction is submitted with a deadline that has passed.
37
+ /// @notice Tokens sent by the swapper as inputs to the order
38
+ /// @dev From ERC-7683
39
+ struct Input {
40
+ /// @dev The address of the ERC20 token on the origin chain
41
+ address token;
42
+ /// @dev The amount of the token to be sent
43
+ uint256 amount;
44
+ }
45
+
46
+ /// @notice Tokens that must be receive for a valid order fulfillment
47
+ /// @dev From ERC-7683
48
+ struct Output {
49
+ /// @dev The address of the ERC20 token on the destination chain
50
+ /// @dev address(0) used as a sentinel for the native token
51
+ address token;
52
+ /// @dev The amount of the token to be sent
53
+ uint256 amount;
54
+ /// @dev The address to receive the output tokens
55
+ address recipient;
56
+ /// @dev The destination chain for this output
57
+ uint32 chainId;
58
+ }
59
+
60
+ /// @notice Thrown when an Order is submitted with a deadline that has passed.
39
61
error OrderExpired ();
40
62
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
- );
63
+ /// @notice Thrown when trying to call `sweep` if not the Builder of the block.
64
+ error OnlyBuilder ();
65
+
66
+ /// @notice Emitted when an Order is submitted for fulfillment.
67
+ event Order (uint256 deadline , Input[] inputs , Output[] outputs );
52
68
53
69
/// @notice Emitted when tokens or native Ether is swept from the contract.
54
70
/// @dev Intended to improve visibility for Builders to ensure Sweep isn't called unexpectedly.
55
71
/// 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 );
72
+ event Sweep (address indexed recipient , address indexed token , uint256 amount );
57
73
58
74
/// @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 {
75
+ /// @dev inputs are provided on the rollup; in exchange,
76
+ /// outputs are expected to be received on the target chain(s).
77
+ /// @dev Fees paid to the Builders for fulfilling the Orders
78
+ /// can be included within the "exchange rate" between inputs and outputs.
79
+ /// @dev The Builder claims the inputs from the contract by submitting `sweep` transactions within the same block.
80
+ /// @dev The Rollup STF MUST NOT apply `initiate` transactions to the rollup state
81
+ /// UNLESS the outputs are delivered on the target chains within the same block.
82
+ /// @param deadline - The deadline by which the Order must be fulfilled.
83
+ /// @param inputs - The token amounts offered by the swapper in exchange for the outputs.
84
+ /// @param outputs - The token amounts that must be received on their target chain(s) in order for the Order to be executed.
85
+ /// @custom:reverts OrderExpired if the deadline has passed.
86
+ /// @custom:emits Order if the transaction mines.
87
+ function initiate (uint256 deadline , Input[] memory inputs , Output[] memory outputs ) external payable {
85
88
// check that the deadline hasn't passed
86
89
if (block .timestamp >= deadline) revert OrderExpired ();
87
90
88
- if (tokenIn == address (0 )) {
89
- require (amountIn == msg .value );
90
- } else {
91
- IERC20 (tokenIn).transferFrom (msg .sender , address (this ), amountIn);
92
- }
91
+ // transfer inputs to this contract
92
+ _transferInputs (inputs);
93
+
94
+ // emit
95
+ emit Order (deadline, inputs, outputs);
96
+ }
93
97
94
- // emit the swap event
95
- emit Swap (targetChainId, tokenIn, tokenOut, recipient, deadline, amountIn, amountOut);
98
+ /// @notice Transfer the Order inputs to this contract, where they can be collected by the Order filler.
99
+ function _transferInputs (Input[] memory inputs ) internal {
100
+ uint256 value = msg .value ;
101
+ for (uint256 i; i < inputs.length ; i++ ) {
102
+ if (inputs[i].token == address (0 )) {
103
+ // this line should underflow if there's an attempt to spend more ETH than is attached to the transaction
104
+ value -= inputs[i].amount;
105
+ } else {
106
+ IERC20 (inputs[i].token).transferFrom (msg .sender , address (this ), inputs[i].amount);
107
+ }
108
+ }
96
109
}
97
110
98
111
/// @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.
112
+ /// @dev Called by the Builder within the same block as users' `initiate` transactions
113
+ /// to claim the `inputs`.
114
+ /// @dev Builder MUST call `sweep` atomically with `fill` (claim Inputs atomically with sending Outputs).
103
115
/// @param recipient - The address to receive the tokens.
104
- function sweep (address token , address recipient ) public {
116
+ /// @param token - The token to transfer.
117
+ /// @custom:emits Sweep
118
+ /// @custom:reverts OnlyBuilder if called by non-block builder
119
+ function sweep (address recipient , address token ) public {
120
+ if (msg .sender != block .coinbase ) revert OnlyBuilder ();
121
+ // send ETH or tokens
105
122
uint256 balance;
106
123
if (token == address (0 )) {
107
124
balance = address (this ).balance;
@@ -110,7 +127,7 @@ abstract contract OrderOrigin {
110
127
balance = IERC20 (token).balanceOf (address (this ));
111
128
IERC20 (token).transfer (recipient, balance);
112
129
}
113
- emit Sweep (token, recipient , balance);
130
+ emit Sweep (recipient, token , balance);
114
131
}
115
132
}
116
133
0 commit comments