# Orders
Submit, cancel, and track limit and trigger orders.
[← Overview](../../../README.md#orders)
## Table of Contents
- [Types](#types)
- [Client Methods](#client-methods)
- [Order Envelope Builder](#order-envelope-builder)
- [State Containers](#state-containers)
- [Examples](#examples)
- [Wire Types](#wire-types)
## Types
### `Order` trait
Common interface shared by `LimitOrder` and `TriggerOrder`. Provides accessors for the six fields present on both types:
| `id()` | `&str` | Unique identifier (`order_hash` for limit, `trigger_order_id` for trigger) |
| `order_hash()` | `&str` | Underlying order hash |
| `market_pubkey()` | `&PubkeyStr` | Parent market |
| `orderbook_id()` | `&OrderBookId` | Which orderbook |
| `side()` | `Side` | `Bid` or `Ask` |
| `created_at()` | `DateTime<Utc>` | Creation timestamp |
Also implemented on `AnyOrder` (delegates to the inner variant).
### `LimitOrder`
A validated, domain-level limit order.
| `order_hash` | `String` | Unique order identifier |
| `market_pubkey` | `PubkeyStr` | Parent market |
| `orderbook_id` | `OrderBookId` | Which orderbook |
| `side` | `Side` | `Bid` (buy) or `Ask` (sell) |
| `price` | `Decimal` | Order price |
| `size` | `Decimal` | Total size |
| `filled_size` | `Decimal` | Amount filled so far |
| `remaining_size` | `Decimal` | Amount remaining |
| `status` | `OrderStatus` | Current status |
| `base_mint` | `PubkeyStr` | Base token mint |
| `quote_mint` | `PubkeyStr` | Quote token mint |
| `outcome_index` | `i16` | Which outcome |
| `tx_signature` | `Option<String>` | On-chain transaction signature |
| `created_at` | `DateTime<Utc>` | Creation timestamp |
### `TriggerOrder`
A take-profit or stop-loss trigger order. Held server-side until the trigger price is hit, then submitted as a limit order.
| `trigger_order_id` | `String` | Trigger order ID |
| `order_hash` | `String` | Underlying order hash |
| `market_pubkey` | `PubkeyStr` | Parent market |
| `orderbook_id` | `OrderBookId` | Which orderbook |
| `trigger_price` | `Decimal` | Price threshold that fires the order |
| `trigger_type` | `TriggerType` | `TakeProfit` (`"TP"`) or `StopLoss` (`"SL"`) |
| `side` | `Side` | `Bid` or `Ask` |
| `amount_in` | `Decimal` | Amount the maker gives |
| `amount_out` | `Decimal` | Amount the maker receives |
| `time_in_force` | `TimeInForce` | Execution constraint when triggered |
| `created_at` | `DateTime<Utc>` | Creation timestamp |
### `OrderStatus`
| `Open` | Resting on the book |
| `Matching` | Currently being matched |
| `Filled` | Fully filled |
| `Cancelled` | Cancelled by user or system |
| `Pending` | Awaiting processing |
### `OrderType`
| `Limit` | Standard limit order |
| `Market` | Market order (immediate execution) |
| `Deposit` | Deposit operation |
| `Withdraw` | Withdrawal operation |
### `TimeInForce`
Execution constraint for trigger orders.
| `Gtc` | `"GTC"` | Good-til-cancelled (default) |
| `Ioc` | `"IOC"` | Immediate-or-cancel |
| `Fok` | `"FOK"` | Fill-or-kill |
| `Alo` | `"ALO"` | Add-liquidity-only (post-only) |
### `TriggerType`
| `TakeProfit` | `"TP"` | Fires when price rises above trigger |
| `StopLoss` | `"SL"` | Fires when price falls below trigger |
### `UserOrderFill`
An order the user participated in (as maker or taker), with nested fill events.
| `order_hash` | `String` | Unique order identifier |
| `market_pubkey` | `PubkeyStr` | Parent market |
| `orderbook_id` | `OrderBookId` | Which orderbook |
| `side` | `Side` | User's side: `Bid` or `Ask` |
| `role` | `Role` | `Maker` or `Taker` |
| `price` | `Decimal` | Order price |
| `size` | `Decimal` | Total order size |
| `filled_size` | `Decimal` | Amount filled |
| `remaining_size` | `Decimal` | Amount remaining |
| `base_mint` | `PubkeyStr` | Base token mint |
| `quote_mint` | `PubkeyStr` | Quote token mint |
| `outcome_index` | `i16` | Which outcome |
| `status` | `OrderStatus` | `Filled`, `Cancelled`, or partially filled |
| `created_at` | `DateTime<Utc>` | Order creation timestamp |
| `fills` | `Vec<OrderFillEvent>` | Individual fill events |
### `OrderFillEvent`
| `fill_amount` | `Decimal` | Amount filled in this event |
| `tx_signature` | `String` | On-chain transaction signature |
| `filled_at` | `DateTime<Utc>` | When the fill occurred |
### `Role`
| `Maker` | User placed the order |
| `Taker` | User filled against the order |
## Client Methods
Access via `client.orders()`.
### `limit_order`
```rust
async fn limit_order(&self) -> LimitOrderEnvelope
```
Create a `LimitOrderEnvelope` pre-seeded with the client's deposit source. Users can still override the deposit source on the returned envelope by calling `.deposit_source()` before signing.
### `trigger_order`
```rust
async fn trigger_order(&self) -> TriggerOrderEnvelope
```
Create a `TriggerOrderEnvelope` pre-seeded with the client's deposit source. Users can still override the deposit source on the returned envelope by calling `.deposit_source()` before signing.
### `submit`
```rust
async fn submit(&self, request: &impl Serialize) -> Result<SubmitOrderResponse, SdkError>
```
Submit a signed limit order. The `request` is typically a `SubmitOrderRequest` produced by an order envelope's `.sign()` or `.finalize()` method. **Not retried** -- non-idempotent.
### `cancel`
```rust
async fn cancel(&self, body: &CancelBody) -> Result<CancelSuccess, SdkError>
```
Cancel a single order by its hash. **Not retried.**
### `cancel_all`
```rust
async fn cancel_all(&self, body: &CancelAllBody) -> Result<CancelAllSuccess, SdkError>
```
Cancel all open orders, optionally scoped to a specific orderbook. **Not retried.**
`CancelAllBody` must include:
- `orderbook_id` in the signed message, using `""` to mean all markets
- `salt`, a unique UUID-like string for replay protection
### `submit_trigger`
```rust
async fn submit_trigger(&self, request: &impl Serialize) -> Result<TriggerOrderResponse, SdkError>
```
Submit a signed trigger order (take-profit or stop-loss). **Not retried.**
### `cancel_trigger`
```rust
async fn cancel_trigger(&self, body: &CancelTriggerBody) -> Result<CancelTriggerSuccess, SdkError>
```
Cancel a trigger order by its ID. **Not retried.**
### `get_user_orders`
```rust
async fn get_user_orders(
&self,
limit: Option<u32>,
cursor: Option<&str>,
) -> Result<UserOrdersResponse, SdkError>
```
Fetch the **authenticated** user's open orders (both limit and trigger) with cursor-based pagination. Wallet is resolved from the `auth_token` cookie. See `get_user_orders_with_auth` for the SSR variant.
### `get_user_order_fills`
```rust
async fn get_user_order_fills(
&self,
market_pubkey: Option<&str>,
limit: Option<u32>,
cursor: Option<&str>,
) -> Result<UserOrderFillsResponse, SdkError>
```
Fetch the **authenticated** user's filled orders (with nested fill events). See `get_user_order_fills_with_auth` for the SSR variant and `get_user_order_fills_by_wallet` for the public path-based variant.
### `get_user_orders_with_auth` / `get_user_order_fills_with_auth`
SSR / server-function variants — accept an explicit `auth_token: &str` instead of using the SDK's process-wide token store. Same wire contract, different credentials path. See [the top-level Authentication section](../../../README.md#authentication).
### `get_user_order_fills_by_wallet`
```rust
async fn get_user_order_fills_by_wallet(
&self,
wallet_address: &str,
market_pubkey: Option<&str>,
limit: Option<u32>,
cursor: Option<&str>,
) -> Result<UserOrderFillsResponse, SdkError>
```
Public path-based variant. Hits `GET /api/users/{wallet_address}/order-fills` and requires no auth.
Fetch a user's filled orders with nested fill events. Includes orders where the user was either maker or taker. Optionally filter by market. Returns orders sorted by most recent fill first.
### On-Chain Instruction & Transaction Builders
Each operation has an `_ix` method returning an `Instruction` and a `_tx` convenience method returning `Result<Transaction, SdkError>`.
#### `cancel_order_ix` / `cancel_order_tx`
```rust
fn cancel_order_ix(&self, maker: &Pubkey, market: &Pubkey, order: &OrderPayload) -> Instruction
fn cancel_order_tx(&self, maker: &Pubkey, market: &Pubkey, order: &OrderPayload) -> Result<Transaction, SdkError>
```
Build a CancelOrder instruction/transaction for on-chain order cancellation.
#### `increment_nonce_ix` / `increment_nonce_tx`
```rust
fn increment_nonce_ix(&self, user: &Pubkey) -> Instruction
fn increment_nonce_tx(&self, user: &Pubkey) -> Result<Transaction, SdkError>
```
Build an IncrementNonce instruction/transaction — invalidates all orders with a nonce lower than the new value.
### Order Helpers
#### `create_bid_order` / `create_ask_order`
```rust
fn create_bid_order(&self, params: BidOrderParams) -> OrderPayload
fn create_ask_order(&self, params: AskOrderParams) -> OrderPayload
```
Create unsigned bid or ask orders from raw parameters.
#### `create_signed_bid_order` / `create_signed_ask_order`
```rust
fn create_signed_bid_order(&self, params: BidOrderParams, keypair: &Keypair) -> OrderPayload
fn create_signed_ask_order(&self, params: AskOrderParams, keypair: &Keypair) -> OrderPayload
```
Create and sign orders in one step. Requires the `native-auth` feature.
#### `hash_order`
```rust
fn hash_order(&self, order: &OrderPayload) -> [u8; 32]
```
Compute the Keccak256 hash of an order (excludes the signature field).
#### `sign_order`
```rust
fn sign_order(&self, order: &mut OrderPayload, keypair: &Keypair)
```
Sign an order in place with the given keypair. Requires the `native-auth` feature.
## Order Envelope Builder
The SDK provides a fluent builder API for constructing and signing orders. The envelope handles field validation, price/size scaling to raw amounts, and signature generation.
### `LimitOrderEnvelope`
For standard limit orders:
```rust
use lightcone::prelude::*;
// Recommended: factory method pre-seeds client deposit source
let request = client.orders().limit_order().await
.maker(keypair.pubkey())
.market(market_pubkey)
.base_mint(base_mint)
.quote_mint(quote_mint)
.bid() // or .ask()
.price("0.55") // human-readable price
.size("100") // human-readable size
.nonce(nonce)
.expiration(0) // 0 = no expiration
// .deposit_source(DepositSource::Global) // override if needed
.apply_scaling(&decimals)? // convert to raw amounts
.sign(&keypair, orderbook_id)?; // sign and produce SubmitOrderRequest
// Alternative: standalone use without a client
let request = LimitOrderEnvelope::new()
.maker(keypair.pubkey())
// ... same chain as above
.sign(&keypair, orderbook_id)?;
```
### `TriggerOrderEnvelope`
For take-profit and stop-loss orders:
```rust
use lightcone::prelude::*;
// Recommended: factory method pre-seeds client deposit source
let request = client.orders().trigger_order().await
.maker(keypair.pubkey())
.market(market_pubkey)
.base_mint(base_mint)
.quote_mint(quote_mint)
.bid()
.price("0.55")
.size("100")
.nonce(nonce)
.take_profit(0.65) // or .stop_loss(0.45)
.gtc() // or .ioc(), .fok(), .alo()
// .deposit_source(DepositSource::Global) // override if needed
.apply_scaling(&decimals)?
.sign(&keypair, orderbook_id)?;
// Alternative: standalone use without a client
let request = TriggerOrderEnvelope::new()
.maker(keypair.pubkey())
// ... same chain as above
.sign(&keypair, orderbook_id)?;
```
### `OrderEnvelope` trait
Both envelope types implement the `OrderEnvelope` trait with these shared methods:
| `.new()` | Create a new envelope (prefer factory methods) |
| `.maker(pubkey)` | Set the maker public key |
| `.market(pubkey)` | Set the market public key |
| `.base_mint(pubkey)` | Set the base token mint |
| `.quote_mint(pubkey)` | Set the quote token mint |
| `.bid()` / `.ask()` | Set the order side |
| `.price(str)` | Set the human-readable price |
| `.size(str)` | Set the human-readable size |
| `.nonce(u32)` | Set the order nonce. When using `submit()`, auto-populated from `client.order_nonce()` if omitted (falls back to 0). |
| `.expiration(i64)` | Set expiration (0 = none) |
| `.deposit_source(ds)` | Set collateral source (`Global` or `Market`). Pre-seeded by factory methods. |
| `.apply_scaling(&decimals)` | Convert price/size to raw amounts using orderbook decimals |
| `.sign(&keypair, orderbook_id)` | Sign with a keypair and produce `SubmitOrderRequest` |
| `.finalize(sig_bs58, orderbook_id)` | Attach an external signature (for Privy/wallet adapter) |
| `.payload()` | Get the raw `OrderPayload` (for manual signing) |
`TriggerOrderEnvelope` adds:
| `.take_profit(price)` | Set trigger type to take-profit at the given price |
| `.stop_loss(price)` | Set trigger type to stop-loss at the given price |
| `.gtc()` / `.ioc()` / `.fok()` / `.alo()` | Set time-in-force |
### Scaling
`apply_scaling()` converts human-readable price and size strings into the raw `amount_in` / `amount_out` values the matching engine expects. You **must** call this before signing. The `DecimalsResponse` from `client.orderbooks().decimals()` provides the required precision.
## State Containers
### `AnyOrder`
Enum wrapping either a `LimitOrder` or `TriggerOrder`. Implements the `Order` trait by delegating to the inner type.
```rust
pub enum AnyOrder {
Limit(LimitOrder),
Trigger(TriggerOrder),
}
```
| `vec_from(limit_orders, trigger_orders)` | Combine both types into a sorted `Vec<AnyOrder>` |
### `UserOpenLimitOrders`
Tracks a user's open limit orders grouped by market pubkey and orderbook ID. Updated from WebSocket user events.
| `new()` | Create empty tracker |
| `get(&market_pubkey, &orderbook_id)` | Get orders for a specific orderbook |
| `get_by_market(&market_pubkey)` | Get orders for a market, grouped by orderbook |
| `upsert(&order_update)` | Insert or update an order from a WS event |
| `remove(order_hash)` | Remove a cancelled/filled order |
| `clear()` | Remove all tracked orders |
### `UserTriggerOrders`
Tracks trigger orders grouped by market pubkey and orderbook ID.
| `new()` | Create empty tracker |
| `get(&market_pubkey, &orderbook_id)` | Get trigger orders for a specific orderbook |
| `get_by_market(&market_pubkey)` | Get trigger orders for a market, grouped by orderbook |
| `get_by_id(trigger_order_id)` | Find a specific trigger order |
| `insert(order)` | Add a trigger order |
| `remove(trigger_order_id)` | Remove a trigger order |
| `all()` | Iterator over all trigger orders |
| `len()` / `is_empty()` | Count helpers |
## Examples
### Full order lifecycle
```rust
use lightcone::prelude::*;
use lightcone::auth::native::sign_login_message;
use solana_keypair::Keypair;
use futures_util::StreamExt;
async fn market_make(client: &LightconeClient, keypair: &Keypair) -> Result<(), SdkError> {
// 1. Authenticate
let nonce = client.auth().get_nonce().await?;
let signed = sign_login_message(keypair, &nonce);
client.auth().login_with_message(
&signed.message, &signed.signature_bs58, &signed.pubkey_bytes, None,
).await?;
// 2. Find a market and its orderbook
let market = client.markets().get(None, Some(1)).await?.markets.into_iter().next().unwrap();
let ob = &market.orderbook_pairs[0];
let decimals = client.orderbooks().decimals(ob.orderbook_id.as_str()).await?;
// 3. Place a bid
let order_nonce = 1u32;
let bid_request = client.orders().limit_order().await
.maker(keypair.pubkey())
.market(market.pubkey.to_pubkey().unwrap())
.base_mint(ob.base.mint.to_pubkey().unwrap())
.quote_mint(ob.quote.mint.to_pubkey().unwrap())
.bid()
.price("0.50")
.size("100")
.nonce(order_nonce)
.apply_scaling(&decimals)?
.sign(keypair, ob.orderbook_id.as_str())?;
let response = client.orders().submit(&bid_request).await?;
println!("Bid placed: {:?}", response);
// 4. Monitor via WebSocket
let mut ws = client.ws_native();
ws.connect().await.unwrap();
ws.subscribe(SubscribeParams::User {
wallet_address: PubkeyStr::from(keypair.pubkey()),
}).unwrap();
let mut open_orders = UserOpenLimitOrders::new();
let mut stream = ws.events();
while let Some(event) = stream.next().await {
match event {
WsEvent::Message(Kind::User(UserUpdate::Order(OrderEvent::Limit(update)))) => {
open_orders.upsert(&update);
println!("Order update: {} -> {:?}", update.order.order_hash, update.order.status);
}
_ => {}
}
}
// 5. Cancel all orders
client.orders().cancel_all(&CancelAllBody {
user_pubkey: keypair.pubkey().into(),
orderbook_id: OrderBookId::from(""),
signature: "...".into(),
timestamp: 1_710_300_000,
salt: generate_cancel_all_salt(),
}).await?;
Ok(())
}
```
### Place a take-profit trigger order
```rust
use lightcone::prelude::*;
async fn place_take_profit(
client: &LightconeClient,
keypair: &solana_keypair::Keypair,
ob: &OrderBookPair,
decimals: &impl lightcone::shared::scaling::OrderbookDecimals,
) -> Result<(), SdkError> {
let request = client.orders().trigger_order().await
.maker(keypair.pubkey())
.market(ob.market_pubkey.to_pubkey().unwrap())
.base_mint(ob.base.mint.to_pubkey().unwrap())
.quote_mint(ob.quote.mint.to_pubkey().unwrap())
.ask()
.price("0.70")
.size("50")
.nonce(2)
.take_profit(0.65)
.gtc()
.apply_scaling(decimals)?
.sign(keypair, ob.orderbook_id.as_str())?;
let response = client.orders().submit_trigger(&request).await?;
println!("Trigger order placed: {:?}", response);
Ok(())
}
```
### Fetch filled orders with pagination
```rust
use lightcone::prelude::*;
async fn show_fill_history(
client: &LightconeClient,
market_pubkey: &str,
) -> Result<(), SdkError> {
// Authenticated user — wallet from JWT cookie. (For a public lookup of
// another wallet, use `get_user_order_fills_by_wallet(wallet, ...)`.)
let response = client.orders().get_user_order_fills(
Some(market_pubkey),
Some(20),
None,
).await?;
for order in &response.orders {
println!("{} {} ({:?}) @ {} — {}/{} filled",
order.side, order.role, order.status,
order.price, order.filled_size, order.size);
for fill in &order.fills {
println!(" fill: {} at {}", fill.fill_amount, fill.filled_at);
}
}
// Paginate
if response.has_more {
let next_page = client.orders().get_user_order_fills(
Some(market_pubkey),
Some(20),
response.next_cursor.as_deref(),
).await?;
}
Ok(())
}
```
## Wire Types
Raw types in `lightcone::domain::order::wire` include `OrderUpdate`, `UserUpdate`, `UserSnapshot`, `UserSnapshotOrder`, `TriggerOrderUpdate`, `OrderEvent`, `ConditionalBalance`, `GlobalDepositBalance`, `AuthUpdate`, `UserOrderFillsResponse`, `UserOrderFill`, `OrderFillEvent`, and `Role`. These are the WebSocket and REST wire formats before domain conversion.
---
[← Overview](../../../README.md#orders)