2 unstable releases
| 0.2.0 | Mar 27, 2026 |
|---|---|
| 0.1.0 | Feb 21, 2026 |
| 0.0.1 |
|
#1308 in Parser implementations
260KB
5.5K
SLoC
O2 SDK for Rust
Official Rust SDK for the O2 Exchange — a fully on-chain order book DEX on the Fuel Network.
Installation
Add to your Cargo.toml:
[dependencies]
o2-sdk = "0.1.0"
tokio = { version = "1", features = ["full"] }
MSRV: Rust 1.75
Quick Start
Recommended first integration path on testnet:
- Create/load owner wallet
- Call
setup_account()(idempotent setup + faucet mint attempt on testnet/devnet) - (Optional) Call
top_up_from_faucet()for an explicit testnet/devnet top-up - Create session
- Place orders
- Read balances/orders
- Settle balances back to your trading account after fills; order funds are moved into the market contract during execution and should be swept after fills or cancellations
use o2_sdk::{Network, O2Client, OrderType, Side};
#[tokio::main]
async fn main() -> Result<(), o2_sdk::O2Error> {
let mut client = O2Client::new(Network::Testnet);
let wallet = client.generate_wallet()?;
let account = client.setup_account(&wallet).await?;
let _ = client.top_up_from_faucet(&wallet).await?;
let market_symbol = "fFUEL/fUSDC";
let mut session = client
.create_session(
&wallet,
&[market_symbol],
std::time::Duration::from_secs(30 * 24 * 3600),
)
.await?;
let market = client.get_market(market_symbol).await?;
let response = client
.create_order(
&mut session,
market_symbol,
Side::Buy,
market.price("0.02")?,
market.quantity("50")?,
OrderType::Spot,
true,
true,
)
.await?;
println!("order tx={}", response.tx_id.unwrap_or_default());
if let Some(trade_account_id) = account.trade_account_id {
let balances = client.get_balances(&trade_account_id).await?;
println!("assets={}", balances.len());
}
let settle = client.settle_balance(&mut session, market_symbol).await?;
println!("settle tx={}", settle.tx_id.unwrap_or_default());
Ok(())
}
get_balances(trade_account_id) is an aggregated view across trading account
and market contracts, so settle_balance(...) does not necessarily change aggregate totals.
Network Configuration
Default network configs:
| Network | REST API | WebSocket | Fuel RPC | Faucet |
|---|---|---|---|---|
Network::Testnet |
https://api.testnet.o2.app |
wss://api.testnet.o2.app/v1/ws |
https://testnet.fuel.network/v1/graphql |
https://fuel-o2-faucet.vercel.app/api/testnet/mint-v2 |
Network::Devnet |
https://api.devnet.o2.app |
wss://api.devnet.o2.app/v1/ws |
https://devnet.fuel.network/v1/graphql |
https://fuel-o2-faucet.vercel.app/api/devnet/mint-v2 |
Network::Mainnet |
https://api.o2.app |
wss://api.o2.app/v1/ws |
https://mainnet.fuel.network/v1/graphql |
none |
API rate limits: https://docs.o2.app/api-endpoints-reference.html#rate-limits.
Use custom config if needed:
use o2_sdk::{Network, NetworkConfig, O2Client};
let mut cfg = NetworkConfig::from_network(Network::Mainnet);
cfg.api_base = "https://my-gateway.example.com".into();
cfg.ws_url = "wss://my-gateway.example.com/v1/ws".into();
cfg.faucet_url = None;
let client = O2Client::with_config(cfg);
[!IMPORTANT] Mainnet note: there is no faucet; account setup requires an owner wallet that already has funds deposited for trading. SDK-native bridging flows are coming soon.
Wallet Security
generate_wallet()/generate_evm_wallet()use cryptographically secure randomness and are suitable for mainnet key generation.- For production custody, use external signers (KMS/HSM/hardware wallets) instead of long-lived in-process private keys.
- See
docs/guides/external-signers.mdfor production signer integration.
Wallet Types and Identifiers
Why choose each wallet type:
- Fuel-native wallet — best for interoperability with other apps in the Fuel ecosystem.
- EVM wallet — best if you want to reuse existing EVM accounts across chains and simplify bridging from EVM chains.
O2 owner identity model:
- O2 owner identity is always Fuel B256 (
0x+ 64 hex chars). - Fuel-native wallets already expose that directly as B256.
- EVM wallets expose both EVM and B256 forms.
- For EVM wallets, B256 is the EVM address zero-left-padded to 32 bytes:
owner_b256 = 0x000000000000000000000000 + evm_address[2:]
Identifier usage:
| Context | Identifier |
|---|---|
| Owner/account/session APIs | owner B256 (wallet.b256_address) |
| Trading account state | trade_account_id (contract ID) |
| Human-visible EVM identity | evm_address |
| Markets | pair ("fFUEL/fUSDC") or market_id |
owner_id vs trade_account_id:
owner_idis wallet identity (B256) used for ownership/auth and session setup.trade_account_idis the trading account contract ID used for balances/orders/account state.setup_account(&wallet)links these by creating/fetching the trading account for that owner.
Features
- Trading — Place, cancel, and manage orders with automatic price/quantity scaling
- Market Data — Fetch order book depth, recent trades, OHLCV candles, and ticker data
- WebSocket Streams — Real-time depth, order, trade, balance, and nonce updates via
Stream - Wallet Support — Fuel-native and EVM wallets with session-based signing
- Batch Actions — Submit up to 5 actions per request (cancel + settle + create in one call)
- Async Runtime — Built on
tokiowithreqwestfor HTTP andtokio-tungstenitefor WebSocket - Type Safety — Strongly typed responses with
serdedeserialization andthiserrorerrors
API Overview
| Method | Description |
|---|---|
generate_wallet() / load_wallet(hex) |
Create or load a Fuel wallet |
generate_evm_wallet() / load_evm_wallet(hex) |
Create or load an EVM wallet |
setup_account(&wallet) |
Idempotent account setup |
top_up_from_faucet(&wallet) |
Explicit faucet top-up to the wallet's trading account (testnet/devnet) |
create_session(&wallet, markets, ttl) |
Create a trading session |
create_order(&mut session, market_symbol, side, price, qty, ...) |
Place an order |
cancel_order(&mut session, order_id, market) |
Cancel a specific order |
cancel_all_orders(&mut session, market) |
Cancel all open orders |
settle_balance(&mut session, market) |
Settle filled order proceeds |
batch_actions(&mut session, actions, calls, collect) |
Submit raw action batch |
get_markets() / get_market(name) |
Fetch market info |
get_depth(market, precision) / get_trades(market, count) |
Order book and trade data |
get_balances(trade_account_id) / get_orders(id, market, ...) |
Account data |
stream_depth(market_id, precision) |
Real-time order book stream |
stream_orders(identities) / stream_trades(market_id) |
Real-time updates |
See AGENTS.md for the complete API reference with all parameters and types.
Guides
| Guide | Description |
|---|---|
| Trading | Order types, batch actions, cancel/replace, and market maker patterns |
| Market Data | Fetching depth, trades, candles, tickers, and balances |
| WebSocket Streams | Real-time data with TypedStream and reconnection handling |
| Error Handling | Error types, recovery patterns, and robust trading loops |
| External Signers | Integrating KMS/HSM via the SignableWallet trait |
| Identifiers and Wallet Types | Fuel vs EVM wallet choice, owner ID mapping, and identifier rules |
Examples
| Example | Description |
|---|---|
quickstart.rs |
Connect, create a wallet, place your first order |
market_maker.rs |
Two-sided quoting loop with cancel/replace |
taker_bot.rs |
Monitor depth and take liquidity |
portfolio.rs |
Multi-market balance tracking and management |
Run an example:
cargo run --example quickstart
Testing
Unit tests (no network required):
cargo test
Integration tests (requires O2_PRIVATE_KEY env var):
O2_PRIVATE_KEY=0x... cargo test -- --ignored --test-threads=1
The --test-threads=1 flag avoids nonce race conditions during integration tests.
AI Agent Integration
See AGENTS.md for an LLM-optimized reference covering all methods, types, error codes, and common patterns.
Dependencies
~14–22MB
~330K SLoC