Skip to main content

USDC

tip

Stable. USDC is one balance on MetaFlux. The perp collateral account and the spendable spot-USDC balance are the same number. One exception: a standard-mode account holds two — see the standard-mode split.

TL;DR

  • One pool. A perp order, a spot buy, an Earn deposit and a withdrawal all spend the same USDC, gated on the same free collateral.
  • The concept still carries several ids: asset: 0 on bridge and withdraw surfaces, asset: 100 on spot market and balance surfaces, an ERC-20 on the EVM, and a separate contract on every external chain.
  • Two number planes. /info and most /exchange fields are whole-USDC decimal strings. bridge_withdraw and the EVM token are 6-decimal integers. Mixing them is an error of 10⁶.
  • usd_class_transfer is rejected on a unified account. There is no second pool to move to. A split standard account is the one exception — see the standard-mode split.

The four identities

One concept, four addressing schemes. This is the table to read before you write any code that names USDC.

SurfaceHow USDC is addressedNumber plane
Perp collateral (the pool)Not a ledger row. It is the account's own balance, read as account_value / withdrawable. Bridge deposits, bridge_withdraw and core_evm_transfer address it as asset: 0whole-USDC decimal string
Spot tokenAsset id 100 — the quote of every */USDC pair and the id of the USDC row in markets_meta tokens[] and in account_state spot.balances[]whole-USDC decimal string
EVM tokenERC-20 at the fixed predeploy 0x00000000000000000000000000000000000100006-decimal integer
External chainsEach chain's own USDC contract, held in MetaBridge custody6-decimal integer on the MTF wire
warning

asset: 0 and asset: 100 both mean USDC. They are not two currencies and, on this network, not two balances either. 0 is the collateral-plane id the bridge and withdraw paths use; 100 is the spot-ledger token id the market and balance surfaces use. Which id an action wants is fixed per action — it is not a free choice:

  • bridge_withdraw and core_evm_transfer take 0. On core_evm_transfer, asset: 100 is rejected with asset not linked to an EVM contract — the spot USDC token id carries no EVM contract binding, because the EVM-side USDC is reached through the collateral-plane id instead. asset defaults to 0; leave it alone.
  • The spot market and balance surfaces — markets_meta, account_state spot.balances[], and every */USDC pair's quote — use 100.

One pool

MetaFlux does not hold a separate spendable spot-USDC ledger. Every USDC movement — perp margin, spot buys, spot-order escrow, Earn deposits, spot-margin positions, account-to-account sends, EVM moves and external withdrawals — debits and credits one account balance: the settled USDC balance that account_state reports in its USDC row and folds into account_value.

The spot token id 100 still exists. It names the */USDC pairs and it still keys the escrow bucket that holds USDC locked behind a resting spot bid. What it no longer keys is a second spendable balance.

info

Self-hosted networks differ. The unified rule is active from block 0 on the public network (chainId 114514) and on mainnet (chainId 8964). A network running any other chain id keeps two separate USDC ledgers — a spot balance and a perp collateral balance — until its validators arm the change by a two-thirds stake vote. On such a network usd_class_transfer is a working action. Check which world you are in with eth_chainId.

Which balance your order spends

All of them spend the pool. What differs is the gate.

You do thisIt spendsGate
Open or add to a perp positionthe poolinitial margin ≤ free collateral
Place a spot buythe poolthe buyable size is clamped to free collateral, not to any spot balance
Rest a spot bidthe poolthe quote cost moves out of the pool into escrow (hold) at admission
Place a spot sellthe base token on the spot ledgeryou must own the base
Deposit to Earnthe poolamount ≤ free collateral
Open a spot-margin positionthe poolits initial margin is held against the pool
bridge_withdraw / core_evm_transfer / a sendthe poolamount ≤ free collateral

Free collateral is the one budget every debit above is measured against:

free collateral = settled USDC balance
− initial margin held by open CROSS perp positions
− initial margin held by open spot-margin positions
− any impending funding CHARGE

The gate figure is signed; the published field is not. The admission and withdrawal checks compare a debit against the raw value above, which goes negative when open profit funds the held margin. The API publishes the same budget as withdrawable, clamped:

withdrawable = max(0, free collateral)

Read it from account_state.withdrawable or the lighter account_state with detail: "margin". A withdrawable of "0" therefore means "nothing to take out", NOT "the account is broke" — see account value. Two rules behind the formula are worth stating:

  • Unrealised gains never count. Free collateral folds an impending funding debit but never an unrealised profit — so a paper gain does not fund a new order, a spot buy or a withdrawal.
  • Nothing is subtracted twice. A committed lock (spot escrow, an Earn deposit, a spot-margin post) is debited out of the balance at the moment it commits. It is gone from the balance, not carried as a separate held term.

Because it is one pool, the two directions are real and intended:

  • A resting spot bid lowers your perp margin headroom for as long as it rests.
  • A perp loss lowers what you can spend on spot or supply to Earn.

Cancel the bid and the escrow returns to the pool.

The standard-mode split

info

Live from node 0.9.7, block 5,710,001. An account already in standard at the swap keeps one balance until it leaves the mode and enters again.

From the swap, an account that enters standard mode holds two USDC wallets:

  • The perp wallet is the collateral account. Perp margin, funding, liquidation, ADL, vaults and bridge withdrawals read and write this one.
  • The spot wallet is spot token 100. Spot orders, spot fills, spot fees, send_asset of USDC, Core→EVM transfers and Earn read and write this one. An Earn deposit the spot wallet cannot fund is refused PRECONDITION_FAILED with the message insufficient balance, not ASSET_INSUFFICIENT_BALANCE.
  • usd_class_transfer is the only lane that crosses. It moves one amount from one wallet to the other.

Four rules follow, and each is deliberate:

  1. A perp loss cannot reach the spot wallet. A split account's perp bankruptcy is absorbed by the insurance fund and ADL, never by its spot USDC.
  2. A split account has no reservations. It is refused spot margin (spot margin is not available in standard mode) and every reservation (a split standard account has no reservations).
  3. Each wallet funds its own orders, with no cap. Perp and option orders are admitted against the perp wallet's free collateral. Spot orders are admitted against the spot wallet. A spot order the spot wallet cannot fund is refused: insufficient spot balance.
  4. Only entry splits. An account already in standard at the swap keeps one balance until it leaves the mode and enters again. Leaving folds the spot wallet back into the pool.
Not live yet

Two parts of the rules above ship with the next node release after 0.9.7: the refusal of every reservation in rule 2, and the whole of rule 3. The spot-margin refusal in rule 2 is live now. Until the release, a live node refuses only a nonzero spot reservation on a split account (spot has its own wallet in standard mode; no spot reservation). It still caps perp and option orders by the perp and option reservations, and it accepts an unfunded spot order as a no-op.

Reading the two wallets. account_value and withdrawable on account_state are the perp wallet. The USDC row of spot.balances is the spot wallet, and total − hold is what a new spot order may spend. Add account_value and that row's total for the account total.

Moving USDC

MoveHowWhat it costs
Perp ↔ spot classusd_class_transfer, split standard accounts onlyNo protocol fee. Rejected on every other account — see below
To another MetaFlux accountsend_assetNo protocol fee
Parent ↔ sub-accountsub_account_transfer / sub_account_spot_transferNo protocol fee
Core → EVMcore_evm_transferNo protocol fee; the amount is rescaled ×10⁶
EVM → CoreAn EVM burn transaction — not an /exchange actionEVM gas
Off the networkbridge_withdrawA bridge withdraw fee, withheld from the released amount
warning

usd_class_transfer is rejected, with USDC is unified; no class transfer needed. Nothing is lost — the move it used to perform has no destination now.

The rejection is in the shared handler, so every route to it rejects: the /exchange action, the CoreWriter UsdClassTransfer from an EVM contract, and a self-send_asset on asset 100 that crosses the spot/perp boundary (which is routed into the same handler by design).

Every debit path is free-collateral gated; no credit path is. A send, an EVM move or a withdrawal is rejected when it would eat collateral that is margining an open position — you cannot withdraw your way below maintenance margin. An incoming credit only raises free collateral, so it needs no gate.

The bridge withdraw fee is a governance parameter in 6-decimal units, withheld from the released amount: you are debited the gross amount, the outbound message carries the net, and the difference accrues to the protocol. The action rejects when amount does not exceed the fee. This page does not publish the live fee value — read the rejection, or quote the withdrawal in your client and compare gross against released.

Precision, surface by surface

SurfaceFieldUnit1 USDC looks like
POST /info readsaccount_value, withdrawable, spot.balances[*].total / .holdwhole-USDC decimal string"1"
POST /exchange send_assetamountwhole-USDC decimal string"1"
POST /exchange core_evm_transferamountwhole-USDC decimal string"1"
POST /exchange bridge_withdrawamount6-decimal integer (uint64)1000000
EVM ERC-20 at 0x…010000balanceOf, transfer6-decimal integer1000000
Bridge deposit attestationsamount6-decimal integer1000000

The one conversion:

evm_or_bridge_units = whole_usdc × 1_000_000
warning

bridge_withdraw is the exception, and it is a 10⁶ trap. It is the only MTF-native USDC field that is a bare integer in base units rather than a decimal string. "amount": 1000000 there is 1 USDC. The same literal on send_asset or core_evm_transfer is 1,000,000 USDC — those fields are whole-USDC strings. Check which action you are signing before you fill the field.

Both planes are exact: MetaFlux holds USDC as a fixed-point decimal, never a float, and the ×10⁶ rescale only repositions the decimal point. See two price planes for the separate question of price scaling.

What the account reads report

Two reads claim to show "your USDC". They do not agree, and one of them shows nothing at all.

account_state

POST /info account_state is the read to use.

FieldWhat it isThe rule behind it
account_valueMark-aware equity: settled USDC plus unrealised perp PnL, unrealised funding and spot-margin unrealised PnLThis is the figure the liquidation engine judges you on. Split standard account: the perp wallet only
spot.balances[0] (name: "USDC", signing_id: 100)total = settled USDC plus escrow; hold = USDC escrowed behind resting spot bidstotal deliberately excludes unrealised PnL, so it never moves with the mark. total − hold is not spendable: hold is spot escrow only and never holds perp margin, so the subtraction leaves the margin in. Use withdrawable. Split standard account: the row is the spot wallet alone, so total − hold is what a spot order may spend — see the standard-mode split
withdrawableWhat a new order, send, withdrawal or Earn deposit may consumeThe budget above, clamped at zero. Split standard account: a USDC send and a spot order read the spot wallet (insufficient spot balance), and so does an Earn deposit (insufficient balance). withdrawable bounds perp and option orders and withdrawals only — see the standard-mode split

Why two numbers. account_value and spot.balances[0].total both look like "my USDC" and differ by unrealised PnL. Use account_value for equity and risk; use spot.balances[0].total for cash that has actually settled. The USDC row is always present, even at zero.

One ledger, one read

account_state spot.balances carries the WHOLE token ledger: the unified USDC pool in row 0, and every spot token after it. There is no second balance read to merge in.

warning

Read withdrawable, not total − hold. hold is spot order escrow only. USDC that margins an open perpetual position stays in total and never enters hold, so the subtraction leaves the margin in and overstates the budget.

Cost basis rides on the same rows. avg_entry_px is the per-token acquisition price spot PnL needs. The USDC row always reads null — a cost basis on the quote asset in terms of itself has no meaning.

A worked check

Claim the testnet faucet, which grants 3000 USDC and 10 MTF, then read account_state:

  • account_value: "3000", and spot.balances carries the USDC row (signing_id 100, total "3000") and an MTF row (asset 104, total "10").

One call, both rows. That is the unification.

USDC on the MetaFlux EVM

USDC on the MetaFlux EVM is Circle's FiatTokenV2_2 implementation behind a proxy, seeded at genesis at the fixed address 0x0000000000000000000000000000000000010000, with 6 decimals. It is a real ERC-20 — balanceOf, transfer, approve — and, being the Circle implementation, it carries permit (EIP-2612) and transferWithAuthorization (EIP-3009).

Core → EVM is core_evm_transfer. The Core debit is atomic at commit and the EVM credit is minted on the next EVM block, so the queued credit is always fully backed. Optional calldata attached to the transfer never unwinds the credit: if it reverts, the transfer still stands — read its receipt.

EVM → Core is not an /exchange action. It must originate as an EVM transaction that burns the EVM USDC; the node then mirrors the confirmed burn onto the Core balance. Posting core_evm_transfer with to_evm: false is rejected. The rule behind it: crediting Core without a confirmed burn would mint value out of nothing.

Full mechanics and timings: Core ↔ EVM transfers.

USDC from outside

All USDC enters and leaves MetaFlux through the self-built MetaBridge custody bridge, co-signed by two thirds of active validator stake. There is no Circle CCTP path.

  • A deposit credits the pool directly — it lands as collateral, ready to margin a perp or fund a spot buy, with no second step.
  • A withdrawal is bridge_withdraw with asset: 0. Only USDC is bridgeable today; any other asset id is rejected.
  • The destination-chain release is asynchronous: the Core debit is immediate at commit, the payout follows co-signing and relay.

Not covered here

  • The live bridge withdraw-fee value. The mechanism is above; the number is a governance parameter and no /info read publishes it.
  • Per-chain USDC contract addresses. See the bridge page.
  • Non-USDC collateral. Cross-asset collateral haircuts are a portfolio-margin topic.

See also

  • Margin modes — how the initial and maintenance margin that reduce free collateral are computed
  • Spot — the escrow model behind hold
  • Earn — the lending pool USDC can be supplied to
  • Bridge — deposits, withdrawals and the co-signing pipeline