> ## Documentation Index
> Fetch the complete documentation index at: https://docs.li.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Resource Model

> 资源生命周期、port 行为、线性消费，以及 asResource 的 graduation。

> [Flow Structure](/composer/composer-api/concepts/flow-anatomy) 展示了 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](/composer/composer-api/guides/build-a-flow#wire-runtime-values)。
* **由某个 op 产生。** 大多数 LI.FI ops（`lifi.swap`、`lifi.zap`）在其 `amountOut` port 上产生一个 resource。接收方 op 会将该 handle 绑定到它自己的输入 port。
* **由 `core.asResource` 晋升。** 有时你需要把*一个外部合约调用的输出*当作被跟踪的资源来处理，例如某个 ERC-4626 vault 从 `deposit()` 返回的凭证代币（receipt token）。`core.asResource` 接收一个类型化 handle 加一份资源声明，并将其晋升为一等公民资源。关于 manifest 签名，参见 [Op Catalog](/composer/composer-api/ops)。

### 线性消费

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](/composer/composer-api/concepts/sweeping-and-amounts)。

## Handles

一个 handle 是由 SDK builder 产生的类型化标量：可以是 flow 输入的输入 handle、某个 op 输出 port 的输出 handle，或一个上下文 handle（`builder.context.sender`、`builder.context.executionAddress`）。SDK 会将 handles 序列化为线格式的 `$ref` 字符串；关于转换规则和 `raw.ref` 应急出口，参见 [References](/composer/composer-api/concepts/ref-grammar)。

与 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](/composer/composer-api/concepts/ref-grammar#raw-refs-escape-hatch)）。这样做时，manifest 中的类型守卫会决定该路径是 handle 还是 resource port；如果你选错了类型参数，校验器会拒绝该 flow。

### `providesMinimum`

某些输出 port 已经编码了一个最小输出不变量。`lifi.swap.amountOut` 是典型例子：聚合器已从该 swap 的 `slippage` 配置中烘焙进 `minOut`，因此该 port 在 manifest 中声明了 `providesMinimum: true`。校验器会拒绝对这些 port 添加冗余的最小输出 guard。关于完整的附加规则，参见 [Guards](/composer/composer-api/concepts/simulation-and-guards#where-guards-can-attach-providesminimum-ports)。

> Port 之间的绑定以 **refs** 的形式书写。下一页 [References](/composer/composer-api/concepts/ref-grammar) 介绍其语法。

## 另请参阅

* [Flow Structure](/composer/composer-api/concepts/flow-anatomy) —— inputs 和节点绑定的线格式形态。
* [References](/composer/composer-api/concepts/ref-grammar) —— SDK handles 如何转换为 refs。
* [Terminal Resources](/composer/composer-api/concepts/sweeping-and-amounts) —— flow 结束时的终端资源处理。
* [Op Catalog](/composer/composer-api/ops) —— `core.asResource` 以及 manifest 中注册的每一个 op。
