Async (queued) withdrawals - deep dive
Level: Advanced
State machine (per epoch, simplified)
OPEN ── closeEpoch() (cutoff) ──► CLOSED ── processing ──► PROCESSED ── claim ──► CLAIMED
│ │
└ user may cancel └ user locked in; WITHDRAWAL_MANAGER may
moveRequestToNextEpoch(...)Roles
| Role | Power |
|---|---|
WITHDRAWAL_MANAGER | Advances epochs (close → process → claim); can move requests to next epoch; can process a partial epoch request for a closed-but-unprocessed epoch (epochID == latestEpochID − 1) at the current exchange rate. |
PRIORITY_WITHDRAWAL_EXECUTOR | Fast-path: settle a user's queued shares against the active epoch immediately, paying grossAssets − unwindCost. |
Priority withdrawal cost bound
unwindCost is supplied by the executor at call time but must be ≤ the admin cap unwindCostCapBP (default 500 bps, ceiling 10,000 bps, set via setUnwindCostCap); above the cap the call reverts. unwindCost is debited from the strategy's reported allocation (via async accounting), and the next accrual reconciles. This is a trusted operational role, not automatic.
Invariants enforced on allocation
Postconditions after allocate(...): idle balance must cover locked assets and, on async vaults, past-epoch unclaimed assets. This protects claims that are already reserved.
Caps (per epoch)
Cap = % of vault TVL processed per epoch. If requests ≤ cap: process normally. If > cap: process up to cap in FIFO, roll the rest. Waiting users keep earning (their shares are not burned until processed) but their final price is set at processing.
Cooldown/lock interplay
DepositLockHook / DepositLockWithFeeHook prevent locked shares from being withdrawn, redeemed or transferred until unlock. Early unlock costs a decaying fee in shares.
Failure modes to understand
| Scenario | Effect |
|---|---|
| Strategy accounting push late | Vault halts (incl. epoch processing) until fixed |
| Strategy missing from deallocation order | Assets counted but unavailable for withdrawals |
| Cap binds in a run on the vault | Multi-epoch queue; FIFO |
| Vault paused | Can't be upgraded until unpaused |
Reading queue state programmatically
Use the subgraph: Vault.isQueueActive, currentEpoch, latestProcessedEpoch, pastEpochUnclaimedAssets, currentEpochRequestedShares, and the WithdrawalQueue / PriorityWithdrawalClaimed / PartialEpochRequestProcessed entities. See Subgraph guide.