Skip to main content
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.
1

Import Required Dependencies

Add the necessary imports to your src/main.rs:
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(())
}
2

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.
3

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());
4

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!("\nSOL-PERP Market Info:");
    println!("  Market Index: {}", perp_market.market_index);
    println!("  Status: {:?}", perp_market.status);
}
5

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:
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>> {
    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!("\nSOL-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.

Build docs developers (and LLMs) love