> ## Documentation Index
> Fetch the complete documentation index at: https://docs.intention.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Matching

> The order book, how price-time priority is represented, and how time-in-force and self-trade prevention resolve against it.

Matching is a stage inside [kernel execution](/protocol/architecture/kernel), not a service the chain talks to. It takes the block's orders in their committed sequence, walks them against the book, and produces fills. It does not move anyone's balance — that is the [Clearinghouse](/protocol/architecture/clearinghouse)'s job, and it happens after matching completes.

Keeping those two apart is what makes the engine testable. Matching answers *what traded against what*. The Clearinghouse answers *what that costs and who now owes whom*.

## The book

Each instrument has its own book, held in memory as three cooperating structures:

<div className="dg" data-dg="matching-book">
  <div className="dg-c" style={{aspectRatio:"720 / 302"}}>
    <svg className="dg-w" viewBox="0 0 720 302" aria-hidden="true">
      <path className="dg-wire dg-soft" d="M 348.60 82.00 L 352.60 82.00" />

      <path className="dg-wire dg-soft" d="M 438.20 82.00 L 442.20 82.00" />

      <path className="dg-wire dg-soft" d="M 527.80 82.00 L 531.80 82.00" />

      <path className="dg-wire dg-soft" d="M 617.40 82.00 L 621.40 82.00" />

      <path className="dg-wire dg--blue" d="M 205.00 68.00 L 238.60 68.00" />

      <path className="dg-head dg--blue" d="M 245.00 68.00 L 238.60 72.40 L 238.60 63.60 Z" />

      <path className="dg-wire dg--sky" d="M 205.00 162.00 L 238.60 162.00" />

      <path className="dg-head dg--sky" d="M 245.00 162.00 L 238.60 166.40 L 238.60 157.60 Z" />
    </svg>

    <div className="dg-band" style={{left:"34.7222%",top:"9.9338%",width:"65.2778%",height:"56.2914%"}}><span className="dg-cap">Slab arena — one preallocated region of order slots</span></div>
    <div className="dg-b dg--blue" style={{left:"0.0000%",top:"9.9338%",width:"27.7778%",height:"25.1656%"}}><span className="dg-t">Price levels</span><span className="dg-s">an ordered map, price → level</span><span className="dg-n">the touch is found by walking to the end, not scanning</span></div>
    <div className="dg-b dg--sky" style={{left:"0.0000%",top:"41.0596%",width:"27.7778%",height:"25.1656%"}}><span className="dg-t">Order index</span><span className="dg-s">order id → slot</span><span className="dg-n">cancel and amend are constant time</span></div>
    <div className="dg-b dg--yellow" style={{left:"36.9444%",top:"19.5364%",width:"11.0556%",height:"15.2318%"}}><span className="dg-t">order</span></div>
    <div className="dg-b dg--yellow" style={{left:"49.3889%",top:"19.5364%",width:"11.0556%",height:"15.2318%"}}><span className="dg-t">order</span></div>
    <div className="dg-b dg--yellow" style={{left:"61.8333%",top:"19.5364%",width:"11.0556%",height:"15.2318%"}}><span className="dg-t">order</span></div>
    <div className="dg-b dg--yellow" style={{left:"74.2778%",top:"19.5364%",width:"11.0556%",height:"15.2318%"}}><span className="dg-t">order</span></div>
    <div className="dg-b dg--yellow" style={{left:"86.7222%",top:"19.5364%",width:"11.0556%",height:"15.2318%"}}><span className="dg-t">order</span></div>
    <div className="dg-b dg-plain dg-left" style={{left:"36.9444%",top:"41.3907%",width:"60.8333%",height:"15.2318%"}}><span className="dg-s">A doubly-linked chain in arrival sequence, so priority within a level is positional rather than computed.</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"0.0000%",top:"74.8344%",width:"100.0000%",height:"20.5298%"}}><span className="dg-t">Why the reuse order is fixed</span><span className="dg-s">Freed slots are reused in a fixed order and the index is seeded fixed. Not a performance choice: two validators reusing slots in different orders would fork.</span></div>
    <div className="dg-lbl" style={{left:"31.2500%",top:"17.8808%",width:"15.2778%",whiteSpace:"normal"}}>head of each level</div>
    <div className="dg-lbl" style={{left:"31.2500%",top:"62.9139%",width:"15.2778%",whiteSpace:"normal"}}>direct lookup</div>
  </div>
</div>

| Structure        | Role                                                                                                                     |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **Price levels** | An ordered map from price to level, so the best bid and best ask are found by walking to the end rather than scanning    |
| **Order chain**  | A doubly-linked list threading orders in arrival sequence, so priority within a level is positional rather than computed |
| **Order index**  | A direct map from order ID to its slot, so cancel and amend are constant-time rather than a search                       |

Orders live in a **slab arena** — a preallocated region with constant-time allocation and release. Order book operations therefore do not allocate on the hot path, and freed slots are reused in a fixed order rather than wherever the allocator happens to put them. That last detail is not a performance choice: if two validators reused slots in different orders, anything that observed slot layout would diverge.

The same discipline applies to the order index, which is seeded fixed rather than randomly. A hash map with per-process seeding is a standard defense against collision attacks; on a consensus execution path it is a fork.

Prices are integers throughout — subtick units, not decimals. See [Precision](/trading/precision) for how that maps to what you submit.

## Priority

Priority is price first, then position in the chain at that price. "Time" is the order's canonical position in the committed block sequence, not when it arrived at a node.

This is what removes the intra-block latency race. Two orders in the same block have a defined precedence that every validator computes identically, and no amount of proximity to a particular node changes it. Between blocks, arrival still matters — but the unit of competition is the block, not the microsecond.

The kernel's stage order compounds this: cancellations execute before aggressive placements within a block, so a resting quote cannot be taken by an order that landed in the same block as its cancel.

## Matching an order

<div className="dg" data-dg="matching-walk">
  <div className="dg-c" style={{aspectRatio:"720 / 334"}}>
    <svg className="dg-w" viewBox="0 0 720 334" aria-hidden="true">
      <path className="dg-wire dg--blue" d="M 134.00 144.00 L 155.60 144.00" />

      <path className="dg-head dg--blue" d="M 162.00 144.00 L 155.60 148.40 L 155.60 139.60 Z" />

      <path className="dg-wire dg--sky" d="M 320.00 144.00 L 345.60 144.00" />

      <path className="dg-head dg--sky" d="M 352.00 144.00 L 345.60 148.40 L 345.60 139.60 Z" />

      <path className="dg-wire dg--green" d="M 510.00 144.00 L 535.60 144.00" />

      <path className="dg-head dg--green" d="M 542.00 144.00 L 535.60 148.40 L 535.60 139.60 Z" />

      <path className="dg-wire dg--sky" d="M 633.00 114.00 L 633.00 80.00 L 241.00 80.00 L 241.00 103.60" />

      <path className="dg-head dg--sky" d="M 241.00 110.00 L 236.60 103.60 L 245.40 103.60 Z" />

      <path className="dg-wire dg--green" d="M 633.00 174.00 L 633.00 213.60" />

      <path className="dg-head dg--green" d="M 633.00 220.00 L 628.60 213.60 L 637.40 213.60 Z" />

      <path className="dg-wire dg--sky" d="M 241.00 178.00 L 241.00 213.60" />

      <path className="dg-head dg--sky" d="M 241.00 220.00 L 236.60 213.60 L 245.40 213.60 Z" />
    </svg>

    <div className="dg-b dg--blue" style={{left:"0.0000%",top:"35.3293%",width:"18.0556%",height:"15.5689%"}}><span className="dg-t">Incoming order</span></div>
    <div className="dg-b dg--yellow dg-round" style={{left:"23.0556%",top:"34.1317%",width:"20.8333%",height:"17.9641%"}}><span className="dg-t">Crosses the book?</span></div>
    <div className="dg-b dg--sky" style={{left:"49.4444%",top:"35.3293%",width:"20.8333%",height:"15.5689%"}}><span className="dg-t">Consume the best opposing level</span></div>
    <div className="dg-b dg--green" style={{left:"75.8333%",top:"35.3293%",width:"24.1667%",height:"15.5689%"}}><span className="dg-t">Emit a fill</span></div>
    <div className="dg-b dg--green" style={{left:"75.8333%",top:"67.0659%",width:"24.1667%",height:"25.7485%"}}><span className="dg-t">Done</span></div>
    <div className="dg-b dg--sky dg-left" style={{left:"10.5556%",top:"67.0659%",width:"45.8333%",height:"25.7485%"}}><span className="dg-t">Rest or reject the remainder, per time-in-force</span><span className="dg-s">GTC rests · IOC cancels it · FOK executes nothing unless it fills in full · post-only is rejected rather than crossing</span></div>
    <div className="dg-lbl" style={{left:"60.6944%",top:"23.9521%",width:"27.7778%",whiteSpace:"normal"}}>remainder — take the next level</div>
    <div className="dg-lbl" style={{left:"87.9167%",top:"58.9820%"}}>nothing left</div>
  </div>
</div>

The matcher repeatedly consumes the head of the opposing side, emitting a fill for each maker it takes, until the incoming order is exhausted or the book no longer crosses. What happens to any remainder is decided by time-in-force:

* **GTC** — rest the remainder on the book.
* **IOC** — cancel the remainder.
* **FOK** — if the order cannot be filled in full, nothing executes at all.
* **ALO** — post-only: if the order would take liquidity, it is rejected rather than crossing.

Fills carry attribution as they are produced. Each fill records where it sits in the sequence of fills for its instrument, and those per-instrument positions are resolved into a single ordering across the block when output is assembled. This is what allows an event, later, to be traced back to the exact transaction and the exact point in the block that caused it.

## Self-trade prevention

When an incoming order would match against resting liquidity from the same owner, the match is suppressed rather than executed. Which side gives way is configurable:

| Mode             | Behavior                                                        |
| ---------------- | --------------------------------------------------------------- |
| **Expire taker** | The incoming order is cancelled                                 |
| **Expire maker** | The resting order is cancelled and the incoming order continues |
| **Expire both**  | Both are cancelled                                              |

Makers cancelled this way are collected during matching and removed as part of the same block, so the book does not carry an order that has already been suppressed.

Ownership for this check is resolved at the account level the book tracks. See [Self-trade prevention](/trading/self-trade-prevention) for the trading-side view.

## What matching does not do

It does not compute fees, realize profit and loss, adjust positions, or check margin. Those happen after matching, in the [Clearinghouse](/protocol/architecture/clearinghouse), driven by the fills matching produced.

It also does not decide whether an order was allowed to exist. Margin adequacy, open-order limits, reduce-only constraints, and market-to-limit conversion are resolved before an order reaches the book. By the time the matcher sees an order, the question is only where it belongs in the book.

## Where to go next

<CardGroup cols={2}>
  <Card title="Clearinghouse" href="/protocol/architecture/clearinghouse">
    What happens to balances and positions once fills exist.
  </Card>

  <Card title="Order types" href="/trading/order-types">
    The trading-side view: what you can submit and how each behaves.
  </Card>

  <Card title="Order book" href="/trading/order-book">
    Depth, levels, and reading the book as a trader.
  </Card>

  <Card title="IntentionKernel" href="/protocol/architecture/kernel">
    Where matching sits in block execution.
  </Card>
</CardGroup>
