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

> Terminal resources, sweepTo semantics, and how amounts are resolved before execution.

> [Guards](/composer/composer-api/concepts/simulation-and-guards) explained how minimums are produced during simulation. This page explains how they're reported, alongside any tokens still held by the proxy at the end.

A Flow describes how resources move through the program. At the end of execution, any balance still held by the per-signer execution proxy needs an explicit destination; otherwise tokens get stranded. **Sweeping** is the mechanism. **Amount preparation** is what fills in values that depend on on-chain state.

This page covers both. They sit at opposite ends of the compile pipeline (see [Execution Model](/composer/composer-api/concepts/execution-model) for the full sequence).

## Terminal resources

A resource is **terminal** when no op in the flow consumes it. There are two ways to arrive there:

* **Producing-op outputs nothing else binds to.** The receipt token from `lifi.zap` into a vault is terminal if no later node consumes it.
* **Dangling split outputs.** When `core.split` produces `{ a, b }` and only `a` is bound downstream, `b` is terminal. See the [`dust-sweep` recipe](/composer/composer-api/recipes/dust-sweep) for the canonical case where this is intentional.

The compiler identifies terminal resources at build time by walking the binding graph. Every terminal resource ends up in `producedResources` on the response and is eligible for sweeping. See [Resource Model](/composer/composer-api/concepts/resources-and-ports) for the resource lifecycle.

## `sweepTo` policy

The `run.sweepTo` field tells the backend where to send terminal resources at the end of execution. Three forms:

* **`builder.context.sender`.** Sweep to the transaction signer. Most flows use this. On the wire, it serialises to `{ "$ref": "context.sender" }`.
* **A literal address** (e.g. `'0xRecipientAddress'`). Sweep to a specific address. Used when the flow's purpose is to deliver tokens to someone other than the signer.
* **Omitted.** No sweep. Terminal resources stay on the per-signer execution proxy. This is rare and mostly useful for flows that intentionally accumulate balance on the proxy across multiple submissions.

When `sweepTo` is set, the compiler appends transfer instructions for every terminal resource. The transfers run **after** all op calls and **after** any attached guards have asserted their invariants.

### What does and does not get swept

* **Does:** all terminal resources, including dangling split outputs and producing-op outputs nothing consumed.
* **Does not:** resources consumed by another op (they are inputs to that op, not residual). Handles (typed scalars). They are not balances and never sweep.

If an input materialiser supplies more of a resource than the consuming op uses, the **unused remainder is also a terminal resource**. This is what makes [`dust-sweep`](/composer/composer-api/recipes/dust-sweep) work: `core.split` partitions an input, only one partition is consumed, and the unbound partition naturally becomes terminal.

### Non-transferable terminal resources

`sweepTo` is a **catch-all**: when set, it tries to transfer *every* residual proxy balance to the target. Most tokens move freely, but some terminal resources are bound and **cannot** be transferred out — most commonly **aTokens (or other collateral receipts) that back an open debt**. Aave's `finalizeTransfer` reverts if moving the aToken would leave the position under-collateralised, so a blanket `sweepTo` over such a balance makes the **whole transaction revert**.

When a flow deliberately leaves a position on the proxy — a borrow, leverage, or debt-migration flow that keeps collateral in place — **don't** set a blanket `sweepTo`. Leave the collateral on the persistent per-signer proxy and emit explicit transfers for only the genuinely-loose tokens: read each with `core.balanceOf` (owner defaults to the proxy) and move it with `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: {},
});
```

The Aave→Morpho [debt-migration recipe](/composer/composer-api/recipes/debt-migration) uses exactly this pattern.

## Amount preparation

Most amounts are known at build time. `directDeposit({ amount: '1000000000000000000' })` supplies a literal value. Some amounts can only be known by reading on-chain state at execution time:

* **`balanceOf` materialiser.** Read the proxy's current balance of a token before consuming it.
* **`call` materialiser.** Invoke a view function and use its return value as the input amount.
* **Vault share-price reads.** When a downstream op needs to know "how many share tokens did the deposit produce," the service resolves that amount from current on-chain state before producing calldata.

The **amount preparation** step (step 4 of the [execution model](/composer/composer-api/concepts/execution-model) pipeline) resolves these. It runs after runtime input resolution and before lowering. Failures surface as `preparation_error` (HTTP 422), typically because the on-chain read returned `0` or reverted.

## Simulated amounts on the response

After simulation, every terminal resource has a known **simulated amount**. The compiler returns these as `producedResources` on `ComposeCompileResult`:

```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 */ },
//   },
// }
```

Each entry carries the resource's own metadata (`kind`, `token`, `chainId`, `owner`, `availability`) plus a `simulated` block:

* **`simulated.amountOut`.** The exact simulated amount at current chain state (the latest block). Display this as "you will receive ≈ X" in your UI.
* **`simulated.amountOutMin`.** The worst-case amount given the slippage / minimum-out guards attached to that resource's output port. Display this as "you will receive **at least** X." When no minimum-out guard is attached (e.g. dust from a split with no slippage protection), `amountOutMin` may equal `amountOut`; check the response rather than assuming it's omitted.
* **Multiple guards.** When several guards target the same port, the **tightest minimum** wins (the largest `simulated.amountOutMin`).

See [Guards](/composer/composer-api/concepts/simulation-and-guards) for how `amountOutMin` is derived from a guard's tolerance.

## See also

* [Dust Sweep recipe](/composer/composer-api/recipes/dust-sweep) — intentional unconsumed input recovered by `sweepTo`.
* [Execution Model](/composer/composer-api/concepts/execution-model) — where amount preparation and sweeping sit in the compile pipeline.
* [Build a Flow → Wire runtime values](/composer/composer-api/guides/build-a-flow#wire-runtime-values) — `directDeposit`, `balanceOf`, and `call` materialisers.
