Uniswap Labs 和 Across 提出了一个新的跨链意图标准 ERC-7683,旨在为基于意图的系统指定跨链操作建立统一的框架,通过共享订单传播服务和执行者网络等基础设施来提高最终用户的体验,从而提高用户意图履行的竞争。
Uniswap Labs 和 Across 提出了一个新的跨链意图标准,为基于意图的系统建立了一个统一的框架来指定跨链操作。随着新标准的提出,Uniswap Labs和Across联合发布了一个以太坊征求意见稿到以太坊魔法师论坛。这两个项目已将该标准提交给 CAKE 工作组 以供他们讨论和审查。
以下标准允许实现跨链价值转移系统的标准 API。该标准提供通用的订单结构,以及一套标准的结算智能合约接口。
基于意图的系统已成为最终用户跨链交互的首选解决方案,因为它抽象了传统桥的复杂性和时间限制。跨链意图系统的一个关键难点是获取足够的流动性以及跨链的活跃 Filler 网络。随着不同链的数量随时间增加,这一挑战可能会加剧。最终结果是用户体验不佳,包括更高的成本、更长的等待时间和高于必要的失败率。
通过实施标准,跨链意图系统可以互操作并共享基础设施,例如订单传播服务和 Filler 网络,从而通过增加履行用户意图的竞争来改善最终用户体验。
本文档中的关键词“必须(MUST)”,“禁止(MUST NOT)”,“需要(REQUIRED)”,“应该(SHALL)”,“不应该(SHALL NOT)”,“应当(SHOULD)”,“不应当(SHOULD NOT)”,“推荐(RECOMMENDED)”,“可以(MAY)”和“可选(OPTIONAL)”应按照 RFC 2119 中的描述进行解释。
术语表
目标链(Destination Chain):意图执行且用户收到资金的链。注意:意图可能涉及多个目标链。
Filler:在目标链上实现用户意图并获得报酬的参与者。
Leg:用户意图的一部分,可以独立于其他部分执行。所有 Leg 必须执行才能认为意图已完成。
源链(Origin chain):用户发送资金的链。
结算系统(Settlement System):一个托管用户存款、验证 Fill 并向 Filler 付款以促进意图的系统。
Settler:在特定链上实现结算系统一部分的合约。
用户(User):就本文档而言,用户是发送订单的最终用户。
订单结构(Order structs)
兼容的跨链订单类型必须可以 ABI 解码为 GaslessCrossChainOrder
或 OnchainCrossChainOrder
类型。
/// @title GaslessCrossChainOrder CrossChainOrder 类型
/// @notice 要由用户签名,传播给 Filler 并提交给源 Settlement 合约的标准订单结构
struct GaslessCrossChainOrder {
/// @dev 订单要由此地址的合约结算。
/// Filler将此订单发送到源链上的此合约地址
address originSettler;
/// @dev 发起交换的用户的地址,
/// 其输入 **Token** 将被获取并托管
address user;
/// @dev 用于订单重放保护的 **Nonce**
uint256 nonce;
/// @dev 源链的 chainId
uint256 originChainId;
/// @dev 订单必须被打开的时间戳
uint32 openDeadline;
/// @dev 订单必须在目标链上完成的时间戳
uint32 fillDeadline;
/// @dev 订单数据的类型标识符。这是一个 EIP-712 类型 **Hash**。
bytes32 orderDataType;
/// @dev 任意的特定于实现的数据
/// 可用于定义 **Token**、金额、目标链、费用、结算参数或任何其他特定于订单类型的信息
bytes orderData;
}
/// @title OnchainCrossChainOrder CrossChainOrder 类型
/// @notice 用户发起订单的标准订单结构,其中用户是 msg.sender。
struct OnchainCrossChainOrder {
/// @dev 订单必须在目标链上完成的时间戳
uint32 fillDeadline;
/// @dev 订单数据的类型标识符。这是一个 EIP-712 类型 **Hash**。
bytes32 orderDataType;
/// @dev 任意的特定于实现的数据
/// 可用于定义 **Token**、金额、目标链、费用、结算参数或任何其他特定于订单类型的信息
bytes orderData;
}
实施此标准的跨链执行系统应该使用可以从任意 orderData
字段解析的子类型。这可能包括诸如转移中涉及的 Token、目标链 ID、履行约束或结算预言机等信息。
所有子类型都应该注册在子类型存储库中,以鼓励基于其功能共享子类型。有关如何使用子类型来支持诸如在用户选择的目标合约上执行 Calldata 之类的行为的示例,请参见示例部分。
ResolvedCrossChainOrder 结构
兼容的跨链订单类型必须可以转换为 ResolvedCrossChainOrder
结构。这意味着 orderData
必须解码为填充 ResolvedCrossChainOrder
结构所需的信息。此外,orderData
应该可以解码为子类型,该子类型可用于进一步的功能,例如跨链 Calldata 执行(有关此示例,请参见示例部分)。user
和 filler
有责任确保 originSettler
支持其订单中包含的子类型。
/// @title ResolvedCrossChainOrder 类型
/// @notice 订单的实现通用表示,旨在供 filler 使用
/// @dev 通过解绑特定于实现的 orderData 来定义完成订单的所有要求。
/// @dev 旨在通过允许 filler 计算任何订单的确切输入和输出信息来改善集成通用化
struct ResolvedCrossChainOrder {
/// @dev 发起转移的用户的地址
address user;
/// @dev 源链的 chainId
uint256 originChainId;
/// @dev 订单必须被打开的时间戳
uint32 openDeadline;
/// @dev 订单必须在目标链上完成的时间戳
uint32 fillDeadline;
/// @dev 此结算系统中的订单的唯一标识符
bytes32 orderId;
/// @dev filler 将发送的最大输出。实际金额可能取决于目标的状态
/// 链(例如,目标 **Dutch Auction**),因此这些输出应被视为 **Filler** 责任的上限。
Output[] maxSpent;
/// @dev 作为订单结算的一部分,必须给予 **Filler** 的最小输出。与 maxSpent 类似,
/// 特殊的订单类型可能无法保证打开时的确切金额,因此应被视为
/// **Filler** 收据的下限。
Output[] minReceived;
/// @dev 此数组中的每个指令都参数化一个 **Fill** 的 **Leg**。这为 **Filler** 提供了信息
/// 执行目标上的 **Fill** 所必需的。
FillInstruction[] fillInstructions;
}
/// @notice 必须接收 **Token** 才能有效完成订单
struct Output {
/// @dev 目标链上 ERC20 **Token** 的地址
/// @dev address(0) 用作原生 **Token** 的哨兵
bytes32 token;
/// @dev 要发送的 **Token** 的数量
uint256 amount;
/// @dev 接收输出 **Token** 的地址
bytes32 recipient;
/// @dev 此输出的目标链
uint256 chainId;
}
/// @title FillInstruction 类型
/// @notice 指令参数化 **Fill** 的每个 **Leg**
/// @dev 提供生成有效 **Fill Leg** 所需的所有原始生成的信息
struct FillInstruction {
/// @dev 订单要由此地址的合约结算
uint64 destinationChainId;
/// @dev 订单要在其上完成的合约地址
bytes32 destinationSettler;
/// @dev 目标 **Settler** 处理 **Fill** 所需的原始链上生成的数据
bytes originData;
}
Open 事件
兼容的 Open
事件必须遵循以下 ABI:
/// @notice 表示订单已打开
/// @param orderId 此结算系统中的唯一订单标识符
/// @param resolvedOrder resolved order,如果改为调用 resolve 而不是 Open,将返回
event Open(bytes32 indexed orderId, ResolvedCrossChainOrder resolvedOrder);
Settlement 接口
兼容的源 Settler 合约实现必须实现 IOriginSettler
接口:
/// @title IOriginSettler
/// @notice 源链上结算合约的标准接口
interface IOriginSettler {
/// @notice 代表用户打开一个 Gasless 跨链订单。
/// @dev 要由 **Filler** 调用。
/// @dev 此方法必须发出 Open 事件
/// @param order GaslessCrossChainOrder 定义
/// @param signature 用户对订单的签名
/// @param originFillerData 结算器所需的任何 **Filler** 定义的数据
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 结算器所需的任何 **Filler** 定义的数据
/// @return ResolvedCrossChainOrder 水合订单数据,包括订单的输入和输出
function resolveFor(GaslessCrossChainOrder calldata order, bytes calldata originFillerData) external view returns (ResolvedCrossChainOrder memory);
/// @notice 将特定的 OnchainCrossChainOrder 解析为通用的 ResolvedCrossChainOrder
/// @dev 旨在改善各种订单类型和结算合约的标准化集成
/// @param order OnchainCrossChainOrder 定义
/// @return ResolvedCrossChainOrder 水合订单数据,包括订单的输入和输出
function resolve(OnchainCrossChainOrder calldata order) external view returns (ResolvedCrossChainOrder memory);
}
兼容的目标结算合约实现必须实现 IDestinationSettler
接口:
/// @title IDestinationSettler
/// @notice 目标链上结算合约的标准接口
interface IDestinationSettler {
/// @notice 在目标链上完成特定订单的一个 **Leg**
/// @param orderId 此订单的唯一订单标识符
/// @param originData 在源链上发出的数据,用于参数化 **Fill**
/// @param fillerData 由 **Filler** 提供的数据,用于通知 **Fill** 或表达其偏好
function fill(bytes32 orderId, bytes calldata originData, bytes calldata fillerData) external;
}
fillerData
实施此标准的跨链执行系统应该使用可以从任意 fillerData
字段解析的子类型。这可能包括诸如所需的 Filler 付款时间和形式的信息。
所有子类型都应该注册在子类型存储库中,以鼓励基于其功能共享子类型。
通用 OrderData
一个关键的考虑因素是确保广泛的跨链意图设计可以在同一标准内工作。为了实现这一点,该规范围绕跨链意图 流程 设计,具有两种变体:无 Gas 和链上。
无 Gas 跨链意图流程
源链:
用户签署一个链下消息,定义其订单的参数
订单传播给 Filler
Filler 调用 resolve 以解包订单的要求
Filler 在源链上打开订单
目标链:
结算:
链上跨链意图流程
源链:
调用者签署一个交易,使用其订单调用打开
Filler 检索发出的事件以确定需求
目标链:
结算:
定制
在此流程中,标准的实施者可以灵活地自定义行为,例如:
价格解析,例如 Dutch Auction(在源或目标上)或基于预言机的定价
履行约束
结算程序
源和目标链操作的排序,例如,在某些结算系统中,Fill 可以在 open
之前发生
orderData
字段允许实现采用这些行为的任意规范,同时仍然使集成商能够解析订单的主要字段。
此功能还促使了 resolve
视图函数和 ResolvedCrossChainOrder
类型的出现。解析使集成 Filler 能够在不了解手头 orderData
字段的具体知识的情况下验证和评估订单。
Fill 指令的发出
该标准的一个重要组成部分是为 Filler 创建一个灵活且强大的机制,以确保其 Fill 有效。为了使 Fill 有效,它通常必须满足以下约束:
它必须在正确的目标链上完成
它必须在正确的目标合约上完成
它必须包括用户在源链上提供的一些(不一定是全部)信息
它可能需要来自源链上 open
调用的某些执行信息(例如,基于打开时间的 Dutch Auction)
ResolvedCrossChainOrder
中的 FillInstruction
数组旨在确保 Filler 通过侦听 Open
或调用 resolve
来轻松满足所有这些要求。
人们可能会注意到,FillInstruction
中的 originData
字段完全不透明。这种不透明性允许 Settler 实现自由自定义其传输的数据。因为 Filler 不必解释此信息,所以不透明性不会导致 Filler 的任何额外实现成本。
此功能还使最终用户、Filler 或订单分发系统可以执行订单启动和 Fill 的端到端模拟,以评估所有 resulting 状态转换,而无需了解特定执行系统的细微差别。
跨兼容性
由于此标准旨在减少用户跨链移动价值的摩擦,因此不应排除非 EVM 生态系统。但是,尝试将每个非 EVM 生态系统都包含进来会大大增加此标准的大小和复杂性,同时省略任何将来出现的内容。
相反,此标准旨在与其他生态系统 क्रॉस-compatible。它标准化了 EVM 链上的接口和数据类型,但允许创建定义其他生态系统中兼容接口、数据类型和流程的同级标准。在这些同级标准中创建的意图应该能够在 EVM 链上完成,反之亦然。
为了确保此跨兼容性,所有外部地址都使用 bytes32
而不是 address
,以允许更大的地址标识符。
Permit2 的使用
此标准未明确要求使用 Permit2,但它确实提供了一种高效且直接的方法来构建符合标准的协议。具体来说,permit2 的 witness
函数允许用户通过单个签名既批准 Token 转移_又_批准订单本身。这还很好地将 Token 的转移与订单的成功启动联系起来。
相比之下,标准批准模型将需要两个单独的签名 - Token 批准(ERC-2612 或链上)和批准订单条款的签名。它还将 Token 批准与订单分离,这意味着由于错误的或不受信任的 Settler 合约,批准的 Token 可能会在任何时候被拿走。
围绕 Permit2 构建符合标准的 Settler 系统时,应考虑以下事项
订单结构中的 nonce
应该是 permit2 Nonce
订单结构中的 openDeadline
应该是 permit2 Deadline
包含解析后的 orderData
的完整订单结构应用作 permit2 调用期间的 Witness 类型。这确保了用户在签署其订单许可时具有最大的透明度。
这是一个 7683 跨链价值转移订单如何包含指示 Filler 代表目标链上的接收者执行任意 Calldata 的示例。此 Calldata 执行由结算合约在 Filler 的 fill() 执行中原子地执行,因此任意合约执行可以利用目标链接收者新转移的价值。在此示例中,一个假设的用户将选择一个已知支持 Message
子类型的 originSettler
。
假设有一个名为 Message
的子类型,它由以下结构定义:
// Message 子类型允许 ERC7683 意图携带在目标链上的目标合约上执行的 Calldata。Filler 在目标链上与之交互的结算合约会将消息解码为智能合约调用,并在 Filler 的 `fill()` 交易中执行调用。
// Message 包含用户希望在目标链上执行的调用。
// target 是目标链上的合约,结算合约将尝试向其发送 callData 和 value。
struct Calls {
address target;
bytes callData;
uint256 value;
}
struct Message {
Calls[] calls;
}
Message
子类型旨在供 7683 用户使用,以激励 Filler 代表用户在目标目标链合约上执行任意 Calldata。例如,结算合约可能会将包含消息信息的 orderData
解码如下:
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 的 Fill 逻辑...
// 处理 Message 子类型:
// 如果任何消息调用失败,则恢复。
uint256 length = message.calls.length;
for (uint256 i = 0; i < length; ++i) {
Call memory call = message.calls[i];
// 如果我们使用 Calldata 调用 EOA,则假定目标指定不正确并恢复。
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 子类型允许用户将目标链合约执行委托给 Filler。但是,由于交易是通过 Filler 执行的,因此 msg.sender
将是 DestinationSettler
,如果目标合约基于 msg.sender
进行身份验证,则此 Message
子类型仅限于。理想情况下,包含 Message 的 7683 订单可以与智能合约钱包(如 ERC-4337 或 EIP-7702 的实现)相结合,以允许完整的跨链委托执行。
评估结算合约安全性
此 ERC 不可知结算系统如何验证 7683 订单履行并退还 Filler。实际上,此 ERC 旨在将评估结算合约安全性的责任委托给 Filler 和创建用户 7683 订单的应用程序。
这种设计决策是受到当今许多可行的跨链消息传递系统的存在所推动的,这些系统为结算合约提供了各种权衡。我们希望该标准最终可以支持一个致力于标准化安全、无需信任的跨链验证系统的 ERC。
- 原文链接: erc7683.org/spec...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!