Drift Protocol uses oracle price feeds from Pyth and Switchboard for accurate asset pricing. This guide covers how to access oracle data, validate prices, and use oracles in your application.
Supported Oracle Sources
Drift supports multiple oracle providers:
Pyth Primary oracle source
Pyth V1 (pull-based)
Pyth Lazer (low-latency)
Pyth Pull Oracle
Switchboard Alternative oracle source
Switchboard V2
Switchboard On-Demand
Quote Asset $1.00 fixed price for stablecoins
Prelaunch For pre-launch markets
Getting Oracle Data
For Perpetual Markets
import { DriftClient , convertToNumber , PRICE_PRECISION } from '@drift-labs/sdk' ;
// Get oracle data for SOL-PERP (market 0)
const oracleData = driftClient . getOracleDataForPerpMarket ( 0 );
console . log ( 'Price:' , convertToNumber ( oracleData . price , PRICE_PRECISION ));
console . log ( 'Slot:' , oracleData . slot );
console . log ( 'Confidence:' , convertToNumber ( oracleData . confidence , PRICE_PRECISION ));
console . log ( 'Has sufficient samples:' , oracleData . hasSufficientNumberOfDataPoints );
// Get oracle data with slot information
const { data : oracleDataWithSlot , slot } = driftClient . getOraclePriceDataAndSlot (
driftClient . getPerpMarketAccount ( 0 ). amm . oracle
);
if ( oracleDataWithSlot ) {
console . log ( 'Oracle price at slot' , slot , ':' , oracleDataWithSlot . price . toString ());
}
For Spot Markets
// Get oracle data for SOL spot market (market 1)
const spotMarket = driftClient . getSpotMarketAccount ( 1 );
const oracleData = driftClient . getOracleDataForSpotMarket ( 1 );
console . log ( 'SOL spot oracle price:' , convertToNumber ( oracleData . price , PRICE_PRECISION ));
console . log ( 'Oracle source:' , spotMarket . oracleSource ); // e.g., OracleSource.PYTH
Oracle Price Data Structure
interface OraclePriceData {
price : BN ; // Current price
slot : number ; // Slot number
confidence : BN ; // Confidence interval
twap : BN | null ; // Time-weighted average price
twapConfidence : BN | null ; // TWAP confidence
hasSufficientNumberOfDataPoints : boolean ; // Data quality flag
}
Current oracle price with PRICE_PRECISION (10^6)
Solana slot when price was updated
Price confidence interval (lower is better)
Time-weighted average price for manipulation resistance
hasSufficientNumberOfDataPoints
Whether oracle has enough data points for reliability
Validating Oracle Data
Check Oracle Validity
import { isOracleValid } from '@drift-labs/sdk' ;
const perpMarket = driftClient . getPerpMarketAccount ( 0 );
const oracleData = driftClient . getOracleDataForPerpMarket ( 0 );
const currentSlot = await driftClient . connection . getSlot ();
const isValid = isOracleValid (
perpMarket . amm ,
oracleData ,
driftClient . getStateAccount (). oracleGuardRails ,
currentSlot
);
if ( ! isValid ) {
console . warn ( '⚠️ Oracle data is invalid or stale!' );
}
Check Price Divergence
import { isOracleTooDivergent } from '@drift-labs/sdk' ;
// Check if oracle price diverges too much from AMM
const perpMarket = driftClient . getPerpMarketAccount ( 0 );
const oracleData = driftClient . getOracleDataForPerpMarket ( 0 );
const divergenceCheck = isOracleTooDivergent (
oracleData . price ,
perpMarket . amm ,
driftClient . getStateAccount (). oracleGuardRails
);
if ( divergenceCheck . tooDivergent ) {
console . warn ( 'Oracle price diverges from AMM!' );
console . log ( 'Oracle price:' , divergenceCheck . oraclePrice . toString ());
console . log ( 'Mark price:' , divergenceCheck . markPrice . toString ());
}
Oracle Guard Rails
Drift uses oracle guard rails to protect against invalid or manipulated prices:
const state = driftClient . getStateAccount ();
const guardRails = state . oracleGuardRails ;
console . log ( 'Slots before stale:' , guardRails . slotsBeforeStaleForAmm );
console . log ( 'Confidence interval max:' , guardRails . confidenceIntervalMaxPercentDivergence );
console . log ( 'Max divergence:' , guardRails . priceDivergence . markOraclePercentDivergence );
Guard Rail Parameters
Number of slots before oracle is considered stale (typically 25-50)
confidenceIntervalMaxPercentDivergence
Maximum allowed confidence interval as percent of price
priceDivergence.markOraclePercentDivergence
Maximum percent divergence between oracle and mark price
Working with Pyth Oracles
Pyth Pull Oracles
Pyth pull oracles require price updates to be pushed on-chain:
import { PublicKey } from '@solana/web3.js' ;
// Get price update from Pyth Hermes API
const response = await fetch (
`https://hermes.pyth.network/api/latest_price_feeds?ids[]= ${ feedId } `
);
const priceData = await response . json ();
// Post price update on-chain
const priceUpdateAccount = /* ... */ ;
await driftClient . postPythPullOracleUpdate ( priceUpdateAccount , priceData );
Pyth Lazer
Pyth Lazer provides ultra-low latency price feeds:
import { PythLazerSubscriber } from '@drift-labs/sdk' ;
// Subscribe to Pyth Lazer feeds
const pythLazer = new PythLazerSubscriber ({
connection: driftClient . connection ,
// Lazer configuration
});
await pythLazer . subscribe ();
Calculating Prices
Bid and Ask Prices
import { calculateBidAskPrice , convertToNumber } from '@drift-labs/sdk' ;
const perpMarket = driftClient . getPerpMarketAccount ( 0 );
const oracleData = driftClient . getOracleDataForPerpMarket ( 0 );
const [ bidPrice , askPrice ] = calculateBidAskPrice (
perpMarket . amm ,
oracleData
);
console . log ( 'Bid price:' , convertToNumber ( bidPrice , PRICE_PRECISION ));
console . log ( 'Ask price:' , convertToNumber ( askPrice , PRICE_PRECISION ));
console . log ( 'Spread:' , convertToNumber ( askPrice . sub ( bidPrice ), PRICE_PRECISION ));
Mark Price (Fair Value)
import { calculateReservePrice } from '@drift-labs/sdk' ;
const perpMarket = driftClient . getPerpMarketAccount ( 0 );
const markPrice = calculateReservePrice ( perpMarket , oracleData );
console . log ( 'Mark price:' , convertToNumber ( markPrice , PRICE_PRECISION ));
Monitoring Oracle Health
import { convertToNumber , PERCENTAGE_PRECISION } from '@drift-labs/sdk' ;
function monitorOracleHealth ( driftClient : DriftClient , marketIndex : number ) {
const perpMarket = driftClient . getPerpMarketAccount ( marketIndex );
const oracleData = driftClient . getOracleDataForPerpMarket ( marketIndex );
const currentSlot = driftClient . getSlot ();
// Check staleness
const slotsSinceUpdate = currentSlot - oracleData . slot ;
if ( slotsSinceUpdate > 25 ) {
console . warn ( `Oracle stale by ${ slotsSinceUpdate } slots` );
}
// Check confidence
const confidencePercent = oracleData . confidence
. mul ( PERCENTAGE_PRECISION )
. div ( oracleData . price );
if ( confidencePercent . gt ( new BN ( 1000 ))) { // > 0.1%
console . warn (
'High confidence interval:' ,
convertToNumber ( confidencePercent , PERCENTAGE_PRECISION ),
'%'
);
}
// Check data quality
if ( ! oracleData . hasSufficientNumberOfDataPoints ) {
console . warn ( 'Insufficient oracle data points' );
}
// Check divergence
const markPrice = calculateReservePrice ( perpMarket , oracleData );
const divergence = markPrice . sub ( oracleData . price ). abs ()
. mul ( PERCENTAGE_PRECISION )
. div ( oracleData . price );
if ( divergence . gt ( new BN ( 100 ))) { // > 0.01%
console . warn (
'Mark/oracle divergence:' ,
convertToNumber ( divergence , PERCENTAGE_PRECISION ),
'%'
);
}
}
// Monitor continuously
setInterval (() => {
monitorOracleHealth ( driftClient , 0 ); // SOL-PERP
}, 5000 );
TWAP for Manipulation Resistance
Drift uses TWAP (Time-Weighted Average Price) to prevent oracle manipulation:
const oracleData = driftClient . getOracleDataForPerpMarket ( 0 );
if ( oracleData . twap ) {
const twapPrice = convertToNumber ( oracleData . twap , PRICE_PRECISION );
const spotPrice = convertToNumber ( oracleData . price , PRICE_PRECISION );
console . log ( 'TWAP price:' , twapPrice );
console . log ( 'Spot price:' , spotPrice );
console . log ( 'Difference:' , Math . abs ( twapPrice - spotPrice ));
}
Drift may use TWAP instead of spot price for certain operations like liquidations to prevent manipulation attacks.
Oracle Events
Listen for oracle price updates:
driftClient . eventEmitter . on ( 'oraclePriceUpdate' , ( pubkey , source , data ) => {
console . log ( 'Oracle update:' );
console . log ( ' Address:' , pubkey . toBase58 ());
console . log ( ' Source:' , source );
console . log ( ' Price:' , convertToNumber ( data . price , PRICE_PRECISION ));
console . log ( ' Slot:' , data . slot );
// Find which market uses this oracle
const perpMarkets = driftClient . getPerpMarketAccounts ();
for ( const market of perpMarkets ) {
if ( market . amm . oracle . equals ( pubkey )) {
console . log ( ' Market:' , market . name );
}
}
});
Best Practices
Always validate oracle data
const oracleData = driftClient . getOracleDataForPerpMarket ( marketIndex );
const currentSlot = await driftClient . connection . getSlot ();
if ( ! isOracleValid ( perpMarket . amm , oracleData , guardRails , currentSlot )) {
throw new Error ( 'Invalid oracle data' );
}
Monitor confidence intervals
High confidence intervals indicate uncertainty. Consider wider price bounds or reduced position sizes when confidence is high.
Stale oracles can lead to inaccurate pricing. Always check the slot difference before using oracle data.
Use TWAP for manipulation resistance
When available, prefer TWAP over spot price for critical operations to prevent flash loan attacks.
Next Steps
Advanced Features Explore advanced SDK capabilities
Oracle Concepts Deep dive into oracle mechanics
Oracle API Oracle data structures and methods
Market Data Access market and oracle data