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-100See 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 thedecodeUserfast 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
getProgramAccountsbydataSize. A hardcoded size silently matches zero accounts after an extension. Filter on the 8-byte discriminator with amemcmpinstead, which is what every SDK map does. - On the Rust side, decode through
velocity-rs’sutils::deser_zero_copy/try_deser_zero_copyorAccountRef, which trim to8 + size_of::<T>()before casting. Do not call Anchor’s derivedT::try_deserializeon 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.
- Base URL:
https://data.velocity.exchange - Data API playground
Market stats
Returns aggregate stats for all markets: volume, open interest, funding rate, oracle price, and market status.
GET https://data.velocity.exchange/stats/marketsResponse 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-PERPTrades
GET https://data.velocity.exchange/trades?symbol=SOL-PERP&limit=100Query 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 ↗
Function fetchReference ↗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.