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