Prices
- Tick size is
0.001globally. Submit a price that’s not a multiple of the tick and the order is rejected (400"invalid price tick"). - Prices are strings, not JSON numbers, on every field listed above (except the
f64minimum_tick_sizereturned by/tick-size). - Empty-book sentinels:
/midpointreturns"0.000",/spreadreturns the bare"0",/last-trade-pricedefaults to"0.5"withside: "". Don’t assume they all share one default.
Sizes
The CTFOrder
makerAmount/takerAmount you sign on-chain are in micro-shares × tick math. The orderbook converts to human-share decimal strings on the way out. Don’t confuse the two when reconciling sizes across REST and WS surfaces.
Timestamps
XO emits timestamps in three different formats depending on the field. Mismatched parsing here is the most common integration bug.
When in doubt: if it’s in a
book snapshot or a WS frame, it’s stringified ms. If it’s a trade match_time or an order’s expiration/created_at, it’s seconds. The SDK uses TimestampSeconds<String> and TimestampMilliSeconds<String> types to enforce this — your code should too.
IDs
Pagination cursors
MarketsPage, OrdersPage, TradesPage all share:
next_cursor is always a string, never null. The literal "LTE=" is the canonical CLOB end-of-stream sentinel — when you see it, stop paginating.
EIP-712 order signing
Domain (mainnet):signature is NOT in the typehash):
taker and feeRateBps are ignored on parse and must not be signed — the typehash above is authoritative. Fees are not part of the signed order; see Fees.
signatureType values:
For
signatureType = 3 the orderbook enforces maker == signer (both set to the smart account) and validates the 65-byte ECDSA signature — produced by the owner EOA — via IERC1271(maker).isValidSignature(orderHash, signature). See Smart accounts for the full hashing reference, including the exact Solidity hashOrder function the exchange runs.
L1 ClobAuth domain (auth, not orders)
The auth headers forPOST /auth/api-key and GET /auth/derive-api-key use a separate EIP-712 domain — no verifyingContract field:
message is the literal string "This message attests that I control the given wallet" by SDK convention. Don’t confuse this domain with the order-signing domain above — they’re separate.