Skip to content

Getting Started

Follow these steps to install the Openfish Rust SDK, pull live market information, and execute your first order.


Market data endpoints are public — no authentication needed. Query the gamma server to browse available markets and grab token IDs.

Terminal window
curl "https://gamma.openfish.fun/markets?active=true&closed=false&limit=1"
use openfish_client_sdk::gamma::Client;
use openfish_client_sdk::gamma::types::request::MarketsRequest;
let client = Client::default();
let request = MarketsRequest::builder()
.closed(false)
.limit(1)
.build();
let markets = client.markets(&request).await?;
let market = &markets[0];
println!("Question: {:?}", market.question);
println!("Token IDs: {:?}", market.clob_token_ids);
// Some(["abc123...", "def456..."]) -- [Yes token ID, No token ID]

Hold on to a token ID from clob_token_ids. The first entry corresponds to the Yes outcome, the second to No.

Every market also carries a condition_id — this is the primary identifier that ties the market together across all services.


Pull in the Openfish client as a dependency:

Terminal window
cargo add openfish-client-sdk

Alternatively, declare it in Cargo.toml directly:

[dependencies]
openfish-client-sdk = { version = "0.4", features = ["clob", "gamma"] }

The SDK is split into feature-gated modules, each mapping to an Openfish backend service:

FeatureModuleDescription
clobopenfish_client_sdk::clobTrading, orders, and order book data
gammaopenfish_client_sdk::gammaMarket metadata and events
dataopenfish_client_sdk::dataAnalytics and positions
bridgeopenfish_client_sdk::bridgeCross-chain deposits and withdrawals
ctfopenfish_client_sdk::ctfConditional token operations (split/merge/redeem)
wsopenfish_client_sdk::wsWebSocket streams for real-time data

Generate API credentials and initialize the trading client. Openfish relies on a two-tier authentication scheme:

  • L1 auth: A wallet signature proves you own the private key
  • L2 auth: The server issues an API key and secret; all subsequent requests carry HMAC signatures derived from these credentials
use std::str::FromStr;
use openfish_client_sdk::POLYGON;
use openfish_client_sdk::auth::{LocalSigner, Signer};
use openfish_client_sdk::clob::{Client, Config};
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 the trading client
// The SDK handles L1 signing and L2 key derivation automatically
// Signature type defaults to EOA (type 0)
let client = Client::new("https://api.openfish.fun", Config::default())?
.authentication_builder(&signer)
.authenticate()
.await?;
  • EOA (type 0): Your wallet signs orders directly and pays its own gas in POL
  • OPENFISH_PROXY (type 1): Orders are signed through your proxy wallet; gas is relayed by Openfish
  • GNOSIS_SAFE (type 2): Orders are signed through your Gnosis Safe multisig

Your funder address must hold USDC.e (to purchase outcome tokens) and POL (for gas when using EOA type 0). Proxy and Safe wallet users can take advantage of Openfish’s gasless relayer instead.


With the token_id from Step 1, submit a limit order:

use openfish_client_sdk::clob::types::Side;
use openfish_client_sdk::types::dec;
// Parse the token ID from Step 1
let token_id = "YOUR_TOKEN_ID".parse()?;
// The SDK auto-fetches tick size, neg risk flag, and fee rate
// No manual lookup required -- the order builder handles it
let order = client
.limit_order()
.token_id(token_id)
.price(dec!(0.50)) // Buy Yes at $0.50 (50% implied probability)
.size(dec!(10)) // 10 shares
.side(Side::Buy)
.build()
.await?;
// Sign and submit
let signed_order = client.sign(&signer, order).await?;
let response = client.post_order(signed_order).await?;
println!("Order ID: {}", response.order_id);
println!("Status: {:?}", response.status);

Once submitted, your order enters the matching engine:

  1. Immediately marketable (your buy price >= the lowest ask): the order fills against resting orders right away
  2. Not yet marketable: the order sits on the book as a GTC order, waiting to be matched or cancelled
  3. On match: the resulting trade is sent to the Exchange contract on Polygon for on-chain settlement

Different order behaviors can be specified at build time:

// Good Till Date -- expires at a specific Unix timestamp
let order = client
.limit_order()
.token_id(token_id)
.price(dec!(0.45))
.size(dec!(20))
.side(Side::Buy)
.order_type("GTD")
.expiration(1735689600) // Unix timestamp
.build()
.await?;
// Fill Or Kill -- must fill entirely or is cancelled
let order = client
.limit_order()
.token_id(token_id)
.price(dec!(0.50))
.size(dec!(10))
.side(Side::Buy)
.order_type("FOK")
.build()
.await?;

Poll the CLOB server for the current status of an order:

let order_info = client.get_order("YOUR_ORDER_ID").await?;
println!("Status: {:?}", order_info.status);
println!("Size matched: {:?}", order_info.size_matched);

Or retrieve all your open orders at once:

let open_orders = client.list_orders().await?;
for order in &open_orders {
println!("{}: {} @ {} -- {:?}", order.id, order.side, order.price, order.status);
}

Remove a single order or clear everything:

// Cancel a single order
client.cancel_order("YOUR_ORDER_ID").await?;
// Cancel all open orders
client.cancel_all().await?;
// Cancel all orders for a specific market
client.cancel_market_orders("CONDITION_ID").await?;

The CLOB server (port 3002) exposes these commonly used endpoints:

EndpointMethodAuthDescription
/marketsGETNoList active markets
/markets/{condition_id}GETNoGet a specific market
/bookGETNoOrder book depth for a token
/priceGETNoCurrent price for a token
/midpointGETNoMidpoint price
/spreadGETNoBid-ask spread
/orderPOSTYesSubmit a new order
/orderDELETEYesCancel an order
/cancel-allDELETEYesCancel all open orders
/data/ordersGETYesList your orders
/data/tradesGETYesList your trades
/auth/derive-api-keyGETL1Derive API credentials

For live data, connect to the WebSocket endpoints:

EndpointDescription
/ws/marketMarket-level events: trades, order book updates, resolutions
/ws/userUser-specific events: order fills, cancellations, balance changes