Skip to main content
The AccountMap is a hybrid account subscription manager that maintains a cache of Drift accounts and keeps them synchronized with the Solana network via WebSocket or RPC polling.

Overview

AccountMap provides a unified interface for subscribing to and managing Drift accounts (User, UserStats, State, etc.) with automatic updates.

Creating an AccountMap

use drift_rs::AccountMap;
use std::sync::Arc;

let account_map = AccountMap::new(
    Arc::clone(&pubsub_client),
    Arc::clone(&rpc_client),
    commitment_config,
);

Methods

Subscribe to Account (WebSocket)

subscribe_account
async fn(&self, account: &Pubkey) -> SdkResult<()>
Subscribe to an account using WebSocket for real-time updates.
// Subscribe to a user account
let user_pubkey = Wallet::derive_user_account(&authority, 0);
account_map.subscribe_account(&user_pubkey).await?;

Subscribe with Callback

subscribe_account_with_callback
async fn(&self, account: &Pubkey, on_account: F) -> SdkResult<()>
Subscribe to an account with a custom callback function that fires on each update.
account_map.subscribe_account_with_callback(
    &user_pubkey,
    |update| {
        println!("Account updated at slot: {}", update.slot);
    }
).await?;

Subscribe with Polling

subscribe_account_polled
async fn(&self, account: &Pubkey, interval: Option<Duration>) -> SdkResult<()>
Subscribe to an account using RPC polling at a fixed interval (default: 5 seconds).
use std::time::Duration;

// Poll every 10 seconds
account_map.subscribe_account_polled(
    &user_pubkey,
    Some(Duration::from_secs(10))
).await?;

Subscribe Polling with Callback

subscribe_account_polled_with_callback
async fn(&self, account: &Pubkey, interval: Option<Duration>, on_account: F) -> SdkResult<()>
Subscribe to an account using RPC polling with a custom callback.

Unsubscribe

unsubscribe_account
fn(&self, account: &Pubkey)
Unsubscribe from an account and remove it from the cache.
account_map.unsubscribe_account(&user_pubkey);

Get Account Data

account_data
fn<T: Pod>(&self, account: &Pubkey) -> Option<T>
Retrieve the cached account data as the specified type.
use drift_rs::types::User;

if let Some(user) = account_map.account_data::<User>(&user_pubkey) {
    println!("User authority: {}", user.authority);
}

Get Account Data with Slot

account_data_and_slot
fn<T: Pod>(&self, account: &Pubkey) -> Option<DataAndSlot<T>>
Retrieve the cached account data along with the slot at which it was retrieved.
if let Some(data_and_slot) = account_map.account_data_and_slot::<User>(&user_pubkey) {
    println!("User data from slot: {}", data_and_slot.slot);
    println!("Authority: {}", data_and_slot.data.authority);
}

Sync User Accounts

sync_user_accounts
async fn(&self, filters: Vec<RpcFilterType>) -> SdkResult<()>
Bulk synchronize all user accounts matching the provided filters using RPC.
// Sync all user accounts for a specific authority
let filters = vec![
    RpcFilterType::Memcmp(Memcmp::new_raw_bytes(8, authority.to_bytes().to_vec()))
];
account_map.sync_user_accounts(filters).await?;

Sync UserStats Accounts

sync_stats_accounts
async fn(&self) -> SdkResult<()>
Bulk synchronize all UserStats accounts from the Drift program.
// Sync all user stats accounts
account_map.sync_stats_accounts().await?;

Iterate Accounts

iter_accounts_with
fn<T: Pod + Discriminator>(&self, f: impl FnMut(&Pubkey, &T, u64))
Iterate over all cached accounts of a specific type.
use drift_rs::types::User;

// Print all cached user accounts
account_map.iter_accounts_with::<User>(|pubkey, user, slot| {
    println!("User {}: authority={}, slot={}", pubkey, user.authority, slot);
});

gRPC Integration

on_account_fn
fn(&self) -> impl Fn(&AccountUpdate)
Returns a callback function suitable for use with gRPC account subscriptions.
// For use with gRPC subscribers
let on_account = account_map.on_account_fn();
grpc_subscriber.subscribe_with_callback(on_account);

Complete Example

use drift_rs::{AccountMap, Wallet};
use drift_rs::types::User;
use std::sync::Arc;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let pubsub = Arc::new(PubsubClient::new("wss://api.mainnet-beta.solana.com").await?);
    let rpc = Arc::new(RpcClient::new("https://api.mainnet-beta.solana.com".to_string()));
    
    let account_map = AccountMap::new(
        pubsub,
        rpc,
        CommitmentConfig::confirmed(),
    );
    
    // Derive user account address
    let authority = /* your authority pubkey */;
    let user_pubkey = Wallet::derive_user_account(&authority, 0);
    
    // Subscribe to the account
    account_map.subscribe_account(&user_pubkey).await?;
    
    // Wait for data to arrive
    tokio::time::sleep(Duration::from_secs(2)).await;
    
    // Access the cached data
    if let Some(user) = account_map.account_data::<User>(&user_pubkey) {
        println!("User authority: {}", user.authority);
        println!("Total deposits: {}", user.total_deposits);
        println!("Open orders: {}", user.open_orders);
    }
    
    // Unsubscribe when done
    account_map.unsubscribe_account(&user_pubkey);
    
    Ok(())
}

Notes

  • Accounts are automatically deduplicated - subscribing to the same account multiple times has no effect
  • WebSocket subscriptions provide real-time updates with lower latency
  • Polled subscriptions are useful when WebSocket connections are unreliable or unavailable
  • The account cache is thread-safe and can be shared across multiple tasks
  • Account data is returned as zero-copy references for efficiency

Build docs developers (and LLMs) love