Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Order Matching

This chapter describes how orders are submitted, matched, filled, and settled in the on-chain perpetual futures order book.

1. Order types

An order can be order:

  • Market — immediate-or-cancel (IOC). Specifies a max_slippage relative to the oracle price. Any unfilled remainder after matching is discarded (unless nothing filled at all, which is an error).
  • Limit — good-till-cancelled (GTC). Specifies a limit_price. Any unfilled remainder is stored as a resting order on the book. If post_only is set, the order is rejected if it would cross the best price on the opposite side — it takes a fast path and never enters the matching engine.

Resting orders on the book are stored as:

FieldDescription
userOwner address
sizeSigned quantity (positive = buy, negative = sell)
reduce_onlyIf true, can only close an existing position
reserved_marginMargin locked for this order

The pair ID, order ID, and limit price are part of the storage key.

2. Order decomposition

Before matching, every fill is decomposed into a closing and an opening portion based on the user’s current position:

Order directionCurrent positionClosing sizeOpening size
Buy (+)Short (−)
Sell (−)Long (+)
Same directionAny

Both closing and opening carry the same sign as the original order size (or are zero). For reduce-only orders, the opening portion is forced to zero — if the resulting fillable size is zero, the transaction is rejected.

3. Target price

The target price defines the worst acceptable execution price for the taker:

Market orders (bid/buy):

Market orders (ask/sell):

Limit orders: (oracle price is ignored).

A price constraint is violated when:

  • Bid:
  • Ask:

4. Matching engine

The matching engine iterates the opposite side of the book in price-time priority:

  • A bid (buy) walks the asks in ascending price order (cheapest first).
  • An ask (sell) walks the bids in descending price order (most expensive first). Bids are stored with bitwise-NOT inverted prices so that ascending iteration over storage keys yields descending real prices.

At each resting order the engine checks two termination conditions:

  1. — the taker is fully filled.
  2. The resting order’s price violates the taker’s price constraint.

If neither condition is met, the fill size is:

After each fill the maker order is updated: reserved margin is released proportionally, and if fully filled the order is removed from the book and open_order_count is decremented.

5. Pre-match margin check

Before matching begins, the taker’s margin is verified (skipped for reduce-only orders). The check ensures the user can afford the worst case — a 100 % fill:

where is the initial margin assuming the full order fills (see Margin §5) and is

This prevents a taker from submitting orders they cannot collateralise.

6. Self-trade prevention

The exchange uses EXPIRE_MAKER mode. When the taker encounters their own resting order on the opposite side:

  1. The maker (resting) order is cancelled (removed from the book).
  2. The taker’s open_order_count and reserved_margin are decremented.
  3. The taker continues matching deeper in the book — no fill occurs for the self-matched order.

7. Fill execution

Each fill between taker and maker is executed as follows:

7a. Funding settlement

Accrued funding is settled on the user’s existing position before the fill:

The negated accrued funding is added to the user’s PnL (positive accrued funding is a cost to longs).

7b. Closing PnL

For the closing portion of the fill:

Long closing (selling to close):

Short closing (buying to close):

The position size is reduced by the closing amount. If the position is fully closed, it is removed from state.

7c. Opening position

For the opening portion of the fill:

  • New position: entry price is set to the fill price.
  • Existing position (same direction): entry price is blended as a weighted average:

7d. OI update

Open interest is updated per side:

  • Closing a long:
  • Closing a short:
  • Opening a long:
  • Opening a short:

8. Trading fees

Fees are charged on every fill:

The fee rate differs by role:

RoleRateExample value
Takertaker_fee_rate0.1 %
Makermaker_fee_rate0 %

Fees are always positive (absolute value of fill size is used). They are routed to the vault via the settlement loop described below.

9. PnL settlement

After all fills in an order are complete, PnLs and fees are settled atomically as in-place USD margin adjustments. No token conversions occur during settlement — all values are pure UsdValue arithmetic.

9a. Fee loop

For each non-vault user with a non-zero fee:

Fees from the vault to itself are skipped (no-op). Processing fees first ensures collected fees augment before any vault losses are absorbed.

9b. PnL loop

Non-vault users:

A user’s margin can go negative temporarily — the outer function handles bad debt (see Liquidation).

Vault:

A negative represents a deficit (bad debt not yet recovered via ADL).

10. Unfilled remainder

After matching completes:

  • Market orders: the unfilled remainder is silently discarded. If nothing was filled at all, the transaction reverts with “no liquidity at acceptable price”.
  • Limit orders (GTC): the unfilled remainder is stored as a resting order. Storage requires:
    • open_order_count < max_open_orders
    • Price is aligned to the pair’s tick size ()
    • Sufficient available margin (skipped for reduce-only orders) — see below

Margin reservation (non-reduce-only):

The unfilled portion’s margin requirement is computed and checked against available margin (see Margin §7–§8):

If the check passes, reserved_margin is increased by and open_order_count is incremented. This is the 0 %-fill scenario check — it ensures the user can afford the order even if nothing fills immediately.

Post-only limit orders take a fast path that bypasses the matching engine entirely. They are rejected if they would cross the best price on the opposite side:

  • Buy:
  • Sell:

If the opposite book is empty, the order always succeeds.

11. Open interest constraint

Each pair has a parameter enforcing a per-side cap:

  • Long opening:
  • Short opening:

The constraint is checked before matching and does not apply to reduce-only orders (which have zero opening size). Long and short OI limits are independent but share the same parameter.

12. Order cancellation

Single cancel

A user can cancel any individual resting order by its order ID.

On cancellation:

  1. The order is removed from the book.
  2. reserved_margin is released (subtracted from the user’s total).
  3. open_order_count is decremented.
  4. If the user state is now empty (no positions, no open orders, no pending unlocks), it is deleted from storage.

Bulk cancel

A user can cancel all of their resting orders across both sides of the book in a single transaction. The contract iterates the user’s resting orders, removing each one and releasing margin. The same cleanup logic applies — if the user state becomes empty after all orders are removed, it is deleted.