Skip to main content
执行模型中,模拟步骤会观测运行时值。Guards 就是这些观测变成链上断言的方式。
在一个 flow 被降级为可执行程序之后,compose 服务会返回 calldata 之前,针对目标链的最新状态(当前链头)对其进行模拟。此模拟产生三样东西:每个终端资源的值(以便填充 producedResources)、每个被 guard 要求观测的 handle 的值,以及一个 gas 估算。Guards 就是把那些观测到的值转化为烘焙进交易本身的链上不变量的机制。

guard 是什么

guard 是附加到某个 op 输出上的不变量。服务在模拟期间读取相关的 port 值,然后将相应的检查烘焙进编译后的 calldata,以便在交易运行时于链上强制执行该不变量。 Guards 不是可以被悄悄放宽的模拟期检查。该检查是最终 calldata 的一部分,并在交易运行时于链上被验证,因此 calldata 本身即保证了该不变量。

guards 何时运行

Guards 在 compose 流水线的模拟与断言步骤中触发(参见 Execution model)。具体来说:
  • 服务针对当前链头模拟该程序,读取 guards 所观测的任何链上值。模拟器会返回每个被观测 port 的具体值。
  • 对于每个 guard,被观测的值会被烘焙进一个强制执行该不变量的检查中。该检查是最终编译程序的一部分。
  • 最终 calldata 会再次针对服务的策略(合约白名单、转账模式、指令数上限)进行验证。
如果被模拟的程序回滚,或断言无法被满足,编译会以 guard_error(HTTP 422)失败,并且不返回任何 calldata。在 simulationPolicy: "allow-revert" 下,calldata 会以 HTTP 206 连同回滚诊断信息一起返回。

guards 可以附加到哪里:providesMinimum port

guard 声明一个 compatibility.selectors 列表,描述它可以附加到哪些 port。编译期校验器会拒绝对它无法保护的 port 的附加。特别地:
  • 已经声明 providesMinimum: true 的 port(路由器或聚合器已经烘焙进一个最小输出)不能携带冗余的最小输出 guard。对这类 port 附加一个 slippage guard 会在编译期产生一个 guard_error,消息为 “Guard targets port … which provides its own minimum”
  • lifi.swapamountOut port 是典型例子:LI.FI 的聚合器已从 slippage 配置字段编码了 minOut,因此你不需要(也不能)再对它附加第二个滑点 guard。
  • lifi.zapamountOut 提供它自己的最小值,这就是为什么 swap-and-zap recipe 会在那里附加一个 slippage guard。
使用 op 的 manifest 来查看哪些输出 port 携带 providesMinimum;当你需要以编程方式进行判别时,使用 References 和 port 类型守卫。

保证的最小值与 amountOutMin

某些 guard(例如 slippage)还会产生一个保证的最小值:给定该 guard 的容差,被观测 port 的最坏情况值。对于任何其输出 port 受此类 guard 保护的终端资源,服务会在响应上以 producedResources[*].simulated.amountOutMin 的形式呈现该值。如果你对同一个 port 附加多个 guard,则最紧的最小值(最大的那个)胜出。 这就是为什么 guard 是编码安全边界的正确位置:你给 guard 的同一个容差值,既会成为一个链上断言,也会成为响应上一个用户可见的字段,因此你的 UI 可以直接显示”你将至少收到 X”,而无需重新计算任何东西。

常见的 guard 失败

guards 做的事

  • Guards 不替代前置条件。 前置条件(Preconditions)描述在交易运行之前必须为真的链上状态(例如,“用户持有至少 X USDC”),并作为状态覆盖馈送给模拟器。Guards 在交易期间运行,并对计算得出的值强制执行不变量。
  • Guards 不改变路由。 一个在用户请求的容差下会失败的滑点 guard 会以 guard_error 的形式暴露;服务不会悄悄地重新路由。如果你想让聚合器遵守该容差,请在 op 上配置它(例如 lifi.swapslippage 字段)。
  • Guards 不在链上运行任意用户代码。 guard 使用一组有界的、经过审计的检查来强制执行其不变量 —— 它无法在链上执行任意逻辑。

另请参阅