Fees
Withdrawals pay a rebalancing fee of up to 2.5%, computed at request time and charged only after the onchain withdrawal confirms. A canceled withdrawal is charged nothing.10,000 USD withdrawal with a +1.2% rebalancing rate charges 10,000 USD * 1.2% = 120 USD after the onchain withdrawal confirms.
Process
- Request. A signed request (
POST /withdrawal/init, verified against the account’s multisig configuration) specifying asset, amount (18-decimal), destination address, and optionally a nonce. - Lock and solvency check. The withdrawal amount is locked. The request is rejected with nothing locked if it would leave the account under-margined once the rebalancing fee is included.
- Delay queue. Every withdrawal waits a delay that scales with the account’s recent withdrawal-capacity usage for that asset, clamped to an onchain minimum and maximum. Queue status:
GET /withdrawal/queue/{user_id}. - Fill. After the delay, the withdrawal is filled from vault liquidity, FIFO per asset. Partial fills are accepted only when at least 50% of the remaining amount can be filled.
- Approval and execution. At the next checkpoint after the fill, the protocol signs an approval bound to the user’s nonce and a
validUntilexpiry. The user retrieves the signature(s) and callswithdraw(...)on the vault contract, paying gas. The vault verifies the signatures against the requiredwithdrawalQuorum, the nonce, and the expiry.
10,000 USDC remains, 4,999 USDC of available liquidity waits, while 5,000 USDC can fill.
Withdrawal capacity
The queue delay is not fixed — it grows with how much of an asset’s withdrawable liquidity the account has recently drawn down. Withdrawing an asset that is already heavily borrowed, or withdrawing the same asset repeatedly in a short span, lengthens the delay; depositing it back, or simply waiting, shortens it. Usage is tracked per account, per asset. Utilization. For each asset the protocol derives a utilization ratio from vault holdings (free plus locked, across chains) and the borrows outstanding against it:u runs from 0 (nothing borrowed) toward 1 (holdings nearly exhausted). It is a protocol-wide measure for the asset; the running total below is kept per account.
Capacity usage. A withdrawal lowers holdings and so raises u; a deposit does the reverse. The account accumulates the change through a convex potential function:
F climbs gently at low utilization and steeply as u nears 1, so an identical withdrawal costs far more capacity when the asset is already scarce. cap_floor (default 5%) floors the denominator, bounding how severe the penalty can get. The total is floored at zero — deposits cannot bank negative usage — and resets to zero after an hour, so only recent activity counts.
Delay. The accumulated usage is normalized to [0, 1] and mapped through onchain delay tiers, then clamped to the configured bounds:
min_delay, max_delay, the tier breakpoints, and cap_floor are onchain configuration; a higher normalized usage selects a higher tier.
Example: with tiers {0 → 1m, 0.3 → 5m, 0.6 → 30m}, min_delay = 1m, and max_delay = 60m, an account at normalized usage 0.4 waits 5m; a further withdrawal that pushes it to 0.7 moves it to the 30m tier.
Expiry and cancellation
- Approval expiry. If a signed approval’s
validUntilpasses before the user callswithdraw(...), the protocol verifies onchain that the withdrawal was never executed, then unlocks the locked funds; the user can submit a new request. An already-executed withdrawal is instead recognized as complete. - Unfilled timeout. A withdrawal that cannot be filled from vault liquidity expires from the queue after 24 hours, with the same onchain non-execution check before funds are unlocked.
- Cancellation. A queued withdrawal can be canceled with a signed cancel request (
POST /withdrawal/cancel). Locked funds return immediately; no rebalancing fee is charged.