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. Five facets handle orderbook operations:
| Facet | Role |
|---|---|
LimitOrdersFacet | Place, cancel, expire orders |
MarketOrdersFacet | Immediate execution (FOK/FAK) |
BatchOrdersFacet | Batch place/cancel and atomic cancel-and-replace |
MatchingFacet | Match crossing orders |
OrderBookFacet | Read-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).
| Side | Price Priority | Time Priority |
|---|---|---|
| BUY | Higher ticks first | FIFO within same tick |
| SELL | Lower ticks first | FIFO within same tick |
Normal fills execute at the ask price (seller's price).
Order Lifecycle
- Place — Order is validated, collateral escrowed, and inserted into the tick-level FIFO queue
- Match — Anyone calls
matchOrders(marketId, maxSteps)to trigger the matching engine - Fill — Three settlement paths: Normal Fill, Mint-to-Fill, Merge-to-Fill (see Matching & Settlement)
- Cancel — Owner cancels, collateral refunded
- Expire — Past-expiry orders are cleaned up inline during matching or via
expireOrders()
The matching engine evaluates paths in fixed priority per step:
- 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 writes —
topOfBookonly updated when the best price changes - Storage deletion refunds — Filled/cancelled orders are deleted for gas rebates
| Constant | Value | Purpose |
|---|---|---|
MAX_ITERATIONS | 50 | Max levels walked per market order |
MAX_INLINE_EXPIRY_RETRIES | 10 | Max expired-order cleanups per fill attempt |
MAX_EXPIRE_BATCH | 100 | Max orders per expireOrders call |
MAX_BATCH_PLACE | 20 | Max orders per batchPlaceOrders / cancelAndReplace |
MAX_BATCH_CANCEL | 100 | Max orders per batchCancelOrders / cancelAndReplace |
Batch Entry Points
For market makers and quoting bots, BatchOrdersFacet exposes three flows:
batchPlaceOrders— up to 20 orders on the same market, single collateral transfer, single SELLsafeBatchTransferFrombatchCancelOrders— up to 100 orders across any markets owned by the callercancelAndReplace— atomically cancel up to 100 and place up to 20 on the same market; freed collateral is reused before re-pulling
See Batch Operations for SDK examples.
What's Next
- Matching & Settlement → — Normal Fill, Mint-to-Fill, Merge-to-Fill explained
- Batch Operations → — batch place, cancel, and atomic cancel-and-replace
- Token Mechanics → — how outcome tokens are minted and burned