11// SPDX-License-Identifier: UNLICENSED
22pragma solidity ^ 0.8.24 ;
33
4+ import {OrdersPermit2, UsesPermit2} from "./permit2/UsesPermit2.sol " ;
5+ import {IOrders} from "./interfaces/IOrders.sol " ;
46import {IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol " ;
57
6- /// @notice Tokens sent by the swapper as inputs to the order
7- /// @dev From ERC-7683
8- struct Input {
9- /// @dev The address of the ERC20 token on the origin chain
10- address token;
11- /// @dev The amount of the token to be sent
12- uint256 amount;
13- }
14-
15- /// @notice Tokens that must be receive for a valid order fulfillment
16- /// @dev From ERC-7683
17- struct Output {
18- /// @dev The address of the ERC20 token on the destination chain
19- /// @dev address(0) used as a sentinel for the native token
20- address token;
21- /// @dev The amount of the token to be sent
22- uint256 amount;
23- /// @dev The address to receive the output tokens
24- address recipient;
25- /// @dev When emitted on the origin chain, the destination chain for the Output.
26- /// When emitted on the destination chain, the origin chain for the Order containing the Output.
27- uint32 chainId;
28- }
29-
308/// @notice Contract capable of processing fulfillment of intent-based Orders.
31- abstract contract OrderDestination {
9+ abstract contract OrderDestination is IOrders , OrdersPermit2 {
3210 /// @notice Emitted when Order Outputs are sent to their recipients.
3311 /// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
3412 event Filled (Output[] outputs );
3513
36- /// @notice Send the Output(s) of any number of Orders.
37- /// The user calls `initiate` on a rollup; the Builder calls `fill` on the target chain aggregating Outputs.
38- /// Builder may aggregate multiple Outputs with the same (`chainId`, `recipient`, `token`) into a single Output with the summed `amount`.
14+ /// @notice Fill any number of Order(s), by transferring their Output(s).
15+ /// @dev Filler may aggregate multiple Outputs with the same (`chainId`, `recipient`, `token`) into a single Output with the summed `amount`.
3916 /// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
4017 /// @param outputs - The Outputs to be transferred.
4118 /// @custom:emits Filled
4219 function fill (Output[] memory outputs ) external payable {
4320 // transfer outputs
21+ _transferOutputs (outputs);
22+
23+ // emit
24+ emit Filled (outputs);
25+ }
26+
27+ /// @notice Fill any number of Order(s), by transferring their Output(s) via permit2 signed batch transfer.
28+ /// @dev Can only provide ERC20 tokens as Outputs.
29+ /// @dev Filler may aggregate multiple Outputs with the same (`chainId`, `recipient`, `token`) into a single Output with the summed `amount`.
30+ /// @dev the permit2 signer is the Filler providing the Outputs.
31+ /// @dev the permit2 `permitted` tokens MUST match provided Outputs.
32+ /// @dev Filler MUST submit `fill` and `intitiate` within an atomic bundle.
33+ /// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
34+ /// @param outputs - The Outputs to be transferred. signed over via permit2 witness.
35+ /// @param permit2 - the permit2 details, signer, and signature.
36+ /// @custom:emits Filled
37+ function fillPermit2 (Output[] memory outputs , OrdersPermit2.Permit2Batch calldata permit2 ) external {
38+ // transfer all tokens to the Output recipients via permit2 (includes check on nonce & deadline)
39+ _permitWitnessTransferFrom (
40+ outputWitness (outputs), _fillTransferDetails (outputs, permit2.permit.permitted), permit2
41+ );
42+
43+ // emit
44+ emit Filled (outputs);
45+ }
46+
47+ /// @notice Transfer the Order Outputs to their recipients.
48+ function _transferOutputs (Output[] memory outputs ) internal {
4449 uint256 value = msg .value ;
4550 for (uint256 i; i < outputs.length ; i++ ) {
4651 if (outputs[i].token == address (0 )) {
@@ -51,19 +56,14 @@ abstract contract OrderDestination {
5156 IERC20 (outputs[i].token).transferFrom (msg .sender , outputs[i].recipient, outputs[i].amount);
5257 }
5358 }
54- // emit
55- emit Filled (outputs);
5659 }
5760}
5861
5962/// @notice Contract capable of registering initiation of intent-based Orders.
60- abstract contract OrderOrigin {
63+ abstract contract OrderOrigin is IOrders , OrdersPermit2 {
6164 /// @notice Thrown when an Order is submitted with a deadline that has passed.
6265 error OrderExpired ();
6366
64- /// @notice Thrown when trying to call `sweep` if not the Builder of the block.
65- error OnlyBuilder ();
66-
6767 /// @notice Emitted when an Order is submitted for fulfillment.
6868 /// @dev NOTE that here, Output.chainId denotes the *destination* chainId.
6969 event Order (uint256 deadline , Input[] inputs , Output[] outputs );
@@ -73,14 +73,15 @@ abstract contract OrderOrigin {
7373 /// Intentionally does not bother to emit which token(s) were swept, nor their amounts.
7474 event Sweep (address indexed recipient , address indexed token , uint256 amount );
7575
76- /// @notice Request to swap ERC20s.
76+ /// @notice Initiate an Order.
77+ /// @dev Filler MUST submit `fill` and `intitiate` + `sweep` within an atomic bundle.
78+ /// @dev NOTE that here, Output.chainId denotes the *target* chainId.
7779 /// @dev inputs are provided on the rollup; in exchange,
7880 /// outputs are expected to be received on the target chain(s).
79- /// @dev Fees paid to the Builders for fulfilling the Orders
80- /// can be included within the "exchange rate" between inputs and outputs.
81- /// @dev The Builder claims the inputs from the contract by submitting `sweep` transactions within the same block.
8281 /// @dev The Rollup STF MUST NOT apply `initiate` transactions to the rollup state
8382 /// UNLESS the outputs are delivered on the target chains within the same block.
83+ /// @dev Fees paid to the Builders for fulfilling the Orders
84+ /// can be included within the "exchange rate" between inputs and outputs.
8485 /// @param deadline - The deadline at or before which the Order must be fulfilled.
8586 /// @param inputs - The token amounts offered by the swapper in exchange for the outputs.
8687 /// @param outputs - The token amounts that must be received on their target chain(s) in order for the Order to be executed.
@@ -97,17 +98,26 @@ abstract contract OrderOrigin {
9798 emit Order (deadline, inputs, outputs);
9899 }
99100
100- /// @notice Transfer the Order inputs to this contract, where they can be collected by the Order filler.
101- function _transferInputs (Input[] memory inputs ) internal {
102- uint256 value = msg .value ;
103- for (uint256 i; i < inputs.length ; i++ ) {
104- if (inputs[i].token == address (0 )) {
105- // this line should underflow if there's an attempt to spend more ETH than is attached to the transaction
106- value -= inputs[i].amount;
107- } else {
108- IERC20 (inputs[i].token).transferFrom (msg .sender , address (this ), inputs[i].amount);
109- }
110- }
101+ /// @notice Initiate an Order, transferring Input tokens to the Filler via permit2 signed batch transfer.
102+ /// @dev Can only provide ERC20 tokens as Inputs.
103+ /// @dev the permit2 signer is the swapper providing the Input tokens in exchange for the Outputs.
104+ /// @dev Filler MUST submit `fill` and `intitiate` within an atomic bundle.
105+ /// @dev NOTE that here, Output.chainId denotes the *target* chainId.
106+ /// @param tokenRecipient - the recipient of the Input tokens, provided by msg.sender (un-verified by permit2).
107+ /// @param outputs - the Outputs required in exchange for the Input tokens. signed over via permit2 witness.
108+ /// @param permit2 - the permit2 details, signer, and signature.
109+ function initiatePermit2 (
110+ address tokenRecipient ,
111+ Output[] memory outputs ,
112+ OrdersPermit2.Permit2Batch calldata permit2
113+ ) external {
114+ // transfer all tokens to the tokenRecipient via permit2 (includes check on nonce & deadline)
115+ _permitWitnessTransferFrom (
116+ outputWitness (outputs), _initiateTransferDetails (tokenRecipient, permit2.permit.permitted), permit2
117+ );
118+
119+ // emit
120+ emit Order (permit2.permit.deadline, _inputs (permit2.permit.permitted), outputs);
111121 }
112122
113123 /// @notice Transfer the entire balance of ERC20 tokens to the recipient.
@@ -118,8 +128,7 @@ abstract contract OrderOrigin {
118128 /// @param token - The token to transfer.
119129 /// @custom:emits Sweep
120130 /// @custom:reverts OnlyBuilder if called by non-block builder
121- function sweep (address recipient , address token ) public {
122- if (msg .sender != block .coinbase ) revert OnlyBuilder ();
131+ function sweep (address recipient , address token ) external {
123132 // send ETH or tokens
124133 uint256 balance;
125134 if (token == address (0 )) {
@@ -131,8 +140,25 @@ abstract contract OrderOrigin {
131140 }
132141 emit Sweep (recipient, token, balance);
133142 }
143+
144+ /// @notice Transfer the Order inputs to this contract, where they can be collected by the Order filler via `sweep`.
145+ function _transferInputs (Input[] memory inputs ) internal {
146+ uint256 value = msg .value ;
147+ for (uint256 i; i < inputs.length ; i++ ) {
148+ if (inputs[i].token == address (0 )) {
149+ // this line should underflow if there's an attempt to spend more ETH than is attached to the transaction
150+ value -= inputs[i].amount;
151+ } else {
152+ IERC20 (inputs[i].token).transferFrom (msg .sender , address (this ), inputs[i].amount);
153+ }
154+ }
155+ }
134156}
135157
136- contract HostOrders is OrderDestination {}
158+ contract HostOrders is OrderDestination {
159+ constructor (address _permit2 ) UsesPermit2 (_permit2) {}
160+ }
137161
138- contract RollupOrders is OrderOrigin , OrderDestination {}
162+ contract RollupOrders is OrderOrigin , OrderDestination {
163+ constructor (address _permit2 ) UsesPermit2 (_permit2) {}
164+ }
0 commit comments