> ## Documentation Index
> Fetch the complete documentation index at: https://docs.0xmonaco.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Account Balances

> Read Monaco spot balances, ledger movements, portfolio state, and live account balance updates.

Account balance reads show what the authenticated wallet has available, locked, and total inside Monaco. Use this page when your app needs balance tables, order-entry validation, movement history, or live balance updates.

## Surface Map

| Surface | Use |
| - | - |
| REST | `GET /api/v1/accounts/balances`, `GET /api/v1/accounts/balances/{assetId}`, `GET /api/v1/accounts/movements`, `GET /api/v1/accounts/funding-payments` |
| TypeScript SDK | `sdk.profile.getUserBalances()`, `sdk.profile.getUserBalanceByAssetId()`, `sdk.profile.getPaginatedUserMovements()`, `sdk.profile.listFundingPayments()` |
| React SDK | `useUserBalances()`, `useUserMovements()`, `useProfile()` |
| WebSocket | `balances`, `movements` |
| gRPC | `AccountsService` |

## Balance Model

Every Monaco balance row is scoped to an asset UUID.

| Field | Meaning |
| - | - |
| `assetId` | Monaco asset UUID used by account, vault, and trading APIs |
| `token` | ERC20 token contract address |
| `symbol` | Display symbol, such as `USDC` or `BTC` |
| `decimals` | Token decimal places |
| `availableBalance` | Human-readable amount available for trading or withdrawal |
| `lockedBalance` | Human-readable amount reserved by open orders or pending operations |
| `totalBalance` | Human-readable available plus locked balance |
| `availableBalanceRaw` | Raw smallest-unit amount available for exact comparisons |
| `lockedBalanceRaw` | Raw smallest-unit amount currently locked |
| `totalBalanceRaw` | Raw smallest-unit available plus locked balance |
| `version` | Opaque, non-negative revision of this spot row. Compare only within the same authenticated user, application, and token; higher is newer. 0 is the unranked legacy sentinel and never participates in ordering: it means the row predates this field or came from a producer that does not stamp it, so treat it exactly as an absent version and fall back to a full field comparison rather than ranking it as oldest. |
| `wrappedNative` | Optional boolean. `true` when the asset is the chain's wrapped-native token (e.g. WSEI): deposits go in as native coin via the vault's payable `depositNative` (no ERC20 approval) and withdrawals pay out native coin. `false` for standard ERC-20 assets. |

Use raw fields for validation and transaction amounts. Use normalized fields for display.

## Read Balances

`getUserBalances` returns paginated balances for the authenticated wallet session. It is cursor-paginated: omit `pageToken` or pass `""` to start, then follow `nextPageToken` until it comes back empty. There is no `page`: the SDK rejects one with a `ValidationError` before sending, and a raw REST request carrying it gets `400`.

```typescript theme={null}
const { balances, total } = await sdk.profile.getUserBalances({
  pageSize: 100,
});

for (const balance of balances) {
  console.log(balance.assetId, balance.symbol, balance.availableBalance);
}

// Walk every page: follow nextPageToken until it comes back empty.
let pageToken = "";
for (;;) {
  const page = await sdk.profile.getUserBalances({ pageToken, pageSize: 100 });
  // ... handle page.balances
  if (page.nextPageToken === "") break;
  pageToken = page.nextPageToken;
}
```

Use `getUserBalanceByAssetId` when validating a specific order, deposit, withdrawal, or transfer.

```typescript theme={null}
const pair = await sdk.market.getTradingPairBySymbol("BTC/USDC");
const quoteBalance = await sdk.profile.getUserBalanceByAssetId(pair.quoteAssetId);

console.log({
  symbol: quoteBalance.symbol,
  available: quoteBalance.availableBalance,
  locked: quoteBalance.lockedBalance,
  total: quoteBalance.totalBalance,
});
```

## Movements

Movements are the ledger history behind balance changes. Use them for account activity, reconciliation, and user-facing transaction history.

```typescript theme={null}
const { movements } = await sdk.profile.getPaginatedUserMovements({
  pageSize: 50,
});

// Walk every page: follow nextPageToken until it comes back empty.
let pageToken = "";
for (;;) {
  const page = await sdk.profile.getPaginatedUserMovements({ pageToken, pageSize: 50 });
  // ... handle page.movements
  if (page.nextPageToken === "") break;
  pageToken = page.nextPageToken;
}
```

Movement rows can include deposits, withdrawals, trades, fees, funding payments, liquidations, rewards, interest, and collateral transfers. Filter server-side with `transactionType` and `entryType`. The two surfaces do not offer identical value sets: the endpoint accepts nine transaction types — `DEPOSIT`, `WITHDRAWAL`, `TRADE`, `FEE`, `FUNDING`, `LIQUIDATION`, `INTEREST`, `REWARD` and `CHAIN_SYNC` — while the SDK's `TransactionType` union (and its Zod schema) covers the first eight, so `CHAIN_SYNC` is selectable only over raw REST. `entryType` is `CREDIT`, `DEBIT`, `LOCK`, `UNLOCK` or `FEE` on both. Both the filter values and the values a row reports are upper-case — a funding row comes back with `transactionType: "FUNDING"` and `entryType: "DEBIT"` or `"CREDIT"` — and the server matches the filter case-insensitively. The lower-case forms (`"funding"`, `"debit"`) are the **live `movements` WebSocket** shape, not the REST one; normalize before comparing a REST row against a WebSocket frame.

**Funding movements read slightly differently from token movements.** Funding payments are unioned into the feed at read time from the funding ledger, and a funding payment is a collateral USD delta rather than a token transfer. So `amount` is a magnitude and the direction lives in `entryType` — `"DEBIT"` when the account paid funding, `"CREDIT"` when it received it — while `decimals` is `0` and `amountRaw` equals `amount` rather than being a smallest-unit integer. Zero-amount funding windows are excluded. Each funding movement carries the same `id` as its live [`movements` WebSocket frame](/websocket#movement-events), so a client reading both the REST history and the live stream deduplicates by `id` — which is what `useUserMovements` already does. Deduplicate on the `id` alone, though: the two payloads are not the same shape. The REST row keeps the upper-case enum values and resolves the asset metadata, while the live frame uses the lower-case forms and carries no resolved asset id.

## Live Balance Updates

React integrations can use `useUserBalances` to fetch balances over REST and keep them updated with WebSocket balance events.

```tsx theme={null}
import { useUserBalances } from "@0xmonaco/react";

function BalancesList() {
  const { balances, loading, subscribed, error, refresh } = useUserBalances();

  if (loading) return <div>Loading balances...</div>;
  if (error) return <button onClick={refresh}>Retry balances</button>;

  return (
    <div>
      <p>{subscribed ? "Live" : "Disconnected"}</p>
      {balances.map((balance) => (
        <div key={balance.assetId}>
          <strong>{balance.symbol ?? balance.token}</strong>
          <span>{balance.availableBalance} available</span>
          <span>{balance.lockedBalance} locked</span>
        </div>
      ))}
    </div>
  );
}
```

Balance events can be triggered by deposits, withdrawals, new orders locking funds, cancelled or expired orders unlocking funds, fills, fees, and margin transfers. Genuine spot-row events carry the same `version` as REST and the subscribe-time snapshot. Apply a higher version, treat an equal version as an idempotent duplicate, and ignore a lower version for that whole row. `updatedAt` remains display/source metadata and does not order balance state.

A collateral transfer that moves the spot wallet row is a genuine spot-row event and is versioned like any other. The `balances` channel also carries older and margin-collateral notifications for compatibility — a parent to risk-bucket allocation, for instance, which re-earmarks collateral inside the margin account and moves no wallet balance. Those frames have no `version`: margin collateral is not a `user_balances` row, the wire identity has no margin-account ID, and a margin-routed deposit may report a delta rather than whole-row state. Do not compare those notifications with a spot snapshot watermark or treat them as authoritative aggregate `(user, token)` state. Handle them fail-open and refresh the corresponding [margin account state](/developers/perps-collateral). Durable margin-collateral reconciliation is not solved by the spot balance version.

## Builder Checklist

* Keep wallet balances, Monaco spot balances, parent margin collateral, and risk-bucket exposure visually separate.
* Use `availableBalanceRaw` for max order, withdrawal, and transfer validation.
* Order spot state by `version`, never by `updatedAt` or WebSocket arrival time.
* Refresh balances after approval, deposit, withdrawal, collateral transfer, order fill, cancel, expiry, funding payment, or liquidation.
* Treat a missing valid balance as zero, but handle `404` separately from authentication or network failures.

## Related Pages

* [Vault Deposits & Withdrawals](/developers/vault) for moving wallet assets into and out of Monaco.
* [Perps Collateral](/developers/perps-collateral) for moving spot balances into margin collateral.
* [Order Management](/developers/trade) for orders that lock and unlock balances.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.