OddMaki
Protocol

Orderbook

OddMaki's fully on-chain Central Limit Order Book (CLOB) — order types, price-time priority, and matching mechanics.

OddMaki runs a fully on-chain CLOB. Every order, fill, and state change happens on Base — no off-chain sequencer, no centralized matching.

CLOB Overview

Each market has two independent books (YES and NO), each with a BUY and SELL side. Four facets handle orderbook operations:

FacetRole
LimitOrdersFacetPlace, cancel, expire orders
MarketOrdersFacetImmediate execution (FOK/FAK)
MatchingFacetMatch crossing orders
OrderBookFacetRead-only queries

Order Types

Limit orders — Resting orders at a specific price tick. Remain on the book until filled, cancelled, or expired.

const hash = await client.trade.placeOrderSimple({
  marketId: 1n,
  outcomeId: 0n,     // YES
  side: 'BUY',
  price: '0.65',     // $0.65
  amount: '100',     // 100 tokens
  expiry: 0n,        // GTC (no expiry)
});

Fill-or-Kill (FOK) — Execute the entire order immediately or revert. No partial fills.

Fill-and-Kill (FAK) — Fill as much as possible immediately, cancel the remainder.

const result = await client.trade.placeMarketOrder({
  marketId: 1n,
  outcomeId: 0n,
  collateralAmount: parseAmount('50', 6),  // spend up to 50 USDC
  maxPriceTick: 80n,                       // slippage protection
  orderType: 'FAK',
});

Price-Time Priority

Prices are integer ticks where price = tick × tickSize / 1e18. With a 1% tick size, prices range from $0.01 (tick 1) to $0.99 (tick 99).

SidePrice PriorityTime Priority
BUYHigher ticks firstFIFO within same tick
SELLLower ticks firstFIFO within same tick

Normal fills execute at the ask price (seller's price).

Order Lifecycle

  1. Place — Order is validated, collateral escrowed, and inserted into the tick-level FIFO queue
  2. Match — Anyone calls matchOrders(marketId, maxSteps) to trigger the matching engine
  3. Fill — Three settlement paths: Normal Fill, Mint-to-Fill, Merge-to-Fill (see Matching & Settlement)
  4. Cancel — Owner cancels, collateral refunded
  5. Expire — Past-expiry orders are cleaned up inline during matching or via expireOrders()

The matching engine evaluates paths in fixed priority per step:

  1. Normal Fill (YES) → 2. Normal Fill (NO) → 3. Mint-to-Fill → 4. Merge-to-Fill → 5. No fill → break

Gas Optimization

  • Doubly-linked tick lists — O(1) insert/remove at head/tail, O(1) top-of-book lookup
  • FIFO queues via order pointers — No array operations, constant-gas enqueue/dequeue
  • Inline expiry cleanup — Matching cleans up expired orders on-the-fly (up to 10 per attempt)
  • Conditional storage writestopOfBook only updated when the best price changes
  • Storage deletion refunds — Filled/cancelled orders are deleted for gas rebates
ConstantValuePurpose
MAX_ITERATIONS50Max levels walked per market order
MAX_INLINE_EXPIRY_RETRIES10Max expired-order cleanups per fill attempt
MAX_EXPIRE_BATCH100Max orders per expireOrders call

What's Next