Skip to Content

Reading Data

This page covers every way to read Velocity state in your app: from the subscribed VelocityClient/User caches, to the stateless VelocityCore decoders, to the Data API REST endpoints.

Account data via VelocityClient / User

Once velocityClient is subscribed (see setup), account data is available synchronously from the User wrapper for the active (or any) subaccount.

Get the active account

const user = velocityClient.getUser(); // active subaccount const otherUser = velocityClient.getUser(1); // subaccount 1 const userAccount = user.getUserAccount(); // raw decoded UserAccount userAccount.authority; userAccount.subAccountId; userAccount.name; // number[]: decode with `decodeName()`

Positions

import { decodeName } from "@velocity-exchange/sdk"; for (const position of userAccount.perpPositions) { if (position.baseAssetAmount.isZero()) continue; position.marketIndex; position.baseAssetAmount; // BASE_PRECISION (1e9), signed: + long, - short position.quoteAssetAmount; // QUOTE_PRECISION (1e6) position.quoteEntryAmount; position.quoteBreakEvenAmount; position.lastCumulativeFundingRate; position.openOrders; position.openBids; position.openAsks; } // Higher-level PnL/health accessors live on `User`, see PnL & Risk const unrealizedPnl = user.getUnrealizedPNL(true, 0); // withFunding=true, market 0 const health = user.getHealth(); // 0-100

See PnL & Risk for getTotalCollateral, getMarginRequirement, getFreeCollateral, and getLeverage.

Open orders

import { isVariant } from "@velocity-exchange/sdk"; for (const order of userAccount.orders) { if (!isVariant(order.status, "open")) continue; order.orderId; order.marketIndex; order.direction; order.price; // PRICE_PRECISION (1e6), 0 for market orders order.triggerPrice; order.baseAssetAmount; order.baseAssetAmountFilled; order.oraclePriceOffset; // BN (i64): always wrap in `new BN(...)` when constructing order.reduceOnly; order.bitFlags; // OrderBitFlag bitmask: postOnly / reduceOnly / HasBuilder }

Spot balances and collateral

for (const position of userAccount.spotPositions) { if (position.scaledBalance.isZero()) continue; position.marketIndex; position.balanceType; // { deposit: {} } | { borrow: {} } position.scaledBalance; // SPOT_BALANCE_PRECISION (1e9); multiply by the market's cumulative interest for token amount } const totalCollateral = user.getTotalCollateral(); // BN, QUOTE_PRECISION (1e6)

Decoding accounts without a subscription: VelocityCore

For read-only tooling (indexers, one-off scripts, serverless functions) where a fully subscribed VelocityClient is overkill, VelocityCore decodes accounts directly from an RPC fetch or a raw buffer:

import { VelocityCore } from "@velocity-exchange/sdk"; import { Connection } from "@solana/web3.js"; const connection = new Connection("<RPC_URL>"); // Fetch + decode a User account in one call, no subscription required const userAccount = await VelocityCore.fetchUserAccount(connection, userAccountPublicKey); // Or decode a buffer you already have (e.g. from a websocket account notification) const decoded = VelocityCore.decodeUserAccount(rawAccountBuffer); // PDA helpers (state, user, perp/spot market, vaults) are re-exported statically const userPda = VelocityCore.pdas.getUserAccountPublicKeySync(programId, authority, 0);

For account types beyond User, build a coder directly against the bundled IDL:

import { VelocityCore } from "@velocity-exchange/sdk"; const coder = VelocityCore.coder(); // defaults to VelocityCore.defaultIdl() const perpMarket = coder.accounts.decode("PerpMarket", rawAccountBuffer);

IDL account names are PascalCase. Any string-keyed coder call must use 'PerpMarket', 'SpotMarket', 'User', 'UserStats', 'State', matching accounts[].name in sdk/src/idl/velocity.json. Passing a lowercase or camelCase name throws Unknown account: <name>.

MarketStatus discriminants and account sizes can shift across program versions. Velocity’s current MarketStatus variants are Initialized (0), Active (1), ReduceOnly (2), Settlement (3), Delisted (4), and PerpMarketAccount is 1304 bytes. Always decode against the current sdk/src/idl/velocity.json, never a hardcoded enum or fixed offset carried over from an earlier integration. See Account Model for the full layout, or the migration guide if you’re porting a raw decoder from an earlier discriminant numbering.

Never validate an account by its exact data length. Zero-copy account structs (User, PerpMarket, SpotMarket, State, UserStats, and others) can grow: when a struct runs out of reserved padding, the protocol grows the deployed accounts with the extend_account instruction, and the account then holds more bytes than an older client expects. Decode length-tolerantly:

  • Read exactly the 8 + size_of::<T>() bytes your client knows and ignore the tail. The SDK already does this: Anchor’s borsh coder and the decodeUser fast path both use start-relative offsets, so an extended account decodes unchanged.
  • Do not assert data.length === EXPECTED_SIZE, and do not derive a slice end from the buffer length.
  • Do not filter getProgramAccounts by dataSize. A hardcoded size silently matches zero accounts after an extension. Filter on the 8-byte discriminator with a memcmp instead, which is what every SDK map does.
  • On the Rust side, decode through velocity-rs’s utils::deser_zero_copy / try_deser_zero_copy or AccountRef, which trim to 8 + size_of::<T>() before casting. Do not call Anchor’s derived T::try_deserialize on a zero-copy type off-chain: it casts the whole tail and panics on any size mismatch.

Extension only ever appends. It never reorders, inserts, widens, or shrinks a field, so offsets you already read stay valid, and newly added bytes read as zero until code writes them. See Account Model.

Market data

Oracle and mark prices

const oraclePriceData = velocityClient.getMMOracleDataForPerpMarket(0); // MMOraclePriceData const perpMarket = velocityClient.getPerpMarketAccount(0); perpMarket.marketStats; // mark/oracle TWAPs live here, not on the top-level PerpMarket perpMarket.oracle; // top-level now (moved off amm.* in the AMM decoupling) perpMarket.oracleSource;

Orderbook (DLOB)

For a live L2/L3 orderbook, subscribe to the DLOB directly with the SDK’s SlotSubscriber / OrderSubscriber / DLOBSubscriber classes. See DLOB for the full setup sequence and Orderbook + DLOB websocket for the hosted DLOB server’s REST/websocket API if you’d rather not run your own DLOB.

Data API (REST)

The Data API provides historical and aggregate data via REST endpoints, without running your own indexer. Use it for dashboards, analytics, or any non-SDK integration.

Velocity’s hosted Data API is live at data.velocity.exchange. The examples below use that host. See Data API.

Market stats

Returns aggregate stats for all markets: volume, open interest, funding rate, oracle price, and market status.

GET https://data.velocity.exchange/stats/markets

Response shape:

[ { "marketIndex": 0, "symbol": "SOL-PERP", "marketType": "perp", "oraclePrice": 123.456, "volume24h": 50000000.0, "openInterest": 12000000.0, "fundingRate": 0.00012, "fundingRate24hAvg": 0.00010, "status": "active" } ]

Funding rates

GET https://data.velocity.exchange/fundingRates?symbol=SOL-PERP

Trades

GET https://data.velocity.exchange/trades?symbol=SOL-PERP&limit=100

Query parameters:

  • symbol: Market symbol (required)
  • limit: Max results (default varies, max typically 1000)
  • pageIndex: For pagination

Note: Amounts are in protocol precision (base: 1e9, quote: 1e6). Divide accordingly for human-readable values. See the Data API glossary for the full column reference, and note that spot-fulfillment-related columns no longer apply (spot DLOB trading and external fulfillment are both removed on Velocity).

Fetching data in code

const marketsRes = await fetch("https://data.velocity.exchange/stats/markets"); const markets = await marketsRes.json(); for (const m of markets) { console.log(`${m.symbol}: price=${m.oraclePrice} OI=${m.openInterest} funding=${m.fundingRate}`); } const tradesRes = await fetch( "https://data.velocity.exchange/trades?symbol=SOL-PERP&limit=100" ); const trades = await tradesRes.json();
Function fetchReference ↗
TypeScript docs unavailable for fetch.

DLOB + Swift for live order flow

For real-time orderbook and order flow, Velocity uses separate services:

  • DLOB server (https://dlob.velocity.exchange): Orderbook snapshots and streaming. See Orderbook + DLOB websocket.
  • Swift server (https://swift.velocity.exchange): Signed message orders for fast execution. See Swift.
Last updated on