Getting Started with Your First Trade
Follow this guide to install the Rust SDK, connect to the platform, and submit a limit order from a clean slate.
Step 1: Install the SDK
Section titled “Step 1: Install the SDK”Add the Openfish client crate to your project:
cargo add openfish-client-sdk --features clobYou will also need an async runtime and a wallet signer:
cargo add tokio --features fullcargo add alloy --features signersStep 2: Set Up Your Client
Section titled “Step 2: Set Up Your Client”The code below derives API credentials from your wallet and produces an authenticated trading client. By default, the SDK assumes an EOA wallet.
use std::str::FromStr;use openfish_client_sdk::POLYGON;use openfish_client_sdk::auth::{LocalSigner, Signer};use openfish_client_sdk::clob::{Client, Config};
#[tokio::main]async fn main() -> anyhow::Result<()> { let private_key = std::env::var("OPENFISH_PRIVATE_KEY")?; let signer = LocalSigner::from_str(&private_key)? .with_chain_id(Some(POLYGON));
// Derive API credentials and initialize client in one step let client = Client::new("https://api.openfish.fun", Config::default())? .authentication_builder(&signer) .authenticate() .await?;
println!("Authenticated successfully"); Ok(())}Proxy and Safe Wallets
Section titled “Proxy and Safe Wallets”When your funds live in a proxy or Safe wallet, pass the appropriate signature type. The SDK computes the funder address automatically through CREATE2:
use openfish_client_sdk::clob::types::SignatureType;
let client = Client::new("https://api.openfish.fun", Config::default())? .authentication_builder(&signer) .signature_type(SignatureType::GnosisSafe) // or SignatureType::Proxy .authenticate() .await?;Before trading: Make sure the funder address holds USDC.e (needed to purchase outcome tokens). EOA wallets additionally require POL to cover gas. Proxy and Safe wallets can bypass gas costs by using the gasless relayer.
Step 3: Place an Order
Section titled “Step 3: Place an Order”Look up a token ID through the Markets API, then construct, sign, and post a limit order.
use openfish_client_sdk::clob::types::Side;use openfish_client_sdk::types::dec;
let token_id = "YOUR_TOKEN_ID".parse()?;
// Build the order (tick size, neg risk, and fee rate are auto-fetched)let order = client .limit_order() .token_id(token_id) .price(dec!(0.50)) .size(dec!(10)) .side(Side::Buy) .build() .await?;
// Sign with your wallet keylet signed_order = client.sign(&signer, order).await?;
// Submit to the CLOBlet response = client.post_order(signed_order).await?;
println!("Order ID: {}", response.order_id);println!("Status: {:?}", response.status);Possible response statuses:
live— the order is sitting on the book, waiting for a matchmatched— the order filled right awaydelayed— the order is held temporarily by a matching delay
Step 4: Check Your Orders
Section titled “Step 4: Check Your Orders”use openfish_client_sdk::clob::types::request::{OrdersRequest, TradesRequest};
// View all open orderslet open_orders = client.orders(&OrdersRequest::default(), None).await?;println!("You have {} open orders", open_orders.data.len());
// View your trade historylet trades = client.trades(&TradesRequest::default(), None).await?;println!("You have made {} trades", trades.data.len());
// Cancel an orderclient.cancel_order(&response.order_id).await?;println!("Order cancelled");Troubleshooting
Section titled “Troubleshooting”L2 AUTH NOT AVAILABLE - Invalid Signature
Section titled “L2 AUTH NOT AVAILABLE - Invalid Signature”This usually means the private key, signature type, or funder address does not match the credentials stored server-side.
- Double-check that
SignatureTypereflects your actual wallet (EOA, Proxy, or GnosisSafe) - Make sure the funder address corresponds to your wallet type
- Call
authenticate()again to re-derive fresh credentials
Order rejected - insufficient balance
Section titled “Order rejected - insufficient balance”The funder address does not hold enough tokens for the order:
- BUY orders: require USDC.e in the funder address
- SELL orders: require outcome tokens in the funder address
- Available balance accounts for amounts reserved by existing open orders
Order rejected - price violates tick size
Section titled “Order rejected - price violates tick size”Every market enforces a minimum price increment. The SDK fetches this automatically when building orders. If you are assembling orders by hand, query the tick size first:
let tick_size = client.tick_size(token_id).await?;Chain ID not set
Section titled “Chain ID not set”The signer must have a chain ID configured. Use .with_chain_id(Some(POLYGON)) when creating the LocalSigner.
Next Steps
Section titled “Next Steps”- Create Orders — Order types, batch orders, and post-only -> orders/create
- Orderbook — Read prices, spreads, and depth before placing orders -> orderbook
- Fees — Understand taker fees and how they affect your orders -> fees