Skip to content

Commit 108be77

Browse files
authored
feat: add updated implementation of ERC-7683 (#632)
Signed-off-by: Matt Rice <matthewcrice32@gmail.com>
1 parent 6376261 commit 108be77

File tree

6 files changed

+419
-166
lines changed

6 files changed

+419
-166
lines changed

contracts/SpokePool.sol

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,24 @@ abstract contract SpokePool is
985985
);
986986
}
987987

988+
function fill(
989+
bytes32 orderId,
990+
bytes calldata originData,
991+
bytes calldata fillerData
992+
) external {
993+
if (keccak256(originData) != orderId) {
994+
revert WrongERC7683OrderId();
995+
}
996+
997+
// Must do a delegatecall because the function requires the inputs to be calldata.
998+
(bool success, bytes memory data) = address(this).delegatecall(
999+
abi.encodeWithSelector(this.fillV3Relay.selector, abi.encodePacked(originData, fillerData))
1000+
);
1001+
if (!success) {
1002+
revert LowLevelCallFailed(data);
1003+
}
1004+
}
1005+
9881006
/**************************************
9891007
* DATA WORKER FUNCTIONS *
9901008
**************************************/

contracts/erc7683/ERC7683.sol

Lines changed: 112 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,147 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later
22
pragma solidity ^0.8.0;
33

4-
/// @notice Tokens sent by the swapper as inputs to the order
5-
struct Input {
6-
/// @dev The address of the ERC20 token on the origin chain
7-
address token;
8-
/// @dev The amount of the token to be sent
9-
uint256 amount;
10-
}
11-
12-
/// @notice Tokens that must be received for a valid order fulfillment
13-
struct Output {
14-
/// @dev The address of the ERC20 token on the destination chain
15-
/// @dev address(0) used as a sentinel for the native token
16-
address token;
17-
/// @dev The amount of the token to be sent
18-
uint256 amount;
19-
/// @dev The address to receive the output tokens
20-
address recipient;
21-
/// @dev The destination chain for this output
22-
uint32 chainId;
23-
}
24-
25-
/// @title CrossChainOrder type
26-
/// @notice Standard order struct to be signed by swappers, disseminated to fillers, and submitted to settlement contracts
27-
struct CrossChainOrder {
4+
/// @title GaslessCrossChainOrder CrossChainOrder type
5+
/// @notice Standard order struct to be signed by users, disseminated to fillers, and submitted to origin settler contracts
6+
struct GaslessCrossChainOrder {
287
/// @dev The contract address that the order is meant to be settled by.
298
/// Fillers send this order to this contract address on the origin chain
30-
address settlementContract;
9+
address originSettler;
3110
/// @dev The address of the user who is initiating the swap,
3211
/// whose input tokens will be taken and escrowed
33-
address swapper;
12+
address user;
3413
/// @dev Nonce to be used as replay protection for the order
3514
uint256 nonce;
3615
/// @dev The chainId of the origin chain
37-
uint32 originChainId;
38-
/// @dev The timestamp by which the order must be initiated
39-
uint32 initiateDeadline;
16+
uint64 originChainId;
17+
/// @dev The timestamp by which the order must be opened
18+
uint32 openDeadline;
4019
/// @dev The timestamp by which the order must be filled on the destination chain
4120
uint32 fillDeadline;
21+
/// @dev Type identifier for the order data. This is an EIP-712 typehash.
22+
bytes32 orderDataType;
23+
/// @dev Arbitrary implementation-specific data
24+
/// Can be used to define tokens, amounts, destination chains, fees, settlement parameters,
25+
/// or any other order-type specific information
26+
bytes orderData;
27+
}
28+
29+
/// @title OnchainCrossChainOrder CrossChainOrder type
30+
/// @notice Standard order struct for user-opened orders, where the user is the msg.sender.
31+
struct OnchainCrossChainOrder {
32+
/// @dev The timestamp by which the order must be filled on the destination chain
33+
uint32 fillDeadline;
34+
/// @dev Type identifier for the order data. This is an EIP-712 typehash.
35+
bytes32 orderDataType;
4236
/// @dev Arbitrary implementation-specific data
4337
/// Can be used to define tokens, amounts, destination chains, fees, settlement parameters,
4438
/// or any other order-type specific information
4539
bytes orderData;
4640
}
4741

4842
/// @title ResolvedCrossChainOrder type
49-
/// @notice An implementation-generic representation of an order
43+
/// @notice An implementation-generic representation of an order intended for filler consumption
5044
/// @dev Defines all requirements for filling an order by unbundling the implementation-specific orderData.
5145
/// @dev Intended to improve integration generalization by allowing fillers to compute the exact input and output information of any order
5246
struct ResolvedCrossChainOrder {
53-
/// @dev The contract address that the order is meant to be settled by.
54-
address settlementContract;
55-
/// @dev The address of the user who is initiating the swap
56-
address swapper;
57-
/// @dev Nonce to be used as replay protection for the order
58-
uint256 nonce;
47+
/// @dev The address of the user who is initiating the transfer
48+
address user;
5949
/// @dev The chainId of the origin chain
60-
uint32 originChainId;
61-
/// @dev The timestamp by which the order must be initiated
62-
uint32 initiateDeadline;
50+
uint64 originChainId;
51+
/// @dev The timestamp by which the order must be opened
52+
uint32 openDeadline;
6353
/// @dev The timestamp by which the order must be filled on the destination chain(s)
6454
uint32 fillDeadline;
65-
/// @dev The inputs to be taken from the swapper as part of order initiation
66-
Input[] swapperInputs;
67-
/// @dev The outputs to be given to the swapper as part of order fulfillment
68-
Output[] swapperOutputs;
69-
/// @dev The outputs to be given to the filler as part of order settlement
70-
Output[] fillerOutputs;
55+
/// @dev The max outputs that the filler will send. It's possible the actual amount depends on the state of the destination
56+
/// chain (destination dutch auction, for instance), so these outputs should be considered a cap on filler liabilities.
57+
Output[] maxSpent;
58+
/// @dev The minimum outputs that must to be given to the filler as part of order settlement. Similar to maxSpent, it's possible
59+
/// that special order types may not be able to guarantee the exact amount at open time, so this should be considered
60+
/// a floor on filler receipts.
61+
Output[] minReceived;
62+
/// @dev Each instruction in this array is parameterizes a single leg of the fill. This provides the filler with the information
63+
/// necessary to perform the fill on the destination(s).
64+
FillInstruction[] fillInstructions;
65+
}
66+
67+
/// @notice Tokens that must be receive for a valid order fulfillment
68+
struct Output {
69+
/// @dev The address of the ERC20 token on the destination chain
70+
/// @dev address(0) used as a sentinel for the native token
71+
bytes32 token;
72+
/// @dev The amount of the token to be sent
73+
uint256 amount;
74+
/// @dev The address to receive the output tokens
75+
bytes32 recipient;
76+
/// @dev The destination chain for this output
77+
uint64 chainId;
7178
}
7279

73-
/// @title ISettlementContract
74-
/// @notice Standard interface for settlement contracts
75-
interface ISettlementContract {
76-
/// @notice Initiates the settlement of a cross-chain order
77-
/// @dev To be called by the filler
78-
/// @param order The CrossChainOrder definition
79-
/// @param signature The swapper's signature over the order
80-
/// @param fillerData Any filler-defined data required by the settler
81-
function initiate(
82-
CrossChainOrder memory order,
83-
bytes memory signature,
84-
bytes memory fillerData
80+
/// @title FillInstruction type
81+
/// @notice Instructions to parameterize each leg of the fill
82+
/// @dev Provides all the origin-generated information required to produce a valid fill leg
83+
struct FillInstruction {
84+
/// @dev The contract address that the order is meant to be settled by
85+
uint64 destinationChainId;
86+
/// @dev The contract address that the order is meant to be filled on
87+
bytes32 destinationSettler;
88+
/// @dev The data generated on the origin chain needed by the destinationSettler to process the fill
89+
bytes originData;
90+
}
91+
92+
/// @title IOriginSettler
93+
/// @notice Standard interface for settlement contracts on the origin chain
94+
interface IOriginSettler {
95+
/// @notice Signals that an order has been opened
96+
/// @param orderId a unique order identifier within this settlement system
97+
/// @param resolvedOrder resolved order that would be returned by resolve if called instead of Open
98+
event Open(bytes32 indexed orderId, ResolvedCrossChainOrder resolvedOrder);
99+
100+
/// @notice Opens a gasless cross-chain order on behalf of a user.
101+
/// @dev To be called by the filler.
102+
/// @dev This method must emit the Open event
103+
/// @param order The GaslessCrossChainOrder definition
104+
/// @param signature The user's signature over the order
105+
/// @param originFillerData Any filler-defined data required by the settler
106+
function openFor(
107+
GaslessCrossChainOrder calldata order,
108+
bytes calldata signature,
109+
bytes calldata originFillerData
85110
) external;
86111

87-
/// @notice Resolves a specific CrossChainOrder into a generic ResolvedCrossChainOrder
112+
/// @notice Opens a cross-chain order
113+
/// @dev To be called by the user
114+
/// @dev This method must emit the Open event
115+
/// @param order The OnchainCrossChainOrder definition
116+
function open(OnchainCrossChainOrder calldata order) external;
117+
118+
/// @notice Resolves a specific GaslessCrossChainOrder into a generic ResolvedCrossChainOrder
88119
/// @dev Intended to improve standardized integration of various order types and settlement contracts
89-
/// @param order The CrossChainOrder definition
90-
/// @param fillerData Any filler-defined data required by the settler
120+
/// @param order The GaslessCrossChainOrder definition
121+
/// @param originFillerData Any filler-defined data required by the settler
91122
/// @return ResolvedCrossChainOrder hydrated order data including the inputs and outputs of the order
92-
function resolve(CrossChainOrder memory order, bytes memory fillerData)
123+
function resolveFor(GaslessCrossChainOrder calldata order, bytes calldata originFillerData)
93124
external
94125
view
95126
returns (ResolvedCrossChainOrder memory);
127+
128+
/// @notice Resolves a specific OnchainCrossChainOrder into a generic ResolvedCrossChainOrder
129+
/// @dev Intended to improve standardized integration of various order types and settlement contracts
130+
/// @param order The OnchainCrossChainOrder definition
131+
/// @return ResolvedCrossChainOrder hydrated order data including the inputs and outputs of the order
132+
function resolve(OnchainCrossChainOrder calldata order) external view returns (ResolvedCrossChainOrder memory);
133+
}
134+
135+
/// @title IDestinationSettler
136+
/// @notice Standard interface for settlement contracts on the destination chain
137+
interface IDestinationSettler {
138+
/// @notice Fills a single leg of a particular order on the destination chain
139+
/// @param orderId Unique order identifier for this order
140+
/// @param originData Data emitted on the origin to parameterize the fill
141+
/// @param fillerData Data provided by the filler to inform the fill or express their preferences
142+
function fill(
143+
bytes32 orderId,
144+
bytes calldata originData,
145+
bytes calldata fillerData
146+
) external;
96147
}

contracts/erc7683/ERC7683Across.sol

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.0;
33

44
import "../external/interfaces/IPermit2.sol";
5-
import { CrossChainOrder } from "./ERC7683.sol";
5+
import { GaslessCrossChainOrder } from "./ERC7683.sol";
66

77
// Data unique to every CrossChainOrder settled on Across
88
struct AcrossOrderData {
@@ -12,44 +12,50 @@ struct AcrossOrderData {
1212
uint256 outputAmount;
1313
uint32 destinationChainId;
1414
address recipient;
15-
uint32 exclusivityDeadlineOffset;
15+
address exclusiveRelayer;
16+
uint32 exclusivityPeriod;
1617
bytes message;
1718
}
1819

19-
struct AcrossFillerData {
20+
struct AcrossOriginFillerData {
2021
address exclusiveRelayer;
2122
}
2223

24+
struct AcrossDestinationFillerData {
25+
uint256 repaymentChainId;
26+
}
27+
28+
bytes constant ACROSS_ORDER_DATA_TYPE = abi.encodePacked(
29+
"AcrossOrderData(",
30+
"address inputToken,",
31+
"uint256 inputAmount,",
32+
"address outputToken,",
33+
"uint256 outputAmount,",
34+
"uint32 destinationChainId,",
35+
"address recipient,",
36+
"address exclusiveRelayer,"
37+
"uint32 exclusivityPeriod,",
38+
"bytes message)"
39+
);
40+
41+
bytes32 constant ACROSS_ORDER_DATA_TYPE_HASH = keccak256(ACROSS_ORDER_DATA_TYPE);
42+
2343
/**
2444
* @notice ERC7683Permit2Lib knows how to process a particular type of external Permit2Order so that it can be used in Across.
2545
* @dev This library is responsible for definining the ERC712 type strings/hashes and performing hashes on the types.
2646
* @custom:security-contact bugs@across.to
2747
*/
2848
library ERC7683Permit2Lib {
29-
bytes private constant ACROSS_ORDER_DATA_TYPE =
30-
abi.encodePacked(
31-
"AcrossOrderData(",
32-
"address inputToken,",
33-
"uint256 inputAmount,",
34-
"address outputToken,",
35-
"uint256 outputAmount,",
36-
"uint32 destinationChainId,",
37-
"address recipient,",
38-
"uint32 exclusivityDeadlineOffset,",
39-
"bytes message)"
40-
);
41-
42-
bytes32 private constant ACROSS_ORDER_DATA_TYPE_HASH = keccak256(ACROSS_ORDER_DATA_TYPE);
43-
4449
bytes internal constant CROSS_CHAIN_ORDER_TYPE =
4550
abi.encodePacked(
46-
"CrossChainOrder(",
47-
"address settlementContract,",
48-
"address swapper,",
51+
"GaslessCrossChainOrder(",
52+
"address originSettler,",
53+
"address user,",
4954
"uint256 nonce,",
5055
"uint32 originChainId,",
51-
"uint32 initiateDeadline,",
56+
"uint32 openDeadline,",
5257
"uint32 fillDeadline,",
58+
"bytes32 orderDataType,",
5359
"AcrossOrderData orderData)"
5460
);
5561

@@ -69,16 +75,16 @@ library ERC7683Permit2Lib {
6975
);
7076

7177
// Hashes an order to get an order hash. Needed for permit2.
72-
function hashOrder(CrossChainOrder memory order, bytes32 orderDataHash) internal pure returns (bytes32) {
78+
function hashOrder(GaslessCrossChainOrder memory order, bytes32 orderDataHash) internal pure returns (bytes32) {
7379
return
7480
keccak256(
7581
abi.encode(
7682
CROSS_CHAIN_ORDER_TYPE_HASH,
77-
order.settlementContract,
78-
order.swapper,
83+
order.originSettler,
84+
order.user,
7985
order.nonce,
8086
order.originChainId,
81-
order.initiateDeadline,
87+
order.openDeadline,
8288
order.fillDeadline,
8389
orderDataHash
8490
)
@@ -96,7 +102,7 @@ library ERC7683Permit2Lib {
96102
orderData.outputAmount,
97103
orderData.destinationChainId,
98104
orderData.recipient,
99-
orderData.exclusivityDeadlineOffset,
105+
orderData.exclusivityPeriod,
100106
keccak256(orderData.message)
101107
)
102108
);

0 commit comments

Comments
 (0)