> ## 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.

# Terminal Resources

> 终端资源、sweepTo 语义，以及数量如何在执行前被解析。

> [Guards](/composer/composer-api/concepts/simulation-and-guards) 解释了最小值如何在模拟期间被产生。本页解释它们如何被报告，以及在 flow 结束时 proxy 仍持有的任何代币如何一并处理。

Flow 描述资源如何在程序中流动。在执行结束时，proxy 仍持有的任何余额都需要一个明确的去向；否则代币会被搁浅。**Sweeping** 就是这一机制。**Amount preparation（数量准备）** 则是填入那些依赖链上状态的值。

本页两者都涵盖。它们位于编译流水线的两端（完整序列参见 [Execution Model](/composer/composer-api/concepts/execution-model)）。

## 终端资源

当 flow 中没有任何 op 消费某个资源时，该资源就是**终端**的。有两种到达此状态的方式：

* **产出型 op 的输出没有其他东西绑定。** 从 `lifi.zap` 存入某个 vault 得到的凭证代币，如果没有后续节点消费它，就是终端的。
* **悬空的 split 输出。** 当 `core.split` 产生 `{ a, b }` 而只有 `a` 被下游绑定时，`b` 就是终端的。关于这被有意为之的典型情形，参见 [`dust-sweep` recipe](/composer/composer-api/recipes/dust-sweep)。

编译器在构建时通过遍历绑定图来识别终端资源。每个终端资源都会出现在响应的 `producedResources` 中，并有资格被 sweep。关于资源生命周期，参见 [Resource Model](/composer/composer-api/concepts/resources-and-ports)。

## `sweepTo` 策略

`run.sweepTo` 字段告诉后端在执行结束时将终端资源发送到哪里。有三种形式：

* **`builder.context.sender`。** Sweep 到交易签名者。大多数 flow 使用这种方式。在线格式上，它序列化为 `{ "$ref": "context.sender" }`。
* **一个字面地址**（例如 `'0xRecipientAddress'`）。Sweep 到一个特定地址。当 flow 的目的是把代币交付给签名者以外的某人时使用。
* **省略。** 不 sweep。终端资源留在每个签名者独立的执行 proxy 上。这种情况很少见，主要用于那些有意在多次提交之间于 proxy 上累积余额的 flow。

当设置了 `sweepTo` 时，编译器会为每个终端资源追加转账指令。这些转账在**所有** op 调用**之后**、并在任何附加的 guard 断言完其不变量**之后**运行。

### 什么会被 sweep，什么不会

* **会：** 所有终端资源，包括悬空的 split 输出，以及产出型 op 中无人消费的输出。
* **不会：** 被另一个 op 消费的资源（它们是那个 op 的输入，不是残余）。Handles（类型化标量）。它们不是余额，永远不会被 sweep。

如果某个输入 materialiser 供应的资源多于消费型 op 所用的量，那么**未使用的余量也是一个终端资源**。这正是让 [`dust-sweep`](/composer/composer-api/recipes/dust-sweep) 起作用的原因：`core.split` 对一个输入进行分区，只有一个分区被消费，而未被绑定的分区自然变成了终端资源。

### 不可转移的终端资源

`sweepTo` 是一个**兜底项**：一旦设置，它会尝试把*每一份*残余的 proxy 余额转移到目标地址。大多数代币可以自由移动，但某些终端资源是被绑定的，**无法**被转出 —— 最常见的是**支撑一笔未平仓债务的 aToken（或其他抵押品凭证）**。如果移动该 aToken 会使仓位处于抵押不足状态，Aave 的 `finalizeTransfer` 就会回滚，因此对这样的余额进行一次一刀切的 `sweepTo` 会使**整笔交易回滚**。

当一个 flow 有意在 proxy 上保留一个仓位时 —— 例如一个保持抵押品在位的借款、杠杆或债务迁移 flow —— **不要**设置一刀切的 `sweepTo`。把抵押品留在持久化的、每个签名者独立的 proxy 上，只对那些真正松散的代币发出显式转账：用 `core.balanceOf`（owner 默认为 proxy）读取每一份，再用 `core.transfer` 移动它。

```ts theme={"system"}
// Read only the loose USDC sitting on the proxy...
const loose = builder.core.balanceOf('read-usdc', {
  bind: {},
  config: { token: USDC },
});

// ...and transfer just that to the signer. The aToken collateral is left untouched.
builder.core.transfer('payout', {
  bind: { amount: loose.balance, recipient: builder.context.sender },
  config: {},
});
```

Aave→Morpho [债务迁移 recipe](/composer/composer-api/recipes/debt-migration) 正是使用了这一模式。

## 数量准备

大多数数量在构建时是已知的。`directDeposit({ amount: '1000000000000000000' })` 供应一个字面值。有些数量只能通过在执行时读取链上状态才能得知：

* **`balanceOf` materialiser。** 在消费某个代币之前，读取 proxy 当前对该代币的余额。
* **`call` materialiser。** 调用一个 view 函数，并将其返回值用作输入数量。
* **Vault 份额价格读取。** 当某个下游 op 需要知道"该存入产生了多少份额代币"时，服务会在生成 calldata 之前，从当前链上状态解析该数量。

**数量准备**步骤（[执行模型](/composer/composer-api/concepts/execution-model)流水线的第 4 步）会解析这些。它在运行时输入解析之后、降级之前运行。失败会以 `preparation_error`（HTTP 422）的形式暴露，通常是因为链上读取返回了 `0` 或发生了回滚。

## 响应上的模拟数量

模拟之后，每个终端资源都有一个已知的**模拟数量**。编译器将它们作为 `ComposeCompileResult` 上的 `producedResources` 返回：

```ts theme={"system"}
result.producedResources;
// {
//   'zap.amountOut': {
//     kind: 'erc20',
//     token: '0x98C2…',
//     chainId: 1,
//     // owner, availability …
//     simulated: { amountOut: '12345678', amountOutMin: '12000000' },
//   },
//   'split.b': {
//     kind: 'erc20',
//     token: '0xA0b8…',
//     chainId: 1,
//     // owner, availability …
//     simulated: { amountOut: '2000000', amountOutMin: '2000000' /* dust, no minimum guard */ },
//   },
// }
```

每一条都携带该资源自身的元数据（`kind`、`token`、`chainId`、`owner`、`availability`）以及一个 `simulated` 块：

* **`simulated.amountOut`。** 在当前链状态（最新区块）下的确切模拟数量。在你的 UI 中把它显示为"你将收到 ≈ X"。
* **`simulated.amountOutMin`。** 给定附加到该资源输出 port 上的滑点 / 最小输出 guard，得到的最坏情况数量。把它显示为"你将**至少**收到 X"。当没有附加最小输出 guard 时（例如没有滑点保护的 split 产生的粉尘），`amountOutMin` 可能等于 `amountOut`；请检查响应，而不要假定它被省略。
* **多个 guard。** 当多个 guard 针对同一个 port 时，**最紧的最小值**胜出（最大的 `simulated.amountOutMin`）。

关于 `amountOutMin` 如何从某个 guard 的容差推导而来，参见 [Guards](/composer/composer-api/concepts/simulation-and-guards)。

## 另请参阅

* [Dust Sweep recipe](/composer/composer-api/recipes/dust-sweep) —— 由 `sweepTo` 回收的有意未消费的输入。
* [Execution Model](/composer/composer-api/concepts/execution-model) —— 数量准备和 sweeping 在编译流水线中的位置。
* [Build a Flow → Wire runtime values](/composer/composer-api/guides/build-a-flow#wire-runtime-values) —— `directDeposit`、`balanceOf` 和 `call` materialisers。
