Skip to content

Commit 5e32da8

Browse files
authored
feat: revise Order event interface (#49)
* feat: revise Order event interface * move structs into contract * rename: OutputFilled * fix: typo: initiate * change Input/Output params to memory to make it easier for smart contracts to integrate * fix: sweep only callable by block builder * add originChainId back
1 parent f4b5429 commit 5e32da8

File tree

1 file changed

+83
-66
lines changed

1 file changed

+83
-66
lines changed

src/Orders.sol

Lines changed: 83 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -5,103 +5,120 @@ import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
55

66
/// @notice Contract capable of processing fulfillment of intent-based Orders.
77
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.
1111
/// @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.
1213
/// @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.
2121
/// @param token - The address of the token to be transferred to the recipient.
2222
/// address(0) corresponds to native Ether.
23-
/// @param recipient - The recipient of the token.
2423
/// @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 {
2625
if (token == address(0)) {
2726
require(amount == msg.value);
2827
payable(recipient).transfer(msg.value);
2928
} else {
3029
IERC20(token).transferFrom(msg.sender, recipient, amount);
3130
}
32-
emit SwapFulfilled(originChainId, token, recipient, amount);
31+
emit OutputFilled(originChainId, recipient, token, amount);
3332
}
3433
}
3534

3635
/// @notice Contract capable of registering initiation of intent-based Orders.
3736
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.
3961
error OrderExpired();
4062

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);
5268

5369
/// @notice Emitted when tokens or native Ether is swept from the contract.
5470
/// @dev Intended to improve visibility for Builders to ensure Sweep isn't called unexpectedly.
5571
/// 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);
5773

5874
/// @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 {
8588
// check that the deadline hasn't passed
8689
if (block.timestamp >= deadline) revert OrderExpired();
8790

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+
}
9397

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+
}
96109
}
97110

98111
/// @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).
103115
/// @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
105122
uint256 balance;
106123
if (token == address(0)) {
107124
balance = address(this).balance;
@@ -110,7 +127,7 @@ abstract contract OrderOrigin {
110127
balance = IERC20(token).balanceOf(address(this));
111128
IERC20(token).transfer(recipient, balance);
112129
}
113-
emit Sweep(token, recipient, balance);
130+
emit Sweep(recipient, token, balance);
114131
}
115132
}
116133

0 commit comments

Comments
 (0)