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 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.zapinto a vault is terminal if no later node consumes it. - Dangling split outputs. When
core.splitproduces{ a, b }and onlyais bound downstream,bis terminal. See thedust-sweeprecipe for the canonical case where this is intentional.
producedResources on the response and is eligible for sweeping. See Resource Model 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.
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.
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.
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:
balanceOfmaterialiser. Read the proxy’s current balance of a token before consuming it.callmaterialiser. 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.
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 asproducedResources on ComposeCompileResult:
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),amountOutMinmay equalamountOut; 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).
amountOutMin is derived from a guard’s tolerance.
See also
- Dust Sweep recipe — intentional unconsumed input recovered by
sweepTo. - Execution Model — where amount preparation and sweeping sit in the compile pipeline.
- Build a Flow → Wire runtime values —
directDeposit,balanceOf, andcallmaterialisers.

