# Setup

> Canonical: https://docs.velocity.exchange/developers/velocity-sdk/setup

This page goes from an empty project to a subscribed `VelocityClient`. Examples use placeholders like `<RPC_URL>` and `<KEYPAIR_PATH>`.

## Install

```bash
bun add @velocity-exchange/sdk
```

## Wallet 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](https://docs.solanalabs.com/cli/install), then point `ANCHOR_WALLET` at the file so SDK code can find it:

```bash
solana-keygen new --outfile ~/.config/solana/my-keypair.json
export ANCHOR_WALLET=~/.config/solana/my-keypair.json
```

Load 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.

  
```js
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.

  
```js
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` |

> **Warning:**
>
> **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](/developers/velocity-sdk/users.md#update-delegate) for what a delegate can and cannot do.

See [Transactions](/developers/velocity-sdk/transactions.md) 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.

  
```js
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](/developers/velocity-sdk/sdk-internals.md#account-subscription-strategies) 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.

  
```js
if (!velocityClient.hasUser(1)) {
  await velocityClient.addUser(1);
}
```
  

See [Users](/developers/velocity-sdk/users.md) 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:

  
```js
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());
// vELoC1audYbSYVRXn1vPaV8Axoa9oU6BYmNGZZBDZ1P
```
  

## Quote 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.

  
```js
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](/developers/migrate-from-drift.md).

### Minting devnet dUSDT

On devnet, mint test collateral from the SDK's `TokenFaucet` before depositing.

  
```js
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
);
```
