FABRKNT
Build OpenHL CLOB — adding the matching engine
Orientation
Lesson 1 of 13·CONTENT15 min50 XP

Treat this page as a workbench, not a blog post. The goal is to extract a reusable mental model from the source and carry it into the rest of the Fabrknt stack.

Course
Build OpenHL CLOB — adding the matching engine
Lesson role
CONTENT
Sequence
1 / 13

Build OpenHL CLOB — adding the matching engine on top of the Reth substrate

Question

Build openhl's central limit order book (CLOB) matching engine in Rust. Hyperliquid runs perp markets on a CLOB; openhl is its open reference. Pinned to openhl SHA f8b3c12.

Principle (minimum model)

  • 13 lessons / 6 modules. Orientation → CLOB types → Matching engine (4 lessons) → Testing (2 lessons) → Bridge integration (3 lessons) → Capstone.
  • The matching engine. A Book per market, holding bids + asks; orders submitted via submit, cancelled via cancel. The book matches incoming orders against resting ones; emits Fills.
  • Pinned to SHA f8b3c12. Byte-for-byte reproducible against the openhl reference.
  • Prerequisites. openhl Funding (state machine discipline) + perp primer (market structure).
  • Pure compute everywhere. No I/O, no async, no state mutation outside the book. Reusable in tests + production.
  • Three matching philosophies. Limit order, Market order, Cancel. Each has its own submit_* function.
  • Production parallel. Hyperliquid HyperCore uses this exact discipline (different code, same shape).

Worked example + steps

Build OpenHL CLOB — adding the matching engine on top of the Reth substrate

The previous course (building-openhl-consensus) ended with a single-validator BFT chain that decides blocks through a real Reth EVM in 0.02 seconds. It decides empty blocks. No transactions. No matching. No price discovery — the mechanism by which a trade's price emerges from the competing best-bid and best-ask of the order book, rather than being dictated by one party.

This course adds the CLOB matching engine — the part of Hyperliquid that turns "I want to buy 10 HYPE at $25" + "I want to sell 5 HYPE at $25" into a real fill. Stage 8a (701 lines) builds the pure state machine; Stage 8d (171 lines) wires it into the bridge so committed blocks now carry the fills the matching engine produced.

By the end of this course, cargo test clob_fills_flow_into_payload passes — a real fill flows from the matching engine through LiveRethEvmBridge::build_payload and into a payload that consensus then commits.

1. What you'll have at the end

A new crates/clob/ crate with:

  • A price-time-priority matching engine that runs in microseconds — pure state machine, no I/O, fully deterministic.
  • Book + Order + Fill types that match what a CEX would call its order book.
  • 12 tests passing: 9 hand-traced scenarios (empty book, FIFO priority, market-order liquidity exhaustion, partial fills, cancellation, no-crossed-book post-match) and 3 proptest invariants exercising 768 random scenarios (quantity conservation, no-crossed-book always, determinism = replayability).

And a new integration test in crates/evm/:

  • clob_fills_flow_into_payload — bootstraps a real Reth node, submits a maker bid + crossing taker sell (maker = a resting order placed earlier; taker = the incoming order that consumes that liquidity) to the bridge's CLOB, asserts the resulting fill appears in the next build_payload output, and asserts that earlier payloads weren't retroactively filled (drain semantics are forward-only).

By the time you finish, you can:

  • Explain why a price-time-priority CLOB is the canonical structure for on-chain perpetual exchanges
  • Reason about the trade-offs between matching engines that buffer fills (this one) vs those that emit them synchronously
  • Reproduce the matching logic from scratch — and modify it (e.g., to support stop orders, post-only orders, or pro-rata matching) by knowing where to cut into the code

2. What you won't have at the end

This course covers Stage 8a + 8d only. It does NOT cover:

  • Stage 9: custom EVM precompiles that read/write CLOB state (= course 8)
  • Stage 8b: funding rate state machine (= course 9)
  • Encoding fills as EVM-executable transactions (= future work past Stage 9 in openhl itself)
  • Liquidations, mark-vs-index pricing, leverage limits

When you finish this course you have a working matching engine producing fills into committed blocks, but those fills are still a parallel list — not yet executable as Ethereum transactions readable by smart contracts. That's what course 8 adds via custom EVM precompiles.

This is honest scoping. A CLOB engine without execution wiring is half the story; the other half (precompiles) is course 8.

3. Prerequisites

You need:

  • building-openhl-consensus complete — or, equivalently, a workspace at the end-of-course-6 state. Your crates/evm/src/live_node.rs should already have LiveRethEvmBridge<P> with provider, chain_spec, validator, and optional engine_handle fields. If yours doesn't, work through course 6 first.
  • Rust 1.95+ — openhl's rust-toolchain.toml pins 1.95.0. Same prerequisite as course 6.
  • Comfort with BTreeMap, VecDeque, Reverse<T>, and proptest. If "natural ordering" and "reverse-ordering trick to walk highest-first" are unfamiliar, skim the std::collections::BTreeMap docs first.

You do not need:

  • Any prior matching-engine experience (we'll build the data structures from scratch)
  • Any prior order-book reading skill (the test scenarios walk every step)
  • Multi-validator setup (still single-validator throughout)

4. Setup confirmation (do this now)

You should already have the two-directory workflow from course 6:

  • ~/code/my-openhl/ — your workspace
  • ~/code/openhl-reference/ — read-only psyto/openhl clone

Bring the reference repo up to date in case Stage 8 commits are newer than your clone:

cd ~/code/openhl-reference
git fetch origin
git log --oneline | head -15
# You should see commits up to and including SHA 0cac571 (Stage 7d) and
# 428cc26 (Stage 8d).

Then confirm your workspace is at the end-of-course-6 state:

cd ~/code/my-openhl
cargo test -p openhl-evm --release 2>&1 | tail -10
# Expect: roughly 38 tests pass workspace-wide, including
# - reth_dev_node_bootstraps (Lesson 11 of course 6)
# - live_bridge_builds_on_real_genesis (Lessons 12–13 of course 6)
# - commit_sends_forkchoice_to_engine_when_handle_installed (Lesson 14 of course 6)

If those tests pass, you're at the right starting point. If they don't, finish course 6 first.

5. The 12-lesson map

#ModuleWhat you buildEnd-of-lesson test
Lesson 0Orientation(this lesson)setup confirmed
Lesson 1CLOB typesnewtypes — AccountId, OrderId, Price, Qty, Side, OrderTypecargo check -p openhl-clob
Lesson 2CLOB typesOrder, Fill, FillResulttypes compile
Lesson 3Matching engineBook struct + Reverse<Price> trick + accessorscargo check -p openhl-clob
Lesson 4Matching enginesubmit_order — Limit order, in-book matchingmatches resting orders
Lesson 5Matching enginesubmit_order — Market orders + crossing + partial fillsedge-case behaviour
Lesson 6Matching enginecancel + empty-level cleanupcancel-by-id works
Lesson 7Testing9 hand-traced unit testsall 9 pass
Lesson 8Testing3 proptest invariants (qty conservation, no-crossed-book, determinism)768 random scenarios pass
Lesson 9Bridge integrationAdd clob + pending_fills to LiveRethEvmBridge; submit_order methodbridge compiles
Lesson 10Bridge integrationbuild_payload drains pending fills; payload_fills(id) inspectorfills appear in payload
Lesson 11Bridge integrationclob_fills_flow_into_payload integration testfull pipeline test passes
Lesson 12Capstonerecap, what's next (precompiles via course 8)(no test — recap)

Lesson 11 is the milestone. Finishing Lesson 11, you have a fill produced by the matching engine flowing through the BFT engine into a real block. Lesson 12 names what's still missing (the fills aren't yet readable from smart contracts — that's course 8).

6. The answer-key discipline (same as course 6)

Every lesson from Lesson 1 through Lesson 11 cites either SHA 55a9dff (Stage 8a) or 428cc26 (Stage 8d). After your lesson test passes:

cd ~/code/openhl-reference
git checkout 55a9dff    # or 428cc26 for Lessons 9–11
diff -u ~/code/my-openhl/crates/clob/src/types.rs ./crates/clob/src/types.rs
# (etc.)

Match meaningfully — same types, same control flow. Whitespace and naming will differ.

7. Setup confirmation — the actual Lesson 0 exercise

Before Lesson 1, run all of these and confirm they pass:

# 1. Rust version
rustc --version    # expect: rustc 1.95.x or later

# 2. End-of-course-6 state
cd ~/code/my-openhl && cargo test -p openhl-evm --release
# Expect: trailing line `test result: ok. 3 passed; 0 failed; ...`

# 3. Reference repo has Stage 8 commits
cd ~/code/openhl-reference && git log --oneline | grep -E "(55a9dff|428cc26)"
# Expect: both SHAs appear

If all three pass, you're ready for Lesson 1.

Final check. In one sentence, what does this course add that course 6 didn't have? If your answer doesn't mention "a matching engine producing fills that flow into committed blocks", re-read §1.

Summary (3 lines)

  • Build openhl CLOB = limit-order-book matching engine. 13 lessons / 6 modules. Pinned to SHA f8b3c12.
  • Prerequisites: openhl Funding + perp primer. Pure compute throughout.
  • Production parallel: Hyperliquid HyperCore. Three matching philosophies (limit / market / cancel).