The DriftClient is the primary interface for interacting with Drift protocol. It provides a unified API for querying market data, managing positions, and executing trades on Solana.
Architecture
The client is built on a cheaply clone-able architecture that encourages cloning for concurrent access:
// DriftClient is cheaply cloneable - shares underlying resources
let client = DriftClient::new(
Context::MainNet,
RpcClient::new("https://rpc.example.com"),
key_pair.into()
).await.expect("initializes");
// Clone for concurrent access - shares connections and memory
let client_clone = client.clone();
It is not recommended to create multiple instances with ::new() as this will not re-use underlying resources such as network connections or memory allocations. Always clone existing instances instead.
Backend Architecture
The DriftClient is a thin wrapper around a singleton DriftClientBackend that handles all heavy-lifting:
pub struct DriftClient {
pub context: Context,
backend: &'static DriftClientBackend,
pub wallet: Wallet,
}
pub struct DriftClientBackend {
rpc_client: Arc<RpcClient>,
pubsub_client: Arc<PubsubClient>,
program_data: ProgramData,
blockhash_subscriber: BlockhashSubscriber,
account_map: AccountMap,
perp_market_map: MarketMap<PerpMarket>,
spot_market_map: MarketMap<SpotMarket>,
oracle_map: OracleMap,
grpc_unsub: RwLock<Option<(UnsubHandle, UnsubHandle)>>,
}
Key architectural components:
- RPC Client: For synchronous queries and transaction submission
- PubSub Client: For WebSocket-based live updates
- Program Data: Cached on-chain program metadata (markets, lookup tables, state)
- Market Maps: Live or cached spot/perp market data
- Oracle Map: Real-time oracle price feeds
- Account Map: User account subscriptions (User, UserStats)
Initialization
Basic Initialization
use drift_sdk::{DriftClient, Context, Wallet};
use solana_rpc_client::nonblocking::rpc_client::RpcClient;
let client = DriftClient::new(
Context::MainNet,
RpcClient::new("https://api.mainnet-beta.solana.com"),
Wallet::from_keypair(keypair)
).await?;
Custom WebSocket URL
For environments with separate HTTP and WebSocket endpoints:
let client = DriftClient::new_with_ws_url(
Context::MainNet,
RpcClient::new("https://rpc.example.com"),
Wallet::from_keypair(keypair),
"wss://ws.example.com"
).await?;
Accessing Program Data
The client initializes with all market configurations cached:
// Get all active spot markets
let spot_markets = client.get_all_spot_market_ids();
// Get all active perp markets
let perp_markets = client.get_all_perp_market_ids();
// Access program data directly
let program_data = client.program_data();
let spot_configs = program_data.spot_market_configs();
let perp_configs = program_data.perp_market_configs();
// Get State account with exchange-level config
let state = client.state_account()?;
Query Modes
The client operates in two modes:
1. Ad-hoc RPC Queries (Default)
Without subscriptions, all data fetches hit the RPC endpoint:
let client = DriftClient::new(context, rpc, wallet).await?;
// Each call makes an RPC request
let sol_perp = client.get_perp_market_account(0).await?;
let oracle_price = client.oracle_price(MarketId::perp(0)).await?;
let user = client.get_user_account(&user_pubkey).await?;
2. Subscription Mode (Live Updates)
After subscribing, queries use cached data updated via WebSocket or gRPC:
let markets = [MarketId::perp(0), MarketId::spot(1)];
// Subscribe to live market data
client.subscribe_markets(&markets).await?;
client.subscribe_oracles(&markets).await?;
// Now uses cached data (no RPC call)
let sol_perp = client.get_perp_market_account(0).await?;
let oracle_price = client.oracle_price(MarketId::perp(0)).await?;
// Unsubscribe when done
client.unsubscribe().await?;
Subscription mode is recommended for applications that need real-time data or make frequent queries. It significantly reduces RPC load and improves response times.
Accessing Markets and Oracles
// Get spot market (cached if subscribed, otherwise RPC)
let usdc_market = client.get_spot_market_account(0).await?;
// Get perp market with slot information
let sol_perp = client.get_perp_market_account_and_slot(0).await?;
println!("SOL-PERP at slot {}", sol_perp.slot);
// Try get from cache only (fails if not subscribed)
let btc_perp = client.try_get_perp_market_account(1)?;
// Get oracle price
let price = client.oracle_price(MarketId::perp(0)).await?;
// Get full oracle data with slot and metadata
let oracle = client.get_oracle_price_data_and_slot(MarketId::perp(0)).await?;
println!("Price: {}, Confidence: {}", oracle.data.price, oracle.data.confidence);
Accessing User Accounts
// Get user account (subaccount)
let user = client.get_user_account(&user_pubkey).await?;
// Get with slot information
let user_with_slot = client.get_user_account_with_slot(&user_pubkey).await?;
// Get UserStats account
let stats = client.get_user_stats(&authority).await?;
// Query orders
let open_orders = client.all_orders(&user_pubkey).await?;
let order = client.get_order_by_id(&user_pubkey, order_id).await?;
// Query positions
let (spot_positions, perp_positions) = client.all_positions(&user_pubkey).await?;
let sol_position = client.perp_position(&user_pubkey, 0).await?;
Market Lookup
Lookup markets by symbol:
// Find market by symbol
let sol_perp = client.market_lookup("SOL-PERP")?;
let usdc_spot = client.market_lookup("USDC")?;
// Symbols are case-insensitive
let btc = client.market_lookup("btc-perp")?;
Transaction Management
// Build a transaction
let tx = client
.init_tx(&user_pubkey, false)
.await?
.cancel_orders([order_id])
.place_orders([new_order])
.build();
// Sign and send
let signature = client.sign_and_send(tx).await?;
// Or simulate first
let sim_result = client.simulate_tx(tx).await?;
if let Some(err) = sim_result.err {
println!("Simulation failed: {:?}", err);
}
Latest Blockhash
Get recent blockhashes for transaction signing:
// Subscribe for live blockhash updates
client.subscribe_blockhashes().await?;
// Get latest blockhash (cached if subscribed)
let blockhash = client.get_latest_blockhash().await?;
Helpers and Utilities
// Get RPC client reference
let rpc = client.rpc();
// Get WebSocket client reference
let ws = client.ws();
// Get wallet
let wallet = client.wallet();
// Get context
let context = client.context;
// Check SDK version compatibility
DriftClient::check_libs()?;
Best Practices
Clone, Don’t Create: Always clone existing DriftClient instances for concurrent access rather than creating new ones.
Subscribe for Hot Paths: If your application frequently queries the same data, subscribe to it. This eliminates RPC overhead and ensures fresh data via WebSocket/gRPC.
Unsubscribe When Done: Always call unsubscribe() when your subscriptions are no longer needed to free network resources.
The client performs URL validation on initialization. Ensure your RPC and WebSocket URLs are valid before calling ::new().