What lives where
Fetch a single market end-to-end
Batch-fetch all live markets
For UIs showing every market, use onegetMultipleAccountsInfo call:
collateral_vaults, similar approach with getMultipleAccounts decoded as token accounts.
NAV from on-chain TWAP
NAV is derived, not stored:Book price and depth (native pools)
The whole book is one account - a single fetch returns price and depth:basePrice to lNavUI for drift:
User position value
Risk and oracle freshness
Subscribing to live updates
For a reactive UI, use Solana’sonAccountChange for the Market PDA:
update_risk_state, every mint/redeem (supply changes), and via TWAP propagation. Frequency: a few updates per minute in normal conditions.
For book prices, subscribe to the per-side Pool accounts with onAccountChange - one account per book, so it’s cheap.
Common pitfalls
Wrong oracle account. Usemarket.oracle_address, never hard-code. The address is per-market; reusing one across markets returns wrong NAV.
TWAP == 0 trap. Right after a market init, TWAP is zero. Fall back to initial_l_price. The frontend’s useMarkets() hook handles this - replicate the pattern.
Stale market data after mint/redeem. Anchor’s account-fetch returns the current state, but if you’re displaying stale numbers from before the user’s tx, refetch after confirmed. The Anchor provider’s sendAndConfirm returns when the cluster is at confirmed - re-fetch then.
Pool not initialized. clp.account.pool.fetchNullable(poolPda) returns null if the book hasn’t been bootstrapped for that (market, side) yet. Use the registry / market-addresses.json as the source of truth for pools that should exist.
Decimals. Everything in account state is in base units (lamports for cUSDC, base units for L/S). Divide by 10^6 for display. Don’t assume decimals from the symbol.
See also
Mint flow
What
mint_paired does on-chain.SDK setup
Bootstrapping a project to make these reads.

