How to detect when an order fills

This guide shows you how to learn that a resting order was filled, partially filled, or cancelled, with the least latency and without polling. It assumes a configured SomniaMarkets instance with a signer, and a market watch opened before the order was placed.

Read the placement result first

A createOrder that crosses the book fills in the same transaction. Its result already says so.

ts
const order = await exchange.createOrder("SOMI/USDso", "limit", "buy", 10, 0.116);
if (order.status === "closed") {
  // fully filled in the placement transaction
} else if (order.status === "open") {
  // resting: order.filled may still be > 0 for a partial fill
}

status is "closed" when remaining is zero, "open" when a remainder rests, and "canceled" when an immediate-or-cancel or market order left an unfillable remainder. The raw fills are in order.info.fills.

Wait for the status to flip

watchOrders(symbol) returns your orders on the market from the live store. The first call returns the current list; each later call resolves when the list changes. Loop until your order is no longer open.

ts
let mine = order;
while (mine.status === "open") {
  const orders = await exchange.watchOrders("SOMI/USDso");
  mine = orders.find((o) => o.id === order.id) ?? mine;
}
console.log(mine.status, "filled", mine.filled, "of", mine.amount);

The list changes on every order event for your account on that market, including partial fills, so the loop also observes filled growing while status stays "open".

Open the market watch before placing. watchOrderBook, watchTrades, or watchOrders on the symbol all open it. The watch learns about an order placed after it opened from the chain event, in the block the order lands. It learns about an order placed before it opened from the indexer snapshot, which lags the chain by the indexing delay, typically a few seconds.

Watch your trades

watchMyTrades(symbol) returns your fills on the market, newest first, and resolves on each new one. Use it when you care about executions rather than order state, for example to update inventory.

ts
let seen = new Set<string>();
while (true) {
  const trades = await exchange.watchMyTrades("SOMI/USDso", 20);
  for (const t of trades) {
    if (seen.has(t.id)) continue;
    seen.add(t.id);
    console.log(t.datetime, t.side, t.amount, "@", t.price, "cost", t.cost);
  }
}

On spot and perp markets side is absent: the pools do not attribute a fill to a side in a way the SDK can map to you. Read t.info.takerIsBid and compare t.info.maker with your address instead. On binary markets side is your own side when the fill carries one.

Use the engine for raw values

exchange.client.getLiveUserOrders(pool, owner) and getLiveUserFills(pool, owner) are the synchronous views behind the two verbs. They return LiveOrder and LiveFill rows with exact raw values as decimal strings; wrap a value in BigInt() to compute with it. The API reference lists the fields. Subscribe to changes with client.subscribeLive(listener).

In React, useLiveUserOrders and useLiveUserFills re-render on the same changes. See Use the React hooks.

Read your own writes at chain head

When a write threw RpcError after sending, or when no watch is open, ask the pool directly. These reads answer from the contract in one round-trip and know only what is open now.

ts
const t = exchange.market("SOMI/USDso");
const owner = exchange.walletAddress;
if (!owner) throw new Error("configure a signer before reading your open orders");
const openIds = await exchange.client.getOwnOpenOrdersOnchain(t.pool, owner);
const stillOpen = openIds.some((id) => id.toString() === order.id);

getOrderOnchain(pool, orderId) returns one order's on-chain state. For history, including filled and cancelled orders, use the indexer reads getOpenOrders, getOrders, and getOrderFills; they lag the chain by the indexing delay.

Choose by need

NeedUseLatency
Did the placement itself fillcreateOrder resultnone
Did a resting order changewatchOrdersnext block
What executed, for inventorywatchMyTradesnext block
Is it still open, after a transport errorgetOwnOpenOrdersOnchainone round-trip
Full history for a reportgetOrders, getOrderFillsindexing delay