Skip to content
🌐Network: Mainnetâ–ŧ

Developer Quick Start - Rust ​

Build your first o2 trade using the official Rust SDK.

Prerequisites ​

  • Rust 1.75+ (install via rustup)

AI Tools ​

Use AI assistants to help build and run your trading bot. See the Skills (gives AI agents the o2 integration workflow and patterns) and the MCP Server (trade via natural language).

1. Set Up Your Project ​

bash
cargo new o2-quickstart && cd o2-quickstart
cargo add o2-sdk
cargo add tokio --features full

Add the dependencies to your Cargo.toml:

toml
[dependencies]
o2-sdk = "0.1"
tokio = { version = "1", features = ["full"] }

2. Get Test Funds ​

â„šī¸

Faucet is not available on mainnet. Deposit funds from your Fuel wallet to your trade account contract via the web-interface or via the Deposit flow.

3. Complete Trading Flow ​

Replace src/main.rs. The SDK handles network configuration, signing, encoding, and nonce management automatically.

Select your network (the SDK has built-in endpoints for each):

Network SelectionMainnet
use o2_sdk::Network;
let network = Network::Mainnet;  // https://api.o2.app
rust
use o2_sdk::{crypto, Network, O2Client, OrderType, Side};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Initialize client
    let mut client = O2Client::new(Network::Testnet);

    // 2. Generate wallet
    // Use generate_wallet() for a new key, or load_wallet("0x...") for an existing one
    let wallet = client.generate_wallet()?;
    println!("Owner address: {}", crypto::to_hex_string(&wallet.b256_address));

    // 3. Set up trading account
    // Idempotent: creates account, mints test tokens (testnet/devnet),
    // and whitelists — safe to call on every startup
    let account = client.setup_account(&wallet).await?;
    let trade_account_id = account.trade_account_id.unwrap();
    println!("Trade account: {trade_account_id}");

    // Wait for faucet funds to settle
    tokio::time::sleep(Duration::from_secs(5)).await;

    // 4. Fetch markets
    let markets = client.get_markets().await?;
    let market_pair = markets[0].symbol_pair();
    println!("Trading on: {market_pair}");

    // 5. Check balances
    let balances = client.get_balances(&trade_account_id).await?;
    for (symbol, bal) in &balances {
        println!("{symbol}: {}", bal.trading_account_balance);
    }

    // 6. Create session
    // Delegates trading authority to an ephemeral keypair.
    // The session is passed explicitly to all trading methods.
    // o2 sponsors gas for all session-based actions.
    let mut session = client
        .create_session(
            &wallet,
            &[market_pair.as_str()],
            Duration::from_secs(30 * 24 * 3600),
        )
        .await?;
    println!("Session created, expiry: {}", session.expiry);

    // 7. Place an order
    // Prices and quantities are human-readable strings — the SDK auto-scales them.
    // settle_first=true auto-prepends a SettleBalance action.
    // Use a low price to avoid immediate fill; notional must be >= ~5 USDC.
    let result = client
        .create_order(
            &mut session,
            market_pair.as_str(),
            Side::Buy,
            "1",
            "5",
            OrderType::Spot,
            true,
            true,
        )
        .await?;

    if result.is_success() {
        println!("Order tx: {}", result.tx_id.as_deref().unwrap_or("?"));

        if let Some(orders) = &result.orders {
            let order = &orders[0];
            println!("Order ID: {}", order.order_id);

            // 8. Check open orders
            tokio::time::sleep(Duration::from_secs(3)).await;
            let open_orders = client
                .get_orders(&trade_account_id, market_pair.as_str(), Some(true), 20)
                .await?;
            println!("Open orders: {}", open_orders.orders.len());

            // 9. Cancel order
            if !open_orders.orders.is_empty() {
                client
                    .cancel_order(
                        &mut session,
                        &open_orders.orders[0].order_id,
                        market_pair.as_str(),
                    )
                    .await?;
                println!("Order cancelled");
            }
        }
    } else {
        println!("Order failed: {:?}", result.message);
    }

    // 10. Check final balance
    let final_balances = client.get_balances(&trade_account_id).await?;
    for (symbol, bal) in &final_balances {
        println!("{symbol}: {}", bal.trading_account_balance);
    }

    println!("Done!");
    Ok(())
}

Run the project:

bash
cargo run

4. Batch Orders ​

Submit up to 5 actions per request using the fluent builder.

Create a new session scoped to the batch market, refresh the nonce, then submit:

rust
use o2_sdk::{OrderType, Side};

let mut session = client
    .create_session(
        &wallet,
        &["fFUEL/fUSDC"],
        std::time::Duration::from_secs(30 * 24 * 3600),
    )
    .await?;
client.refresh_nonce(&mut session).await?;

let actions = client
    .actions_for("fFUEL/fUSDC")
    .await?
    .settle_balance()
    .create_order(Side::Buy, "0.1", "50", OrderType::Spot)
    .create_order(Side::Buy, "1", "5", OrderType::Spot)
    .create_order(Side::Sell, "2", "5", OrderType::PostOnly)
    .build()?;

let result = client
    .batch_actions(&mut session, "fFUEL/fUSDC", actions, true)
    .await?;

println!("Batch tx: {}", result.tx_id.as_deref().unwrap_or("?"));
if let Some(orders) = &result.orders {
    for order in orders {
        println!("{} order: {}", order.side, order.order_id);
    }
}

WARNING

create_order automatically prepends a SettleBalance action when settle_first is true. When using batch_actions, add .settle_balance() explicitly if needed. The total action count (including settle) must not exceed 5.

5. Deposit & Withdraw ​

The easiest way to deposit and withdraw funds is through the o2 trading UI. The UI supports deposits from and withdrawals to EVM chains (Base, Ethereum) and Fuel wallets, and handles all bridging, wrapping, and unwrapping automatically.

Programmatic deposits & withdrawals coming soon

Fully integrated deposit and withdrawal support via the o2-sdk crate is currently in development.

In the meanwhile, if you want to go ahead, you can refer to the Fast Bridge docs and implement the deposit and withdrawal flow directly. For contract addresses and asset IDs, see the Network Identifiers page.

Key Concepts ​

ConceptDescription
Trading AccountOn-chain contract holding your balances and order state
SessionEphemeral keypair authorized to trade. Passed as &mut session to all trading methods
NonceCounter for replay protection. Auto-managed by the SDK
Gas Sponsorshipo2 pays gas fees for all session-based actions
setup_account()Idempotent — creates account, funds via faucet, whitelists. Safe on every startup
SettleBalanceMoves filled order proceeds into your available balance. Auto-prepended by create_order

Nonce management

The SDK auto-manages nonces. If you encounter errors after a failed transaction, call refresh_nonce() to re-sync:

rust
client.refresh_nonce(&mut session).await?;

SDK Reference ​

For the full API reference, advanced patterns (market making, WebSocket streams, external signers), and more examples, see the SDK documentation on GitHub.