Skip to main content
The drift-rs SDK provides a powerful subscription model that keeps local data synchronized with on-chain state via WebSocket or gRPC. This eliminates repeated RPC calls and ensures real-time updates.

Subscription Types

The SDK supports three subscription backends:
  1. WebSocket - Individual account subscriptions via Solana’s WebSocket API
  2. gRPC - High-throughput subscriptions via Yellowstone gRPC (Geyser)
  3. RPC Polling - Fallback polling for environments without WebSocket access

WebSocket Subscriptions

Market Subscriptions

Subscribe to spot and perp market accounts for live updates:
let markets = [MarketId::perp(0), MarketId::spot(1), MarketId::spot(2)];

// Subscribe to market account updates
client.subscribe_markets(&markets).await?;

// Subscribe to all markets
client.subscribe_all_markets().await?;

// Subscribe to just spot markets
client.subscribe_all_spot_markets().await?;

// Subscribe to just perp markets
client.subscribe_all_perp_markets().await?;
After subscribing, market queries use cached data:
// No RPC call - uses cached data
let sol_perp = client.get_perp_market_account(0).await?;
let usdc_spot = client.get_spot_market_account(0).await?;

// Try cached only (errors if not subscribed)
let btc_perp = client.try_get_perp_market_account(1)?;

Oracle Subscriptions

Subscribe to oracle price feeds by market:
let markets = [MarketId::perp(0), MarketId::spot(1)];

// Subscribe to oracle updates
client.subscribe_oracles(&markets).await?;

// Subscribe to all oracles
client.subscribe_all_oracles().await?;

// Now oracle queries use cached data
let price = client.oracle_price(MarketId::perp(0)).await?;
let oracle_data = client.try_get_oracle_price_data_and_slot(MarketId::perp(0))?;
Markets can share oracle pubkeys. The SDK automatically deduplicates subscriptions to ensure only one WebSocket subscription per unique oracle.

Account Subscriptions

Subscribe to individual User or UserStats accounts:
// Subscribe to a user account (subaccount)
client.subscribe_account(&user_pubkey).await?;

// Get data from cache
let user = client.get_user_account(&user_pubkey).await?;

// Unsubscribe when done
client.unsubscribe_account(&user_pubkey)?;

Blockhash Subscription

Subscribe to latest blockhashes for transaction signing:
// Start blockhash subscription
client.subscribe_blockhashes().await?;

// Get latest blockhash (cached, refreshed every 2 seconds)
let blockhash = client.get_latest_blockhash().await?;

Subscription with Callbacks

Register callbacks to react to updates:
let markets = [MarketId::perp(0)];

client.subscribe_markets_with_callback(&markets, |update| {
    println!("Market updated: {:?} at slot {}", update.pubkey, update.slot);
}).await?;

client.subscribe_oracles_with_callback(&markets, |update| {
    println!("Oracle updated: {:?}", update.pubkey);
}).await?;

gRPC Subscriptions (Yellowstone)

For high-performance applications, use gRPC subscriptions:
use drift_sdk::GrpcSubscribeOpts;
use solana_sdk::commitment_config::CommitmentLevel;

let opts = GrpcSubscribeOpts {
    // Cache all perp and spot markets
    oraclemap: true,
    
    // Cache all User accounts
    usermap: true,
    
    // Cache all UserStats accounts  
    user_stats_map: true,
    
    // Commitment level
    commitment: Some(CommitmentLevel::Confirmed),
    
    // Enable interslot updates for faster data
    interslot_updates: true,
    
    ..Default::default()
};

// Start gRPC subscription
client.grpc_subscribe(
    "http://grpc.endpoint.com:10000".to_string(),
    "your-x-token".to_string(),
    opts,
    true  // sync on startup
).await?;

// All queries now use gRPC-synced cache
let sol_perp = client.get_perp_market_account(0).await?;
let user = client.get_user_account(&user_pubkey).await?;

// Unsubscribe gRPC
client.grpc_unsubscribe();
gRPC and WebSocket subscriptions are mutually exclusive. Attempting to use WebSocket subscriptions after connecting via gRPC will return an error.

gRPC with Custom Callbacks

Register custom callbacks for gRPC updates:
use drift_sdk::grpc::AccountFilter;
use drift_sdk::types::accounts::User;

let mut opts = GrpcSubscribeOpts::default();

// Add custom account filter and callback
let filter = AccountFilter::partial()
    .with_discriminator(User::DISCRIMINATOR);

opts.on_account = Some(vec![
    (filter, Box::new(|update| {
        println!("User account updated: {}", update.pubkey);
    }))
]);

// Add slot update callback
opts.on_slot = Some(Box::new(|slot| {
    println!("New slot: {}", slot);
}));

// Add transaction callback
opts.on_transaction = Some(Box::new(|tx_update| {
    println!("Transaction: slot {}", tx_update.slot);
}));

client.grpc_subscribe(endpoint, token, opts, true).await?;

gRPC Connection Options

Configure connection settings:
use drift_sdk::grpc::GrpcConnectionOpts;

let connection_opts = GrpcConnectionOpts {
    timeout_ms: Some(30_000),
    connect_timeout_ms: Some(10_000),
    max_decoding_message_size: 1024 * 1024 * 512, // 512MB
    compression: true,
    ..Default::default()
};

let mut opts = GrpcSubscribeOpts::default();
opts.connection_opts = connection_opts;

RPC Polling Subscriptions

For environments without WebSocket support:
use std::time::Duration;

// Poll account every 5 seconds
client.subscribe_account_polled(
    &user_pubkey,
    Duration::from_secs(5)
).await?;

// Data is refreshed via polling
let user = client.get_user_account(&user_pubkey).await?;

Caching Behavior

MarketMap

Market accounts are cached in MarketMap<T> structures:
pub struct MarketMap<T> {
    marketmap: Arc<DashMap<u16, DataAndSlot<T>>>,
    subscriptions: DashMap<u16, UnsubHandle>,
    latest_slot: Arc<AtomicU64>,
    // ...
}
Key features:
  • Thread-safe: Uses DashMap for concurrent access
  • Slot-aware: Tracks slot for each update
  • Automatic updates: WebSocket streams update the map automatically

OracleMap

Oracle prices are cached by (Pubkey, OracleSource):
pub struct OracleMap {
    oraclemap: Arc<DashMap<(Pubkey, u8), Oracle>>,
    oracle_by_market: ReadOnlyView<MarketId, (Pubkey, OracleSource)>,
    // ...
}

pub struct Oracle {
    pub pubkey: Pubkey,
    pub data: OraclePriceData,
    pub source: OracleSource,
    pub slot: u64,
    pub raw: Vec<u8>,
}
Key features:
  • Source-aware: Same oracle pubkey can be used with different sources (e.g., PythLazer vs PythLazer1M)
  • Shared oracles: Markets sharing oracles only create one subscription
  • Mixed sources: Handles markets using the same oracle with different precision

AccountMap

User accounts are cached in AccountMap:
pub struct AccountMap {
    inner: Arc<DashMap<Pubkey, AccountSlot>>,
    subscriptions: Arc<DashMap<Pubkey, AccountSub<Subscribed>>>,
    // ...
}

struct AccountSlot {
    raw: Arc<[u8]>,
    slot: Slot,
    write_version: u64,
}
Supports:
  • WebSocket subscriptions
  • RPC polling
  • gRPC updates with write version tracking

Data Freshness

All subscriptions track the slot when data was last updated:
// Get data with slot information
let market = client.get_perp_market_account_and_slot(0).await?;
println!("Market data from slot: {}", market.slot);

let user = client.get_user_account_with_slot(&user_pubkey).await?;
println!("User data from slot: {}", user.slot);

let oracle = client.get_oracle_price_data_and_slot(MarketId::perp(0)).await?;
println!("Oracle price from slot: {}", oracle.slot);

Unsubscribing

Unsubscribe All

// Unsubscribe from all WebSocket subscriptions
client.unsubscribe().await?;

// Unsubscribe from gRPC
client.grpc_unsubscribe();

Unsubscribe Specific Accounts

// Unsubscribe from specific account
client.unsubscribe_account(&user_pubkey)?;

Best Practices

Use gRPC for Production: For high-frequency applications, gRPC provides better performance and lower latency than WebSocket subscriptions.
Subscribe to What You Need: Only subscribe to markets and accounts your application actively uses to minimize network overhead.
Handle Reconnections: The SDK automatically reconnects WebSocket subscriptions on failure. gRPC subscriptions retry up to 3 times.
Subscriptions are a no-op if already active. Multiple calls to subscribe_markets with the same markets won’t create duplicate subscriptions.

Build docs developers (and LLMs) love