Skip to main content

Orders

The ctx.orders module handles order placement and management. Write operations require a signer — the SDK handles EIP-712 signing automatically.
import { ContextClient } from "@contextwtf/sdk"

const ctx = new ContextClient({
  apiKey: process.env.CONTEXT_API_KEY!,
  signer: { privateKey: process.env.PRIVATE_KEY! as `0x${string}` },
})

Place a limit order

Create a signed limit order that sits on the book until filled or canceled:
const result = await ctx.orders.create({
  marketId: "0x1234...",
  outcome: "yes",
  side: "buy",
  priceCents: 45,   // 45¢ per contract
  size: 10,          // 10 contracts
})

console.log("Order placed:", result.order.nonce)
console.log("Status:", result.order.status)

Limit order parameters

ParamTypeDescription
marketIdstringMarket ID (hex)
outcome"yes" | "no"Which outcome to trade
side"buy" | "sell"Buy or sell
priceCentsnumberPrice in cents (1-99)
sizenumberNumber of contracts (min 0.01)
expirySecondsnumberTime until expiry (default: 1 year)
inventoryModeConstraint0 | 1 | 2Controls token minting behavior (default: 0)
makerRoleConstraint0 | 1 | 2Controls maker/taker role (default: 0)

Inventory mode

ValueNameDescription
0ANYFill can mint new tokens or use existing inventory (recommended)
1REQUIRE_INVENTORYMaker must already hold the outcome tokens
2REQUIRE_NO_INVENTORYSettlement mints complete sets from maker’s deposited TUSD on fill. Use this for sell orders when you don’t hold tokens but have TUSD deposited.

Maker role constraint

ValueNameDescription
0ANYNo constraint (recommended default)
1MAKER_ONLYOrder must be the resting (maker) side of the trade
2TAKER_ONLYOrder must fill immediately or gets voided
Do not use makerRoleConstraint: 1 (MAKER_ONLY). When two maker-only orders cross, Settlement reverts with InvalidRoleConstraint, poisoning the entire batch and blocking all trading on the market. Use 0 (ANY) instead.
The SDK converts your human-friendly values (cents, contracts, “yes”/“no”) into the on-chain format (BigInts, outcome indices) and signs with EIP-712 automatically.

Place a market order

Create a market order that fills immediately at the best available price:
const result = await ctx.orders.createMarket({
  marketId: "0x1234...",
  outcome: "yes",
  side: "buy",
  maxPriceCents: 55,  // won't pay more than 55¢
  maxSize: 10,         // buy up to 10 contracts
})

console.log("Order placed:", result.order.nonce)
console.log("Status:", result.order.status)
console.log("Filled:", result.order.percentFilled, "%")
Market orders take the best available liquidity up to your price and size limits. If the order can’t fill completely, the remaining portion is voided.

Market order parameters

ParamTypeDescription
marketIdstringMarket ID (hex)
outcome"yes" | "no"Which outcome to trade
side"buy" | "sell"Buy or sell
maxPriceCentsnumberMaximum price in cents (1-99) — price ceiling for buys, floor for sells
maxSizenumberMaximum number of contracts to fill (min 0.01)
expirySecondsnumberTime until expiry (default: 1 year)

Cancel an order

Cancel by nonce:
const result = await ctx.orders.cancel("0xnonce...")

if (result.success) {
  console.log("Cancelled")
} else if (result.alreadyCancelled) {
  console.log("Already cancelled")
}

Cancel and replace

Atomically cancel an existing order and place a new one:
const result = await ctx.orders.cancelReplace(
  "0xold-nonce...",   // nonce of order to cancel
  {
    marketId: "0x1234...",
    outcome: "yes",
    side: "buy",
    priceCents: 50,    // updated price
    size: 10,
  },
)

console.log("Cancelled:", result.cancel.success)
console.log("New order:", result.create.order.nonce)

Bulk operations

Create multiple orders

const results = await ctx.orders.bulkCreate([
  {
    marketId: "0x1234...",
    outcome: "yes",
    side: "buy",
    priceCents: 40,
    size: 5,
  },
  {
    marketId: "0x1234...",
    outcome: "yes",
    side: "buy",
    priceCents: 35,
    size: 10,
  },
])

for (const r of results) {
  console.log(`${r.order.nonce}: ${r.success ? "placed" : "failed"}`)
}

Cancel multiple orders

const results = await ctx.orders.bulkCancel([
  "0xnonce1...",
  "0xnonce2...",
])

Mixed bulk operations

Create and cancel orders in a single request:
const result = await ctx.orders.bulk(
  // Orders to create
  [
    { marketId: "0x...", outcome: "yes", side: "buy", priceCents: 45, size: 10 },
  ],
  // Nonces to cancel
  ["0xold-nonce..."],
)

for (const r of result.results) {
  console.log(`${r.type}: ${r.success ? "ok" : "failed"}`)
}

Query orders

List orders

const { orders, cursor } = await ctx.orders.list({
  trader: "0xAddress...",
  marketId: "0x1234...",
  status: "open",
  limit: 50,
})

List all (auto-paginate)

Automatically fetches all pages:
const allOrders = await ctx.orders.listAll({
  trader: "0xAddress...",
  status: "filled",
})

Your orders

Shorthand for filtering by your signer’s address:
// Your open orders
const { orders } = await ctx.orders.mine()

// Your open orders for a specific market
const { orders: marketOrders } = await ctx.orders.mine("0x1234...")

// All your orders (auto-paginate)
const allMyOrders = await ctx.orders.allMine()

Get a single order

const order = await ctx.orders.get("order-id")
console.log(order.status)       // "open" | "filled" | "cancelled" | "expired" | "voided"
console.log(order.percentFilled) // 0-100

Recent orders

Fetch orders within a time window:
const { orders } = await ctx.orders.recent({
  trader: "0xAddress...",
  windowSeconds: 3600, // last hour
})

Simulate order execution

Preview how an order would fill against the current book:
const sim = await ctx.orders.simulate({
  marketId: "0x1234...",
  trader: "0xAddress...",
  maxSize: "10000000",   // 10 contracts (raw)
  maxPrice: "500000",    // 50¢ (raw)
  outcomeIndex: 1,       // Yes (0 = No, 1 = Yes)
  side: "bid",
})

console.log("Fill size:", sim.summary.fillSize)
console.log("Fill cost:", sim.summary.fillCost)
console.log("Slippage:", sim.summary.slippageBps, "bps")
console.log("Sufficient collateral:", sim.collateral.isSufficient)
The simulate endpoint uses raw on-chain values (6 decimal places), unlike orders.create() which accepts human-friendly cents and contracts.

Order lifecycle

Orders move through these states:
StatusDescription
openResting on the book, waiting for fills
filledFully matched and settled
cancelledCancelled by the trader
expiredPast the order’s expiry time
voidedRejected by the system (see void reasons below)
All terminal states (filled, cancelled, expired, voided) are final. Orders track partial fills via filledSize, remainingSize, and percentFilled.

Void reasons

When an order is voided, voidReason indicates why:
ReasonDescription
UNFILLED_MARKET_ORDERMarket order couldn’t fill (insufficient liquidity)
UNDER_COLLATERALIZEDInsufficient balance in Holdings contract
MISSING_OPERATOR_APPROVALWallet hasn’t approved the Settlement operator