Skip to main content
This example demonstrates how to place swift taker orders on Drift. Swift orders are signed order messages that can be filled by makers before being placed on-chain, enabling faster execution and better prices.

What It Does

The swift taker:
  • Creates signed order messages
  • Submits orders to the Swift API
  • Supports isolated position deposits
  • Demonstrates deposit+trade atomic operations
  • Shows order parameter configuration

Complete Source Code

use argh::FromArgs;
use base64::Engine;
use drift_rs::{
    swift_order_subscriber::{SignedOrderInfo, SignedOrderType},
    types::{
        MarketType, OrderParams, OrderType, PositionDirection, 
        SignedMsgOrderParamsMessage
    },
    Context, DriftClient, RpcClient, TransactionBuilder, Wallet,
};
use nanoid::nanoid;
use reqwest::header;

#[derive(FromArgs)]
struct SwiftTakerArgs {
    /// make a depositTrade request
    #[argh(switch)]
    deposit_trade: bool,
    /// isolated position deposit amount
    #[argh(option)]
    isolated_position: Option<u64>,
}

#[tokio::main]
async fn main() {
    let _ = env_logger::init();
    let _ = dotenv::dotenv();
    
    let wallet: Wallet = drift_rs::utils::load_keypair_multi_format(
        &std::env::var("PRIVATE_KEY").expect("base58 PRIVATE_KEY set"),
    )
    .unwrap()
    .into();
    
    let args: SwiftTakerArgs = argh::from_env();

    let use_mainnet = std::env::var("MAINNET").is_ok();
    let context = if use_mainnet {
        Context::MainNet
    } else {
        Context::DevNet
    };
    
    let rpc_url = std::env::var("RPC_URL")
        .unwrap_or_else(|_| "https://api.devnet.solana.com".to_string());
    let drift = DriftClient::new(context, RpcClient::new(rpc_url), wallet.clone())
        .await
        .expect("initialized client");

    let latest_slot = drift.rpc().get_slot().await.expect("get slot") + 200;

    // Create order parameters
    let order_params = OrderParams {
        market_index: 0,
        market_type: MarketType::Perp,
        order_type: OrderType::Oracle,
        base_asset_amount: 100_000_000,  // 0.1 SOL
        direction: PositionDirection::Long,
        auction_start_price: Some(1_00),
        auction_end_price: Some(1_000),
        auction_duration: Some(20),
        ..Default::default()
    };
    
    // Create signed message
    let signed_order_params = SignedMsgOrderParamsMessage {
        sub_account_id: 0,
        signed_msg_order_params: order_params,
        slot: latest_slot,
        uuid: nanoid!(8).as_bytes().try_into().unwrap(),
        take_profit_order_params: None,
        stop_loss_order_params: None,
        max_margin_ratio: None,
        builder_idx: None,
        builder_fee_tenth_bps: None,
        isolated_position_deposit: args.isolated_position,
    };
    
    let swift_order_type = SignedOrderType::authority(signed_order_params);
    let signed_msg = hex::encode(swift_order_type.to_borsh());
    let signature = drift.wallet.sign_message(signed_msg.as_bytes()).unwrap();

    let swift_order_request = serde_json::json!({
        "message": signed_msg,
        "taker_authority": wallet.authority().to_string(),
        "taker_pubkey": wallet.default_sub_account().to_string(),
        "signature": base64::prelude::BASE64_STANDARD.encode(signature.as_ref()),
    });

    if args.deposit_trade {
        let signed_order_info = SignedOrderInfo::authority(
            *drift.wallet.authority(), 
            signed_order_params, 
            signature
        );
        // SOL deposit, 0 = usdc, 1 = sol
        swift_deposit_trade(
            &drift,
            100_000_000,  // 0.1 SOL
            1,            // SOL market
            swift_order_request,
            signed_order_info,
        )
        .await;
    } else {
        swift_place_order(&drift, swift_order_request).await;
    }
}

async fn swift_place_order(
    drift: &DriftClient, 
    swift_order_request: serde_json::Value
) {
    println!("sending swift order: {swift_order_request:?}");
    
    let swift_url = if drift.context == Context::MainNet {
        "https://swift.drift.trade/orders"
    } else {
        "https://master.swift.drift.trade/orders"
    };
    
    let swift_cli = reqwest::Client::new();
    let req = swift_cli
        .post(swift_url)
        .header(header::CONTENT_TYPE, "application/json")
        .json(&swift_order_request)
        .build();
    
    let res = swift_cli.execute(req.unwrap()).await;
    let response = res.unwrap().text().await.unwrap();
    dbg!(response);
}

async fn swift_deposit_trade(
    drift: &DriftClient,
    deposit_amount: u64,
    deposit_market_index: u16,
    swift_order_request: serde_json::Value,
    signed_order_info: SignedOrderInfo,
) {
    println!(
        "sending swift depositTrade order: {swift_order_request:?}, deposit amount: {deposit_amount}, market: {deposit_market_index}"
    );
    
    let taker_subaccount = drift.wallet().default_sub_account();
    let taker_account_data = drift
        .get_user_account(&taker_subaccount)
        .await
        .expect("user account exists");

    let spot_market_config = drift
        .program_data()
        .spot_market_config_by_index(deposit_market_index)
        .unwrap();
    
    let create_ata_ix = 
        spl_associated_token_account::instruction::create_associated_token_account_idempotent(
            drift.wallet().authority(),
            drift.wallet().authority(),
            &spot_market_config.mint,
            &spot_market_config.token_program(),
        );

    let unsigned_tx = TransactionBuilder::new(
        drift.program_data(),
        taker_subaccount,
        std::borrow::Cow::Borrowed(&taker_account_data),
        false,
    )
    .add_ix(create_ata_ix)
    .deposit(deposit_amount, deposit_market_index, None, None)
    .place_swift_order(&signed_order_info, &taker_account_data)
    .build();
    
    let signed_tx = drift
        .wallet()
        .sign_tx(
            unsigned_tx.clone(),
            drift.get_latest_blockhash().await.unwrap(),
        )
        .unwrap();

    let sim_res = drift.simulate_tx(unsigned_tx).await;
    dbg!(sim_res);

    let req = serde_json::json!({
        "deposit_tx": base64::prelude::BASE64_STANDARD.encode(
            bincode::serialize(&signed_tx).unwrap()
        ),
        "swift_order": swift_order_request,
    });

    let swift_url = if drift.context == Context::MainNet {
        "https://swift.drift.trade/depositTrade"
    } else {
        "https://master.swift.drift.trade/depositTrade"
    };
    
    let swift_cli = reqwest::Client::new();
    let req = swift_cli
        .post(swift_url)
        .header(header::CONTENT_TYPE, "application/json")
        .json(&req)
        .build();
    
    let res = swift_cli.execute(req.unwrap()).await;
    let response = res.unwrap().text().await.unwrap();
    dbg!(response);
}

Key Concepts

Swift Orders

Swift orders are signed messages that represent order intent:
  • Off-chain: Orders exist as signed messages before on-chain placement
  • Fast Execution: Makers can fill orders immediately
  • Better Prices: Auction mechanism encourages competitive pricing
  • Flexible: Support isolated positions, TP/SL, and more

Order Parameters

let order_params = OrderParams {
    market_index: 0,                    // SOL-PERP
    market_type: MarketType::Perp,
    order_type: OrderType::Oracle,      // Oracle-based pricing
    base_asset_amount: 100_000_000,     // 0.1 SOL
    direction: PositionDirection::Long,
    auction_start_price: Some(1_00),    // Start 1 cent above oracle
    auction_end_price: Some(1_000),     // End $10 above oracle
    auction_duration: Some(20),         // 20 slots
    ..Default::default()
};

Auction Mechanism

Swift orders use a Dutch auction:
  1. Start Price: Most favorable to taker
  2. End Price: Least favorable to taker
  3. Duration: Price linearly interpolates over slots
  4. Incentive: Makers compete to fill at best price

Isolated Positions

Isolated positions separate risk:
isolated_position_deposit: Some(100_000_000), // 0.1 SOL collateral
Benefits:
  • Limited downside to deposited amount
  • Separate from main account
  • Useful for risky trades

Two Modes

1. Simple Swift Order

Just place the order:
cargo run --example swift-taker
Flow:
  1. Create and sign order message
  2. POST to Swift API
  3. Makers monitor and fill
  4. Order placed on-chain when filled

2. Deposit + Trade

Atomically deposit and place order:
cargo run --example swift-taker -- --deposit-trade \
  --isolated-position 100000000
Flow:
  1. Create signed transaction with deposit
  2. Create swift order message
  3. POST both to depositTrade endpoint
  4. Transaction executes atomically
Benefits:
  • Onboard and trade in one step
  • No need for existing balance
  • Atomic operation (all or nothing)

Running the Example

Simple Order

# Set environment
export PRIVATE_KEY="your-base58-key"
export RPC_URL="https://api.devnet.solana.com"

# Run
cargo run --example swift-taker

Deposit Trade

# With isolated position
cargo run --example swift-taker -- \
  --deposit-trade \
  --isolated-position 100000000

API Endpoints

Place Order

Endpoint: POST /orders Payload:
{
  "message": "hex-encoded-order",
  "taker_authority": "pubkey",
  "taker_pubkey": "sub-account-pubkey",
  "signature": "base64-signature"
}

Deposit Trade

Endpoint: POST /depositTrade Payload:
{
  "deposit_tx": "base64-encoded-transaction",
  "swift_order": {
    "message": "hex-encoded-order",
    "taker_authority": "pubkey",
    "taker_pubkey": "sub-account-pubkey",
    "signature": "base64-signature"
  }
}

Advanced Features

Take Profit / Stop Loss

Add TP/SL to your swift order:
take_profit_order_params: Some(OrderParams {
    order_type: OrderType::TakeProfitLimit,
    price: 150_000_000,  // Take profit at $150
    ..Default::default()
}),
stop_loss_order_params: Some(OrderParams {
    order_type: OrderType::StopLossLimit,
    price: 50_000_000,   // Stop loss at $50
    ..Default::default()
}),

Builder Fees

Pay fees to specific builders:
builder_idx: Some(0),
builder_fee_tenth_bps: Some(10),  // 0.1 bps

Best Practices

  1. Set Realistic Auction Params: Balance speed vs price improvement
  2. Use Appropriate Duration: Longer = better price, slower fill
  3. Monitor Fills: Subscribe to events to track execution
  4. Handle Errors: Swift API may reject invalid orders
  5. Test on DevNet: Always test with DevNet first

Troubleshooting

Order Not Filled:
  • Auction params too aggressive
  • Insufficient liquidity
  • Market conditions changed
depositTrade Failed:
  • Insufficient deposit amount
  • ATA not created
  • Invalid order parameters
Signature Invalid:
  • Check message encoding
  • Verify wallet authority
  • Ensure correct serialization

Next Steps

  • Monitor swift orders as a maker (Swift Maker)
  • Implement custom auction strategies
  • Build a trading UI on top of Swift
  • Add risk management and position sizing

Build docs developers (and LLMs) love