Skip to main content

Upgrade the vault

Idempotent. No wallet signature and no 2FA code. The upgrade moves no money and cancels no orders, and it cannot be undone.
From activation on, deposits (depositERC20, relay, on-ramp) go to the generation-2 vault. Read vaultAddress from GET /api/me/vault before each deposit.

Move USDC from the generation-1 vault

Moves free USDC into the generation-2 vault without leaving the platform: no deposit-limit usage, no bonus or referral trigger, no deposit or withdrawal notification, no 2FA code. One move can be open per wallet.
1

Create the move

Omit amount to move all free USDC (usdcMove.freeUsdc in GET /api/me/vault; null for a key without portfolio:read). 201 is a new move; 200 returns your open move, which may be for another amount or already signed. Every new move is atomic (mode: "atomic") — one platform transaction withdraws from your generation-1 vault directly to your wallet, pulls it with your USDC permit, and deposits it into your generation-2 vault; it lands whole or not at all. The answer carries transfer.signRequests: withdraw (WithdrawERC20 with to set to YOUR wallet; also returned as signRequest), and permit (USDC EIP-2612 Permit ):
amount is in 6-decimal units. signRequests is null once the move is signed.
JIT exposure warning (warnings): If moving funds leaves collateral-exempt (JIT) market-maker quotes unfunded on the generation-1 vault, both POST …/transfer and POST …/signature include warnings: [{ code: 'jit_quotes_unfunded_after_move', vaultAddress, unfundedNotional }]. The move is not blocked, but warned so market makers can adjust resting quotes.
2

Check and sign both

Check that signRequests.withdraw.domain.verifyingContract is your generation-1 vault, token is usdc from GET /api/platform/contracts, to is your wallet address, and amount matches the move. For signRequests.permit, check that verifyingContract is the USDC contract and value matches the amount. Sign both typed data payloads with your wallet and send both signatures:
202. Sending the same signatures again returns the same answer.
3

Follow it

Poll GET /api/me/vault/upgrade/transfer/{id}, or watch vault_move_updated on user_activity, until completed, failed, or withdrawn_to_wallet (for legacy moves: deposit_confirmed, refunded, failed or needs_ops).
After a crash, call the create again: with a move open it returns that move (200), never a second one. If its signRequests is null, do not sign again; poll it.

Statuses

Limits and timing

  • Sign before signRequests.withdraw.message.deadline (or signRequest.message.deadline, 1 hour after creation by default), else 409 vault_move_expired; create a new move.
  • The withdraw half of an atomic move is locked exclusively to the migration batch and cannot be cancelled via DELETE /api/me/withdrawals/:id (409 vault_move_withdrawal_not_cancellable). If execution cannot proceed on chain, the lock is released automatically once proven dead.
  • Each atomic move executes in a single multicall transaction (txHash).
  • Only available USDC moves. USDC locked by open orders or pending operations stays (409 insufficient_free_balance). Cancel the generation-1 orders you no longer want first. Source generation-1 free USDC (usdcMove.freeUsdc on GET /api/me/vault and the cap on POST /api/me/vault/upgrade/transfer) uses min(ownerStateAvailable, balancesRowAvailable) when the exchange owner state answers (RND-2091). If the source generation-1 exchange.balances row cannot be read, POST /api/me/vault/upgrade/transfer always returns retryable 503 vault_move_unavailable (retryable: true), whereas GET /api/me/vault falls back to the owner-state figure when owner state answered for that vault and degrades usdcMove to null only when owner state could not supply the figure either.
  • While the move is open, the amount is locked on the generation-1 vault.
  • Keep enough USDC on the generation-1 vault for the legacy quotes you keep until the generation-1 trading halt.
  • KYC withdrawal tier (RND-1873 / RND-1879). The caller must hold minimum KYC withdrawal tier 1 (403 withdraw_kyc_tier_required). KYB-waived partner callers pass.
  • Scope-specific withdrawals freeze. When platform or withdrawals-only kill-switches are active, transfer creation and signing return 503 platform_frozen / 503 withdrawals_frozen (retryAfterSec).
  • Limiting factors (limitedBy). On GET /api/me/vault, usdcMove.limitedBy clarifies why movableUsdc is less than freeUsdc, reporting bonus_lock, withdraw_limit, withdraw_blocked, platform_frozen, withdrawals_frozen, partner_restricted, or withdraw_tier. Offer the move only when movableUsdc > 0.