Async redeem semantics
Sync vs. async vault withdrawals, the settlement guard, and the never-trapped invariant.
Aqua0's vaults are per-asset, per-chain ERC-4626 vaults (e.g. Base aqUSDC and Unichain aqUSDC
are separate vaults with independent share prices) that additionally implement ERC-7540 for the
case where your shares represent capital currently deployed rather than idle. This page explains
when you get an instant redemption versus a request/claim flow, and the guarantee that backs the
async path.
The vault API reference endpoints referenced below (prepare-redeem, prepare-request-redeem,
claim, withdrawability, pending-requests) are not yet published. The API reference tab
auto-generates from the live OpenAPI spec once the backend ships these routes; this page describes
the on-chain/protocol semantics you should expect ahead of that. Check
API overview for current endpoint availability.
Overview
Every deposit mints vault shares against totalAssets() = idle + deployedAssets. Which redemption
path applies depends only on whether the assets backing your redemption are currently idle in the
vault or out working at a venue via an adapter — you don't choose the path, the vault determines it.
Sync vs async paths
| Sync (free balance) | Async (deployed capital) | |
|---|---|---|
| Applies when | The vault has enough idle balance to cover the redemption | The redemption requires unwinding/settling deployed capital |
| Call | Standard ERC-4626 redeem | ERC-7540 requestRedeem → wait → claim |
| Latency | Immediate, same transaction | Request now, claim once claimable (see below) |
| Share price used | Current, at time of redeem | Locked in at request time per ERC-7540 semantics |
If you don't know in advance which path a given redemption will take, call the withdrawability check first (see Status & errors for the general polling pattern this follows).
Requesting a redemption
For deployed capital, requestRedeem starts the async flow. The vault does not immediately move
assets — it records the request and begins freeing the underlying position (e.g. unwinding an
adapter position, waiting for a cross-chain settlement to land). This is not a delay imposed for
its own sake; it reflects that the capital is genuinely in use elsewhere at request time.
The settlement guard
A pending request becomes claimable when either:
- Attested-free — the protocol's signer attests that the underlying capital backing the request has actually been freed (adapter unwound, settlement landed), or
- Timeout backstop —
now >= requestTime + timeout[route]elapses, regardless of attestation.
Every route's timeout is itself bounded: timeout[route] <= GLOBAL_MAX_TIMEOUT, an immutable
7-day ceiling that no owner, governance, or upgrade path can raise. Beta ships tighter defaults —
same-chain routes at 1 hour, cross-chain routes at 12 hours — chosen conservatively above
measured settlement latency, with room to tighten further as real production data comes in. They
can only ever be tuned down toward the observed latency, never up past the 7-day ceiling.
Never-trapped invariant
This two-path guard is what makes the design never-trapped: even in a worst case where the attestation path fails entirely (e.g. the attesting signer is offline), every request still becomes claimable on its own once its timeout elapses — at most 7 days after the request, by construction. There is no scenario in which a valid redemption request can be withheld indefinitely.
Claiming
Once a request is claimable, call claim to receive the underlying assets. Claiming does not
require the original attestation to still be valid — the guard condition is evaluated at claim time
using the same attested-free OR timeout-elapsed logic, so a request that only clears via the
timeout backstop claims exactly the same way as one that clears via attestation.
Per-chain share isolation and the cross-chain filler caveat
Shares are not fungible across chains: Base aqUSDC and Unichain aqUSDC are separate vaults
with independent share prices, each reflecting only that chain's realized and reported PnL.
This does not mean capital deposited on one chain can only ever be used on that same chain. Chain-A vault capital can still back a strategy or filler that executes on Chain-B — that's a deliberate, first-class case, not a contradiction of per-chain isolation. It's precisely why the hybrid PnL model (see Non-custodial design) needs a bounded cross-chain reporter in the first place: the Chain-A vault needs a way to value capital that is currently working on Chain-B, before the cross-chain settlement lands and becomes an on-chain fact.
Status & error states
Track a pending redemption the same way you'd track a swap (see Status & errors for the general pattern):
- requested —
requestRedeemsucceeded, capital-freeing in progress - claimable (attested) — signer attested the capital is free; claim now for the fastest path
- claimable (timeout) — the per-route timeout elapsed; claim now
- claimed (terminal, success)
Treat "requested" as expected/normal for deployed-capital redemptions on any route, especially cross-chain ones — it is not an error state. Only escalate if a request is not claimable well past its route's timeout, which should not be possible by design.
FAQ
Can a redemption ever be permanently stuck?
No — by design every request is claimable within its route's timeout, and every route's timeout is
capped by the immutable 7-day GLOBAL_MAX_TIMEOUT. See "Never-trapped invariant" above.
Does requesting a redemption lock in my share price immediately? Yes, per ERC-7540 semantics — the exchange rate is fixed at request time, not at claim time.
Why would my redemption go through the async path instead of instantly? Only when the vault doesn't have enough idle balance to cover it — i.e. your shares represent capital currently deployed via an adapter rather than sitting idle in the vault.