Skip to main content
The OracleMap manages oracle price feeds for all Drift markets with support for real-time WebSocket subscriptions.

Overview

pub struct OracleMap {
    pub oraclemap: Arc<DashMap<(Pubkey, u8), Oracle, ahash::RandomState>>,
    subscriptions: DashMap<Pubkey, UnsubHandle, ahash::RandomState>,
    pub oracle_by_market: ReadOnlyView<MarketId, (Pubkey, OracleSource), ahash::RandomState>,
    shared_oracles: ReadOnlyView<Pubkey, OracleShareMode, ahash::RandomState>,
    latest_slot: Arc<AtomicU64>,
    commitment: CommitmentConfig,
    pubsub: Arc<PubsubClient>,
}
OracleMap provides:
  • Shared oracle data - Multiple markets can use the same oracle
  • Real-time updates - WebSocket subscriptions for live price feeds
  • Multi-source support - Handles Pyth, Switchboard, and other oracle types
  • Thread-safe access - Concurrent reads without blocking

Oracle Types

Oracle

The Oracle struct contains price data and metadata:
pub struct Oracle {
    pub pubkey: Pubkey,
    pub data: OraclePriceData,
    pub source: OracleSource,
    pub slot: u64,
    pub raw: Vec<u8>,
}
pubkey
Pubkey
Oracle account address
data
OraclePriceData
Parsed price data with price, confidence, and validity
source
OracleSource
Oracle type (Pyth, Pyth1K, Pyth1M, PythStableCoin, Switchboard, etc.)
slot
u64
Slot when the data was fetched
raw
Vec<u8>
Raw account data

OracleSource

Supported oracle sources:
pub enum OracleSource {
    Pyth,          // Standard Pyth oracle
    Pyth1K,        // Pyth with 1K price precision
    Pyth1M,        // Pyth with 1M price precision  
    PythStableCoin,// Pyth stable coin oracle
    PythPull,      // Pyth pull-based oracle
    Switchboard,   // Switchboard oracle
    QuoteAsset,    // Quote asset (USDC) as oracle
    PythLazer,     // Pyth Lazer low-latency oracle
    Prelaunch,     // Pre-launch market oracle
}

Constructor

new()

Create a new OracleMap.
pub fn new(
    pubsub_client: Arc<PubsubClient>,
    all_oracles: &[(MarketId, Pubkey, OracleSource)],
    commitment: CommitmentConfig,
) -> Self
pubsub_client
Arc<PubsubClient>
required
Shared WebSocket client for subscriptions
all_oracles
&[(MarketId, Pubkey, OracleSource)]
required
Complete list of all market oracles
commitment
CommitmentConfig
required
RPC commitment level
The constructor builds internal mappings for:
  • Market → Oracle lookup
  • Shared oracle detection (one oracle used by multiple markets)
  • Mixed source detection (markets with different OracleSource using same oracle pubkey)

Subscription Methods

subscribe()

Subscribe to oracle updates for specific markets.
pub async fn subscribe(&self, markets: &[MarketId]) -> SdkResult<()>
Example:
let markets = vec![MarketId::perp(0), MarketId::spot(1)];
oraclemap.subscribe(&markets).await?;

// Oracle prices are now updated in real-time

subscribe_with_callback()

Subscribe with a custom callback for each oracle update.
pub async fn subscribe_with_callback<F>(
    &self,
    markets: &[MarketId],
    on_account: F,
) -> SdkResult<()>
where
    F: Fn(&AccountUpdate) + Send + Sync + 'static + Clone
Example:
oraclemap.subscribe_with_callback(&markets, |update| {
    println!("Oracle updated: {:?} at slot {}", update.pubkey, update.slot);
}).await?;

is_subscribed()

Check if an oracle is subscribed.
pub fn is_subscribed(&self, oracle: &Pubkey) -> bool

unsubscribe()

Unsubscribe from a specific oracle.
pub async fn unsubscribe(&self, oracle: &Pubkey) -> SdkResult<()>

unsubscribe_all()

Unsubscribe from all oracles.
pub fn unsubscribe_all(&self)

Data Access Methods

map()

Get a reference to the internal oracle map.
pub fn map(&self) -> Arc<DashMap<(Pubkey, u8), Oracle, ahash::RandomState>>
The map is keyed by (Pubkey, u8) where the u8 is the OracleSource as a discriminant.

get()

Get oracle data for a specific market.
pub fn get(&self, market: &MarketId) -> Option<Oracle>
Example:
if let Some(oracle) = oraclemap.get(&MarketId::perp(0)) {
    println!("SOL-PERP price: {}", oracle.data.price);
    println!("Confidence: {}", oracle.data.confidence);
    println!("Slot: {}", oracle.slot);
}

get_oracle_info()

Get the oracle pubkey and source for a market.
pub fn get_oracle_info(&self, market: &MarketId) -> Option<(Pubkey, OracleSource)>

get_latest_slot()

Get the latest slot from oracle updates.
pub fn get_latest_slot(&self) -> u64

Sync Methods

sync()

Fetch oracle data from RPC for specified markets.
pub async fn sync(
    &self,
    markets: &[MarketId],
    rpc_client: &RpcClient,
) -> SdkResult<()>
Use this to populate oracle data without subscribing, or to refresh data. Example:
let markets = vec![MarketId::perp(0), MarketId::perp(1)];
oraclemap.sync(&markets, &rpc_client).await?;

// Oracle data is now available
let sol_oracle = oraclemap.get(&MarketId::perp(0)).unwrap();

gRPC Integration

on_account_fn()

Get a callback function for gRPC account updates.
pub fn on_account_fn(&self) -> impl Fn(&GrpcAccountUpdate) + Send + Sync + 'static
This is used internally by DriftClient when subscribing via gRPC. Example:
let mut grpc_client = DriftGrpcClient::new(endpoint, token);

// Register oracle update handler
grpc_client.on_account(
    AccountFilter::firehose(),
    oraclemap.on_account_fn(),
);

Oracle Sharing

Multiple markets can share the same oracle. The OracleMap handles this efficiently:
// Both SOL spot and SOL perp use the same Pyth oracle
let sol_spot_oracle = oraclemap.get(&MarketId::spot(1)).unwrap();
let sol_perp_oracle = oraclemap.get(&MarketId::perp(0)).unwrap();

assert_eq!(sol_spot_oracle.pubkey, sol_perp_oracle.pubkey);

Mixed Sources

Some oracles can be used with different OracleSource types:
enum OracleShareMode {
    Normal { source: OracleSource },
    Mixed { sources: Vec<OracleSource> },
}
For example, a Pyth oracle might be used as Pyth by one market and Pyth1K by another.

Complete Example

use drift_rs::{DriftClient, Context, MarketId};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = DriftClient::new(
        Context::MainNet,
        rpc_client,
        wallet,
    ).await?;

    // Subscribe to oracles for specific markets
    let markets = vec![
        MarketId::perp(0),  // SOL-PERP
        MarketId::perp(1),  // BTC-PERP
        MarketId::spot(1),  // SOL spot
    ];
    
    client.subscribe_oracles(&markets).await?;

    // Access oracle data
    loop {
        tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
        
        let sol_oracle = client
            .try_get_oracle_price_data_and_slot(MarketId::perp(0))
            .unwrap();
        
        println!("SOL price: ${:.2}", 
            sol_oracle.data.price as f64 / 1e6
        );
        println!("Confidence: ${:.4}",
            sol_oracle.data.confidence as f64 / 1e6  
        );
        println!("Slot: {}", sol_oracle.slot);
    }
}

Performance Notes

Efficient Shared OraclesThe OracleMap automatically detects when multiple markets share the same oracle and only subscribes once, reducing WebSocket bandwidth.
Thread-Safe Concurrent AccessThe internal DashMap allows concurrent reads without locking, making it safe to access oracle data from multiple threads.

See Also

Build docs developers (and LLMs) love