Skip to main content
Flow Structure 展示了 resources 和 handles 在线格式中出现的位置。本页介绍它们如何表现:消费规则、port 行为,以及 asResource 的 graduation。
Flow 的运行时对象有两种形态:resources(在程序中流动并拥有余额的代币)和 handles(类型化标量值,如 uint256addressbool)。Op 调用通过 ports 与它们连接,manifest 会为 port 声明类型和模式。编译器在构建时强制执行本页的规则,因此大多数”我以为这里能通过类型检查”的意外都可以追溯到这里。

Resources

一个 resource 表示执行 proxy 持有的一份余额:要么是 ERC-20({ kind: "erc20", token, chainId }),要么是原生资产({ kind: "native", chainId })。 Resources 有三种创建方式:
  • 声明为 inputs。 一个 ResourceInput 声明 flow 所消费的一个资源。运行时 materialiser(例如 directDepositbalanceOf)会在执行前使实际数量可用。参见 Build a Flow → Wire runtime values
  • 由某个 op 产生。 大多数 LI.FI ops(lifi.swaplifi.zap)在其 amountOut port 上产生一个 resource。接收方 op 会将该 handle 绑定到它自己的输入 port。
  • core.asResource 晋升。 有时你需要把一个外部合约调用的输出当作被跟踪的资源来处理,例如某个 ERC-4626 vault 从 deposit() 返回的凭证代币(receipt token)。core.asResource 接收一个类型化 handle 加一份资源声明,并将其晋升为一等公民资源。关于 manifest 签名,参见 Op Catalog

线性消费

Resources 默认是线性的:对于给定资源,恰好只有一个下游 call 可以消费它。如果两个 op 都将同一个资源 handle 绑定为消费型输入,编译器会以 linearity_error 拒绝该 flow。这保证了 proxy 永远不会试图把同一份余额花费两次。 复制模式绑定让某个 op 在不消费的情况下读取一个资源:core.approve 会引用一个资源以授权某个 spender,同时把余额留给之后的消费型绑定使用。同一资源的复制模式读取可以与一个消费型绑定共存。op manifest 的 port 元数据决定哪个绑定消费、哪个仅读取。你无需手动跟踪这一点;SDK 的类型化 handles 已对该规则进行了建模。

终端资源

一个从未被任何 op 消费的资源就是一个终端资源。这种情况的发生,要么是因为它由某个终端 op 产生(例如 vault 存入的凭证代币),要么是因为某个 core.split 的输出被有意悬空。终端资源在执行结束时会留在 proxy 上,并受该 flow 的 sweepTo 策略约束。关于完整的 sweep 语义,参见 Terminal Resources

Handles

一个 handle 是由 SDK builder 产生的类型化标量:可以是 flow 输入的输入 handle、某个 op 输出 port 的输出 handle,或一个上下文 handle(builder.context.senderbuilder.context.executionAddress)。SDK 会将 handles 序列化为线格式的 $ref 字符串;关于转换规则和 raw.ref 应急出口,参见 References 与 resources 不同,handles 是非线性的:同一个 handle 可以被许多下游 op 引用。读取一个 handle 并不会”消费”它。一个表示截止时间或滑点容差的 uint256 可以被接入到每个需要它的 op 中。

Ports

Op 在 manifest 中声明类型化的输入和输出 ports。每个 port 都有三个属性,校验器在构建时检查它们:
  • 类型(Type)。 port 携带的值:一个 resource(代币余额)或一个 handle(类型化标量)。
  • 模式(Mode)。 对于 resource port,绑定是消费该资源还是仅读取它。默认是消费;复制模式 port(例如 core.approve 的)读取资源而不消费它。
  • 可用性(Availability)。 port 是输入(op 从中消费或读取)还是输出(op 产生一个其他 op 可以绑定的值)。
handle 与 port 的类型或模式不匹配的绑定会在编译期被拒绝。

Port 判别

尽可能使用 builder 返回的类型化 handle(swap.amountOut);port 的类型和模式是隐式的,校验器会推断它们。仅当类型化 API 无法触及某些路径时,才使用 raw.ref<T>(path)(参见 References)。这样做时,manifest 中的类型守卫会决定该路径是 handle 还是 resource port;如果你选错了类型参数,校验器会拒绝该 flow。

providesMinimum

某些输出 port 已经编码了一个最小输出不变量。lifi.swap.amountOut 是典型例子:聚合器已从该 swap 的 slippage 配置中烘焙进 minOut,因此该 port 在 manifest 中声明了 providesMinimum: true。校验器会拒绝对这些 port 添加冗余的最小输出 guard。关于完整的附加规则,参见 Guards
Port 之间的绑定以 refs 的形式书写。下一页 References 介绍其语法。

另请参阅

  • Flow Structure —— inputs 和节点绑定的线格式形态。
  • References —— SDK handles 如何转换为 refs。
  • Terminal Resources —— flow 结束时的终端资源处理。
  • Op Catalog —— core.asResource 以及 manifest 中注册的每一个 op。