Skip to content
← Build OpenHL CLOB — adding the matching engine
Orientation
Lesson 1 of 13·CONTENT15 min50 XP
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 SHAs 55a9dff (Stage 8a) / 428cc26 (Stage 8d).

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 SHAs 55a9dff (Stage 8a) / 428cc26 (Stage 8d). Byte-for-byte reproducible against the openhl reference.
  • Prerequisites. The openhl consensus course (the Reth substrate this course builds on) + perp primer (market structure).
  • Pure compute everywhere. No I/O, no async, no state mutation outside the book. Reusable in tests + production.
  • Three operations. Limit order, Market order, Cancel. Book::submit routes Limit / Market to the private submit_limit / submit_market; cancel is Book::cancel.
  • 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 openhl consensus course 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 (= the openhl precompiles course)
  • Stage 8b: funding rate state machine (= the openhl funding course)
  • 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 the openhl precompiles course adds via custom EVM precompiles.

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

3. Prerequisites

You need:

  • The openhl consensus course complete — or, equivalently, a workspace at the state where the openhl consensus course ends. 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 the openhl consensus course first.
  • Rust 1.95+ — openhl's rust-toolchain.toml pins 1.95.0. Same prerequisite as the openhl consensus course.
  • 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 the openhl consensus course:

  • ~/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-consensus-course state:

cd ~/code/my-openhl
cargo test -p openhl-evm --release 2>&1 | tail -10
# Expect: the openhl-evm tests pass, including
# - reth_dev_node_bootstraps (consensus course: booting a live Reth node)
# - live_bridge_builds_on_real_genesis (consensus course: LiveRethEvmBridge + validate_payload)
# - commit_sends_forkchoice_to_engine_when_handle_installed (consensus course: commit → forkchoice)

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

5. The 13-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 → submit_limit + match_at_level — Limit orders, in-book matchingmatches resting orders
Lesson 5Matching enginesubmit_market — 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 the openhl precompiles course)(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 the openhl precompiles course).

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

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.

One caveat for Lessons 9–11. In openhl's history, Stage 8a/8d (55a9dff, 428cc26) sit on top of Stage 7c (0844d58); Stage 7d (0cac571, commit → forkchoice) was committed later. So live_node.rs at 428cc26 has no engine_handle field, no with_engine_handle, no forkchoice call in commit, and no commit_sends_forkchoice_to_engine_when_handle_installed test. If your workspace has the consensus course's Stage 7d work, those lines show up in the diff; ignore them.

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-consensus-course state
cd ~/code/my-openhl && cargo test -p openhl-evm --release
# Expect: every `test result:` line reads `ok` with 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 the openhl consensus course 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 SHAs 55a9dff (Stage 8a) / 428cc26 (Stage 8d).
  • Prerequisites: the openhl consensus course + perp primer. Pure compute throughout.
  • Production parallel: Hyperliquid HyperCore. Three operations (limit / market / cancel).