Skip to main content
Every deposit is gated by caps recorded against the vault’s owner EOA — no matter who sends the transaction (your EOA, or the relayer in a gasless deposit): a per-deposit cap, a rolling 30-day cap and a rolling 12-month cap. The caps are on-chain in the DepositLimitRegistry contract — the platform sets each wallet’s tier and caps from its KYC level, and vault.depositERC20(...) consults the registry on every call. A wallet whose tier has not been set yet cannot deposit at all (KycRequired()).

Caps by KYC level

The platform writes the same figure to the per-deposit and 30-day caps, so in practice the 12-month total is the one that binds, and a single deposit can use the whole remaining 12-month allowance. The effective cap can be lower than the table: a deposit limit the user sets for responsible gambling, or a compliance override, tightens it, and testnet wallets can carry different figures. Always read the live values from GET /api/me/deposit-limits rather than hard-coding the table. Caps only rise when the wallet clears a higher KYC level — there is no API to raise them. Partner-specific arrangements are agreed at onboarding — contact your integration manager.

Partner-backed sub-accounts

Sub-accounts under a multi_wallet partner with requirePerWalletKyc=false (see Partner kinds) are provisioned at the VIP tier with no per-wallet custom cap the moment the first request for the sub-account hits the API, so no on-chain deposit ceiling applies. Deposit / trade / withdraw restrictions are cleared in the same auto-onboarding step. No admin call required after the partner key is issued — the five on-chain ops (setTier(VIP), setCustomCaps(0, 0, 0), setDepositsRestricted(false), setTradingRestricted(false), setWithdrawalsRestricted(false)) are enqueued automatically and confirmed on-chain within ~30 sec. setCustomCaps(0, 0, 0) clears any per-wallet override, so the VIP tier’s defaults apply. The partner has taken KYB-level responsibility for its sub-accounts at the partner record. Need a per-sub-account deposit cap? Contact your integration manager.

Rolling-window semantics

Caps are enforced over rolling windows, not calendar months:
  • Per deposit: a single deposit must stay ≤ the per-deposit cap.
  • 30-day: deposits recorded in the current UTC day and the previous 29 days (1-day buckets), plus this deposit, must stay ≤ the 30-day cap.
  • 12-month: deposits recorded in the current 30-day bucket and the previous 11 (360 days in total), plus this deposit, must stay ≤ the 12-month cap.
Buckets are aligned to Unix time (UTC midnight for the 1-day buckets, multiples of 30 days since the epoch for the 12-month buckets — not calendar months). A deposit stops counting when its bucket leaves the window: 29–30 days after it was made for the 30-day window, 330–360 days for the 12-month window. A deposit that fails any check is rejected on-chain — the transaction reverts, no USDC moves, and the sender still pays gas for the revert. The registry runs its checks in this order and reports the first one that fails:

Reading your current usage

All amounts are decimal USDC strings. tier is the wallet’s on-chain registry tier — 0 = not set yet, 1–3 = capped tiers, 4 = VIP, 5 = rejected. It is not the KYC level itself: Basic and Standard both read 1 (Enhanced reads 2, Advanced 3), so tell levels apart by caps, not by tier. While tier is 0, vault.depositERC20 reverts with KycRequired() — see Initialisation timing below. A cap of "79228162514264337593543.950335" (type(uint96).max) is the on-chain “no cap” sentinel, not a real limit — treat that window and its remaining as unlimited. A VIP wallet with no tighter limit reads it for every cap. used sums the deposits still inside each rolling window at read time, so a deposit stops counting the moment its bucket rolls out — there is no window-reset timestamp to wait for. remaining is cap − used, floored at 0. After a deposit mines, used and remaining can lag the chain by up to a minute. restrictions.depositBlocked is true whenever a deposit would be refused right now: tier not set, a rejected wallet, a deposit restriction, or a cap already fully used — check remaining before attributing it to compliance. tradeBlocked and withdrawBlocked report the same for trading and withdrawals (a Basic wallet can deposit but not withdraw).

Initialisation timing

The platform writes a wallet’s tier and caps to DepositLimitRegistry in its own back-office transactions, driven by the wallet’s KYC level (for partner-backed sub-accounts, the VIP tier on the first request). This runs independently of vault deploy: GET /api/me/vault returning deployed: true does not mean the wallet can deposit yet. Until the tier lands, vault.depositERC20 reverts with KycRequired() (selector 0x709ea86e). Before the first deposit, poll GET /api/me/deposit-limits until tier is non-zero and restrictions.depositBlocked is false. Stop polling at tier: 5: the wallet was rejected by compliance and deposits revert with UserIsRejected(). If tier is 1–4 but depositBlocked stays true, the cause is a deposit restriction or a used-up cap, not timing.

Exceeding the cap

When a deposit reverts with SingleDepositCapExceeded(), Window30dCapExceeded() or Window12mCapExceeded(), the on-chain transaction does not settle and no USDC moves — the sender simply pays gas for the revert. Recovery options:
  1. Deposit a smaller amount. Stay ≤ the smallest of caps.singleDeposit, remaining.thirtyDay and remaining.twelveMonth from GET /api/me/deposit-limits. Right after a deposit mines, remaining can still show the pre-deposit figure for up to a minute, so allow for any deposit you made in the last minute.
  2. Wait for older deposits to roll out. used drops as buckets leave the window (see Rolling-window semantics); re-read GET /api/me/deposit-limits.
  3. Move to a higher KYC level. The cap follows the wallet’s KYC level; for partner-specific arrangements contact your integration manager.

Withdrawals are not symmetric

Cap accounting is deposit-only — withdrawing USDC from the vault does not “free up” deposit cap retroactively. The window is a sliding view of inflow, not a net-flow accounting. This matches the regulatory intent (AML deposit ceilings) and the on-chain DepositLimitRegistry implementation.

Next

Deposits overview

Approve + depositERC20 lifecycle.

Backend auto-deploy

How the platform deploys your vault; deposit limits are set separately.