Overview
The Rust SDK provides OrderClient for order creation, EIP-712 signing, and order management. It supports:
OrderType::Gtc for resting limit orders
OrderType::Fak for fill-and-kill limit orders
OrderType::Fok for fill-or-kill market orders
Prerequisites
Before placing orders, you need:
- an authenticated
Client
- a private key for EIP-712 signing
- market venue data (fetched via
get_market() and cached automatically)
use limitless_exchange_rust_sdk::Client;
let client = Client::new()?;
let private_key = std::env::var("PRIVATE_KEY")?;
let order_client = client.new_order_client(&private_key, None)?;
The OrderClient lazily fetches your profile on the first order to determine your owner_id and fee rate. CHAIN_ID defaults to 8453 (Base mainnet) unless you override it.
Token Approvals
Before your first trade on a given venue, you must approve the exchange contracts to spend your tokens. This is a one-time on-chain setup per venue.
Approve USDC and Conditional Tokens to the exchange contract:let market = client.markets.get_market("your-market-slug").await?;
let exchange = market
.venue
.as_ref()
.expect("venue")
.exchange
.clone();
println!("approve USDC and CTF to {}", exchange);
NegRisk markets also require Conditional Token approval to the adapter contract:let market = client.markets.get_market("your-negrisk-slug").await?;
let venue = market.venue.as_ref().expect("venue");
println!("exchange {}", venue.exchange);
println!("adapter {}", venue.adapter.as_ref().expect("adapter"));
Approvals are on-chain transactions that cost gas. Use venue.exchange for both CLOB and NegRisk, and additionally venue.adapter for NegRisk markets.
GTC Orders
GTC orders remain on the book until filled or explicitly cancelled.
use limitless_exchange_rust_sdk::{CreateOrderParams, GtcOrderArgs, OrderType, Side};
let market = client.markets.get_market("btc-above-100k-march-2025").await?;
let tokens = market.tokens.as_ref().expect("market tokens");
let result = order_client
.create_order(CreateOrderParams {
order_type: OrderType::Gtc,
market_slug: market.slug.clone(),
args: GtcOrderArgs {
token_id: tokens.yes.clone(),
side: Side::Buy,
price: 0.65,
size: 10.0,
expiration: None,
nonce: None,
taker: None,
post_only: false,
}
.into(),
})
.await?;
println!("order id {}", result.order.id);
Post-only GTC order
Use post_only: true to ensure the order never crosses the spread as a taker:
let result = order_client
.create_order(CreateOrderParams {
order_type: OrderType::Gtc,
market_slug: market.slug.clone(),
args: GtcOrderArgs {
token_id: tokens.yes.clone(),
side: Side::Buy,
price: 0.65,
size: 10.0,
expiration: None,
nonce: None,
taker: None,
post_only: true,
}
.into(),
})
.await?;
GtcOrderArgs
| Field | Type | Description |
|---|
token_id | String | Token ID from market data |
side | Side | Side::Buy or Side::Sell |
price | f64 | Price between 0 and 1, tick-aligned to 0.001 |
size | f64 | Number of shares |
post_only | bool | Optional. Rejects the order if it would immediately match |
expiration | Option<String> | Must be "0" / None (the default). Non-zero expiration is not currently supported and is rejected by the API. |
nonce | Option<i32> | Must be 0 / None (the default). Non-zero nonce is not currently supported and is rejected by the API. |
taker | Option<String> | Optional taker address |
Self-Trade Prevention
Set stp_policy on CreateOrderParams to control what happens when your incoming order would match your own resting order on the same token. It is a top-level request field — not part of the EIP-712 signed order args — and applies to any order type. Leave it None to keep the server default, cancel_maker.
use limitless_exchange_rust_sdk::{CreateOrderParams, GtcOrderArgs, OrderType, Side, StpPolicy};
let result = order_client
.create_order(CreateOrderParams {
order_type: OrderType::Gtc,
market_slug: market.slug.clone(),
stp_policy: Some(StpPolicy::CancelMaker), // CancelMaker | CancelTaker | CancelBoth
args: GtcOrderArgs {
token_id: tokens.yes.clone(),
side: Side::Buy,
price: 0.65,
size: 10.0,
expiration: None,
nonce: None,
taker: None,
post_only: false,
}
.into(),
})
.await?;
| Value | Result |
|---|
StpPolicy::CancelMaker | Default. Cancels your conflicting resting order and continues with the incoming order. |
StpPolicy::CancelTaker | Rejects the incoming order before it self-trades. |
StpPolicy::CancelBoth | Cancels your conflicting resting order and rejects the incoming order. |
The create-order response carries an execution field with the outcome:
let execution = &result.execution;
if execution.settlement_status == "CANCELED" && execution.reason.as_deref() == Some("STP_TAKER_REJECTED") {
println!("order rejected by self-trade prevention");
} else if !execution.stp_maker_cancels.is_empty() {
println!("cancelled own resting orders: {:?}", execution.stp_maker_cancels);
}
execution field | Type | Description |
|---|
settlement_status | String | CANCELED when a cancel_taker / cancel_both order is rejected. |
reason | Option<String> | STP_TAKER_REJECTED when the incoming order was rejected. |
stp_maker_cancels | Vec<String> | Resting order ids cancelled by cancel_maker / cancel_both. |
Self-trade prevention blocks same-profile matches on the same token only. Orders on a different token of the same profile are unaffected. The wire field is always stpPolicy, regardless of the SDK.
FAK Orders
FAK orders use the same price + size inputs as GTC, but any unmatched remainder is cancelled immediately.
use limitless_exchange_rust_sdk::{CreateOrderParams, FakOrderArgs, OrderType, Side};
let response = order_client
.create_order(CreateOrderParams {
order_type: OrderType::Fak,
market_slug: market.slug.clone(),
args: FakOrderArgs {
token_id: tokens.yes.clone(),
side: Side::Buy,
price: 0.45,
size: 10.0,
expiration: None,
nonce: None,
taker: None,
}
.into(),
})
.await?;
println!("order id {}", response.order.id);
println!("maker matches {}", response.maker_matches.len());
post_only is not supported for FAK orders.
FOK Orders
FOK orders execute immediately and fully, or are cancelled entirely. Instead of price and size, you pass maker_amount.
For buys, maker_amount is the total USDC to spend:use limitless_exchange_rust_sdk::{CreateOrderParams, FokOrderArgs, OrderType, Side};
let response = order_client
.create_order(CreateOrderParams {
order_type: OrderType::Fok,
market_slug: market.slug.clone(),
args: FokOrderArgs {
token_id: tokens.yes.clone(),
side: Side::Buy,
maker_amount: 10.0,
expiration: None,
nonce: None,
taker: None,
}
.into(),
})
.await?;
println!("order id {}", response.order.id);
For sells, maker_amount is the number of shares to sell:let response = order_client
.create_order(CreateOrderParams {
order_type: OrderType::Fok,
market_slug: market.slug.clone(),
args: FokOrderArgs {
token_id: tokens.yes.clone(),
side: Side::Sell,
maker_amount: 10.0,
expiration: None,
nonce: None,
taker: None,
}
.into(),
})
.await?;
FokOrderArgs
| Field | Type | Description |
|---|
token_id | String | Token ID from market data |
side | Side | Side::Buy or Side::Sell |
maker_amount | f64 | BUY: USDC to spend. SELL: shares to sell. Max 6 decimals |
expiration | Option<String> | Must be "0" / None (the default). Non-zero expiration is not currently supported and is rejected by the API. |
nonce | Option<i32> | Must be 0 / None (the default). Non-zero nonce is not currently supported and is rejected by the API. |
taker | Option<String> | Optional taker address |
Build and Sign Separately
For advanced flows, build and sign orders without submitting them:
use limitless_exchange_rust_sdk::{GtcOrderArgs, Side};
let unsigned = order_client
.build_unsigned_order(
GtcOrderArgs {
token_id: tokens.yes.clone(),
side: Side::Buy,
price: 0.65,
size: 10.0,
expiration: None,
nonce: None,
taker: None,
post_only: false,
}
.into(),
)
.await?;
let signature = order_client.sign_order_for_market(&market.slug, &unsigned).await?;
println!("{}", signature);
You can also override the signing config explicitly:
use limitless_exchange_rust_sdk::OrderSigningConfig;
let signature = order_client.sign_order_with_config(
&unsigned,
OrderSigningConfig {
chain_id: 8453,
contract_address: "0xYourExchangeContract".to_string(),
},
)?;
Cancelling Orders
Cancel a single order:
let message = order_client.cancel("abc123-def456").await?;
println!("{}", message);
Cancel all orders in a market:
let message = order_client.cancel_all(&market.slug).await?;
println!("{}", message);