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 â
cargo new o2-quickstart && cd o2-quickstart
cargo add o2-sdk
cargo add tokio --features fullAdd the dependencies to your Cargo.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):
use o2_sdk::Network;
let network = Network::Mainnet; // https://api.o2.appuse 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:
cargo run4. 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:
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 â
| Concept | Description |
|---|---|
| Trading Account | On-chain contract holding your balances and order state |
| Session | Ephemeral keypair authorized to trade. Passed as &mut session to all trading methods |
| Nonce | Counter for replay protection. Auto-managed by the SDK |
| Gas Sponsorship | o2 pays gas fees for all session-based actions |
| setup_account() | Idempotent â creates account, funds via faucet, whitelists. Safe on every startup |
| SettleBalance | Moves 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:
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.