Lesson 17 — The Reth extension pattern — library, not fork
Question
Reth as a library, not a fork. Extend without forking the codebase. Use the NodeBuilder + trait impls; track upstream.
Principle (minimum model)
- Library pattern. Import Reth as
Cargo.tomldep; instantiateNodeBuilder; customise via trait impls. Upstream upgrades = bump version. - Fork pattern (anti-pattern). Clone Reth; modify in-tree; diverge from upstream. Hard to track upstream; freeze the version.
- Why library wins. Smaller maintenance burden; track upstream; community ecosystem.
- OP Stack uses library. OP-Reth = Reth + custom executor + custom chainspec. Tracks Reth upstream.
- bera-reth uses library. Custom Consensus impl + chainspec. Tracks upstream.
- Tempo uses library. Custom Pool + custom precompiles. Tracks upstream.
- Anti-pattern: forking. Some custom L1s fork. Heavy maintenance burden; upstream improvements blocked.
Worked example + steps
The Reth extension pattern — library, not fork
If you've worked with op-geth, bsc-geth, or bor (Polygon), you know the geth-fork story: clone the upstream, apply your patches, rebase forever. Every upstream merge is a weekend of conflict resolution, and the audit surface drifts away from mainline.
Reth was designed to make this model obsolete. Optimism, Base, Berachain, Scroll, Seismic, Sova, alphanet, and Tempo all run on Reth — and almost none of them are forks in the traditional sense. They are node crates that depend on reth as a library and override the parts they care about via traits.
1. The two models
| Model | How it works | Cost over time |
|---|---|---|
| Fork model (geth-style) | Clone upstream, patch source, rebase periodically | Drift cost is superlinear — patches and upstream diverge, conflicts compound |
| Extension model (reth-style) | Depend on reth crates, implement chain-specific traits in a separate crate | Drift cost is localized — your code changes only when trait signatures change |
Reth's whole architecture is built around the second model. The NodeBuilder / components / ChainSpec pattern you saw in the Intermediate course exists precisely so that you never have to patch reth's source to ship a chain.
2. Why Paradigm chose this
Paradigm builds Reth, alphanet, and Tempo. They are their own customer. Three forces pushed them toward the extension model:
- Rebase pain is real. Optimism's op-geth had a fork-divergence story bad enough that the org sponsored a rewrite — folded into reth itself as
crates/optimism/. - Audit surface. An auditor reading a fork has to diff against upstream and reason about every patch. An auditor reading a node crate sees one repo, one set of traits implemented.
- Composability. Berachain wants reth + custom consensus. Scroll wants reth + zk-friendly state. Seismic wants reth + encrypted txs. The extension model lets all three coexist; the fork model would force each to maintain their own divergent copy.
The result: reth's trait architecture is the API for building a chain.
3. What you actually customize
A Reth-based chain typically overrides these slots:
ChainSpec— fork heights, gas params, precompile schedule, genesisConfigureEvm/ block execution strategy — execution layer, custom precompiles, deposit-tx handlingPayloadBuilder— how blocks get produced (sequencer mode for L2s)- Pool / mempool policy — what txs are admitted, in what order
- Custom RPC namespaces — exposing chain-specific endpoints via
extend_rpc_modules - Custom consensus — for non-Ethereum-PoS chains
Everything else (P2P, MDBX storage, staged sync, ExEx, trie commitments) comes from reth for free.
4. Concrete examples to read
Order them from "shipped to mainnet" → "R&D":
crates/optimism/in paradigmxyz/reth — Optimism / Base / Mode / OP Stack. The most production-tested extension on the planet.- paradigmxyz/alphanet — Paradigm's own OP-Stack testnet for trying custom precompiles (EIP-7212 P-256 verify, etc.) before they exist on mainnet.
- SovaNetwork/sova-reth — Reth as a Bitcoin execution layer.
- SeismicSystems/seismic-reth — Reth with encrypted transactions.
Each of these is a study in "what's the smallest patch the chain needs?" The answer is usually a few thousand lines in a node crate, not a fork of 200k lines of execution client.
5. Why this matters for what you're building
If you are building anything that touches a Reth-based chain — a bridge, a settlement layer, a custom node, a sequencer integration — you need to read at the trait level, not the binary level. The question "how does Tempo handle X?" reduces to "which trait does Tempo's node crate override, and how?"
Tempo's source is now public at tempoxyz/tempo, and you should read it through the lens of that question: "which of these standard slots did they customize, and why?" One concrete data point before you open it: tempoxyz/reth is 0 commits ahead, 1374 behind upstream Paradigm Reth — they did not fork Reth at all. Every payments-specific customization lives in the tempoxyz/tempo crate as a dependency-level extension.
6. Practice
- Open reth's workspace Cargo.toml and find every crate matching
reth-optimism-* - Note what each one owns (chainspec? evm? payload? rpc?)
- List the 6 customization slots you'd fill to ship a real chain
- Identify which one is "the consensus-rules-of-my-chain" slot
You should now be able to read any Reth-based chain repo without flinching at the directory structure.
Final check: in one sentence, what is the structural reason a Reth-based chain rarely needs to patch reth's source? If your answer doesn't reference trait-based extension and NodeBuilder composition, the lesson hasn't stuck. Re-read sections 1 and 2.
Summary (3 lines)
- Reth as a library, not a fork. Import + customise via trait impls + track upstream.
- Library examples: OP-Reth / bera-reth / Tempo. All track Reth upstream.
- Fork pattern is anti-pattern: heavy maintenance, upstream improvements blocked. Next: op-stack-on-reth.