Lesson 3 — Reading op-rbuilder — the Reth-based OP Stack sequencer
Question
op-rbuilder is the Rust sequencer that powers OP-Reth nodes — written by Flashbots, built on Reth SDK. Read the actual block-building loop, see exactly where it integrates with Reth's payload builder, and trace what a sequencer does block-by-block.
Principle (minimum model)
- op-rbuilder lives at
flashbots/op-rbuilder. Built on Reth SDK; replaces Reth's defaultEthereumPayloadBuilderwith a customisedOpPayloadBuilderthat handles OP-specific deposit transactions and L1 cost calculation. - Reth SDK integration point.
NodeBuilder::launch_with_op_attributesregistersOpPayloadBuilderas the payload builder service. Same pattern as Reth's default — just a different builder type. OpPayloadBuilder::build_payloadis the core function. ReceivesOpPayloadBuilderAttributesfrom the consensus layer; iterates the mempool; executes transactions withrevm; computes the state root; emits anExecutionPayload.- Three OP-specific things on top of Reth. (1) Deposit transactions (L1 → L2 messages, prepended at the top of every block). (2) L1 cost on every L2 tx (the L1 calldata cost for posting the batch). (3) System transactions (sequencer fee, L1 info, etc.).
- The block-building loop. Pull tx from mempool → simulate with revm → check gas + state validity → include or skip → continue until gas limit or empty mempool → finalize.
- MEV-aware builders. Flashbots' op-rbuilder can also accept bundles via a private RPC, the way L1 builders accept Flashbots bundles. Sequencers can monetise MEV directly.
- Read order.
crates/payload/builder/src/builder.rs(entry point) →crates/payload/builder/src/execute.rs(revm integration) →crates/payload/builder/src/cost.rs(L1 cost). ~3 K lines of Rust to read top to bottom.
Worked example + steps
Reading op-rbuilder — the Reth-based OP Stack sequencer
If you spin up your own OP Stack chain today, the binary producing blocks is almost certainly flashbots/op-rbuilder — Paradigm's Rust block builder for OP-derived rollups. Every Reth-based L2 either runs it directly or forks from it. It's the production reference for "sequencer on Reth," and it's the code you'd read if you wanted to understand what a real sequencer does once the marketing diagrams stop.
1. The op-rbuilder architecture
flowchart TB
EngineAPI["Engine API<br/>(from rollup consensus)"] -->|forkchoiceUpdated| Builder["Block Builder"]
Mempool["L2 Mempool"] -->|pending txs| Builder
L1Inbox["L1 Inbox<br/>(deposits + force-includes)"] -->|deposit events| Builder
Builder -->|build block| EVM["revm<br/>(execution)"]
EVM -->|state changes| State["State DB"]
Builder -->|signed block| EngineAPI2["Engine API<br/>(getPayload response)"]
Builder -->|broadcast| P2P["P2P network<br/>(propagate to nodes)"]
Three input streams:
- Engine API — consensus tells us what to build on top of
- Mempool — user txs waiting for inclusion
- L1 Inbox — deposit txs that must be included
The builder takes these, applies the OP Stack rules for ordering, and produces a block.
2. OP Stack ordering rules
A sequencer building an OP block must respect specific rules:
- Deposits first: any deposit txs from the L1 inbox must come at the top of the block
- L1 epoch attribution: the block must reference an L1 block (its "L1 origin")
- Sequencer signature: the block must be signed by the active sequencer key
- Gas limit: must be within OP-specific bounds (different from mainnet)
- Force inclusions: if any L1-inboxed txs have aged past deadline, must include them
These are the OP-specific block validity rules. op-rbuilder enforces all of them.
Sequencer can choose:
- Which user txs to include
- Order of user txs
- L1 epoch to attribute to
- Block timestamp (within bounds)
Sequencer cannot choose:
- Whether to include deposits (must)
- Whether to skip force-included txs (must include)
- Block validity rules (gas limit, base fee math)
Violating any "cannot" = L1 verification fails = your block gets reorged.
3. Reading op-rbuilder source
Key files (paths may shift across versions; navigate via search):
| Path | Role |
|---|---|
crates/builder/src/payload.rs | The core block-building loop |
crates/builder/src/ordering.rs | Tx ordering strategy |
crates/builder/src/deposit.rs | Deposit tx handling |
crates/builder/src/seal.rs | Block sealing + signing |
The main building function looks roughly like:
async fn build_payload(
attributes: PayloadAttributes,
parent: BlockHash,
state: StateProvider,
pool: TxPool,
) -> eyre::Result<ExecutionPayload> {
let mut block_env = BlockEnv::from(parent, attributes);
let mut executor = Executor::new(state, &block_env);
let mut included_txs = Vec::new();
// 1. Force-include deposit txs
for deposit_tx in attributes.l1_deposits {
executor.execute(&deposit_tx)?;
included_txs.push(deposit_tx);
}
// 2. Include force-inclusion txs (aged inbox)
for force_tx in attributes.force_included {
executor.execute(&force_tx)?;
included_txs.push(force_tx);
}
// 3. Pull from mempool until gas limit
let pending = pool.best_pending();
for tx in pending {
if executor.gas_used() + tx.gas_limit() > block_env.gas_limit {
break;
}
match executor.execute(&tx) {
Ok(_) => included_txs.push(tx),
Err(_) => continue, // skip failed txs
}
}
// 4. Seal block (compute roots, sign)
let payload = ExecutionPayload {
header: build_header(&block_env, &executor, &included_txs),
transactions: included_txs,
};
Ok(payload)
}
That's the sequencer loop in ~30 lines. The complexity in production op-rbuilder is in:
- Async execution (build while accepting more txs)
- Reorg handling (when the parent changes mid-build)
- MEV-aware ordering (prefer high-fee txs, sandwich-resistant ordering)
- Gas estimation accuracy
🔍 Find in repo. Open op-rbuilder's payload builder source and find the actual
build_payload(or equivalent). How does it handle the case where the parent block changes mid-build?
4. The MEV question — what does the sequencer extract?
Whoever picks the tx order picks who profits. For OP Stack chains, three positions on how aggressively the sequencer monetizes that power:
| Position | What sequencer does | Examples |
|---|---|---|
| Vanilla FIFO | Order by submission time | Naive implementations |
| Priority-fee ordering | Order by gas tip (like Ethereum mainnet) | OP Stack default |
| MEV-aware with builder market | Accept external bids for block construction | OP Stack with op-rbuilder + bundle markets |
op-rbuilder supports the third — chains can configure whether to accept external bundles from builders/searchers (third-party block constructors that compete to build the most valuable block). The bundle market pays the sequencer for blockspace.
This is where Flashbots-style PBS (proposer-builder separation — split who chooses the block from who builds it) comes to L2: builders compete to construct the most profitable block, the sequencer accepts the winning bid.
5. The pre-confirmation game
A single sequencer's killer UX feature is the pre-confirmation: the moment you submit a tx, the sequencer signs back "yes, this will be included in block N at position M." You can show the user "confirmed" in 100 ms — long before any L1 finality.
This trick only works with one sequencer. Multi-party setups have to vote, and voting takes round trips.
In op-rbuilder: the mempool admission step is where pre-confirmations would be issued. If the sequencer signs "I commit to including this tx," users can treat it as final without waiting for L1 finality.
The sequencer commits to inclusion. If the L2 reorgs (which can happen), the tx might not be in the canonical chain anymore. The sequencer might also be slashed in some designs if pre-conf is violated.
6. For Tempo's sequencer
Tempo's sequencer (operated by Paradigm) almost certainly:
- Uses op-rbuilder or a similar Rust block builder
- Implements OP-Stack-like ordering rules (deposits first, force inclusions, signature)
- Issues pre-confirmations to merchants (sub-second UX)
- Supports priority-fee ordering with merchant-priority bumps
- Has emergency halt authority for fraud detection
The architectural pattern is the same as any OP Stack L2; the business logic on top (merchant priority, fraud detection) is specific.
7. Practice
- Clone op-rbuilder (or browse online)
- Find the deposit tx handling code
- Trace what happens when a user submits via the sequencer's RPC
- Identify: how does op-rbuilder handle a mempool tx that pays priority fee but reverts in execution?
8. Reading list
- op-rbuilder repo
- Optimism sequencer spec — how the L2 chain is derived
- Paradigm rbuilder talk — design philosophy
Final check: in one sentence, what's the core consensus-enforced constraint on a sequencer that prevents it from arbitrarily reordering or excluding user txs forever? If your answer doesn't reference "L1 force-inclusion + deadline," re-read §2.
Summary (3 lines)
- op-rbuilder = Flashbots-built Rust sequencer on top of Reth SDK. Replaces Reth's default
EthereumPayloadBuilderwithOpPayloadBuilderfor OP-specific deposit txs + L1 cost + system txs. - Integration point:
NodeBuilder::launch_with_op_attributes. Core loop: pull mempool → simulate revm → include/skip → finalize. MEV-aware via Flashbots-style private RPC. - Read order:
builder.rs→execute.rs→cost.rs(~3 K lines). Next lesson: fraud proofs vs ZK validity proofs — the two state-root verification models.