Skip to Content
DevelopersDrift SDKSetup

Setup

The examples below use placeholders like <RPC_URL> and <KEYPAIR_PATH>.

Program Addresses

NetworkProgram ID
Drift (mainnet & devnet)dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH
Drift VaultsvAuLTsyrvSfZRuRB3XgvkPwNGgYSs9YRYymVebLKoxR

You can also import the Drift program ID directly from the SDK:

import { DRIFT_PROGRAM_ID } from "@drift-labs/sdk"; // The Drift program's public key on mainnet-beta and devnet. // Use this when deriving PDAs or referencing the program directly. console.log(DRIFT_PROGRAM_ID.toBase58()); // dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH
Variable DRIFT_PROGRAM_IDReference ↗
PropertyTypeRequired
toString
() => string
Yes
charAt
(pos: number) => string
Yes
charCodeAt
(index: number) => number
Yes
concat
(...strings: string[]) => string
Yes
indexOf
(searchString: string, position?: number | undefined) => number
Yes
lastIndexOf
(searchString: string, position?: number | undefined) => number
Yes
localeCompare
{ (that: string): number; (that: string, locales?: string | string[] | undefined, options?: CollatorOptions | undefined): number; (that: string, locales?: LocalesArgument, options?: CollatorOptions | undefined): number; }
Yes
match
{ (regexp: string | RegExp): RegExpMatchArray | null; (matcher: { [Symbol.match](string: string): RegExpMatchArray | null; }): RegExpMatchArray | null; }
Yes
replace
{ (searchValue: string | RegExp, replaceValue: string): string; (searchValue: string | RegExp, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: { ...; }, replaceValue: string): string; (searchValue: { ...; }, replacer: (substring: string, ...args: any[]) => string): string; }
Yes
search
{ (regexp: string | RegExp): number; (searcher: { [Symbol.search](string: string): number; }): number; }
Yes
slice
(start?: number | undefined, end?: number | undefined) => string
Yes
split
{ (separator: string | RegExp, limit?: number | undefined): string[]; (splitter: { [Symbol.split](string: string, limit?: number | undefined): string[]; }, limit?: number | undefined): string[]; }
Yes
substring
(start: number, end?: number | undefined) => string
Yes
toLowerCase
() => string
Yes
toLocaleLowerCase
{ (locales?: string | string[] | undefined): string; (locales?: LocalesArgument): string; }
Yes
toUpperCase
() => string
Yes
toLocaleUpperCase
{ (locales?: string | string[] | undefined): string; (locales?: LocalesArgument): string; }
Yes
trim
() => string
Yes
length
number
Yes
substr
(from: number, length?: number | undefined) => string
Yes
valueOf
() => string
Yes
codePointAt
(pos: number) => number | undefined
Yes
includes
(searchString: string, position?: number | undefined) => boolean
Yes
endsWith
(searchString: string, endPosition?: number | undefined) => boolean
Yes
normalize
{ (form: "NFC" | "NFD" | "NFKC" | "NFKD"): string; (form?: string | undefined): string; }
Yes
repeat
(count: number) => string
Yes
startsWith
(searchString: string, position?: number | undefined) => boolean
Yes
anchor
(name: string) => string
Yes
big
() => string
Yes
blink
() => string
Yes
bold
() => string
Yes
fixed
() => string
Yes
fontcolor
(color: string) => string
Yes
fontsize
{ (size: number): string; (size: string): string; }
Yes
italics
() => string
Yes
link
(url: string) => string
Yes
small
() => string
Yes
strike
() => string
Yes
sub
() => string
Yes
sup
() => string
Yes
padStart
(maxLength: number, fillString?: string | undefined) => string
Yes
padEnd
(maxLength: number, fillString?: string | undefined) => string
Yes
trimEnd
() => string
Yes
trimStart
() => string
Yes
trimLeft
() => string
Yes
trimRight
() => string
Yes
matchAll
(regexp: RegExp) => RegExpStringIterator<RegExpExecArray>
Yes
replaceAll
{ (searchValue: string | RegExp, replaceValue: string): string; (searchValue: string | RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
Yes
at
(index: number) => string | undefined
Yes
isWellFormed
() => boolean
Yes
toWellFormed
() => string
Yes
__@iterator@2718
() => StringIterator<string>
Yes

Wallet / Authentication

To interact with Solana you need a keypair, which consists of a public key and a private key. The private key is used to sign transactions and should be kept secure.

Generate a new keypair using the Solana CLI :

solana-keygen new --outfile ~/.config/solana/my-keypair.json

To allow SDK code to use this keypair, set the ANCHOR_WALLET environment variable to the path of the keypair file:

export ANCHOR_WALLET=~/.config/solana/my-keypair.json

Then load the keypair in your code:

import { Wallet, loadKeypair } from "@drift-labs/sdk"; const keyPairFile = `${process.env.HOME}/.config/solana/my-keypair.json`; const wallet = new Wallet(loadKeypair(keyPairFile));
Example Wallet / AuthenticationReference ↗
TypeScript docs unavailable for Wallet / Authentication.

Make sure the wallet has some SOL, as it is used to pay for transaction fees and rent for account initializations.

Install

npm i @drift-labs/sdk

Create a Drift Client

At a minimum you provide a Solana connection, a wallet, and the env. Then call subscribe() to start receiving account updates.

import { Connection } from "@solana/web3.js"; import { DriftClient, Wallet, loadKeypair } from "@drift-labs/sdk"; const connection = new Connection("<RPC_URL>", "confirmed"); const wallet = new Wallet(loadKeypair("<KEYPAIR_PATH>")); const driftClient = new DriftClient({ connection, wallet, env: "mainnet-beta", }); await driftClient.subscribe();
await driftClient.subscribe();
Method DriftClient.subscribeReference ↗
Returns
Promise<boolean>
await driftClient.unsubscribe();
Method DriftClient.unsubscribeReference ↗
Returns
Promise<void>

Key DriftClient parameters:

ParameterDescriptionOptionalDefault
connectionSolana RPC connectionNo
walletWallet used to sign transactionsNo
envdevnet or mainnet-beta, used to derive market accountsYes
perpMarketIndexesPerp market accounts to subscribe toYesDerived from env
spotMarketIndexesSpot market accounts to subscribe toYesDerived from env
oracleInfosOracle accounts to subscribe toYesDerived from env
accountSubscriptionWebSocket or polling subscription modeYesWebSocket
activeSubAccountIdWhich subaccount to use initiallyYes0
subAccountIdsAll subaccount IDs to subscribe toYes[]
authorityAuthority you’re signing for, only set for delegated accountsYeswallet.publicKey

Delegated accounts: When signing on behalf of a delegated account, you must explicitly set subAccountIds, activeSubAccountId, and authority. Omitting any of these will cause the client to subscribe to the wrong accounts.

Account Subscriptions (WebSocket vs Polling)

For most bots, websocket subscriptions are the easiest way to keep markets and users up to date. For read-only workflows or when you need tighter control over RPC load, you can switch to polling with a BulkAccountLoader.

import { BulkAccountLoader } from "@drift-labs/sdk";
Class BulkAccountLoaderReference ↗
PropertyTypeRequired
connection
Connection
Yes
commitment
Commitment
Yes
pollingFrequency
number
Yes
accountsToLoad
Map<string, AccountToLoad>
Yes
bufferAndSlotMap
Map<string, BufferAndSlot>
Yes
errorCallbacks
Map<string, (e: any) => void>
Yes
intervalId
Timeout
No
loadPromise
Promise<void>
No
loadPromiseResolver
() => void
Yes
lastTimeLoadingPromiseCleared
number
Yes
mostRecentSlot
number
Yes
addAccount
(publicKey: PublicKey, callback: (buffer: Buffer, slot: number) => void) => Promise<string>
Yes
removeAccount
(publicKey: PublicKey, callbackId: string) => void
Yes
addErrorCallbacks
(callback: (error: Error) => void) => string
Yes
removeErrorCallbacks
(callbackId: string) => void
Yes
chunks
<T>(array: readonly T[], size: number) => T[][]
Yes
load
() => Promise<void>
Yes
loadChunk
(accountsToLoadChunks: AccountToLoad[][]) => Promise<void>
Yes
handleAccountCallbacks
(accountToLoad: AccountToLoad, buffer: Buffer, slot: number) => void
Yes
getBufferAndSlot
(publicKey: PublicKey) => BufferAndSlot | undefined
Yes
getSlot
() => number
Yes
startPolling
() => void
Yes
stopPolling
() => void
Yes
log
(msg: string) => void
Yes
updatePollingFrequency
(pollingFrequency: number) => void
Yes
import { BulkAccountLoader } from "@drift-labs/sdk"; const accountLoader = new BulkAccountLoader(connection, "confirmed", 0); const driftClient = new DriftClient({ connection, wallet, env: "mainnet-beta", accountSubscription: { type: "polling", accountLoader, }, // Optional: explicitly list markets/oracles to load. // perpMarketIndexes: [0, 1], // spotMarketIndexes: [0], // oracleInfos: [{ publicKey: ORACLE_PUBKEY, source: ORACLE_SOURCE }], });
Example Polling subscriptionReference ↗
TypeScript docs unavailable for Polling subscription.

Multiple Subaccounts

Drift supports multiple subaccounts per wallet, each with its own isolated position and order state. This lets you run separate strategies (e.g., a market-making bot and a hedging bot) under the same authority without cross-contaminating risk or PnL. Use addUser() to subscribe to additional subaccounts after initialization.

if (!driftClient.hasUser(1)) { await driftClient.addUser(1); }
Method DriftClient.hasUserReference ↗
ParameterTypeRequired
subAccountId
number
No
authority
PublicKey
No
Returns
boolean
await driftClient.addUser(1);
Method DriftClient.addUserReference ↗
ParameterTypeRequired
subAccountId
number
Yes
authority
PublicKey
No
userAccount
UserAccount
No
Returns
Promise<boolean>

High Leverage Mode

High Leverage Mode allows eligible accounts to trade with increased maximum leverage (up to 50x on supported markets) compared to the standard 10x cap. It is an opt-in feature that requires the account to meet specific criteria set by the protocol. You can fetch the current high leverage configuration to check limits and eligibility conditions.

import { getHighLeverageModeConfigPublicKey } from "@drift-labs/sdk"; const pda = getHighLeverageModeConfigPublicKey(driftClient.program.programId); const config = await driftClient.program.account.highLeverageModeConfig.fetch(pda);
Function getHighLeverageModeConfigPublicKeyReference ↗
ParameterTypeRequired
programId
PublicKey
Yes
Returns
PublicKey
Last updated on