Users
How it works
A user account is the onchain account that holds your positions, orders, and collateral on Velocity. Each wallet can create multiple user accounts (called subaccounts), identified by a numeric ID (0, 1, 2, etc.). All subaccounts under the same wallet share cross-margin, meaning collateral and risk are calculated across all of them together.
User accounts store your perp positions (long/short), spot balances (deposits/borrows), open orders, and leverage settings. When you interact with Velocity (place orders, deposit, trade), you’re modifying data in your user account. The account is a Solana PDA (Program Derived Address) owned by the Velocity program.
Subaccounts are useful for separating strategies, isolating risk between different trading styles, or delegating specific accounts to bots while keeping others manual. You can switch between subaccounts using the SDK, and each one maintains its own positions and orders while sharing the wallet’s overall collateral pool.
SDK Usage
Most write actions in Velocity are done through VelocityClient. For account-level reads (positions, orders, health), you’ll typically use a User.
Initialize a User Account
Create a new Velocity user subaccount (PDA) for your wallet and return the transaction signature + user account address.
// Assumes you already constructed and subscribed `velocityClient`.
const [txSig, userAccountPublicKey] = await velocityClient.initializeUserAccount(0, "my-account");
console.log(txSig, userAccountPublicKey.toBase58());Method VelocityClient.initializeUserAccountReference ↗
Method VelocityClient.initializeUserAccountReference ↗| Parameter | Type | Required |
|---|---|---|
subAccountId | numberSub-account id to create; defaults to `0`. | No |
name | stringDisplay name (max 32 bytes); see `getInitializeUserAccountIxs` for the default. | No |
referrerInfo | ReferrerInfoReferrer's `referrer`/`referrerStats` public keys, if attributing this
account to a referrer. | No |
txParams | TxParamsOptional compute-unit/priority-fee overrides for the transaction. | No |
| Returns |
|---|
Promise<[string, PublicKey]> |
Get the Next Subaccount ID
Return the next available subaccount ID for this client instance.
const nextId = await velocityClient.getNextSubAccountId();
console.log(nextId);Method VelocityClient.getNextSubAccountIdReference ↗
Method VelocityClient.getNextSubAccountIdReference ↗| Returns |
|---|
Promise<number> |
Get a User and Subscribe
Import the User class and subscribe to account updates for a specific user instance.
import { User } from "@velocity-exchange/sdk";
const user = velocityClient.getUser();
await user.subscribe();Class UserReference ↗
Class UserReference ↗| Property | Type | Required |
|---|---|---|
velocityClient | VelocityClient | Yes |
userAccountPublicKey | PublicKey | Yes |
accountSubscriber | UserAccountSubscriber | Yes |
_isSubscribed | boolean | Yes |
eventEmitter | StrictEventEmitter<EventEmitter, UserAccountEvents> | Yes |
isSubscribed | booleanTrue only when both `subscribe()` has completed and the underlying `accountSubscriber` itself reports subscribed. | Yes |
subscribe | (userAccount?: UserAccount | undefined) => Promise<boolean>Subscribes to this `User` account (websocket/polling/gRPC/custom per
`UserConfig.accountSubscription`) and awaits the initial account fetch.
Must resolve before any `get*`/margin/PnL accessor is called — those
throw `NotSubscribedError` until this has completed. | Yes |
fetchAccounts | () => Promise<void>Forces the account subscriber to re-fetch the `User` account from RPC (bypassing any push/poll cadence). | Yes |
unsubscribe | () => Promise<void>Removes all event listeners and tears down the account subscription. | Yes |
getUserAccount | () => UserAccount | undefinedReturns the cached user account.
- **Throws** `NotSubscribedError` if the subscriber has not been subscribed
yet — reading the account before `subscribe()` resolves is a
programming error, not a missing-account condition.
- Returns `undefined` when subscribed but no account was found on chain.
Because `subscribe()` awaits the initial fetch, an `undefined` here means
the account does not exist (or has not yet been observed), not that data
is "still loading". | Yes |
getUserAccountOrThrow | () => UserAccountLike `getUserAccount` but throws instead of returning `undefined`
when the account was not found. Use at call sites that structurally
require the account to exist. (Still propagates `NotSubscribedError` when
called before subscribing.)
Delegates to `getUserAccount` (rather than the subscriber directly)
so callers that override `getUserAccount` see the override here too. | Yes |
forceGetUserAccount | () => Promise<UserAccount | undefined>Bypasses the cached subscriber state and force-fetches the `User` account
directly from the RPC (via `fetchAccounts`), then returns the freshly
cached value. Useful right after sending a transaction, when the
websocket/polling subscriber may not yet have observed the update. | Yes |
getUserAccountAndSlot | () => DataAndSlot<UserAccount> | undefinedReturns the cached user account together with the slot at which it was
last observed. Same `undefined`/`NotSubscribedError` contract as `getUserAccount`. | Yes |
getUserAccountAndSlotOrThrow | () => DataAndSlot<UserAccount>Like `getUserAccountAndSlot` but throws instead of returning
`undefined` when the account was not found. Use at call sites that
structurally require the account to exist. (Still propagates
`NotSubscribedError` when called before subscribing.) | Yes |
getPerpPositionForUserAccount | (userAccount: UserAccount, marketIndex: number) => PerpPosition | undefinedFinds the perp position for `marketIndex` on an explicit `userAccount`
snapshot rather than the cached account. Only matches "active" positions
(see `getActivePerpPositionsForUserAccount`) — a market the user has never
touched (or has fully closed and settled) returns `undefined` even though
the on-chain array always has a fixed-size slot for every market. | Yes |
getPerpPosition | (marketIndex: number) => PerpPosition | undefinedGets the user's current position for a given perp market. If the user has no position returns undefined | Yes |
getPerpPositionOrEmpty | (marketIndex: number) => PerpPositionLike `getPerpPosition`, but returns a zeroed-out placeholder position
(see `getEmptyPosition`) instead of `undefined` when the user has no
active position in `marketIndex`. Convenient for math helpers that need a
`PerpPosition` shape unconditionally (e.g. buying-power/leverage calcs). | Yes |
getPerpPositionOrThrow | (marketIndex: number) => PerpPositionLike `getPerpPosition`, but throws instead of returning `undefined` when
the user has no active position in `marketIndex`. | Yes |
getPerpPositionAndSlot | (marketIndex: number) => DataAndSlot<PerpPosition | undefined>Like `getPerpPosition`, but also returns the slot at which the underlying
`UserAccount` was observed. | Yes |
getSpotPositionForUserAccount | (userAccount: UserAccount, marketIndex: number) => SpotPosition | undefinedFinds the spot position for `marketIndex` on an explicit `userAccount`
snapshot. Unlike `getPerpPositionForUserAccount`, this does not filter to
"active" positions first — it returns whatever fixed-size slot entry
exists for that market index, even if the position is empty/available. | Yes |
getSpotPosition | (marketIndex: number) => SpotPosition | undefinedGets the user's current position for a given spot market. If the user has no position returns undefined | Yes |
getSpotPositionAndSlot | (marketIndex: number) => DataAndSlot<SpotPosition | undefined>Like `getSpotPosition`, but also returns the slot at which the underlying
`UserAccount` was observed. | Yes |
getEmptySpotPosition | (marketIndex: number) => SpotPositionReturns a zeroed-out (no deposit/borrow) placeholder `SpotPosition` for `marketIndex`. | Yes |
getTokenAmount | (marketIndex: number) => BNReturns the token amount for a given market. The spot market precision is based on the token mint decimals.
Positive if it is a deposit, negative if it is a borrow. | Yes |
getEmptyPosition | (marketIndex: number) => PerpPositionReturns a zeroed-out placeholder `PerpPosition` for `marketIndex` (no size, no orders, cross margin). | Yes |
isPositionEmpty | (position: PerpPosition) => booleanReturns true if `position` has no size and no open orders (a market slot that can be treated as unused). | Yes |
getIsolatePerpPositionTokenAmount | (perpMarketIndex: number) => BNReturns the isolated-margin quote deposit backing a given perp position,
i.e. `PerpPosition.isolatedPositionScaledBalance` converted to a token
amount. This is the collateral segregated to that single isolated
position, separate from the user's cross-margin free collateral. | Yes |
getTotalIsolatedPositionDeposits | () => BNReturns the total USD value of deposits across all isolated perp positions. | Yes |
getClonedPosition | (position: PerpPosition) => PerpPositionReturns a shallow copy of `position`. Mutating the clone does not affect the cached account. | Yes |
getOrderForUserAccount | (userAccount: UserAccount, orderId: number) => Order | undefinedFinds an order by its program-assigned `orderId` on an explicit `userAccount` snapshot. | Yes |
getOrder | (orderId: number) => Order | undefinedFinds an order in the cached `UserAccount` by its program-assigned `orderId`. | Yes |
getOrderAndSlot | (orderId: number) => DataAndSlot<Order | undefined>Like `getOrder`, but also returns the slot at which the underlying `UserAccount` was observed. | Yes |
getOrderByUserIdForUserAccount | (userAccount: UserAccount, userOrderId: number) => Order | undefinedFinds an order by its caller-assigned `userOrderId` (a client-chosen tag,
distinct from the program-assigned `orderId`) on an explicit `userAccount`
snapshot. | Yes |
getOrderByUserOrderId | (userOrderId: number) => Order | undefinedFinds an order in the cached `UserAccount` by its caller-assigned
`userOrderId` (a client-chosen tag, distinct from the program-assigned `orderId`). | Yes |
getOrderByUserOrderIdAndSlot | (userOrderId: number) => DataAndSlot<Order | undefined>Like `getOrderByUserOrderId`, but also returns the slot at which the underlying `UserAccount` was observed. | Yes |
getOpenOrdersForUserAccount | (userAccount?: UserAccount | undefined) => Order[] | undefinedFilters an explicit `userAccount` snapshot's orders down to those with
`OrderStatus.Open`. | Yes |
getOpenOrders | () => Order[]Returns all of the user's orders with `OrderStatus.Open`. Empty array (not `undefined`) if there are none or no account is loaded. | Yes |
getOpenOrdersAndSlot | () => DataAndSlot<Order[]>Like `getOpenOrders`, but also returns the slot at which the underlying `UserAccount` was observed. | Yes |
getUserAccountPublicKey | () => PublicKeyReturns this `User`'s account address (does not require the account to be subscribed or to exist on chain). | Yes |
exists | () => Promise<boolean>Checks directly via RPC (bypassing the subscriber cache) whether the `User` account exists on chain. | Yes |
getPerpBidAsks | (marketIndex: number) => [BN, BN]Returns the position's total resting open-order bid/ask size in a perp market. | Yes |
getPerpBuyingPower | (marketIndex: number, collateralBuffer?: any, maxMarginRatio?: number | undefined, positionType?: "isolated" | "cross" | undefined) => BNcalculates Buying Power = free collateral / initial margin ratio
For `positionType: 'isolated'`, the buying power is capped by the
lesser of (a) the user's cross free collateral and (b) the free quote
asset value in the perp's quote spot market — mirroring that an isolated
position can only draw down as much quote collateral as is actually
available to isolate into it. | Yes |
resolveMaxMarginRatio | any | Yes |
getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount | (marketIndex: number, freeCollateral: BN, baseAssetAmount: BN, perpMarketMaxMarginRatio?: number | undefined) => BNConverts a free-collateral amount directly into buying power for a perp
market, given the (hypothetical) resulting base position size — used
internally so the margin ratio (which can vary with position size via the
IMF factor) reflects the post-trade size rather than the current size. | Yes |
getFreeCollateral | (marginCategory?: MarginCategory | undefined, perpMarketIndex?: number | undefined) => BNcalculates Free Collateral = Total collateral - margin requirement
When `perpMarketIndex` is provided, returns the free collateral scoped to
that market's isolated margin bucket (the isolated quote deposit plus its
unrealized PnL, minus its own margin requirement) rather than the user's
cross-margin free collateral. If the user has no isolated position open in
that market, returns `ZERO` rather than throwing. | Yes |
getMarginRequirement | { (marginCategory: MarginCategory, liquidationBuffer?: any, strict?: boolean | undefined, includeOpenOrders?: boolean | undefined): BN; (marginCategory: MarginCategory, liquidationBuffer?: any, strict?: boolean | undefined, includeOpenOrders?: boolean | undefined, perpMarketIndex?: number | undefined): BN; }Calculates the margin requirement based on the specified parameters.
When `perpMarketIndex` is passed, returns the isolated margin requirement
for that market's isolated position only (`ZERO` if none exists) rather
than the cross-margin requirement. `liquidationBuffer`, when non-zero,
selects the buffered variant (`marginRequirementPlusBuffer` /
`MarginContext.liquidation`), which pads the requirement to build in the
state account's `liquidationMarginBufferRatio` — the same buffer keepers
apply so a position doesn't get flagged for liquidation and immediately
clear again. | Yes |
getInitialMarginRequirement | (perpMarketIndex?: number | undefined) => BNInitial margin requirement — the collateral needed to open/maintain a
position at initial (as opposed to maintenance) margin ratios, using
strict (TWAP-bounded) oracle pricing. This is what gates new orders and
increases in leverage. | Yes |
getMaintenanceMarginRequirement | (liquidationBuffer?: any, perpMarketIndex?: number | undefined) => BNMaintenance margin requirement — the minimum collateral below which the
position becomes eligible for liquidation. Uses non-strict oracle pricing
and includes open orders' worst-case impact by default. | Yes |
getActivePerpPositionsForUserAccount | (userAccount: UserAccount) => PerpPosition[]Filters an explicit `userAccount` snapshot's fixed-size perp position
array down to slots that are actually "active": nonzero base or quote
amount, an outstanding open order count, or a nonzero isolated-margin
quote deposit (a position can be flat but still isolated-funded). | Yes |
getActivePerpPositions | () => PerpPosition[]Returns the cached account's active perp positions. See `getActivePerpPositionsForUserAccount` for the activity criteria. | Yes |
getActivePerpPositionsAndSlot | () => DataAndSlot<PerpPosition[]>Like `getActivePerpPositions`, but also returns the slot at which the underlying `UserAccount` was observed. | Yes |
getActiveSpotPositionsForUserAccount | (userAccount: UserAccount) => SpotPosition[]Filters an explicit `userAccount` snapshot's spot positions to those that are not `isSpotPositionAvailable` (i.e. have a nonzero balance, orders, or cumulative deposits). | Yes |
getActiveSpotPositions | () => SpotPosition[]Returns the cached account's active spot positions. See `getActiveSpotPositionsForUserAccount` for the activity criteria. | Yes |
getActiveSpotPositionsAndSlot | () => DataAndSlot<SpotPosition[]>Like `getActiveSpotPositions`, but also returns the slot at which the underlying `UserAccount` was observed. | Yes |
getUnrealizedPNL | (withFunding?: boolean | undefined, marketIndex?: number | undefined, withWeightMarginCategory?: MarginCategory | undefined, strict?: boolean | undefined, liquidationBuffer?: any) => BNCalculates unrealized position price PnL, summed across all active perp
positions (or a single one if `marketIndex` is given).
When `withWeightMarginCategory` is supplied, the PnL is asset-weighted
for margin purposes: profitable positions are scaled down by
`calculateUnrealizedAssetWeight` (an unrealized gain is a less-trusted
asset than settled collateral), and — for `'Initial'` margin specifically
— the *per-position* weighted gain is additionally capped at
`MAX_POSITIVE_UPNL_FOR_INITIAL_MARGIN` (**$100**, QUOTE_PRECISION), a
safety guard against a single dangerously-configured or manipulated
market inflating buying power. Losses are never capped, and a
`liquidationBuffer` (if provided) further inflates negative PnL to
mirror the on-chain liquidation-buffer treatment. | Yes |
getUnrealizedFundingPNL | (marketIndex?: number | undefined) => BNCalculates unrealized funding payment PnL — the funding accrued since
each position's `lastCumulativeFundingRate` was last settled, not yet
reflected in `quoteAssetAmount`. | Yes |
getSpotMarketAssetAndLiabilityValue | (marketIndex?: number | undefined, marginCategory?: MarginCategory | undefined, liquidationBuffer?: any, includeOpenOrders?: boolean | undefined, strict?: boolean | undefined, now?: any) => { ...; }Computes the combined weighted asset value and weighted liability value
across the user's spot positions (worst-case, including open-order
exposure by default), plus the net quote balance. This is the core spot
side of the margin system that `getTotalCollateral`/`getMarginRequirement`
build on. | Yes |
getSpotMarketLiabilityValue | (marketIndex?: number | undefined, marginCategory?: MarginCategory | undefined, liquidationBuffer?: any, includeOpenOrders?: boolean | undefined, strict?: boolean | undefined, now?: any) => BNConvenience wrapper around `getSpotMarketAssetAndLiabilityValue` returning only `totalLiabilityValue`. See that method for parameter semantics. Returns QUOTE_PRECISION (1e6). | Yes |
getSpotLiabilityValue | (tokenAmount: BN, strictOraclePrice: StrictOraclePrice, spotMarketAccount: SpotMarketAccount, marginCategory?: MarginCategory | undefined, liquidationBuffer?: any) => BNThin wrapper around the `math/spotBalance` `getSpotLiabilityValue` helper that supplies the user's `maxMarginRatio`. Returns QUOTE_PRECISION (1e6), negative. | Yes |
getSpotMarketAssetValue | (marketIndex?: number | undefined, marginCategory?: MarginCategory | undefined, includeOpenOrders?: boolean | undefined, strict?: boolean | undefined, now?: any) => BNConvenience wrapper around `getSpotMarketAssetAndLiabilityValue` returning only `totalAssetValue`. See that method for parameter semantics. Returns QUOTE_PRECISION (1e6), non-negative. | Yes |
getSpotAssetValue | (tokenAmount: BN, strictOraclePrice: StrictOraclePrice, spotMarketAccount: SpotMarketAccount, marginCategory?: MarginCategory | undefined) => BNThin wrapper around the `math/spotBalance` `getSpotAssetValue` helper that supplies the user's `maxMarginRatio`. Returns QUOTE_PRECISION (1e6), non-negative. | Yes |
getSpotPositionValue | (marketIndex: number, marginCategory?: MarginCategory | undefined, includeOpenOrders?: boolean | undefined, strict?: boolean | undefined, now?: any) => BNNet spot value (`totalAssetValue - totalLiabilityValue`) for a single spot market. See `getSpotMarketAssetAndLiabilityValue` for parameter semantics. Returns QUOTE_PRECISION (1e6), can be negative. | Yes |
getNetSpotMarketValue | (withWeightMarginCategory?: MarginCategory | undefined) => BNNet spot value (`totalAssetValue - totalLiabilityValue`) across all spot
markets combined. | Yes |
getTotalCollateral | (marginCategory?: MarginCategory | undefined, strict?: boolean | undefined, includeOpenOrders?: boolean | undefined, liquidationBuffer?: any, perpMarketIndex?: number | undefined) => BNCalculates Total Collateral: net spot collateral value plus weighted
unrealized perp PnL (see `getUnrealizedPNL`'s `$100`-per-position cap
under `'Initial'` margin). This is the numerator side of the margin
system; `getFreeCollateral`/`getMarginRequirement` are derived from it.
When `perpMarketIndex` is provided, returns the isolated total collateral
for that market's isolated position bucket instead of the cross-margin
total — and **throws** if the user has no isolated margin calculation for
that market (unlike `getFreeCollateral`, which swallows the same case and
returns `ZERO`). | Yes |
getLiquidationBuffer | () => Map<number | "cross", BN>Builds the liquidation-buffer map to pass into margin calculations while
a liquidation is in progress: `'cross'` is set to the state account's
`liquidationMarginBufferRatio` if cross margin is being liquidated, and
each isolated perp position currently flagged `BeingLiquidated` or
`Bankruptcy` gets the same buffer under its market index. Positions not
currently being liquidated are omitted (no buffer applied). | Yes |
getHealth | (perpMarketIndex?: number | undefined) => numberCalculates a user's health score by comparing total collateral against
the maintenance margin requirement: `100 * (1 - maintenanceMarginReq / totalCollateral)`,
clamped to `[0, 100]` and rounded to the nearest integer. `100` means no
maintenance requirement (or a requirement of zero with non-negative
collateral); `0` means at or past the maintenance threshold (liquidatable)
or that collateral is non-positive.
Short-circuits to `0` if the relevant scope is already flagged as being
liquidated: cross margin via `isCrossMarginBeingLiquidated` (when
`perpMarketIndex` is omitted), or the specific isolated position via
`isIsolatedPositionBeingLiquidated` (when `perpMarketIndex` is given). | Yes |
calculateWeightedPerpPositionLiability | (perpPosition: PerpPosition, marginCategory?: MarginCategory | undefined, liquidationBuffer?: any, includeOpenOrders?: boolean | undefined, strict?: boolean | undefined) => BNComputes a single perp position's margin-weighted liability value: worst-case
(or current, if `includeOpenOrders` is false) base amount, valued at the
oracle price (or `expiryPrice` if the market is in settlement, which also
zeroes the margin ratio), scaled by the applicable margin ratio for
`marginCategory`. Underlies `getPerpMarketLiabilityValue`,
`getTotalPerpPositionLiability`, and the leverage/liquidation-price math. | Yes |
getPerpMarketLiabilityValue | (marketIndex: number, marginCategory?: MarginCategory | undefined, liquidationBuffer?: any, includeOpenOrders?: boolean | undefined, strict?: boolean | undefined) => BNMargin-weighted liability value of a single perp position. Thin wrapper
around `calculateWeightedPerpPositionLiability` for the position in
`marketIndex`; see that method for the worst-case/margin-ratio semantics. | Yes |
getTotalPerpPositionLiability | (marginCategory?: MarginCategory | undefined, liquidationBuffer?: any, includeOpenOrders?: boolean | undefined, strict?: boolean | undefined) => BNSums `calculateWeightedPerpPositionLiability` across every active perp
position — the perp side of the margin requirement (see `getMarginRequirement`). | Yes |
getPerpPositionValue | (marketIndex: number, oraclePriceData: Pick<OraclePriceData, "price">, includeOpenOrders?: boolean | undefined) => BNValues a perp position's base-asset notional at a caller-supplied oracle
price rather than looking one up internally — useful for pricing against
a simulated/custom price. Returns `ZERO` (via `getPerpPositionOrEmpty`) if
the user has no position in `marketIndex`. | Yes |
getPerpLiabilityValue | (marketIndex: number, oraclePriceData: OraclePriceData, includeOpenOrders?: boolean | undefined) => BNUnweighted (no margin ratio applied) perp liability notional at a
caller-supplied oracle price. Returns `ZERO` (via `getPerpPositionOrEmpty`)
if the user has no position in `marketIndex`. | Yes |
getPositionSide | (currentPosition: Pick<PerpPosition, "baseAssetAmount">) => PositionDirection | undefinedReturns `PositionDirection.LONG`/`SHORT` from the sign of `baseAssetAmount`, or `undefined` if the position is flat. | Yes |
getPositionEstimatedExitPriceAndPnl | (position: PerpPosition, amountToClose?: any, useAMMClose?: boolean | undefined) => [BN, BN]calculates average exit price (optionally for closing up to 100% of position) | Yes |
getLeverage | (includeOpenOrders?: boolean | undefined, perpMarketIndex?: number | undefined) => BNcalculates current user leverage which is (total liability size) / (net asset value) | Yes |
calculateLeverageFromComponents | ({ perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue, }: { perpLiabilityValue: BN; perpPnl: BN; spotAssetValue: BN; spotLiabilityValue: BN; }) => BNCombines the components from `getLeverageComponents` into a single leverage ratio: `(perpLiability + spotLiability) / (spotAsset + perpPnl - spotLiability)`. Returns TEN_THOUSAND (1e4) precision; `ZERO` if net asset value is zero. | Yes |
getLeverageComponents | (includeOpenOrders?: boolean | undefined, marginCategory?: MarginCategory | undefined, perpMarketIndex?: number | undefined) => { perpLiabilityValue: BN; perpPnl: BN; spotAssetValue: BN; spotLiabilityValue: BN; }Gathers the four raw components (`perpLiabilityValue`, `perpPnl`,
`spotAssetValue`, `spotLiabilityValue`, all QUOTE_PRECISION/1e6) that
`calculateLeverageFromComponents` combines into a leverage ratio.
When `perpMarketIndex` is given, scopes to a single isolated position:
`spotAssetValue` becomes that position's isolated quote deposit and
`spotLiabilityValue` is `ZERO` (isolated positions carry no spot
liability of their own). Otherwise sums across the whole account, and
folds in `getTotalIsolatedPositionDeposits` as additional spot asset
value when `marginCategory` is unweighted. | Yes |
isDustDepositPosition | (spotMarketAccount: SpotMarketAccount) => booleanReturns true if the user's deposit position in `spotMarketAccount` is
non-empty but worth less than `DUST_POSITION_SIZE` (QUOTE_PRECISION) —
i.e. too small to be economically worth withdrawing/settling. Only
evaluates deposits (returns false for borrows or an empty position). | Yes |
getSpotMarketAccountsWithDustPosition | () => SpotMarketAccount[]Returns every spot market where the user holds a dust-sized deposit; see `isDustDepositPosition`. | Yes |
getTotalLiabilityValue | (marginCategory?: MarginCategory | undefined) => BNSum of the user's total perp position liability (worst-case, open orders
included) and total spot liability value (worst-case, open orders included). | Yes |
getTotalAssetValue | (marginCategory?: MarginCategory | undefined) => BNSum of the user's total spot asset value and total unrealized perp PnL
(with funding). When `marginCategory` is omitted (unweighted), also
includes `getTotalIsolatedPositionDeposits`. | Yes |
getNetUsdValue | () => BNUnweighted net USD value of the account: net spot market value, plus
unrealized (funding-inclusive) perp PnL, plus isolated position deposits. | Yes |
getTotalAllTimePnl | () => BNCalculates the all-time P&L of the user: current net USD value
(`getNetUsdValue`), plus lifetime total withdraws, minus lifetime total
deposits. Equivalent to "everything the account is worth now, plus
everything ever taken out, minus everything ever put in". | Yes |
getMaxLeverageForPerp | (perpMarketIndex: number, _marginCategory?: MarginCategory | undefined) => BNcalculates max allowable leverage exceeding hitting requirement category
for large sizes where imf factor activates, result is a lower bound | Yes |
getMaxLeverageForSpot | (spotMarketIndex: number, direction: PositionDirection) => BNcalculates max allowable leverage exceeding hitting requirement category | Yes |
getMarginRatio | () => BNcalculates margin ratio: 1 / leverage | Yes |
canBeLiquidated | () => AccountLiquidatableStatus & { isolatedPositions: Map<number, AccountLiquidatableStatus>; } | Yes |
getLiquidationStatuses | (marginCalc?: MarginCalculation | undefined) => Map<number | "cross", AccountLiquidatableStatus>New API: Returns liquidation status for cross and each isolated perp position.
Map keys:
- 'cross' for cross margin
- marketIndex (number) for each isolated perp position
Each `canBeLiquidated` compares maintenance total collateral against the
maintenance margin requirement for that scope. If `marginCalc` is not
supplied, one is computed under `'Maintenance'` with the account's
current `getLiquidationBuffer()` applied — i.e. this defaults to the same
buffered check the on-chain liquidation instructions use, not a bare
maintenance-margin comparison. | Yes |
isBeingLiquidated | () => booleanReturns true if cross margin or any isolated perp position is currently flagged as being liquidated or bankrupt. | Yes |
isCrossMarginBeingLiquidated | () => booleanReturns true if the account-level `UserStatus` has `BEING_LIQUIDATED` or `BANKRUPT` set (cross margin, not per-isolated-position). | Yes |
canCrossMarginBeLiquidated | (marginCalc?: MarginCalculation | undefined) => booleanReturns true if cross margin is currently below maintenance requirement (no buffer). | Yes |
hasIsolatedPositionBeingLiquidated | () => booleanReturns true if any active perp position has `PositionFlag.BeingLiquidated` or `PositionFlag.Bankruptcy` set. | Yes |
isIsolatedPositionBeingLiquidated | (perpMarketIndex: number) => booleanReturns true if the specific perp position in `perpMarketIndex` has `PositionFlag.BeingLiquidated` or `PositionFlag.Bankruptcy` set. False (not throw) if the user has no position there. | Yes |
getLiquidatableIsolatedPositions | (marginCalc?: MarginCalculation | undefined) => number[]Returns true if any isolated perp position is currently below its maintenance requirement (no buffer). | Yes |
canIsolatedPositionMarginBeLiquidated | (isolatedMarginCalculation: IsolatedMarginCalculation) => booleanReturns true if `isolatedMarginCalculation`'s collateral is below its margin requirement (no buffer). | Yes |
hasStatus | (status: UserStatus) => booleanReturns true if the account's `UserStatus` bitmask has `status` set. | Yes |
isBankrupt | () => booleanReturns true if the account's `UserStatus` has `BANKRUPT` set (equity insufficient to cover liabilities; awaiting bankruptcy resolution). | Yes |
needsToSettleFundingPayment | () => booleanChecks if any user position cumulative funding differs from respective market cumulative funding | Yes |
spotLiquidationPrice | (marketIndex: number, positionBaseSizeChange?: any) => BNCalculate the liquidation price of a spot position — the oracle price at
which maintenance free collateral would hit zero, extrapolating linearly
from the current free collateral and the position's per-unit-price
sensitivity (`calculateFreeCollateralDeltaForSpot`). If a perp market
shares the same oracle as this spot market, that perp position's
sensitivity is folded in too (scaled for any oracle-source unit
difference), since a single price move affects both simultaneously. | Yes |
liquidationPrice | (marketIndex: number, positionBaseSizeChange?: any, estimatedEntryPrice?: any, marginCategory?: MarginCategory | undefined, includeOpenOrders?: boolean | undefined, offsetCollateral?: any, marginType?: MarginType | undefined) => BNCalculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade.
Like `spotLiquidationPrice`, this extrapolates linearly from current free
collateral (`totalCollateral - marginRequirement`, plus `offsetCollateral`)
and the position's price sensitivity; if a spot market shares the same
oracle, its sensitivity is folded in too. When `marginType === 'Isolated'`,
free collateral and the margin requirement are scoped to that market's
isolated bucket instead of the cross-margin account (and the spot-oracle
cross-contribution above is skipped). | Yes |
calculateEntriesEffectOnFreeCollateral | (market: PerpMarketAccount, oraclePrice: BN, perpPosition: PerpPosition, positionBaseSizeChange: BN, estimatedEntryPrice: BN, includeOpenOrders: boolean, marginCategory?: MarginCategory | undefined) => BNHelper for `liquidationPrice`: estimates the net change to free collateral
from simultaneously (a) realizing PnL on `positionBaseSizeChange` entered
at `estimatedEntryPrice` (assuming the worst/taker fee tier) versus the
oracle price, and (b) the resulting change in margin requirement from the
new position size. Only component (a) applies under `'Maintenance'`
(matching `liquidationPrice`'s default); under other margin categories
only the margin-requirement delta is applied. | Yes |
calculateFreeCollateralDeltaForPerp | (market: PerpMarketAccount, perpPosition: PerpPosition, positionBaseSizeChange: BN, oraclePrice: BN, marginCategory?: MarginCategory | undefined, includeOpenOrders?: boolean | undefined) => anyHelper for `liquidationPrice`: the derivative of free collateral with
respect to the perp market's oracle price, for the proposed post-trade
position (`positionBaseSizeChange` applied to the current, or worst-case
if `includeOpenOrders`, base amount). Used as the linear-extrapolation
slope to solve for the price at which free collateral hits zero. | Yes |
calculateFreeCollateralDeltaForSpot | (market: SpotMarketAccount, signedTokenAmount: BN, marginCategory?: MarginCategory | undefined) => BNHelper for `spotLiquidationPrice`/`liquidationPrice`: the derivative of
free collateral with respect to the spot market's oracle price, for a
position of `signedTokenAmount` (positive = deposit, negative = borrow). | Yes |
liquidationPriceAfterClose | (positionMarketIndex: number, closeQuoteAmount: BN, estimatedEntryPrice?: any) => BNCalculates the estimated liquidation price for a position after closing a quote amount of the position. | Yes |
getMarginUSDCRequiredForTrade | (targetMarketIndex: number, baseSize: BN, estEntryPrice?: any, perpMarketMaxMarginRatio?: number | undefined) => BNCalculates the margin required to open a trade of `baseSize` in `targetMarketIndex`, scalar only — does not account for trade direction or existing positions/whether the trade is actually risk-increasing. | Yes |
getCollateralDepositRequiredForTrade | (targetMarketIndex: number, baseSize: BN, collateralIndex: number, perpMarketMaxMarginRatio?: number | undefined) => BNConverts `getMarginUSDCRequiredForTrade`'s USDC margin requirement into
how much of `collateralIndex`'s token a user would need to deposit to
cover it, accounting for that collateral's scaled initial asset weight
(a lower-weighted asset requires proportionally more deposited). | Yes |
getMaxTradeSizeUSDCForPerp | (targetMarketIndex: number, tradeSide: PositionDirection, maxMarginRatio?: number | undefined, positionType?: "isolated" | "cross" | undefined) => { tradeSize: BN; oppositeSideTradeSize: BN; }Separates the max trade size into two parts:
- tradeSize: The maximum trade size for target direction
- oppositeSideTradeSize: the trade size for closing the opposite direction | Yes |
getMaxTradeSizeUSDCForSpot | (targetMarketIndex: number, direction: PositionDirection, currentQuoteAssetValue?: any, currentSpotMarketNetValue?: any) => BNGet the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc. | Yes |
getMaxSwapAmount | ({ inMarketIndex, outMarketIndex, calculateSwap, iterationLimit, }: { inMarketIndex: number; outMarketIndex: number; calculateSwap?: (inAmount: BN) => BN; iterationLimit?: number; }) => { inAmount: BN; outAmount: BN; leverage: BN; }Calculates the max amount of token that can be swapped from inMarket to outMarket
Assumes swap happens at oracle price | Yes |
cloneAndUpdateSpotPosition | (position: SpotPosition, tokenAmount: BN, market: SpotMarketAccount) => SpotPositionReturns a cloned `SpotPosition` with `tokenAmount` (signed, positive =
deposit / negative = borrow) applied on top of the existing balance —
used to simulate the post-trade/post-swap position without mutating the
cached account. | Yes |
calculateSpotPositionFreeCollateralContribution | (spotPosition: SpotPosition, strictOraclePrice: StrictOraclePrice) => BNWorst-case free-collateral contribution (under `'Initial'` margin) of a single spot position. Returns QUOTE_PRECISION (1e6). | Yes |
calculateSpotPositionLeverageContribution | (spotPosition: SpotPosition, strictOraclePrice: StrictOraclePrice) => { totalAssetValue: BN; totalLiabilityValue: BN; }Worst-case (under `'Initial'` margin) asset/liability value split of a single spot position, for use in leverage calculations. Both fields QUOTE_PRECISION (1e6), non-negative. | Yes |
accountLeverageAfterSwap | ({ inMarketIndex, outMarketIndex, inAmount, outAmount, }: { inMarketIndex: number; outMarketIndex: number; inAmount: BN; outAmount: BN; }) => BNEstimates what the user leverage will be after swap | Yes |
accountLeverageRatioAfterTrade | (targetMarketIndex: number, targetMarketType: MarketType, tradeQuoteAmount: BN, tradeSide: PositionDirection, includeOpenOrders?: boolean | undefined) => BNReturns the leverage ratio for the account after adding (or subtracting) the given quote size to the given position | Yes |
getUserFeeTier | (marketType: MarketType, now?: any) => FeeTierLooks up the user's fee tier from the state account's fee structure.
For perp markets, the tier is selected by the user's rolling 30-day
volume (`getUser30dRollingVolumeEstimate`, QUOTE_PRECISION) against fixed
breakpoints — $2M, $10M, $20M, $80M, $200M — picking the lowest-index
tier whose breakpoint the user's volume is still under (tier 5, the
lowest fees, if volume meets or exceeds the top breakpoint). Spot markets
always use tier 0 (no volume-based discount). | Yes |
calculatePerpTakerFee | (quoteAmount: BN, marketIndex?: number | undefined, isReferee?: boolean | undefined, builderInfo?: Pick<OrderParams, "builderIdx" | "builderFeeTenthBps"> | undefined) => BNCalculates how much perp fee will be taken for a given sized trade.
When `marketIndex` is provided, delegates to `VelocityClient.getMarketFees`
for that specific market's taker-fee multiplier (which itself applies the
market's `feeAdjustment`, the referee discount, and — when `builderInfo` is
passed — the builder fee). Otherwise uses the volume-based fee tier from
`getUserFeeTier(MarketType.PERP)`; if the user is a referee (determined
from `UserStats.referrerStatus`'s `IsReferred` flag unless `isReferee` is
explicitly passed), the tier's `refereeFeeNumerator`/`refereeFeeDenominator`
proportion is subtracted from the fee as a discount, and — when `builderInfo`
carries a builder code — the builder fee (`quoteAmount * builderFeeTenthBps /
100_000`) is added on top, mirroring the program's `builder_fee` (`math/fees.rs`). | Yes |
getWithdrawalLimit | (marketIndex: number, reduceOnly?: boolean | undefined) => BNCalculates a user's max withdrawal amounts for a spot market. If reduceOnly is true,
it will return the max withdrawal amount without opening a liability for the user.
Combines three caps: the market-wide withdraw/borrow guard
(`calculateWithdrawLimit`, a rolling-window rate limit on the spot
market), the user's own deposit balance, and how much their free
collateral supports withdrawing/borrowing. If `canBypassWithdrawLimits`
returns `canBypass: true` (see that method), the market-wide withdraw
limit floor is raised to the user's full deposit amount — letting a
small, healthy, always-net-positive depositor withdraw in full even if
the market-wide guard would otherwise throttle them. | Yes |
canBypassWithdrawLimits | (marketIndex: number) => { canBypass: boolean; netDeposits: BN; depositAmount: BN; maxDepositAmount: BN; }Determines whether the user can bypass the spot market's rolling
withdraw-guard limit for `marketIndex`. `canBypass` is true only when
**all** of the following hold:
- The user currently holds a deposit (not a borrow) in the market.
- Their lifetime net deposits (`totalDeposits - totalWithdraws`) are
non-negative — they have never net-withdrawn more than they net-deposited.
- Their `cumulativeDeposits` for the position has never gone negative
(no history of having borrowed and repaid in this market).
- Their current deposit amount is below `maxDepositAmount`, i.e. 10% of
the spot market's `withdrawGuardThreshold`.
This lets a small, well-behaved depositor withdraw their own funds in
full even while the market-wide withdraw guard is actively throttling
larger movements. Used by `getWithdrawalLimit`. | Yes |
canMakeIdle | (slot: BN) => booleanDetermines whether the user can be marked idle (excluded from userMap
subscriptions by default, and skipped by most keeper crank passes) as of
`slot`. Requires: not already idle; inactive for the required window
since `lastActiveSlot` (1 hour / 9,000 slots if equity is under $1,000,
otherwise 1 week / 1,512,000 slots); not currently being liquidated; and
no open perp positions, borrows, spot open orders, or open orders of any kind. | Yes |
canBeDeleted | (userStatsAccount?: UserStatsAccount | undefined, now?: any) => { canDelete: boolean; reason?: string; }Determines whether this `User` (sub)account can be deleted (checked
before sending a delete-user instruction, to give a friendlier error than
an on-chain revert). Returns `canDelete: false` with a `reason` string if
any of the following hold: it's a referrer's sub-account 0 (referrers
cannot delete their primary account); the account is bankrupt or being
liquidated; it has any non-empty perp/spot position or open order; or
(when the state account charges an initialize-user fee) the account is a
"fresh" account — younger than `ACCOUNT_AGE_DELETION_CUTOFF_SECONDS`,
measured from its earliest recorded filler/maker/taker volume timestamp —
that is not currently idle. | Yes |
getSafestTiers | () => { perpTier: number; spotTier: number; }Returns the numerically-lowest (i.e. safest) contract/asset tier across
the user's active positions — perp tiers from active perp positions,
spot tiers only from spot **borrows** (deposits are skipped, since asset
tier only restricts borrowing exposure). Defaults to `4` (the
second-riskiest tier index) when the user has no positions of that kind —
this is a permissive default intended for callers doing tier-safety
comparisons (see `perpTierIsAsSafeAs` in `math/tiers`), not a claim that
"no position" is itself a risky tier. | Yes |
getPerpPositionHealth | ({ marginCategory, perpPosition, oraclePriceData, quoteOraclePriceData, includeOpenOrders, }: { marginCategory: MarginCategory; perpPosition: PerpPosition; oraclePriceData?: OraclePriceData; quoteOraclePriceData?: OraclePriceData; includeOpenOrders?: boolean; }) => HealthComponentBreaks down a single perp position's contribution to the margin system
as a `HealthComponent`: worst-case base size, its unweighted liability
value, the applicable margin ratio (`weight`), and the resulting
weighted margin requirement (`weightedValue`, which includes the
position's open-order margin add-on). Used to build up
`getHealthComponents`' `perpPositions` array (e.g. for UI breakdowns of
"what's consuming my margin"). | Yes |
getHealthComponents | ({ marginCategory, }: { marginCategory: MarginCategory; }) => HealthComponentsBuilds a full breakdown of every component feeding into the user's
margin calculation, for UI/diagnostic display: `deposits` and `borrows`
(one `HealthComponent` per non-quote spot market with a nonzero
worst-case position, plus a synthetic entry for the net quote balance),
`perpPositions` (via `getPerpPositionHealth`, one per active perp
position), and `perpPnl` (each position's weighted unrealized PnL — see
`getUnrealizedPNL` for the `'Initial'`-margin $100 cap that also applies
here). | Yes |
getTotalPerpPositionValueExcludingMarket | anyGet the total position value, excluding any position coming from the given target market | Yes |
getMMOracleDataForPerpMarket | any | Yes |
getOracleDataForPerpMarket | any | Yes |
getOracleDataForSpotMarket | any | Yes |
getActivePositions | () => { activePerpPositions: number[]; activeSpotPositions: number[]; }Get the active perp and spot positions of the user. | Yes |
getMarginCalculation | (marginCategory?: MarginCategory | undefined, opts?: { strict?: boolean; includeOpenOrders?: boolean; liquidationBufferMap?: Map<number | "cross", BN>; } | undefined) => MarginCalculationCompute the full margin calculation for the user's account.
Prioritize using this function instead of calling getMarginRequirement or getTotalCollateral multiple times.
Consumers can use this to avoid duplicating work across separate calls.
Mirrors the on-chain margin accumulation in `math/margin.rs`, splitting
contributions into cross-margin and per-market isolated buckets
(`MarginCalculation.isolatedMarginCalculations`, keyed by perp market
index — see `isPerpPositionIsolated`) and tracking whether the account
holds any isolated-tier liability (`withPerpIsolatedLiability` /
`withSpotIsolatedLiability`, consumed by
`validateAnyIsolatedTierRequirements`). A perp position's isolated
quote-deposit collateral only counts toward that position's own isolated
bucket, never the cross-margin total.
Also enforces pool-id consistency: every spot/perp position's market must
match the user's `poolId`, **except** a pool-1 user is allowed to hold a
quote-asset deposit (not borrow) even though the quote spot market itself
belongs to pool 0 — throws `InvalidPoolId: ...` otherwise. | Yes |
isPerpPositionIsolated | (perpPosition: PerpPosition) => booleanReturns true if `perpPosition` was opened/is held under isolated margin
(`PositionFlag.IsolatedPosition` set) — segregated to its own margin
bucket (see `getMarginCalculation`) rather than sharing cross-margin
collateral with the rest of the account. | Yes |
validateAnyIsolatedTierRequirements | (calculation: MarginCalculation) => { valid: boolean; reason?: string; }Pre-flight check for `IsolatedAssetTierViolation`: mirrors
`validate_any_isolated_tier_requirements` in `math/margin.rs`. A user
holding an isolated-tier perp or spot liability may not simultaneously
carry other liabilities (besides a single usdc borrow, for a perp
isolated liability), unless they are reduce-only.
Specifically, if `calculation.withPerpIsolatedLiability` is set (an
isolated-*contract-tier* perp liability exists) and the user is not
`UserStatus.REDUCE_ONLY`: more than one perp liability is invalid; margin
trading enabled is invalid; and any spot liability other than a single
USDC borrow is invalid. If `calculation.withSpotIsolatedLiability` is set
(an isolated-*asset-tier* spot liability exists) and not reduce-only: any
perp liability, or more than the one isolated-tier spot liability, is invalid. | Yes |
Get active subaccount user
Get the User object for the currently active subaccount.
const user = velocityClient.getUser();Method VelocityClient.getUserReference ↗
Method VelocityClient.getUserReference ↗| Parameter | Type | Required |
|---|---|---|
subAccountId | numberSub-account id; defaults to `this.activeSubAccountId`. | No |
authority | PublicKeyAuthority owning the sub-account; defaults to `this.authority`. | No |
| Returns |
|---|
User |
Get a specific subaccount user
Get the User object for a specific subaccount ID.
const user = velocityClient.getUser(1);Method VelocityClient.getUserReference ↗
Method VelocityClient.getUserReference ↗| Parameter | Type | Required |
|---|---|---|
subAccountId | numberSub-account id; defaults to `this.activeSubAccountId`. | No |
authority | PublicKeyAuthority owning the sub-account; defaults to `this.authority`. | No |
| Returns |
|---|
User |
Read account via User
Read raw onchain UserAccount data from a User instance.
const account = velocityClient.getUser().getUserAccount();
console.log(account);Method User.getUserAccountReference ↗
Method User.getUserAccountReference ↗| Returns |
|---|
UserAccount | undefined |
Read account via VelocityClient
Read raw onchain UserAccount data directly from VelocityClient for the active subaccount.
const account = velocityClient.getUserAccount();
console.log(account);Method VelocityClient.getUserAccountReference ↗
Method VelocityClient.getUserAccountReference ↗| Parameter | Type | Required |
|---|---|---|
subAccountId | numberSub-account id; defaults to `this.activeSubAccountId`. | No |
authority | PublicKeyAuthority owning the sub-account; defaults to `this.authority`. | No |
| Returns |
|---|
UserAccount | undefined |
Refresh user accounts
Force-refresh subscribed user-related accounts from RPC.
const user = velocityClient.getUser(0);
await user.fetchAccounts();Method User.fetchAccountsReference ↗
Method User.fetchAccountsReference ↗| Returns |
|---|
Promise<void> |
Derived Addresses (User Account)
User accounts are Program Derived Addresses (PDAs) - deterministic addresses generated from seeds (the program ID, wallet authority, and subaccount ID). PDAs are a Solana concept that lets you calculate an account’s address without making an RPC call.
This is useful when you need to reference a user account address before fetching it, such as when constructing transactions or querying multiple accounts in parallel.
For a deeper understanding of PDAs and program structure, see Program Structure.
Derive the user account PDA for a given subaccount without an RPC fetch.
const userAccountPublicKey = await velocityClient.getUserAccountPublicKey(0);
console.log(userAccountPublicKey.toBase58());Method VelocityClient.getUserAccountPublicKeyReference ↗
Method VelocityClient.getUserAccountPublicKeyReference ↗| Parameter | Type | Required |
|---|---|---|
subAccountId | numberSub-account id; defaults to `this.activeSubAccountId`. | No |
authority | PublicKeyAuthority owning the sub-account; defaults to `this.authority`. | No |
| Returns |
|---|
Promise<PublicKey> |
Query User State (Orders / Positions)
Get token amount (deposit vs borrow)
Return your token balance for a spot market as a signed value (+ deposit, - borrow).
import { BN } from "@velocity-exchange/sdk";
const user = velocityClient.getUser();
const tokenAmount = user.getTokenAmount(0);
const isDeposit = tokenAmount.gte(new BN(0));
const isBorrow = tokenAmount.lt(new BN(0));
console.log({ tokenAmount: tokenAmount.toString(), isDeposit, isBorrow });Method User.getTokenAmountReference ↗
Method User.getTokenAmountReference ↗| Parameter | Type | Required |
|---|---|---|
marketIndex | number | Yes |
| Returns |
|---|
BN |
Get a perp position
Get your perp position record for a market index (or undefined if no position exists).
const position = velocityClient.getUser().getPerpPosition(0);
console.log(position?.baseAssetAmount.toString());Method User.getPerpPositionReference ↗
Method User.getPerpPositionReference ↗| Parameter | Type | Required |
|---|---|---|
marketIndex | number | Yes |
| Returns |
|---|
PerpPosition | undefined |
Get an order by order id
Look up a specific order using its internal Velocity orderId.
const order = velocityClient.getUser().getOrder(1);
console.log(order);Method User.getOrderReference ↗
Method User.getOrderReference ↗| Parameter | Type | Required |
|---|---|---|
orderId | number | Yes |
| Returns |
|---|
Order | undefined |
Get an order by user order id
Look up a specific order using your custom userOrderId.
const order = velocityClient.getUser().getOrderByUserOrderId(1);
console.log(order);Method User.getOrderByUserOrderIdReference ↗
Method User.getOrderByUserOrderIdReference ↗| Parameter | Type | Required |
|---|---|---|
userOrderId | number | Yes |
| Returns |
|---|
Order | undefined |
Get all open orders
Return all currently open (unfilled/uncancelled) orders for this user.
const orders = velocityClient.getUser().getOpenOrders();
console.log(orders.length);Method User.getOpenOrdersReference ↗
Method User.getOpenOrdersReference ↗| Returns |
|---|
Order[] |
Active Subaccount
The active subaccount is the default subaccount that VelocityClient methods operate on when you don’t explicitly specify a subaccount ID. By default, the active subaccount is 0.
Many SDK methods use the active subaccount implicitly:
velocityClient.getUser()- returns the User for the active subaccountvelocityClient.getUserAccount()- returns the account data for the active subaccountvelocityClient.placePerpOrder(orderParams)- places an order on the active subaccountvelocityClient.getSpotPosition(marketIndex)- gets position from the active subaccountvelocityClient.getUser().getPerpPosition(marketIndex)- gets position from the active subaccount
You can change which subaccount is active using switchActiveUser(), or you can explicitly pass a subaccount ID to methods that support it (like getUser(subAccountId)).
Switch Active Subaccount
Change the active subaccount context used by default in VelocityClient methods.
// Switch to subaccount 1.
await velocityClient.switchActiveUser(1);Method VelocityClient.switchActiveUserReference ↗
Method VelocityClient.switchActiveUserReference ↗| Parameter | Type | Required |
|---|---|---|
subAccountId | numberSub-account id to make active. | Yes |
authority | PublicKeyAuthority to switch to; defaults to the current `this.authority` (no change). | No |
| Returns |
|---|
Promise<void> |
Update Delegate
A delegate is another wallet that can trade on behalf of your user account without being able to withdraw funds. This is useful for allowing trading bots to manage positions while keeping withdrawal authority secure.
Learn more: Delegated Accounts
Set or update the delegate authority allowed to trade on a subaccount.
import { PublicKey } from "@solana/web3.js";
await velocityClient.updateUserDelegate(new PublicKey("<DELEGATE_PUBKEY>"), 0);Method VelocityClient.updateUserDelegateReference ↗
Method VelocityClient.updateUserDelegateReference ↗| Parameter | Type | Required |
|---|---|---|
delegate | PublicKeyNew delegate public key; pass `PublicKey.default` to remove the delegate. | Yes |
subAccountId | numberSub-account id to update; defaults to `0`. | No |
| Returns |
|---|
Promise<string> |
A delegate can sign most trading instructions (place/cancel orders, etc.) on a subaccount’s behalf, but withdraw always requires the true owning authority’s own signature — delegates can never withdraw funds out of the protocol.
Delegate-initiated internal transfers
A delegate can move a subaccount’s collateral to another of the owner’s subaccounts via transferDepositByDelegate, but only once the owner has opted in. This opt-in is a single flag on the owner’s UserStats account (delegatePermissions) that gates delegate transfers across all of that owner’s subaccounts — it does not affect direct deposits/withdrawals or trading, and it is off by default.
Step 1 — the owner opts in (signed by the owner’s own wallet, not the delegate):
// Run with a VelocityClient whose wallet is the account owner (authority).
await velocityClient.updateUserAllowDelegateTransfer(true);Method VelocityClient.updateUserAllowDelegateTransferReference ↗
Method VelocityClient.updateUserAllowDelegateTransferReference ↗| Parameter | Type | Required |
|---|---|---|
allowDelegateTransfer | booleanNew value for the flag. | Yes |
| Returns |
|---|
Promise<string> |
Step 2 — the delegate transfers a deposit between the owner’s subaccounts:
// Run with a VelocityClient constructed with `authority: <OWNER_PUBKEY>` and a
// delegate wallet, per the "Delegated accounts" note in Setup.
const marketIndex = 0; // quote asset market
const amount = velocityClient.convertToSpotPrecision(marketIndex, 100);
await velocityClient.transferDepositByDelegate(
amount,
marketIndex,
0, // fromSubAccountId (owner's subaccount)
1 // toSubAccountId (owner's subaccount)
);Method VelocityClient.transferDepositByDelegateReference ↗
Method VelocityClient.transferDepositByDelegateReference ↗| Parameter | Type | Required |
|---|---|---|
amount | anyAmount to transfer, in the spot market's own token precision. | Yes |
marketIndex | numberSpot market index of the balance to transfer. | Yes |
fromSubAccountId | numberSub-account id to debit. | Yes |
toSubAccountId | numberSub-account id to credit. | Yes |
txParams | TxParamsOptional compute-unit/priority-fee overrides for the transaction. | No |
| Returns |
|---|
Promise<string> |
If the owner has not called updateUserAllowDelegateTransfer(true), the delegate’s transferDepositByDelegate call is rejected on-chain regardless of which subaccount the delegate is authorized on.
Update Margin Settings
Margin trading settings control how your account can use leverage and borrow against collateral. You can enable/disable margin trading or set custom margin ratios to control risk.
Learn more: Margin
Enable or disable margin trading permissions for one or more subaccounts.
await velocityClient.updateUserMarginTradingEnabled([
{ marginTradingEnabled: true, subAccountId: 0 },
]);Method VelocityClient.updateUserMarginTradingEnabledReference ↗
Method VelocityClient.updateUserMarginTradingEnabledReference ↗| Parameter | Type | Required |
|---|---|---|
updates | { marginTradingEnabled: boolean; subAccountId: number; }[]List of `{ marginTradingEnabled, subAccountId }` updates to apply. | Yes |
| Returns |
|---|
Promise<string> |
Set a custom margin ratio cap for one or more subaccounts.
// marginRatio is a number scaled by 10000 (MARGIN_PRECISION)
// e.g. 10000 = 1x max leverage, 5000 = 2x, 2000 = 5x
await velocityClient.updateUserCustomMarginRatio([
{ marginRatio: 5000, subAccountId: 0 }, // 2x max leverage
]);Method VelocityClient.updateUserCustomMarginRatioReference ↗
Method VelocityClient.updateUserCustomMarginRatioReference ↗| Parameter | Type | Required |
|---|---|---|
updates | { marginRatio: number; subAccountId: number; }[]List of `{ marginRatio, subAccountId }` updates to apply.
`marginRatio` is in `MARGIN_PRECISION` (1e4 = 100%), e.g. `2000` = 20% margin ratio = 5x max
leverage. | Yes |
txParams | TxParamsOptional compute-unit/priority-fee overrides for the transaction. | No |
| Returns |
|---|
Promise<string> |
Delete a User Account
If a subaccount has no assets/liabilities, it can be deleted to reclaim rent.
Delete an empty subaccount and close its user account.
await velocityClient.deleteUser(1);Method VelocityClient.deleteUserReference ↗
Method VelocityClient.deleteUserReference ↗| Parameter | Type | Required |
|---|---|---|
subAccountId | numberSub-account id to delete; defaults to `0`. | No |
txParams | TxParamsOptional compute-unit/priority-fee overrides for the transaction. | No |
| Returns |
|---|
Promise<string> |