This guide will walk you through creating your first drift-rs application that connects to Drift Protocol, subscribes to markets and oracles, and fetches real-time data.
Prerequisites
Make sure you’ve completed the installation steps and have a Rust project set up with drift-rs.
Basic Example: Market Data Subscriber
Let’s create a simple application that subscribes to market and oracle updates.
Import Required Dependencies
Add the necessary imports to your src/main.rs: use drift_rs :: {
DriftClient , RpcClient , Wallet , Context ,
types :: MarketId ,
};
use solana_sdk :: signature :: Keypair ;
#[tokio :: main]
async fn main () -> Result <(), Box < dyn std :: error :: Error >> {
// Initialize logger
env_logger :: init ();
// Your code will go here
Ok (())
}
Initialize DriftClient
Create a DriftClient instance with your RPC endpoint: // Create RPC client
let rpc_url = std :: env :: var ( "RPC_URL" )
. unwrap_or_else ( | _ | "https://api.mainnet-beta.solana.com" . to_string ());
let rpc_client = RpcClient :: new ( rpc_url );
// Create a wallet (read-only for this example)
let wallet = Wallet :: read_only (
solana_sdk :: pubkey! ( "11111111111111111111111111111111" )
);
// Initialize Drift client
let client = DriftClient :: new (
Context :: MainNet ,
rpc_client ,
wallet ,
)
. await
. expect ( "connects to Drift" );
println! ( "✅ Connected to Drift Protocol" );
For read-only operations (fetching data), you can use a dummy wallet. For signing transactions, you’ll need a real keypair.
Subscribe to Markets and Oracles
Subscribe to specific markets to receive live updates: // Define markets to track
let markets = vec! [
MarketId :: perp ( 0 ), // SOL-PERP
MarketId :: spot ( 1 ), // USDC spot market
];
// Subscribe to market account updates
client . subscribe_markets ( & markets )
. await
. expect ( "subscribes to markets" );
// Subscribe to oracle price feeds
client . subscribe_oracles ( & markets )
. await
. expect ( "subscribes to oracles" );
println! ( "📡 Subscribed to {} markets" , markets . len ());
Fetch Real-Time Data
Access the cached data from your subscriptions: // Wait a moment for initial data to arrive
tokio :: time :: sleep ( tokio :: time :: Duration :: from_secs ( 2 )) . await ;
// Get oracle price data
if let Some ( oracle ) = client . try_get_oracle_price_data_and_slot ( MarketId :: perp ( 0 )) {
let price = oracle . data . price as f64 / 1_000_000.0 ; // Convert to dollars
println! ( "SOL-PERP Oracle Price: ${:.2}" , price );
println! ( " Slot: {}" , oracle . slot);
println! ( " Confidence: {}" , oracle . data . confidence);
}
// Get market account data
if let Ok ( perp_market ) = client . try_get_perp_market_account ( 0 ) {
println! ( " \n SOL-PERP Market Info:" );
println! ( " Market Index: {}" , perp_market . market_index);
println! ( " Status: {:?}" , perp_market . status);
}
Clean Up
Unsubscribe when you’re done: // Unsubscribe from all updates
client . unsubscribe () . await ? ;
println! ( " \n ✅ Disconnected gracefully" );
Ok (())
Complete Example
Here’s the complete working example:
use drift_rs :: {
DriftClient , RpcClient , Wallet , Context ,
types :: MarketId ,
};
use solana_sdk :: signature :: Keypair ;
#[tokio :: main]
async fn main () -> Result <(), Box < dyn std :: error :: Error >> {
env_logger :: init ();
// Create RPC client
let rpc_url = std :: env :: var ( "RPC_URL" )
. unwrap_or_else ( | _ | "https://api.mainnet-beta.solana.com" . to_string ());
let rpc_client = RpcClient :: new ( rpc_url );
// Create wallet (read-only for data fetching)
let wallet = Wallet :: read_only (
solana_sdk :: pubkey! ( "11111111111111111111111111111111" )
);
// Initialize Drift client
let client = DriftClient :: new (
Context :: MainNet ,
rpc_client ,
wallet ,
)
. await
. expect ( "connects to Drift" );
println! ( "✅ Connected to Drift Protocol \n " );
// Subscribe to markets
let markets = vec! [
MarketId :: perp ( 0 ), // SOL-PERP
MarketId :: spot ( 1 ), // USDC
];
client . subscribe_markets ( & markets ) . await ? ;
client . subscribe_oracles ( & markets ) . await ? ;
println! ( "📡 Subscribed to {} markets \n " , markets . len ());
// Wait for initial data
tokio :: time :: sleep ( tokio :: time :: Duration :: from_secs ( 2 )) . await ;
// Fetch oracle price
if let Some ( oracle ) = client . try_get_oracle_price_data_and_slot ( MarketId :: perp ( 0 )) {
let price = oracle . data . price as f64 / 1_000_000.0 ;
println! ( "SOL-PERP Oracle Price: ${:.2}" , price );
println! ( " Slot: {}" , oracle . slot);
println! ( " Confidence: {}" , oracle . data . confidence);
}
// Fetch market data
if let Ok ( perp_market ) = client . try_get_perp_market_account ( 0 ) {
println! ( " \n SOL-PERP Market Info:" );
println! ( " Market Index: {}" , perp_market . market_index);
println! ( " Status: {:?}" , perp_market . status);
}
// Clean up
client . unsubscribe () . await ? ;
println! ( " \n ✅ Disconnected gracefully" );
Ok (())
}
Run Your Application
RPC_URL = https://api.mainnet-beta.solana.com cargo run
You should see output similar to:
✅ Connected to Drift Protocol
📡 Subscribed to 2 markets
SOL-PERP Oracle Price: $143.25
Slot: 285123456
Confidence: 50000
SOL-PERP Market Info:
Market Index: 0
Status: Active
✅ Disconnected gracefully
Advanced Example: Subscribe with Callbacks
For real-time updates, use callbacks to handle account changes as they happen:
use drift_rs :: {
DriftClient , RpcClient , Wallet , Context ,
types :: { MarketId , AccountUpdate , accounts :: PerpMarket },
};
use anchor_lang :: AccountDeserialize ;
#[tokio :: main]
async fn main () -> Result <(), Box < dyn std :: error :: Error >> {
let client = DriftClient :: new (
Context :: MainNet ,
RpcClient :: new ( "https://api.mainnet-beta.solana.com" ),
Wallet :: read_only ( solana_sdk :: pubkey! ( "11111111111111111111111111111111" )),
)
. await ? ;
// Subscribe with a callback function
let markets = vec! [ MarketId :: perp ( 0 )];
client . subscribe_markets_with_callback ( & markets , | update : & AccountUpdate | {
// Deserialize the market account
if let Ok ( market ) = PerpMarket :: try_deserialize ( & mut & update . data[ .. ]) {
println! ( "Market {} updated at slot {}" ,
market . market_index,
update . slot
);
}
})
. await ? ;
// Keep the program running to receive updates
println! ( "Listening for updates... Press Ctrl+C to exit" );
tokio :: signal :: ctrl_c () . await ? ;
Ok (())
}
Working with User Accounts
To fetch user account data and positions:
use drift_rs :: {
DriftClient , RpcClient , Wallet , Context ,
types :: accounts :: User ,
};
use solana_sdk :: signature :: Keypair ;
#[tokio :: main]
async fn main () -> Result <(), Box < dyn std :: error :: Error >> {
let wallet = Wallet :: from ( Keypair :: new ());
let client = DriftClient :: new (
Context :: MainNet ,
RpcClient :: new ( "https://api.mainnet-beta.solana.com" ),
wallet . clone (),
)
. await ? ;
// Get the default subaccount address
let subaccount = wallet . default_sub_account ();
// Subscribe to account updates
client . subscribe_account ( & subaccount ) . await ? ;
// Wait for data
tokio :: time :: sleep ( tokio :: time :: Duration :: from_secs ( 1 )) . await ;
// Get account data from cache
if let Ok ( user_account ) = client . try_get_account :: < User >( & subaccount ) {
println! ( "Authority: {}" , user_account . authority);
println! ( "Sub Account ID: {}" , user_account . sub_account_id);
// List open positions
for position in user_account . perp_positions {
if position . base_asset_amount != 0 {
println! ( "Position in market {}: {}" ,
position . market_index,
position . base_asset_amount
);
}
}
}
Ok (())
}
Next Steps
API Reference Explore the complete API documentation
Market Making Example Learn how to build a market maker bot
Event Subscriber Subscribe to order fills and protocol events
DLOB Builder Build decentralized limit order books
Common Patterns
Using gRPC Subscriptions
For advanced use cases, gRPC provides more features:
use drift_rs :: { DriftClient , GrpcSubscribeOpts , AccountFilter };
use drift_rs :: types :: accounts :: User ;
use drift_rs :: drift_idl :: traits :: Discriminator ;
let client = DriftClient :: new (
Context :: MainNet ,
RpcClient :: new ( "https://api.mainnet-beta.solana.com" ),
wallet ,
)
. await ? ;
// Subscribe via gRPC (automatically subscribes to all markets and oracles)
client . grpc_subscribe (
"https://grpc.example.com" . into (),
"YOUR_API_TOKEN" . into (),
GrpcSubscribeOpts :: default ()
. on_slot ( | new_slot | {
println! ( "New slot: {}" , new_slot );
})
. on_account (
AccountFilter :: partial () . with_discriminator ( User :: DISCRIMINATOR ),
| account | {
println! ( "User account updated" );
}
)
) . await ? ;
Getting All Market IDs
// Get all active perp markets
let perp_markets = client . get_all_perp_market_ids ();
// Get all active spot markets
let spot_markets = client . get_all_spot_market_ids ();
// Get all markets (perp + spot)
let all_markets = client . get_all_market_ids ();
// Subscribe to everything
client . subscribe_markets ( & all_markets ) . await ? ;
client . subscribe_oracles ( & all_markets ) . await ? ;
The client is cheaply clone-able. You can clone it and share it across async tasks without overhead.
Always call client.unsubscribe().await before your application exits to properly clean up WebSocket connections.