Skip to Content
Velocity ProtocolBorrow & LendWithdrawal & Borrow Limits

Withdrawal & Borrow Limits

Every withdrawal and every new borrow is checked against your own account health, but it is also checked against a second, independent set of market-level guard rails. These market-level checks exist to protect a spot market’s liquidity buffer for all depositors. They can throttle or reject an action even when the requesting account is comfortably above its margin requirement.

These limits are not a margin or liquidation check. They apply on top of (not instead of) the standard withdraw margin requirement. See Withdraw and Close Account. A perfectly healthy account can still have a withdrawal or borrow rejected because the market it’s withdrawing from is near its liquidity limit.

There are two market-level calculations, one on each side of the vault:

  • Rolling withdrawal/borrow limits, an always-on formula, recalculated on every action, made of two checks (a level/TWAP-drift check and a utilization check), that caps how far a market’s deposit base can drain or its borrows can grow relative to its own 24-hour trailing averages.
  • Daily deposit cap: an opt-in mirror of the same idea on the deposit side, which caps how far a market’s total deposits may grow above their own 24-hour average in a window. Off by default; see Daily deposit cap.

The rolling calculation is sized by one per-market parameter, the withdraw guard threshold, an admin-configured notional value (capped at ~$10,000) that feeds every formula in the calculation above and also sets the size of the small-depositor exception budget below. It does not pause withdrawals on its own; see Protocol Guard Rails for the separate pause mechanism.

The rolling calculation is enforced on withdrawals, borrows, subaccount-to-subaccount transfers, and the settlement leg of token swaps, see What is Borrow & Lend? for how deposits/borrows relate to swaps and cross-collateral.

Rolling withdrawal & borrow limits

The check runs on every withdraw-shaped action against the affected spot market. It computes, from the market’s current balances and its 24-hour token/utilization trailing averages, two bounds:

  • Minimum deposit level: the lowest the market’s total deposits are allowed to fall to.
  • Maximum borrow level: the highest the market’s total borrows are allowed to rise to.

Each bound is the stricter of two independent calculations: a level/TWAP-drift check and a utilization check. Whichever constraint binds first wins.

Level check

Minimum deposits. The floor on deposits is the lower of “deposit TWAP minus the market’s withdraw circuit-breaker percentage” or “deposit TWAP minus the withdraw guard threshold”:

min_deposit_token = deposit_token_twap − max(deposit_token_twap × withdraw_circuit_breaker_bps / 10_000, min(withdraw_guard_threshold, deposit_token_twap))

The withdraw circuit-breaker is a per-market setting, in basis points (10_000 = 100%). If a market has never set one (the field reads 0), it defaults to 2_500 bps (25%), so the minimum-deposit floor defaults to 75% of the deposit TWAP unless the guard threshold cuts deeper. A larger guard threshold subtracts more from the TWAP, so it always loosens this floor. If the threshold is at or above the TWAP, that term collapses to the TWAP and the floor is set entirely by the circuit-breaker percentage.

The breaker is set per market by an admin update, and it must be at or below 10_000 bps. Authority is split by direction: a warm admin may keep or tighten it (any value at or below the 2,500 bps default), while raising it above 2,500 bps loosens the breaker and requires the cold admin. A tighter breaker means a smaller share of the deposit TWAP can leave per window.

Maximum borrows. The ceiling on borrows depends on whether the market is the main pool or an isolated pool, since isolated pools are tuned to tolerate materially higher utilization:

Main poolIsolated pool
Utilization-floor candidate1/3 (≈33%) of the lesser of deposits and deposit TWAP1/2 (50%) of the lesser of deposits and deposit TWAP
TWAP-drift candidateborrow TWAP plus 1/5 (20%) of the lesser of deposits and deposit TWAPborrow TWAP plus 1/3 (≈33%) of the lesser of deposits and deposit TWAP
Hard ceiling13/14 (≈92.9%) of the lesser of deposits and deposit TWAP19/20 (95%) of the lesser of deposits and deposit TWAP

The candidate value is the greater of the utilization-floor and TWAP-drift candidates, capped at the hard ceiling, then floored at the market’s withdraw guard threshold and capped again at the market’s total deposit ceiling times its max-borrow fraction, if that fraction is configured. In effect: borrows can grow by roughly 20% (main pool) or 33% (isolated) beyond the trailing borrow TWAP per window, but never above the pool’s hard utilization ceiling.

Utilization check

Separately, the market computes a maximum tolerable utilization for this action:

max_withdraw_utilization = max(optimal_utilization, utilization_twap + (100% − utilization_twap) / 2)

That is, never below the market’s target optimal utilization, and otherwise halfway between the current 24-hour utilization TWAP and 100%. This is translated back into token terms:

  • The minimum deposit level implied by utilization is the current borrow amount divided by the maximum tolerable utilization, capped so it never blocks withdrawals once the resulting deposits would still exceed the guard threshold.
  • The maximum borrow level implied by utilization is the maximum tolerable utilization times the current deposit amount, floored at the guard threshold so it never blocks small borrows.

Combining the checks

min_deposit_token = max(level-check min deposit, utilization-check min deposit) // stricter = higher max_borrow_token = min(level-check max borrow, utilization-check max borrow) // stricter = lower

The action is then validated against the market’s post-action balances:

  • If the account’s resulting position in this market is a borrow, both conditions must hold: the resulting borrow amount is at or below the maximum borrow level and the resulting deposit amount is at or above the minimum deposit level.
  • If the account stays a depositor, only the minimum-deposit condition is required.
  • If no specific account is passed in (a market-wide check with no user context), both conditions are required regardless of who ends up borrowing.

A withdrawal that draws an account’s deposit balance down to zero and then keeps going (opening a borrow) is checked more strictly than a withdrawal that only reduces an existing deposit, because it additionally has to clear the maximum-borrow ceiling.

Small-depositor exception

If the global check fails, one narrow exception can still let a withdrawal through. It is a per-account eligibility filter: it permits a depositor to withdraw their own principal if they have never net-withdrawn more than they’ve deposited, their position has always been a net-positive deposit, and their resulting deposit balance plus the amount withdrawn stays under 1/10th of the market’s withdraw guard threshold.

That per-account filter alone carries no information about how much the market can afford to release, so the market-level check also enforces a budget on top of it: the whole eligible cohort, no matter how many accounts qualify, can take the market’s deposits down by at most one withdraw guard threshold below the level-check floor. Because the withdraw guard threshold is itself capped at ~$10,000 notional, total exception outflow per market per TWAP window is bounded at roughly that amount, regardless of how many small accounts try to use the exception.

The per-market withdraw guard threshold

Each spot market carries a single admin-set withdraw guard threshold (in token-precision units) that feeds directly into the rolling formulas above: the level-check floor, the max-borrow ceiling, the utilization checks, and the small-depositor exception budget all read it, and it also acts as the market’s designated “small market” cutout: deposits below it are never blocked from withdrawal, and borrows below it are never blocked from opening.

Setting it is validated against a hard notional cap and requires only the warm-or-cold admin key, not cold-only:

  • Max notional: ≈$10,000, priced at the greater of the live oracle price and its 5-minute TWAP.
  • The update instruction requires warm-or-cold admin authority, and always resolves the market’s own oracle so the correct price feed is used to price the cap.

Full mechanics (including why the notional cap exists and how it interacts with oracle swaps) are documented in Protocol Guard Rails § Withdraw Guard Threshold.

Daily deposit cap

Deposits can be throttled too. The deposit side mirrors the level check above, using the same 24-hour deposit TWAP, and is governed by two per-market fields:

  • Max deposit growth (basis points): how far above the 24-hour deposit TWAP the market’s total deposits may grow in a window. 0 disables the cap entirely, which is the value every market is created with, so a market has no deposit cap until an admin sets one.
  • Deposit guard threshold (token-precision units): a floor below which the cap never binds. It is the deposit-side mirror of the withdraw guard threshold: deposits are always allowed to reach at least this level, however small the TWAP is.
max_deposit_token = max(deposit_token_twap + deposit_token_twap × max_deposit_bps_per_day / 10_000, deposit_guard_threshold)

The check is a level predicate over the whole market: after the deposit, the market’s total deposit token amount must be at or below this maximum. It is not a per-user quota, so your deposit can be rejected because of everyone else’s deposits in the same window, not because of your own size.

Because it is a level predicate, the check only applies when the market’s deposit level actually grew. It compares the deposit token amount before and after the balance update and returns early when the level did not rise. Withdrawals and borrow repayments share the same credit path, and those are exactly the actions that bring a market back under its cap, so they are never blocked by it.

A rejected deposit fails with a daily deposit limit error. Both fields are set together by a single admin update, which requires the warm-or-cold admin key and carries no notional cap of its own.

The deposit cap is one of several reasons a deposit can be refused. The market’s aggregate deposit ceiling, the market-scoped Deposit pause, reduce-only status, and pool-id mismatch are all separate checks, applied on the same instruction. See Block Conditions.

Where this shows up for you

  • Withdrawals and borrows: the most common place you’ll see this; see Withdraw and Close Account.
  • Subaccount transfers: moving collateral between your own subaccounts still draws down the source market, so it’s checked too.
  • Token swaps: the sell-leg of a swap is a withdrawal from that market’s perspective; see What is Borrow & Lend?.
  • Deposits: checked against the daily deposit cap, when the market has one configured.

If a withdrawal or borrow fails with a “daily withdraw limit” style error or a “market withdraws paused” error, check whether the market itself is near its rolling limit or has a tripped guard threshold before assuming there’s an issue with your account. The same goes for a deposit rejected with a daily deposit limit error. These are all market-wide conditions, not account-specific ones.

Last updated on