Upgrade the vault
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
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).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(orsignRequest.message.deadline, 1 hour after creation by default), else409 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
availableUSDC 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.freeUsdconGET /api/me/vaultand the cap onPOST /api/me/vault/upgrade/transfer) usesmin(ownerStateAvailable, balancesRowAvailable)when the exchange owner state answers (RND-2091). If the source generation-1exchange.balancesrow cannot be read,POST /api/me/vault/upgrade/transferalways returns retryable503 vault_move_unavailable(retryable: true), whereasGET /api/me/vaultfalls back to the owner-state figure when owner state answered for that vault and degradesusdcMovetonullonly 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). OnGET /api/me/vault,usdcMove.limitedByclarifies whymovableUsdcis less thanfreeUsdc, reportingbonus_lock,withdraw_limit,withdraw_blocked,platform_frozen,withdrawals_frozen,partner_restricted, orwithdraw_tier. Offer the move only whenmovableUsdc> 0.