Skip to content

Order book and mark ​

The Market contract holds the order book and does the matching. The book has two kinds of liquidity: real resting orders and maker ladders. Takers see one merged book.

Source: contracts/src/Market.sol, especially _place, _match, setLadder, _quote, and _updateMark.

Orders ​

An order has a side, a price in annualized variance, a size in contracts, a type, and an expiry.

FieldRule
PriceGreater than zero, at most 100
SizeGreater than zero, at most 1,000 contracts
TypeLimit, or IOC (immediate or cancel)
ExpiryFrom the current block time to 3,600 seconds later
Match limit (maxMatches)From 1 to 32 match attempts for the order

The terminal sends each session order with an expiry of 120 seconds and a match limit of 32.

A "market order" in the terminal is an IOC order with a price limit.

Results of an order ​

The contract emits OrderResult with three quantities:

QuantityMeaning
filledThe size that traded
restingThe size that stays in the book
canceledThe size that the contract removed

A confirmed order can have zero fills. The remainder of an IOC order is always canceled. The remainder of a limit order rests only if it does not cross the opposite side and the account has enough margin. If not, the contract cancels it.

The book holds at most 128 real resting orders. A new resting order fails with BOOK_FULL above that limit.

Matching ​

The contract matches a new order against the best opposite price of the merged book. Real orders with the same price keep time priority.

Each match attempt does these checks:

  1. Self trade. If the best opposite order belongs to the taker, the contract cancels that resting order or that ladder level. It does not fill it.
  2. Maker risk. If the maker cannot pass the margin check after the fill, the contract removes the maker order. For a ladder, it takes the rest of that ladder side out of the book until the next quote.
  3. Taker risk. If the taker cannot pass the margin check after the fill, matching stops.

An attempt that removes an order counts against the match limit.

Observation gate

After an observation boundary passes, no order fills until the contract accepts the observation for that boundary. An order that does not cross can rest. Cancellation stays available. Thus a trader cannot see a price move, enter, and receive the payment for that interval.

Maker ladders ​

A ladder is a set of virtual quotes of one maker. The contract does not store each level. It calculates the price and the size of a level from the ladder shape and from the center of the last quote.

StepCallCaller
Register a shapesetLadder(index, first, step, levels, base, sizeStep)The maker
Move all laddersquote(center, tick, validUntil, updateData)The quoter of the market

Level i of a ladder rests at this distance from the center, on each side:

text
price offset = (first + i * step) * tick
size         = (base + i * sizeStep) * 0.01 contracts

A market has 4 ladder slots. A ladder has at most 32 levels. The full size of a ladder side must be 1,000 contracts or less, and the maker must have the margin for it. The maker accepts that the quoter sets the center of all ladders.

A quote is valid until validUntil, which is at most 3,600 seconds after the call. A fill uses up ladder size from the inside level. The next quote restores the full ladder.

One quote call writes one storage slot. It can also carry the signed oracle observation in updateData. This is the one call of the operator loop. See Oracle and observations.

The ladders of the demo scripts ​

The local scripts and the testnet scripts use the same two shapes (ladderShapes in scripts/common.ts). The tick is 0.001.

MakerFirst levelStepLevelsSize of level 0Size increase for each level
Maker 01 tick2 ticks101.00 contract15 contracts
Maker 12 ticks2 ticks101.50 contracts15 contracts

Maker 0 rests on the odd ticks and maker 1 rests on the even ticks. The book thus shows 20 levels on each side. The inside level of maker 0 is one contract, so the 2 contracts of mark depth always come from two owners.

The mark ​

The mark is the variance price that the contract uses for the fixed charge, for margin, and for mark profit and loss.

The contract calculates it in this sequence:

  1. Take the best 2 contracts on the bid side. Calculate their average price.
  2. Take the best 2 contracts on the ask side. Calculate their average price.
  3. The mark is the midpoint of the two average prices.

The result is valid only if all of these conditions are true:

  • Each side has the full 2 contracts of depth.
  • The measured depth of each side has two or more different owners.
  • The bid average is not above the ask average.
  • The spread is 20% of the midpoint or less.

If a condition fails, the mark freezes. The stored mark stays in use. A mark also counts as frozen when it is older than two observation intervals, or when the oracle is halted.

Two owners are not two independent makers

Two addresses do not prove independent ownership or a fair price. The owner rule only makes a single account unable to set the mark alone.

When the mark updates ​

CallMark update
quoteYes
submitObservationYes, before the contract saves the next charge
withdrawYes, and the withdrawal needs a valid mark
liquidateYes, and the liquidation needs a valid mark
updateMarkYes. Any address can call it.

The initial mark of a new market is a deployment input. It starts frozen until the book gives a valid mark.

During a mark freeze ​

  • Matching continues. Trades can remove crossed quotes, and new quotes can restore the depth.
  • A fill that adds exposure must pass the margin check at the last valid mark and at its own execution price.
  • Withdrawals and liquidations wait for a valid mark.
  • A frozen mark stays the charge of the next funding interval. There is no fallback to a historical price.

The order queue of QueuedMarket ​

QueuedMarket extends Market. It adds an on-chain FIFO queue of IOC requests and two combined calls.

CallEffect
enqueue(requests)Adds up to 16 requests. Reserves margin for them. The queue holds at most 128 pending requests.
cancelQueued(id)The owner removes a request and releases its reservation.
clearQueue(maxOrders)Any address processes up to 16 requests in queue sequence.
quoteAndClear(...)The quoter moves the ladders, then clears the queue, in one transaction.
quoteAndTake(...)The quoter moves the ladders, then places one IOC order for its own account, in one transaction.

Each queued request executes as an IOC order against the same book, with a new margin check. An expired or rejected request cannot block later requests. A request that fails because the call has too little gas reverts the batch, so the order stays in the queue.

The web application does not show the queue. The services report queue: true in GET /markets for a QueuedMarket.

Operator fills on testnet ​

On testnet, the operator pushes the fills:

  • The standard loop (pnpm testnet serve) sends a random taker order for each market at a fixed interval.
  • The fast loop (pnpm testnet serve-fast) uses quoteAndTake. One transaction moves the ladders, accepts a due observation, and fills one IOC order of the operator.

The operator fill model is for testnet only

These fills use the collateral of the operator. They keep the book, the mark, and the trade feed active while no independent traders are there. They are not the fill model for a market with real funds. In that market, independent makers and takers supply the orders, and quoteAndTake is not necessary. An order of a user goes through the same matching and the same margin checks in the two cases.

Deployed on Monad testnet.