diff --git a/ERCS/erc-7683.md b/ERCS/erc-7683.md index 2d8dbaf75f..ef27ce5ef3 100644 --- a/ERCS/erc-7683.md +++ b/ERCS/erc-7683.md @@ -12,7 +12,7 @@ created: 2024-04-11 ## Abstract -The following standard allows for the implementation of a standard API for cross-chain trade execution systems. This standard provides a generic `CrossChainOrder` struct, as well as a standard `ISettlementContract` smart contract interface. +The following standard allows for the implementation of a standard API for cross-chain value-transfer systems. This standard provides generic order structs, as well as a standard set of settlement smart contract interfaces. ## Motivation @@ -24,28 +24,53 @@ By implementing a standard, cross-chain intents systems can interoperate and sha The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. -### CrossChainOrder struct +### Glossary of Terms -A compliant cross-chain order type MUST be ABI decodable into the `CrossChainOrder` type. +- **Destination Chain**: the chain where the intent is executed and the user receives funds. Note: intents can involve multiple destination chains. +- **Filler**: a participant who fulfils a user intent on the destination chain(s) and receives payment as a reward. +- **Leg**: a portion of the user intent that can be executed independently from others. All legs must be executed for an intent to be considered fulfilled. +- **Origin chain**: the chain where the user sends funds. +- **Settlement System**: a system that custodies user deposits, verifies fills, and pays fillers for the purpose of faciliating intents. +- **Settler**: a contract that implements part of the settlement system on a particular chain. +- **User**: for the purposes of this document, the user is the end-user who is sending the order. + +### Order structs + +A compliant cross-chain order type MUST be ABI decodable into either `GaslessCrossChainOrder` or `OnchainCrossChainOrder` type. ```solidity -/// @title CrossChainOrder type -/// @notice Standard order struct to be signed by swappers, disseminated to fillers, and submitted to settlement contracts -struct CrossChainOrder { +/// @title GaslessCrossChainOrder CrossChainOrder type +/// @notice Standard order struct to be signed by users, disseminated to fillers, and submitted to origin settler contracts +struct GaslessCrossChainOrder { /// @dev The contract address that the order is meant to be settled by. /// Fillers send this order to this contract address on the origin chain - address settlementContract; + address originSettler; /// @dev The address of the user who is initiating the swap, /// whose input tokens will be taken and escrowed - address swapper; + address user; /// @dev Nonce to be used as replay protection for the order uint256 nonce; /// @dev The chainId of the origin chain - uint32 originChainId; - /// @dev The timestamp by which the order must be initiated - uint32 initiateDeadline; + uint256 originChainId; + /// @dev The timestamp by which the order must be opened + uint32 openDeadline; + /// @dev The timestamp by which the order must be filled on the destination chain + uint32 fillDeadline; + /// @dev Type identifier for the order data. This is an EIP-712 typehash. + bytes32 orderDataType; + /// @dev Arbitrary implementation-specific data + /// Can be used to define tokens, amounts, destination chains, fees, settlement parameters, + /// or any other order-type specific information + bytes orderData; +} + +/// @title OnchainCrossChainOrder CrossChainOrder type +/// @notice Standard order struct for user-opened orders, where the user is the msg.sender. +struct OnchainCrossChainOrder { /// @dev The timestamp by which the order must be filled on the destination chain uint32 fillDeadline; + /// @dev Type identifier for the order data. This is an EIP-712 typehash. + bytes32 orderDataType; /// @dev Arbitrary implementation-specific data /// Can be used to define tokens, amounts, destination chains, fees, settlement parameters, /// or any other order-type specific information @@ -53,125 +78,290 @@ struct CrossChainOrder { } ``` -Cross-chain execution systems implementing this standard SHOULD create a custom sub-type that can be parsed from the arbitrary `orderData` field. This may include information such as the tokens involved in the swap, the destination chain IDs, fulfillment constraints or settlement oracles. +Cross-chain execution systems implementing this standard SHOULD use a sub-type that can be parsed from the arbitrary `orderData` field. This may include information such as the tokens involved in the transfer, the destination chain IDs, fulfillment constraints or settlement oracles. + +All sub-types SHOULD be registered in a subtypes repository to encourage sharing of sub-types based on their functionality. See the examples section for an example of how sub-types can be used to support behavior like executing calldata on a target contract of the user's choice on the destination chain. ### ResolvedCrossChainOrder struct -A compliant cross-chain order type MUST be convertible into the `ResolvedCrossChainOrder` struct. +A compliant cross-chain order type MUST be convertible into the `ResolvedCrossChainOrder` struct. This means that the `orderData` must be decoded into the information needed to populate the `ResolvedCrossChainOrder` struct. Additionally, `orderData` SHOULD be decodable into a sub-type, which can be used for further functionality such as cross-chain calldata execution (see the examples section for an example of this). It is the responsibility of the `user` and the `filler` to ensure that the `originSettler` supports their order's contained sub-type. ```solidity /// @title ResolvedCrossChainOrder type -/// @notice An implementation-generic representation of an order +/// @notice An implementation-generic representation of an order intended for filler consumption /// @dev Defines all requirements for filling an order by unbundling the implementation-specific orderData. /// @dev Intended to improve integration generalization by allowing fillers to compute the exact input and output information of any order struct ResolvedCrossChainOrder { - /// @dev The contract address that the order is meant to be settled by. - address settlementContract; - /// @dev The address of the user who is initiating the swap - address swapper; - /// @dev Nonce to be used as replay protection for the order - uint256 nonce; + /// @dev The address of the user who is initiating the transfer + address user; /// @dev The chainId of the origin chain - uint32 originChainId; - /// @dev The timestamp by which the order must be initiated - uint32 initiateDeadline; + uint256 originChainId; + /// @dev The timestamp by which the order must be opened + uint32 openDeadline; /// @dev The timestamp by which the order must be filled on the destination chain(s) uint32 fillDeadline; - - /// @dev The inputs to be taken from the swapper as part of order initiation - Input[] swapperInputs; - /// @dev The outputs to be given to the swapper as part of order fulfillment - Output[] swapperOutputs; - /// @dev The outputs to be given to the filler as part of order settlement - Output[] fillerOutputs; -} - -/// @notice Tokens sent by the swapper as inputs to the order -struct Input { - /// @dev The address of the ERC20 token on the origin chain - address token; - /// @dev The amount of the token to be sent - uint256 amount; + /// @dev The unique identifier for this order within this settlement system + bytes32 orderId; + + /// @dev The max outputs that the filler will send. It's possible the actual amount depends on the state of the destination + /// chain (destination dutch auction, for instance), so these outputs should be considered a cap on filler liabilities. + Output[] maxSpent; + /// @dev The minimum outputs that must to be given to the filler as part of order settlement. Similar to maxSpent, it's possible + /// that special order types may not be able to guarantee the exact amount at open time, so this should be considered + /// a floor on filler receipts. + Output[] minReceived; + /// @dev Each instruction in this array is parameterizes a single leg of the fill. This provides the filler with the information + /// necessary to perform the fill on the destination(s). + FillInstruction[] fillInstructions; } /// @notice Tokens that must be receive for a valid order fulfillment struct Output { /// @dev The address of the ERC20 token on the destination chain /// @dev address(0) used as a sentinel for the native token - address token; + bytes32 token; /// @dev The amount of the token to be sent uint256 amount; /// @dev The address to receive the output tokens - address recipient; + bytes32 recipient; /// @dev The destination chain for this output - uint32 chainId; + uint256 chainId; } +/// @title FillInstruction type +/// @notice Instructions to parameterize each leg of the fill +/// @dev Provides all the origin-generated information required to produce a valid fill leg +struct FillInstruction { + /// @dev The contract address that the order is meant to be settled by + uint64 destinationChainId; + /// @dev The contract address that the order is meant to be filled on + bytes32 destinationSettler; + /// @dev The data generated on the origin chain needed by the destinationSettler to process the fill + bytes originData; +} ``` -### ISettlementContract interface +### Open event -A compliant settlement contract implementation MUST implement the `ISettlementContract` interface: +A compliant `Open` event MUST adhere to the following abi: ```solidity -/// @title ISettlementContract -/// @notice Standard interface for settlement contracts -interface ISettlementContract { - /// @notice Initiates the settlement of a cross-chain order - /// @dev To be called by the filler - /// @param order The CrossChainOrder definition - /// @param signature The swapper's signature over the order - /// @param fillerData Any filler-defined data required by the settler - function initiate(CrossChainOrder order, bytes signature, bytes fillerData) external; - - /// @notice Resolves a specific CrossChainOrder into a generic ResolvedCrossChainOrder +/// @notice Signals that an order has been opened +/// @param orderId a unique order identifier within this settlement system +/// @param resolvedOrder resolved order that would be returned by resolve if called instead of Open +event Open(bytes32 indexed orderId, ResolvedCrossChainOrder resolvedOrder); +``` + +### Settlement interfaces + +A compliant origin settler contract implementation MUST implement the `IOriginSettler` interface: + +```solidity +/// @title IOriginSettler +/// @notice Standard interface for settlement contracts on the origin chain +interface IOriginSettler { + /// @notice Opens a gasless cross-chain order on behalf of a user. + /// @dev To be called by the filler. + /// @dev This method must emit the Open event + /// @param order The GaslessCrossChainOrder definition + /// @param signature The user's signature over the order + /// @param originFillerData Any filler-defined data required by the settler + function openFor(GaslessCrossChainOrder calldata order, bytes calldata signature, bytes calldata originFillerData) external; + + /// @notice Opens a cross-chain order + /// @dev To be called by the user + /// @dev This method must emit the Open event + /// @param order The OnchainCrossChainOrder definition + function open(OnchainCrossChainOrder calldata order) external; + + /// @notice Resolves a specific GaslessCrossChainOrder into a generic ResolvedCrossChainOrder + /// @dev Intended to improve standardized integration of various order types and settlement contracts + /// @param order The GaslessCrossChainOrder definition + /// @param originFillerData Any filler-defined data required by the settler + /// @return ResolvedCrossChainOrder hydrated order data including the inputs and outputs of the order + function resolveFor(GaslessCrossChainOrder calldata order, bytes calldata originFillerData) external view returns (ResolvedCrossChainOrder memory); + + /// @notice Resolves a specific OnchainCrossChainOrder into a generic ResolvedCrossChainOrder /// @dev Intended to improve standardized integration of various order types and settlement contracts - /// @param order The CrossChainOrder definition - /// @param fillerData Any filler-defined data required by the settler - /// @returns ResolvedCrossChainOrder hydrated order data including the inputs and outputs of the order - function resolve(CrossChainOrder order, bytes fillerData) external view returns (ResolvedCrossChainOrder); + /// @param order The OnchainCrossChainOrder definition + /// @return ResolvedCrossChainOrder hydrated order data including the inputs and outputs of the order + function resolve(OnchainCrossChainOrder calldata order) external view returns (ResolvedCrossChainOrder memory); } ``` +A compliant destination settlement contract implementation MUST implement the `IDestinationSettler` interface: + +```solidity +/// @title IDestinationSettler +/// @notice Standard interface for settlement contracts on the destination chain +interface IDestinationSettler { + /// @notice Fills a single leg of a particular order on the destination chain + /// @param orderId Unique order identifier for this order + /// @param originData Data emitted on the origin to parameterize the fill + /// @param fillerData Data provided by the filler to inform the fill or express their preferences + function fill(bytes32 orderId, bytes calldata originData, bytes calldata fillerData) external; +} +``` + +### fillerData + +Cross-chain execution systems implementing this standard SHOULD use a sub-type that can be parsed from the arbitrary `fillerData` field. This may include information such as the desired timing or form of payment for the filler + +All sub-types SHOULD be registered in a subtypes repository to encourage sharing of sub-types based on their functionality. + ## Rationale ### Generic OrderData -A key consideration is to ensure that a broad range of cross-chain intent designs can work within the same standard. To enable this, the specification is designed around a standard cross-chain intents *flow*, while allowing for varying implementation details within that flow. +A key consideration is to ensure that a broad range of cross-chain intent designs can work within the same standard. To enable this, the specification is designed around a cross-chain intents _flow_, with two variations: gasless and onchain. -Standard cross-chain intents flow: +#### Gasless cross-chain intents flow -1. The swapper signs an off-chain message defining the parameters of their order +Origin Chain: +1. The user signs an off-chain message defining the parameters of their order 2. The order is disseminated to fillers -3. The filler initiates the trade on the origin chain -4. The filler fills the order on the destination chain -5. A cross-chain settlement process takes place to settle the order +3. The filler calls resolve to unpack the order's requirements +4. The filler opens the order on the origin chain + +Destination Chain(s): +- The filler fills each leg of the order on the destination chain(s) + +Settlement: +- A cross-chain settlement process takes place to settle the order + +#### Onchain cross-chain intents flow + +Origin Chain: +1. The caller signs a transaction calling open with their order +2. The filler retrieves the emitted event to determine requirements + +Destination Chain(s): +- The filler fills each leg of the order on the destination chain(s) + +Settlement: +- A cross-chain settlement process takes place to settle the order + +#### Customization Within this flow, implementers of the standard have design flexibility to customize behavior such as: -- Price resolution, e.g. dutch auctions or oracle-based pricing +- Price resolution, e.g. dutch auctions (on origin or destination) or oracle-based pricing - Fulfillment constraints -- Settlement procedures. +- Settlement procedures +- Ordering of the origin and destination chain actions, e.g. the fill could happen before `open` in some settlement systems The `orderData` field allows implementations to take arbitrary specifications for these behaviors while still enabling integrators to parse the primary fields of the order. This functionality also motivated the `resolve` view function and `ResolvedCrossChainOrder` type. Resolution enables integrating fillers to validate and assess orders without specific knowledge of the `orderData` field at hand. +### Emission of Fill Instructions + +An important component of the standard is creating a flexible and robust mechanism for fillers to ensure their fills are valid. For a fill to be valid, +it typically must satisfy the following constraints: + +1. It must be filled on the correct destination chain(s) +2. It must be filled on the correct destination contract +3. It must include some (not necessarily all) information from the order that the user provided on the origin chain +4. It may require some execution information from the `open` call on the origin chain (ex. dutch auctions based on open timing) + +The `FillInstruction` array in `ResolvedCrossChainOrder` is intended to ensure it's simple for the filler to meet all of these requirements by either +listening for the `Open` or by calling `resolve`. + +One may notice that the `originData` field within `FillInstruction` is completely opaque. This opaqueness allows the settler implementations to +freely customize the data they transmit. Because fillers do not need to interpret this information, the opaqueness does not result in any +additional implementation costs on fillers. + +This functionality also makes it feasible for a user, filler, or order distribution system to perform an end-to-end simulation of the order initiation +and fill to evaluate all resulting state transitions without understanding the nuances of a particular execution system. + +### Cross-compatibility + +Since this standard is intended to reduce friction for users moving value across chains, non-EVM ecosystems should not be excluded. However, attempting +to pull each non-EVM ecosystem in would dramatically increase the size and complexity of this standard, while ommitting any that come in the future. + +Instead, this standard is intended to be cross-compatible with other ecosystems. It standardizes interfaces and data types on EVM chains, but allows +for the creation of sibling standards that define compatible interfaces, data types, and flows within other ecosystems. Intents created within these +sibling standards should be able to be filled on an EVM chain and vice versa. + +To ensure this cross-compatibility, all foreign addresses use `bytes32` rather than `address` to allow for larger address identifiers. + ### Usage of Permit2 -Permit2 is not specifically required by this standard, but does provide an efficient and straightforward approach to building standard-adherent protocols. Specifically, the `witness` functions of permit2 allow swappers to both approve the token transfer *and* the order itself with a single signature. This also nicely couples the transfer of tokens with a successful initiation of the order. +Permit2 is not specifically required by this standard, but does provide an efficient and straightforward approach to building standard-adherent protocols. Specifically, the `witness` functions of permit2 allow users to both approve the token transfer _and_ the order itself with a single signature. This also nicely couples the transfer of tokens with a successful initiation of the order. -In contrast, a standard approval model would require two separate signatures - a token approval (either [ERC-2612](./eip-2612.md) or on-chain) and a signature to approve the terms of the swap. It also decouples the token approval from the swap, meaning approved tokens could potentially be taken at any time due to a buggy or untrusted settler contract. +In contrast, a standard approval model would require two separate signatures - a token approval (either [ERC-2612](./eip-2612.md) or on-chain) and a signature to approve the terms of the order. It also decouples the token approval from the order, meaning approved tokens could potentially be taken at any time due to a buggy or untrusted settler contract. When building a standard-compliant settler system around Permit2, the following considerations should be made - `nonce` in the order struct should be a permit2 nonce -- `initiateDeadline` in the order struct should be the permit2 deadline -- A full order struct including the parsed `orderData` should be used as the witness type during the permit2 call. This ensures maximum transparency to the swapper as they sign their order permit. +- `openDeadline` in the order struct should be the permit2 deadline +- A full order struct including the parsed `orderData` should be used as the witness type during the permit2 call. This ensures maximum transparency to the user as they sign their order permit. + +### Examples + +This is an example of how a 7683 cross-chain value transfer order can include instructions to the filler to execute arbitrary calldata on behalf of the recipient on the destination chain. This calldata execution is performed by the settlement contract atomically within the filler's fill() execution, so the arbitrary contract execution can take advantage of the destination chain recipient's newly transferred value. A hypothetical user in this example would select a `originSettler` that is known to support the `Message` sub-type. + +Let there be a sub-type called `Message`, which is defined by the following structs: + +```solidity +// The Message subtype allows ERC7683 intents to carry calldata that is executed on a target contract on the destination chain. The settlement contract that the filler interacts with on the destination chain will decode the message into smart contract calls and execute the calls within the filler's `fill()` transaction. + +// The Message contains calls that the user wants executed on the destination chain. +// The target is a contract on the destination chain that the settlement contract will attempt to send callData and value to. +struct Calls { + address target; + bytes callData; + uint256 value; +} + +struct Message { + Calls[] calls; +} +``` + +The `Message` sub-type is designed to be used by a 7683 user to incentivize a filler to to execute arbitrary calldata on a target destination chain contracton the user's behalf. For example, the settlement contract might decode the `orderData` containing the message information as follows: + +```solidity +function fill(bytes32 orderId, bytes calldata originData, bytes calldata fillerData) public { + ( + address user, + uint32 fillDeadline, + Output memory fillerOutput, + Message memory message + ) = abi.decode(originData); + + // ...Do some preprocessing on the parameters here to validate the order... + + // ...Execute the fill logic of the ResolvedCrossChainOrder... + + // Handle the Message subtype: + + // Revert if any of the message calls fail. + uint256 length = message.calls.length; + for (uint256 i = 0; i < length; ++i) { + Call memory call = message.calls[i]; + + // If we are calling an EOA with calldata, assume target was incorrectly specified and revert. + if (call.callData.length > 0 && call.target.code.length == 0) { + revert InvalidCall(i, calls); + } + + (bool success, ) = call.target.call{ value: call.value }(call.callData); + if (!success) revert CallReverted(i, message.calls); + } + } +``` + +In this example, the Message sub-type allows the user to delegate destination chain contract execution to fillers. However, because transactions are executed via filler, the `msg.sender` would be the `DestinationSettler`, making this `Message` sub-type limited if the target contract authenticates based on the `msg.sender`. Ideally, 7683 orders containing Messages can be combined with smart contract wallets like implementations of [ERC-4337](./eip-4337.md) or [EIP-7702](./eip-7702.md) to allow complete cross-chain delegated execution. + ## Security Considerations - +#### Evaluating settlement contract security + +This ERC is agnostic of how the settlement system validates a 7683 order fulfillment and refunds the filler. In fact, this ERC is designed to delegate the responsibility of evaluating the settlement contract's security to the filler and the application that creates the user's 7683 order. + +This design decision is motivated by the existence of many viable cross-chain messaging systems today offering settlement contracts a variety of tradeoffs. We hope that this standard can eventually support an ERC dedicated to standardizing a safe, trustless, cross-chain verification system. ## Copyright