Flow Structure 展示了 resources 和 handles 在线格式中出现的位置。本页介绍它们如何表现:消费规则、port 行为,以及 asResource 的 graduation。
Flow 的运行时对象有两种形态:resources(在程序中流动并拥有余额的代币)和 handles(类型化标量值,如 uint256、address、bool)。Op 调用通过 ports 与它们连接,manifest 会为 port 声明类型和模式。编译器在构建时强制执行本页的规则,因此大多数”我以为这里能通过类型检查”的意外都可以追溯到这里。
Resources
一个 resource 表示执行 proxy 持有的一份余额:要么是 ERC-20({ kind: "erc20", token, chainId }),要么是原生资产({ kind: "native", chainId })。
Resources 有三种创建方式:
- 声明为 inputs。 一个
ResourceInput声明 flow 所消费的一个资源。运行时 materialiser(例如directDeposit、balanceOf)会在执行前使实际数量可用。参见 Build a Flow → Wire runtime values。 - 由某个 op 产生。 大多数 LI.FI ops(
lifi.swap、lifi.zap)在其amountOutport 上产生一个 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.sender、builder.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 可以绑定的值)。
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。

