Alert Source Discuss
⚠️ Draft Standards Track: ERC

ERC-7683: 跨链意图

用于跨链交易执行系统的接口。

Authors Mark Toda (@marktoda), Matt Rice (@mrice32), Nick Pai (@nicholaspai)
Created 2024-04-11
Discussion Link https://ethereum-magicians.org/t/erc-cross-chain-intents-standard/19619

摘要

以下标准允许为跨链价值转移系统实现标准 API。该标准提供了通用的订单结构,以及一套标准的结算智能合约接口。

动机

基于意图的系统通过抽象化传统桥接的复杂性和时间限制,已成为终端用户跨链交互的首选解决方案。跨链意图系统的一个关键难题是获取足够的流动性,以及跨链的活跃执行者网络。随着不同链的数量随着时间推移而增加,这一挑战可能会加剧。最终导致用户体验不佳,包括更高的成本、更长的等待时间和比必要更高的失败率。

通过实施标准,跨链意图系统可以互操作并共享基础设施,例如订单分发服务和执行者网络,从而通过增加履行用户意图的竞争来改善终端用户体验。

规范

本文档中的关键词“必须”,“不得”,“必需”,“应该”,“不应该”,“推荐”,“可以”和“可选”应按照 RFC 2119 中的描述进行解释。

术语表

  • 目标链 (Destination Chain): 执行意图并且用户收到资金的链。注意:意图可能涉及多个目标链。
  • 执行者 (Filler): 在目标链上履行用户意图并获得报酬作为奖励的参与者。
  • 腿 (Leg): 用户意图的一部分,可以独立于其他部分执行。所有腿都必须执行,才能认为意图已完成。
  • 起始链 (Origin chain): 用户发送资金的链。
  • 结算系统 (Settlement System): 托管用户存款、验证执行情况并向执行者支付报酬以促进意图的系统。
  • 结算者 (Settler): 在特定链上实现结算系统一部分的合约。
  • 用户 (User): 就本文档而言,用户是发送订单的终端用户。

订单结构

符合规范的跨链订单类型必须可以 ABI 解码为 GaslessCrossChainOrderOnchainCrossChainOrder 类型。

/// @title GaslessCrossChainOrder CrossChainOrder 类型
/// @notice 标准订单结构,供用户签名、分发给执行者,并由执行者提交给起始结算者合约
struct GaslessCrossChainOrder {
	/// @dev 订单要由其结算的合约地址。
	/// 执行者将此订单发送到起始链上的此合约地址
	address originSettler;
	/// @dev 发起交换的用户的地址,
	/// 其输入代币将被拿走并托管
	address user;
	/// @dev 用于订单重放保护的 Nonce
	uint256 nonce;
	/// @dev 起始链的 chainId
	uint256 originChainId;
	/// @dev 必须打开订单的时间戳
	uint32 openDeadline;
	/// @dev 必须在目标链上完成订单的时间戳
	uint32 fillDeadline;
	/// @dev 订单数据的类型标识符。这是一个 EIP-712 typehash。
	bytes32 orderDataType;
	/// @dev 任意的特定于实现的数据
	/// 可用于定义代币、金额、目标链、费用、结算参数,
	/// 或任何其他特定于订单类型的信息
	bytes orderData;
}

/// @title OnchainCrossChainOrder CrossChainOrder 类型
/// @notice 用于用户打开的订单的标准订单结构,其中用户是提交订单创建交易的人
struct OnchainCrossChainOrder {
	/// @dev 必须在目标链上完成订单的时间戳
	uint32 fillDeadline;
	/// @dev 订单数据的类型标识符。这是一个 EIP-712 typehash。
	bytes32 orderDataType;
	/// @dev 任意的特定于实现的数据
	/// 可用于定义代币、金额、目标链、费用、结算参数,
	/// 或任何其他特定于订单类型的信息
	bytes orderData;
}

实现此标准的跨链执行系统应该使用可以从任意 orderData 字段解析的子类型。这可能包括诸如转移中涉及的代币、目标链 ID、履行约束或结算预言机等信息。

所有子类型都应该注册到子类型存储库中,以鼓励基于其功能共享子类型。有关如何使用子类型来支持诸如在用户选择的目标合约上于目标链上执行 calldata 之类的行为的示例,请参见示例部分。

ResolvedCrossChainOrder 结构

符合规范的跨链订单类型必须可转换为 ResolvedCrossChainOrder 结构。这意味着 orderData 必须被解码为填充 ResolvedCrossChainOrder 结构所需的信息。此外,orderData 应该可以解码为子类型,该子类型可用于进一步的功能,例如跨链 calldata 执行(有关此示例,请参见示例部分)。userfiller 有责任确保 originSettler 支持其订单中包含的子类型。

/// @title ResolvedCrossChainOrder 类型
/// @notice 订单的实现通用表示,供执行者使用
/// @dev 通过解绑特定于实现的 orderData,定义了填写订单的所有要求。
/// @dev 旨在通过允许执行者计算任何订单的确切输入和输出信息来改进集成通用性
struct ResolvedCrossChainOrder {
	/// @dev 发起转移的用户的地址
	address user;
	/// @dev 起始链的 chainId
	uint256 originChainId;
	/// @dev 必须打开订单的时间戳
	uint32 openDeadline;
	/// @dev 必须在目标链上完成订单的时间戳
	uint32 fillDeadline;
	/// @dev 此结算系统中此订单的唯一标识符
	bytes32 orderId;

	/// @dev 执行者将发送的最大输出量。实际数量可能取决于目标的状态
	///      链(例如,目标荷兰式拍卖),因此这些输出应被视为对执行者责任的上限。
	Output[] maxSpent;
	/// @dev 必须作为订单结算的一部分提供给执行者的最小输出量。与 maxSpent 类似,
	///      特殊的订单类型可能无法保证打开时的确切数量,因此应将其视为
	///      执行者收据的下限。将`Output`的`recipient`设置为 address(0) 表示执行者不是
    ///      创建此订单时已知。
	Output[] minReceived;
	/// @dev 此数组中的每个指令都参数化了执行的单个腿。这为执行者提供了必要的信息
	///      在目标上执行填充。
	FillInstruction[] fillInstructions;
}

/// @notice 有效订单履行必须收到的代币
struct Output {
	/// @dev 目标链上 ERC20 代币的地址
	/// @dev address(0) 用作原生代币的哨兵
	bytes32 token;
	/// @dev 要发送的代币数量
	uint256 amount;
	/// @dev 接收输出代币的地址
	bytes32 recipient;
	/// @dev 此输出的目标链
	uint256 chainId;
}

/// @title FillInstruction 类型
/// @notice 参数化填充的每个腿的指令
/// @dev 提供了生成有效填充腿所需的所有起始生成的信息
struct FillInstruction {
	/// @dev 此指令旨在填充的链
	uint256 destinationChainId;
	/// @dev 此指令旨在填充的合约地址
	bytes32 destinationSettler;
	/// @dev 目标结算者处理填充所需的起始链上生成的数据
	bytes originData;
}

Open 事件

符合规范的 Open 事件必须遵守以下 abi:

/// @notice 表示已打开订单
/// @param orderId 此结算系统中的唯一订单标识符
/// @param resolvedOrder 如果调用 resolve 而不是 Open 将返回的已解析订单
event Open(bytes32 indexed orderId, ResolvedCrossChainOrder resolvedOrder);

结算接口

符合规范的起始结算者合约实现必须实现 IOriginSettler 接口:

/// @title IOriginSettler
/// @notice 起始链上结算合约的标准接口
interface IOriginSettler {
	/// @notice 代表用户打开无 Gas 跨链订单。
	/// @dev 要由执行者调用。
	/// @dev 此方法必须发出 Open 事件
	/// @param order GaslessCrossChainOrder 定义
	/// @param signature 用户对订单的签名
	/// @param originFillerData 结算者需要的任何执行者定义的数据
	function openFor(GaslessCrossChainOrder calldata order, bytes calldata signature, bytes calldata originFillerData) external;

	/// @notice 打开跨链订单
	/// @dev 要由用户调用
	/// @dev 此方法必须发出 Open 事件
	/// @param order OnchainCrossChainOrder 定义
	function open(OnchainCrossChainOrder calldata order) external;

	/// @notice 将特定的 GaslessCrossChainOrder 解析为通用的 ResolvedCrossChainOrder
	/// @dev 旨在改善各种订单类型和结算合约的标准化集成
	/// @param order GaslessCrossChainOrder 定义
	/// @param originFillerData 结算者需要的任何执行者定义的数据
	/// @return ResolvedCrossChainOrder 包含订单输入和输出的 hydrated 订单数据
	function resolveFor(GaslessCrossChainOrder calldata order, bytes calldata originFillerData) external view returns (ResolvedCrossChainOrder memory);

	/// @notice 将特定的 OnchainCrossChainOrder 解析为通用的 ResolvedCrossChainOrder
	/// @dev 旨在改善各种订单类型和结算合约的标准化集成
	/// @param order OnchainCrossChainOrder 定义
	/// @return ResolvedCrossChainOrder 包含订单输入和输出的 hydrated 订单数据
	function resolve(OnchainCrossChainOrder calldata order) external view returns (ResolvedCrossChainOrder memory);
}

符合规范的目标结算合约实现必须实现 IDestinationSettler 接口:

/// @title IDestinationSettler
/// @notice 目标链上结算合约的标准接口
interface IDestinationSettler {
	/// @notice 在目标链上填充特定订单的单个腿
	/// @param orderId 此订单的唯一订单标识符
	/// @param originData 起始链上发出的用于参数化填充的数据
	/// @param fillerData 执行者提供的数据,用于通知填充或表达其偏好
	function fill(bytes32 orderId, bytes calldata originData, bytes calldata fillerData) external;
}

fillerData

实现此标准的跨链执行系统应该使用可以从任意 fillerData 字段解析的子类型。这可能包括诸如执行者所需的付款时间和形式之类的信息。

所有子类型都应该注册到子类型存储库中,以鼓励基于其功能共享子类型。

理由

通用 OrderData

一个关键的考虑因素是确保各种跨链意图设计可以在同一标准内工作。为了实现这一点,该规范围绕跨链意图_流程_设计,具有两种变体:无 Gas 和链上。

无 Gas 跨链意图流程

原链:

  1. 用户签署一条链下消息,定义其订单的参数
  2. 订单分发给执行者
  3. 执行者调用 resolve 以解包订单的要求
  4. 执行者在原链上打开订单

目标链:

  • 执行者在目标链上填充订单的每个腿

结算:

  • 进行跨链结算流程以结算订单

链上跨链意图流程

原链:

  1. 调用者签署一笔交易,以其订单调用 open
  2. 执行者检索发出的事件以确定要求

目标链:

  • 执行者在目标链上填充订单的每个腿

结算:

  • 进行跨链结算流程以结算订单

自定义

在此流程中,标准的实施者可以灵活地自定义行为,例如:

  • 价格分辨率,例如荷兰式拍卖(在起点或目标)或基于预言机的定价
  • 履行约束
  • 结算程序
  • 起始链和目标链操作的排序,例如,在某些结算系统中,填充可能发生在 open 之前

orderData 字段允许实现采用这些行为的任意规范,同时仍然使集成商能够解析订单的主要字段。

此功能还激发了 resolve 视图函数和 ResolvedCrossChainOrder 类型。分辨率使集成执行者能够在不了解手头的 orderData 字段的具体知识的情况下验证和评估订单。

发出填充指令

该标准的一个重要组成部分是创建一种灵活而强大的机制,供执行者确保其填充有效。为了使填充有效, 它通常必须满足以下约束:

  1. 必须在正确的目标链上填充
  2. 必须在正确的目标合约上填充
  3. 必须包含用户在原链上提供的一些(不一定是全部)订单信息
  4. 可能需要来自原链上 open 调用的某些执行信息(例如,基于打开时间的荷兰式拍卖)

ResolvedCrossChainOrder 中的 FillInstruction 数组旨在通过以下方式确保执行者可以轻松满足所有这些要求: 收听 Open 或调用 resolve

人们可能会注意到 FillInstruction 中的 originData 字段完全是不透明的。此不透明性允许结算者实现者 自由自定义他们传输的数据。因为执行者不需要解释此信息,所以不透明性不会导致任何 执行者上的额外实施成本。

此功能还使用户、执行者或订单分发系统能够对订单启动执行端到端模拟 和填充,以评估所有产生的状态转换,而无需了解特定执行系统的细微差别。

跨兼容性

由于此标准旨在减少用户跨链移动价值的摩擦,因此不应排除非 EVM 生态系统。但是,尝试 拉入每个非 EVM 生态系统将大大增加此标准的大小和复杂性,同时省略了将来出现的任何生态系统。

相反,此标准旨在与其他生态系统跨兼容。它标准化了 EVM 链上的接口和数据类型,但允许 创建定义其他生态系统中兼容接口、数据类型和流程的同级标准。在这些中创建的意图 同级标准应该能够在 EVM 链上填充,反之亦然。

为了确保这种跨兼容性,所有外部地址都使用 bytes32 而不是 address,以允许更大的地址标识符。

Permit2 的使用

Permit2 不是此标准专门要求的,但确实提供了一种有效而直接的方法来构建符合标准的协议。具体来说,permit2 的 witness 函数允许用户通过单个签名同时批准代币转移_和_订单本身。这还巧妙地将代币转移与订单的成功启动结合在一起。

相反,标准批准模型将需要两个单独的签名 - 一个代币批准(ERC-2612 或链上)和一个签名来批准订单条款。它还将代币批准与订单分离,这意味着由于有缺陷或不受信任的结算者合约,批准的代币可能随时被拿走。

围绕 Permit2 构建符合标准的结算者系统时,应考虑以下事项

  • 订单结构中的 nonce 应该是 permit2 nonce
  • 订单结构中的 openDeadline 应该是 permit2 截止日期
  • 包含已解析的 orderData 的完整订单结构应在 permit2 调用期间用作见证类型。这确保了用户在签署其订单许可时具有最大的透明度。

例子

这是一个 7683 跨链价值转移订单如何包含指示执行者代表目标链上的接收者执行任意 calldata 的示例。此 calldata 执行由结算合约在执行者的 fill() 执行中以原子方式执行,因此任意合约执行可以利用目标链接收者新转移的价值。在此示例中,一个假设的用户将选择一个已知支持 Message 子类型的 originSettler

假设有一个名为 Message 的子类型,它由以下结构定义:

// Message 子类型允许 ERC7683 意图携带在目标链上的目标合约上执行的 calldata。执行者在目标链上与之交互的结算合约会将 message 解码为智能合约调用,并在执行者的 `fill()` 交易中执行调用。

// Message 包含用户希望在目标链上执行的调用。
// target 是目标链上的一个合约,结算合约将尝试向其发送 callData 和 value。
struct Calls {
  address target;
  bytes callData;
  uint256 value;
}

struct Message {
  Calls[] calls;
}

Message 子类型旨在供 7683 用户使用,以激励执行者代表用户在目标目标链合约上执行任意 calldata。例如,结算合约可能会按如下方式解码包含消息信息的 originData

function fill(bytes32 orderId, bytes calldata originData, bytes calldata fillerData) public {
	(
		address user,
		uint32 fillDeadline,
		Output memory fillerOutput,
		Message memory message
	) = abi.decode(originData);

	// ...在此处对参数进行一些预处理以验证订单...

	// ...执行 ResolvedCrossChainOrder 的填充逻辑...

	// 处理 Message 子类型:

        // 如果任何消息调用失败,则回滚。
        uint256 length = message.calls.length;
        for (uint256 i = 0; i < length; ++i) {
            Call memory call = message.calls[i];

            // 如果我们正在使用 calldata 调用 EOA,则假定 target 指示不正确并回滚。
            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);
        }
    }

在此示例中,Message 子类型允许用户将目标链合约执行委托给执行者。但是,由于交易是通过执行者执行的,因此 msg.sender 将是 DestinationSettler,如果目标合约基于 msg.sender 进行身份验证,则使此 Message 子类型受到限制。理想情况下,包含 Message 的 7683 订单可以与智能合约钱包(如 ERC-4337EIP-7702 的实现)结合使用,以实现完整的跨链委托执行。

安全注意事项

评估结算合约安全性

此 ERC 与结算系统如何验证 7683 订单履行并退还执行者无关。实际上,此 ERC 旨在将评估 Settlement 合约安全性的责任委托给创建用户 7683 订单的执行者和应用程序。

这种设计决策是出于当今许多可行的跨链消息传递系统的存在,这些系统为结算合约提供了各种权衡。我们希望该标准最终可以支持一个致力于标准化安全、无需信任的跨链验证系统的 ERC。

版权

CC0 下放弃版权和相关权利。

Citation

Please cite this document as:

Mark Toda (@marktoda), Matt Rice (@mrice32), Nick Pai (@nicholaspai), "ERC-7683: 跨链意图 [DRAFT]," Ethereum Improvement Proposals, no. 7683, April 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7683.