Setup
This page goes from an empty project to a subscribed VelocityClient. Examples use placeholders like <RPC_URL> and <KEYPAIR_PATH>.
Install
bun add @velocity-exchange/sdkWallet and authentication
To interact with Solana you need a keypair: a public key and a private key. The private key signs transactions and should be kept secure. Generate one with the Solana CLI, then point ANCHOR_WALLET at the file so SDK code can find it:
solana-keygen new --outfile ~/.config/solana/my-keypair.json
export ANCHOR_WALLET=~/.config/solana/my-keypair.jsonLoad it with loadKeypair, and wrap it in the SDK's Wallet. The wallet needs some SOL: it pays transaction fees and the rent for any account the SDK initializes for that authority.
import { Wallet, loadKeypair } from "@velocity-exchange/sdk";
const keyPairFile = `${process.env.HOME}/.config/solana/my-keypair.json`;
const wallet = new Wallet(loadKeypair(keyPairFile));Create a Velocity client
At a minimum the client takes a Solana connection, a wallet, and the env. Call subscribe() to start receiving account updates, and unsubscribe() on shutdown so websocket handles and polling intervals are released. Nothing that reads cached state returns useful data before subscribe() resolves.
import { Connection } from "@solana/web3.js";
import { VelocityClient, Wallet, loadKeypair } from "@velocity-exchange/sdk";
const connection = new Connection("<RPC_URL>", "confirmed");
const wallet = new Wallet(loadKeypair("<KEYPAIR_PATH>"));
const velocityClient = new VelocityClient({
connection,
wallet,
env: "mainnet-beta",
});
await velocityClient.subscribe();
// ... place orders, read positions ...
await velocityClient.unsubscribe();Client configuration
| Parameter | Description | Optional | Default |
|---|---|---|---|
connection | Solana RPC connection | No | |
wallet | Wallet used to sign transactions | No | |
env | devnet or mainnet-beta, used to derive market accounts | Yes | mainnet-beta |
perpMarketIndexes | Perp market accounts to subscribe to | Yes | Derived from env |
spotMarketIndexes | Spot market accounts to subscribe to | Yes | Derived from env |
oracleInfos | Oracle accounts to subscribe to | Yes | Derived from env |
accountSubscription | Websocket, polling, or gRPC subscription mode | Yes | Websocket |
activeSubAccountId | Which subaccount to use initially | Yes | 0 |
subAccountIds | All subaccount IDs to subscribe to | Yes | [] |
authority | Authority the wallet signs for, only set for delegated accounts | Yes | wallet.publicKey |
txSender | Transaction sender used to broadcast and confirm | Yes | RetryTxSender |
txHandler | Builder and signer used for every transaction | Yes | A TxHandler on this connection and wallet |
txParams | Compute-unit limit and priority fee | Yes | computeUnits: 600000, computeUnitsPrice: 0 |
Delegated accounts. Signing on behalf of a delegated account requires setting subAccountIds, activeSubAccountId, and authority explicitly. Omit any of the three and the client subscribes to the wrong accounts. See Users for what a delegate can and cannot do.
See Transactions for the four available tx senders, blockhash caching, and the compute-unit and priority-fee options.
Account subscriptions
For most bots the default websocket subscription is the easiest way to keep markets and users up to date. For read-only workflows, or for tighter control over RPC load, switch to polling with a BulkAccountLoader. Its constructor takes (connection, commitment, pollingFrequencyMs); a frequency of 0 polls as fast as the loader is driven.
import { Connection } from "@solana/web3.js";
import { BulkAccountLoader, VelocityClient } from "@velocity-exchange/sdk";
const accountLoader = new BulkAccountLoader(connection, "confirmed", 1000);
const velocityClient = new VelocityClient({
connection,
wallet,
env: "mainnet-beta",
accountSubscription: {
type: "polling",
accountLoader,
},
// Optional: explicitly list markets and oracles to load.
// perpMarketIndexes: [0, 1],
// spotMarketIndexes: [0],
// oracleInfos: [{ publicKey: ORACLE_PUBKEY, source: ORACLE_SOURCE }],
});SDK Internals compares polling, websocket, and gRPC, and covers what BulkAccountLoader batches.
Multiple subaccounts
Velocity supports multiple subaccounts per wallet, each with its own position and order state. That allows separate strategies, say a market-making bot and a hedging bot, under one authority without their risk or PnL mixing. Subscribe to an extra subaccount after initialization with addUser(), guarded by hasUser() so a repeat call is a no-op.
if (!velocityClient.hasUser(1)) {
await velocityClient.addUser(1);
}See Users for switching the active subaccount, delegates, and per-subaccount margin settings.
Program addresses
| Network | Program ID |
|---|---|
| Velocity (mainnet and devnet) | vELoC1audYbSYVRXn1vPaV8Axoa9oU6BYmNGZZBDZ1P |
| Velocity Vaults | vAuLTsyrvSfZRuRB3XgvkPwNGgYSs9YRYymVebLKoxR |
Velocity uses the same program ID on devnet and mainnet-beta. It is an entirely new deployment, so user accounts must be re-initialized and balances start fresh; no prior onchain state carries over. Rather than pasting the address, import it:
import { VELOCITY_PROGRAM_ID } from "@velocity-exchange/sdk";
// The Velocity program's public key on mainnet-beta and devnet.
// Use this when deriving PDAs or referencing the program directly.
console.log(VELOCITY_PROGRAM_ID.toBase58());
// vELoC1audYbSYVRXn1vPaV8Axoa9oU6BYmNGZZBDZ1PQuote mint
The protocol's quote asset mint is environment-specific and available from the SDK's config presets. On mainnet-beta the quote asset is USDT (Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB). On devnet, spot market index 0 is dUSDT (GqmEqYsy8EyvofDpmtFxK8zhYrgWgNokAtYoduQdL7v6), a placeholder quote token at 1e6 precision rather than real USDT.
import { getConfig, initialize } from "@velocity-exchange/sdk";
initialize({ env: "devnet" });
console.log(getConfig().QUOTE_MINT_ADDRESS.toBase58());
// devnet: GqmEqYsy8EyvofDpmtFxK8zhYrgWgNokAtYoduQdL7v6 (dUSDT)
// mainnet-beta: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB (USDT)An integration ported from a different quote asset should check the migration guide.
Minting devnet dUSDT
On devnet, mint test collateral from the SDK's TokenFaucet before depositing.
import { PublicKey } from "@solana/web3.js";
import { BN, TokenFaucet } from "@velocity-exchange/sdk";
// <FAUCET_PROGRAM_ID> is the devnet token-faucet program's own program ID,
// a separate deployment from the Velocity program. It is not a fixed
// documented constant: read it from the devnet environment or deploy config.
const tokenFaucet = new TokenFaucet(
connection,
wallet,
new PublicKey("<FAUCET_PROGRAM_ID>"),
new PublicKey("GqmEqYsy8EyvofDpmtFxK8zhYrgWgNokAtYoduQdL7v6") // dUSDT mint (devnet)
);
const [associatedTokenAccount] = await tokenFaucet.createAssociatedTokenAccountAndMintTo(
wallet.publicKey,
new BN(1_000_000_000) // 1,000 dUSDT at 1e6 precision
);