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>,
}
Parsed price data with price, confidence, and validity
Oracle type (Pyth, Pyth1K, Pyth1M, PythStableCoin, Switchboard, etc.)
Slot when the data was fetched
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
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);
}
}
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