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
Bookper market, holding bids + asks; orders submitted viasubmit, cancelled viacancel. The book matches incoming orders against resting ones; emitsFills. - 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+Filltypes 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 nextbuild_payloadoutput, 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-consensuscomplete — or, equivalently, a workspace at the end-of-course-6 state. Yourcrates/evm/src/live_node.rsshould already haveLiveRethEvmBridge<P>withprovider,chain_spec,validator, and optionalengine_handlefields. If yours doesn't, work through course 6 first.- Rust 1.95+ — openhl's
rust-toolchain.tomlpins1.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 thestd::collections::BTreeMapdocs 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-onlypsyto/openhlclone
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
| # | Module | What you build | End-of-lesson test |
|---|---|---|---|
| Lesson 0 | Orientation | (this lesson) | setup confirmed |
| Lesson 1 | CLOB types | newtypes — AccountId, OrderId, Price, Qty, Side, OrderType | cargo check -p openhl-clob |
| Lesson 2 | CLOB types | Order, Fill, FillResult | types compile |
| Lesson 3 | Matching engine | Book struct + Reverse<Price> trick + accessors | cargo check -p openhl-clob |
| Lesson 4 | Matching engine | submit_order — Limit order, in-book matching | matches resting orders |
| Lesson 5 | Matching engine | submit_order — Market orders + crossing + partial fills | edge-case behaviour |
| Lesson 6 | Matching engine | cancel + empty-level cleanup | cancel-by-id works |
| Lesson 7 | Testing | 9 hand-traced unit tests | all 9 pass |
| Lesson 8 | Testing | 3 proptest invariants (qty conservation, no-crossed-book, determinism) | 768 random scenarios pass |
| Lesson 9 | Bridge integration | Add clob + pending_fills to LiveRethEvmBridge; submit_order method | bridge compiles |
| Lesson 10 | Bridge integration | build_payload drains pending fills; payload_fills(id) inspector | fills appear in payload |
| Lesson 11 | Bridge integration | clob_fills_flow_into_payload integration test | full pipeline test passes |
| Lesson 12 | Capstone | recap, 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).