The Transaction Anatomy of Ethereum Native Account Abstraction

The Quantum Upgrade of Ethereum's Execution Layer (2/5)

  1. The Security Skeleton of Ethereum Account Abstraction
  2. The Transaction Anatomy of Ethereum Native Account Abstraction
  3. The Life of a Post-Quantum Transaction
  4. The World Around a Transaction
  5. The Network-Wide Replay Machine
Contents

Part one took ERC-4337’s security skeleton apart and ended on a single sentence: the bundler is the price of wanting account abstraction without a hard fork. The protocol accepts only one kind of transaction, EOA-initiated, ECDSA-signed, sender-pays, so someone has to translate, front the money, and carry the risk. EIP-8141, native account abstraction, chooses to pay the price on the other side of the ledger: hard-fork once, and teach the protocol to recognize transactions whose validity logic lives in account code. The middleman disappears, and the problems it used to carry, the unpaid window and the simulation risk, move into the protocol with their luggage. This article dissects the 8141 transaction itself: how the envelope is sealed, how frames are laid out, what the two strange addresses are, how authorization gets spoken aloud, how the signature hash is computed, and how the mempool machine stands guard. One honest caveat up front: 8141 is still a draft, so the type number and parameters below (0x06, 100000) are subject to whatever finally ships in a fork.

The through-line continues from part one. There the axis was who pays for gas and who guarantees payment; here the camera turns to the other half of the question: who gets to ask the validation question, in what identity, and how the account answers. Three generations of the system gave three answers, and 8141’s answer is: the protocol comes knocking in person.

Three generations of prime movers

Part one established the iron law of contracts: no private key, forever passive, every execution of any bytecode must be woken by a call, and every call chain traces back to a transaction. Contracts have no heartbeat and no timers; code that is never called is dead data in the state tree. So who initiates the first call into the account’s validation code?

legacy/1559: The transaction IS the call. The protocol verifies ECDSA natively,
outside the EVM, then makes the single top-level call as the sender.
Account code plays no part in validation at all.
4337: The prime mover is an ordinary L1 transaction from the bundler,
hitting the EntryPoint contract, which (as on-chain bytecode)
then CALLs your account's validateUserOp.
The thing calling you is another contract on the chain.
8141: The prime mover is the client's frame loop: native code,
belonging to no on-chain entity. It reads the frame list, builds
the call context, and feeds your account's bytecode to the EVM.
The thing calling you is the protocol itself.

One sentence aligns all three: in legacy, the protocol does validation itself and never asks the account; in 4337, a contract subcontracts it to the account; in 8141, the protocol knocks on the account’s door in person. It welds legacy’s “the protocol handles it” to 4337’s “the account answers in code,” and deletes the contract middleman between them.

Three generations of prime movers: the protocol does it, a contract subcontracts it, the protocol knocks

Passivity is not an implementation detail; it is the foundation of the security model. VERIFY frames (coming shortly) can afford STATICCALL-strict semantics because the person answering an exam question needs no pen that rewrites the exam; the mempool dares to simulate validation code because a passive pure function’s answer is re-askable, and asking the same question twice, once in simulation and once on chain, should yield the same answer. The 7562 sandbox bans from part one were, at bottom, an upgrade of re-askability from a fact to a discipline; 8141 writes the same discipline straight into the protocol.

The envelope: dispatch on one byte

Start from the outermost layer. EIP-2718 defines the typed transaction envelope: a typed transaction is the bare byte concatenation TransactionType ‖ TransactionPayload, with type numbers from 0x00 to 0x7f and the payload an opaque byte array whose internal encoding each type chooses for itself. The living genealogy on mainnet: the prefix-less legacy transaction (a bare RLP list); 0x01 is EIP-2930’s access-list transaction; 0x02 is EIP-1559’s two-fee-rate transaction, today’s mainstream; 0x03 is EIP-4844’s blob transaction; 0x04 is EIP-7702, one of part one’s protagonists. 8141 intends to use 0x06, but the draft space is a fender-bender in progress: EIP-8202 has claimed 0x05, EIP-8105 claims both 0x05 and 0x06, and EIP-7727 is also in the queue. Draft-stage numbers count for nothing; whoever ships in a fork first settles it.

Why can one byte dispatch unambiguously? For that you go down to RLP’s foundation. When RLP serializes anything, the first byte encodes both “what structure” and “how long,” and the complete rule set is five lines:

0x00–0x7f single byte, self-encoding: a value ≤ 127 encodes as itself
0x80–0xb7 short string: 0x80 + length (content 0–55 bytes)
0xb8–0xbf long string: 0xb7 + n, where n = how many bytes the length field
itself occupies (a two-layer structure)
0xc0–0xf7 short list: 0xc0 + total payload length (0–55 bytes)
0xf8–0xff long list: 0xf7 + n, as above (two-layer)

A legacy transaction is defined as rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s]). The top level is a list, so its first byte can only land at 0xc0 or above; and since r and s alone take 64 bytes, the payload can never squeeze into a short list’s 55 bytes, so a bare legacy transaction always begins with 0xf8 or 0xf9 (a 300-byte payload becomes f9 01 2c: the first byte says “the next 2 bytes are the length field,” and the length field holds 0x012c = 300). EIP-2718 places type numbers in 0x00 through 0x7f, fully disjoint from the list-prefix range, so one byte read suffices for dispatch at zero parsing cost; the 0x80 to 0xbf band is deliberately left blank as a buffer.

A layering confusion hides here that deserves to be nailed down: RLP is a grammar; transaction parsing is a protocol; the type byte is written in the protocol, not in the grammar. An RLP decoder knows only those five prefix rules and has never heard of “transactions” or “types”; the 2718 dispatch rule lives in client code. The same byte 0x02 reads as “a 1559-type transaction” to the transaction parser and as “the integer 2” to the RLP decoder; the meaning comes from the layer doing the reading. Feed the full 02 f8 6a … directly to an RLP decoder and it errors: it reads 0x02, considers one complete item finished, and finds a large tail of leftover bytes (geth reports something like trailing bytes). The whole type ‖ payload is not valid RLP, and 2718 says in so many words that it is an opaque byte string.

So how does this non-RLP object get into a block body, which is a genuine RLP list? A list’s payload is its elements’ encodings laid end to end, and the decoder finds its footing element by element, each self-delimiting. Splicing type ‖ payload in bare is a disaster: the 0x02 gets torn off and parsed as the integer 2, and the remaining bytes get silently misread as some untyped transaction with the wrong number of fields, which is worse than a crash. The fix is to issue it a passport: wrap the whole transaction as one atomic item using the long-string prefix. b9 01 2c tells the list decoder, “the next 300 bytes are one unit, do not interpret the contents, step over them.” Here is the real shape of two transactions lying side by side in a block body:

f9 xx xx ← the transaction list: long-list prefix
b9 01 2c ← element 1's shell: a 300-byte atomic string
02 f9 01 28 … ← the typed transaction, untouched (1 + 3 + 296 = 300 bytes)
f8 6c ← element 2: a legacy transaction, itself a list, enters bare
80 85 … ← the 108-byte nine-field payload

One line to close it out: stripping the type byte is a read-time dispatch action; the string shell is a storage-time packing format; a transaction’s canonical form is always the complete type ‖ payload, and whoever wants to put it inside an RLP list must issue it a passport. These byte-level details are not just trivia: 8141’s transaction hash, signature hash, and mempool decoding are all built on this encoding, and every section below steps on it.

Envelope and passport: one-byte dispatch, and the string shell inside the block body

Frames: a call the protocol makes on your behalf

Open the 8141 payload and the core is a frame list, at most 64 frames. A frame equals one top-level call that the protocol makes on the transaction’s behalf, with six fields each minding one thing. The two that matter most are mode and flags, and they answer two fully orthogonal questions: mode answers only who this call’s msg.sender is and under what rules it executes; flags answers only what this frame is statically authorized to do.

There are three modes, in one-to-one correspondence with 4337’s roles. A SENDER frame runs with your account as caller, acting in your identity; it is the only mode allowed to carry value, and it corresponds to the callData part of a UserOp. A VERIFY frame runs with 0xaa as caller (next section explains that address), under STATICCALL semantics; the only “write” it is allowed is APPROVE, and if it reverts the whole transaction is void. It corresponds to validateUserOp. A DEFAULT frame also runs with 0xaa as caller but as an ordinary non-static call, handling protocol-side chores such as deploying a new account or running a paymaster’s postOp, corresponding to those moments in 4337 where EntryPoint itself calls the factory or postOp.

Flags is a small bit field. The low two bits declare the APPROVE scope this frame is allowed: none, payment only, execution only, or both. The point of a static declaration shows up in the mempool: a node can classify every frame with zero execution, and recognizing the shape of the validation prefix becomes a purely structural operation. In part one, bundlers protected themselves with dynamic heuristics; here that is replaced by protocol-level static decidability. The third bit is the atomic flag, whose meaning is “bind me and the next frame into one atomic group”: a run of consecutive set-bit frames plus the first unset frame after them forms a group, and if any member fails, the whole group rolls back, the remaining members are marked skipped, and their budget is refunded. A VERIFY frame can neither set the bit nor be bound into a group, because its failure voids the entire transaction and “skipped” is meaningless for it; the last frame naturally cannot set the bit either, having no next frame.

Every static constraint comes with its reason. Value may appear only in SENDER frames, because 0xaa has no authority over your funds. An execution-approving VERIFY frame must target yourself, because only your own code is qualified to say “I consent to execute.” A payment-approving VERIFY frame may target anyone, because paying on someone’s behalf is a paymaster’s legitimate business. An empty target means yourself: RLP’s empty string costs 1 byte against an address’s 21, saving 20 bytes on the most frequent shape, one of the reasons 8141’s minimal transaction compresses to a 139-byte baseline.

Two standard shapes, and the feel of frames clicks into place:

Self-paying single operation (two frames):
F0 VERIFY target empty (→ self) flags 0x3 after the sig check, approve
payment and execution in one step
F1 SENDER → USDC.transfer
Sponsored batch (five frames):
F0 VERIFY → self flags 0x2 approve execution ┐ order locked: F1's guard
F1 VERIFY → paymaster flags 0x1 approve payment ┘ requires execution approved first
F2 SENDER → USDC atomic bit set ┐
F3 SENDER → DEX atomic bit unset ┘ atomic group {F2, F3}
F4 DEFAULT → paymaster postOp

The paymaster dares to approve payment in F1 because its code can use the cross-frame introspection instructions (FRAMEPARAM, FRAMEDATALOAD) to check on the spot that F2 and F3 really contain a call that pays it enough. In part one, a paymaster needed a stake and a whitelist exemption before it dared operate; here, its self-protection becomes reading the frame list directly.

The five-frame sponsored batch: mode sets identity, flags set permission, the payment line falls after the validation prefix

Two strange addresses: 0xaa and 0x8141

Two addresses recur throughout the spec, and they are two entirely different kinds of thing; a side-by-side look is the clearest.

0xaa, called ENTRY_POINT in the spec, shares only a name with 4337’s EntryPoint: it has no code, no deployment, is never called, and there is no such thing as “triggering” it. 4337’s EntryPoint is a real on-chain contract that bundlers call with ordinary transactions; 8141 moves the whole orchestration into client native code, a frame loop inside the state-transition function of geth and reth. But the EVM’s call model demands that every call have a caller, and VERIFY and DEFAULT frames are “calls the protocol makes on the transaction’s behalf,” so the spec anoints a constant: such calls always carry address(0xaa) as caller. It is the protocol’s identity placeholder in the EVM’s world, forever in the subject position, never in the target position. Writing require(msg.sender == address(0xaa)) in account code is the analogue of 4337’s require(msg.sender == entryPoint), and it is harder still: 0xaa has no private key and no code, an ordinary call’s msg.sender is always some real account, and the only way you can ever observe 0xaa as caller is that the protocol is executing you in a VERIFY or DEFAULT frame right now. One easter egg: 0xaa literally spells “AA,” and the APPROVE instruction’s opcode happens to be 0xaa as well; address and instruction share a number, a deliberate pun by the spec’s authors.

0x8141, called EXPIRY_VERIFIER, is the other kind of thing: it has code, and it is a predeploy. At the moment of the fork, the protocol simply endows this address with spec-defined behavior; nobody ever ran a deployment transaction. The precedents are EIP-4788’s beacon-root contract and EIP-2935’s historical-hash contract. Usage: to give a transaction an expiry, put a VERIFY frame at the very front of the frame list, target 0x8141, with 8 bytes of big-endian timestamp in data; that piece of spec-defined code checks block.timestamp ≤ expiry and reverts otherwise, voiding the transaction. The address’s literal value equals the EIP number, a keepsake in the same family of humor as 0xaa. What is actually worth remembering is the design rationale: why a frame pointing at a predeploy rather than a transaction field? First, format orthogonality: everything is a frame, no special case for expiry. Second, static mempool readability: seeing target 0x8141 with exactly 8 bytes of data, a node reads off the transaction’s time-to-live with zero execution and evicts it on the dot. Third, part one explained why the validation phase bans TIMESTAMP, and expiry is a legitimate need; folding the ban’s single exemption into one piece of spec-defined code shrinks the audit surface to a point. The protocol opened a window for a legitimate need, and it is a window with a frame.

APPROVE: authorization spoken in the first person

Now to the deepest watershed between 8141 and 4337. In 4337, the account returns a value from validateUserOp, and the EntryPoint contract interprets that value and does the bookkeeping: authorization is a return value construed by a middleman. In 8141, account code executes an instruction and the protocol recognizes it directly: authorization is an action spoken aloud.

APPROVE is a real EVM instruction, opcode 0xaa, embedded in your account’s bytecode, the same kind of thing as ADD and MSTORE. A VERIFY frame’s execution context has caller equal to 0xaa and ADDRESS equal to your account, and the instruction’s guard is precisely that ADDRESS must equal the frame’s resolved target: it is your code, in your identity, speaking approval. 0xaa executes nothing; it has no code; it is the one who knocks, not the one who signs. A fully parameterized APPROVE(0x3) completes four things at once: it sets the sender-approved flag, sets the payer to you, increments the nonce, and pre-deducts gas money at the transaction’s maximum cost. Note where the bookkeeping lands: these are transaction-level context variables living in the client’s memory, not storage slots of any contract; the protocol interprets the instruction’s semantics directly. In part one, 4337’s prefund was a real transfer locked into the EntryPoint contract’s escrow; 8141’s pre-deduction is an entry in the protocol’s own books. The payment line stands exactly where it stood, failure before it has no payer and failure after it burns only the signer, but the line has moved down from contract logic into a protocol primitive.

One audit note in passing: DELEGATECALL preserves ADDRESS, so a delegated library can execute APPROVE just as effectively. Delegating your validation logic out delegates not a function call but the qualification to speak approval in your identity; the library must be treated as fully trusted.

The canonical signature hash: one old rule, two generations

An 8141 signature is no longer v, r, s on the envelope but a signature list, each entry a four-tuple [scheme, signer, msg, signature]. To understand its hashing rule, first go back to legacy for a piece of foundation, where an old rule has been in force since the genesis block.

Start with a paradox of temporal order. A legacy transaction has nine fields, and v, r, s are its own last three fields, not some external attachment. But a signature is the output of running a private key over some digest; at the moment of signing, v, r, s do not exist yet. “Hash all nine fields and sign that” would require the signature to commit to data containing itself, which cannot be done. The only way out: the signature hash is computed over the first six fields only. Two pipelines, each minding its own:

Signing (wallet side):
sighash = keccak(rlp([six fields, chainId, 0, 0])) ← no v/r/s
ECDSA(sk, sighash) → r, s, recovery bit → assemble v
full transaction = rlp([six fields, v, r, s])
txHash = keccak(full transaction bytes) ← includes v/r/s
Verifying (node side):
split out v/r/s, independently recompute sighash from the rest
ecrecover(sighash, v, r, s) → recover the sender's address

The two hashes thus divide the labor: sighash is the cryptographic anchor, the object the private key commits to, the entire seat of authorization; txHash is the identifier, the name in explorers, RPC, and logs. One corollary is worth a beat of silence: a legacy transaction has no from field. The sender is not declared, it is recovered from the signature. This recovery-style identity works only for schemes like ECDSA that support public-key recovery; 8141 must support arbitrary schemes, so it promotes sender to an explicit first-class field, recovery becomes declaration, and the signature list’s job is to prove the declaration.

Two more pieces of history that will be reincarnated. First, EIP-155 needed to mix chainId into the signed content to stop cross-chain replay, and its technique was to fill the three v, r, s seats with chainId and two zero placeholders before hashing: seats preserved, contents excised. Second, ECDSA has s-value malleability: for the same sighash, both (r, s) and (r, n−s) are mathematically valid, so a third party could flip an in-flight transaction’s s and obtain “the same” transaction with authorization intact but a different txHash, confusing every system that tracks state by txHash; the Mt. Gox episode in the Bitcoin ecosystem made this class of confusion famous. Ethereum’s fix was EIP-2: enforce low-s, ruling any s above half the curve order invalid outright, killing malleability with a canonical-encoding requirement. The word deserves one formal sentence: when a third party holding none of your keys can alter some bytes of an in-flight transaction and still end up with a valid transaction, the values affected by such alteration are called malleable. Flipping s changes txHash, the name; sighash, the authorization anchor, never moves. Hold on to both stories; each returns in a new shape in 8141 and again in the post-quantum world.

Now the canonical signature hash of 8141, whose definition is two lines:

compute_sig_hash(tx):
for every signature entry whose msg is empty, blank out its raw signature bytes
return keccak(0x06 ‖ rlp(tx)) # type number as prefix: cross-type domain separation

Why must the bytes be excised? Build the cycle by hand once and it becomes obvious. Suppose no excision, and naively hash all bytes: fill everything in, put a placeholder in the signature seat, compute H₁, sign H₁ to get s₁, write s₁ into the transaction, the bytes change, recompute and get H₂ ≠ H₁; the node will check whether s signed H₂, but s₁ signed H₁, so it fails. Re-sign for H₂ to get s₂, write it in, the bytes change again, H₃ ≠ H₂, fail again. You are chasing a target that moves every time your pen touches paper. Ending the chase requires not “sign once more” but a self-consistent solution:

s* = Sign(sk, H(tx containing s*))

A solution of f(x) = x is called a fixed point. The rigorous version of “unsolvable” here is: there exists no method of solving it. A secure hash’s avalanche property makes H behave as a black-box random function, the equation offers no algebraic structure to exploit, and the only “algorithm” is blind trial at success probability around 2⁻²⁵⁶ per attempt. In a cryptographic context, that is equivalent to unsolvable. Excision evicts the signature from the preimage by definition: H depends only on non-signature content, so compute H first, then sign, then fill in; writing the bytes no longer changes H, the target is nailed down, one signature lands. Aligned with legacy bit for bit: legacy’s sighash computes over six fields with the v, r, s seats treated as absent; 8141’s canonical hash computes over the whole transaction with the signature seats of empty-msg entries treated as blank, while entries with an explicit msg keep their signature bytes in place. The same old rule, “the signed thing must not contain the signature itself,” generalized onto a multi-signature list. This generation is more elegant by one degree: transaction hash and signature hash share a single formula, the only difference being whether empty-msg entries’ signature bytes are present (transaction hash) or blanked (canonical signature hash); and chainId is now a first-class field, so EIP-155’s placeholder theater retires with honors. And nail down a point that slips past easily: this is not a before-signing and after-signing pair. Once the bytes are filled back in, the transaction exists and travels only in its full-byte form; at verification time a node re-derives H by re-reading that same full transaction with the empty-msg entries’ signature seats blanked. The two hashes are two readings of one final set of bytes, coexisting, one serving as the name and the other as the anchor.

What is this recurring msg field? It belongs to each signature entry and answers exactly one question: which digest did this signature sign. Empty means “I signed this very transaction’s canonical signature hash.” That is the main signature, the direct heir of legacy’s v, r, s, the most powerful and load-bearing entry in the whole transaction; when an EOA sends a frame transaction with default logic, the single entry it carries is one empty-msg SECP256K1 signature, and that one entry is the entire authorization. An explicit 32 bytes means “I signed this other digest,” say a permit or a session-key mandate; it is auxiliary evidence. Why does “signing this transaction” use emptiness rather than writing the hash H into msg? Because it cannot be written: msg is part of rlp(tx), hence part of H’s preimage, and writing H in would change H, the chase again. An object cannot write its own hash inside its own body; it can only plant a self-referential symbol that points. Empty is that symbol. The signing target has exactly two possibilities, this transaction (unwritable, so point) or some other digest (unguessable, so it must be written), and the two values are the inevitable shape of a logical dichotomy, not two items that happened to make a feature list.

Explicit msg has its own discipline. Its bytes must stay in the preimage, committed by the canonical hash, or a relayer could swap them in flight without disturbing any empty-msg signer. But see the direction clearly: the commitment is one-way. The explicit-msg entry is held tight by the transaction, yet it makes zero commitment back: it merely imports into the transaction one client-guaranteed fact, “signer S did sign digest M,” and nothing more; it neither authenticates nor authorizes this transaction. Hence a classic trap: if VERIFY code checks only an explicit-msg signature and then APPROVEs, that signature can be scissored out and pasted onto another transaction with a completely different frame list, where it grants passage again, because no link of the authorization chain was anchored to this transaction. The discipline is one line: authorization must somewhere close onto the non-malleable anchor, either by checking the canonical hash (the TXPARAM instruction exposes it) or by explicitly constraining every subsequent frame. The correct posture is the two values working together, the standard session-key paradigm:

entry 0: [SECP256K1, session key K, empty, s₀] ← K signs the canonical hash:
anchors "this one transaction"
entry 1: [SECP256K1, owner, digest M, s₁] ← owner's earlier mandate M:
"K may trade on DEX X, cap Y, until T"
VERIFY frame code:
read entry 1: confirm owner signed M (client already verified; read the fact)
check every SENDER frame against M's semantics
read entry 0: confirm the canonical hash was signed by K → APPROVE

Entry 1 answers “by what right does K act”; entry 0 answers “was this one sent by K.” Missing 1, K is a key without an owner; missing 0, the authorization floats free and can be replayed at will. In one line: an empty msg locks the transaction, an explicit msg hands it evidence; safe account logic always makes the evidence serve the lock, never replace it.

The excision rule pays two dividends. The first is parallel signing: multiple empty-msg entries are all treated as blank in H’s definition, so H is identical for every signer and depends on nobody’s bytes; multiple signers can sign in parallel, in any order, with no “you sign first so I can compute the hash” queue. A 2-of-2 of ECDSA plus P256, or a transitional hybrid of ECDSA plus Falcon, is native expression in this format. The second dividend runs deeper: the signature bytes of empty-msg entries are by definition in nobody’s preimage, so deleting them from a block someday and substituting an aggregate proof disturbs no signature. A move forced by mathematics was designed into an asset by the spec’s authors. Who this “aggregation ticket” was issued to, who was refused, and why, is the heart of the next article.

One formula, two hashes: two readings of the same signed transaction, hashed as-is for the transaction hash, empty-msg signatures blanked for the canonical signature hash

The mempool machine: free failure must be caught early

Part one’s lesson meets its final exam here: programmable validation opens the DoS door, free input leveraging real CPU. 4337 outsourced that risk to bundlers; 8141 takes it back into the protocol’s own mempool and re-corrals it with a two-layer validation machine.

Layer A is mempool admission. A node receiving a frame transaction proceeds: dispatch on the first byte into the frame-transaction decoder; RLP-decode the nine fields and run every purely static check, frame count at most 64, reserved flag bits, mode constraints, signature-entry encoding compliance; compute the canonical signature hash and verify all protocol-scheme signatures up front; check that the nonce connects exactly and that one sender has at most one transaction pending in the pool (the reincarnation of 2938’s mempool rule from part one); then the step unique to frame transactions, since the payer cannot be read statically, simulate the validation prefix, whose shape must match one of the prescribed forms, under trace rules throughout, with the prefix frames’ gas limits plus the signature fee summing to at most 100000, stopping the moment the payer lands; finally check the payer’s balance and reservations. Only if everything passes does the transaction enter the pool and get relayed. Layer B is block execution, the full state transition: nonce, signatures, the frame loop, atomic groups, the final payer check, fee deduction and refund, per-frame receipts.

Three conclusions worth copying down.

First, admission for a traditional transaction is O(1): one ecrecover plus two state reads, zero EVM. Frame transactions turn “who pays” into a Turing-complete question, and the entire mission of the mempool rules is to corral that opening back into “decidable by at most a hundred thousand gas of simulation.” Part one’s three shackles from 2938 acquire a concrete number here.

Second, invalid is not revert. A failed signature check means invalid: the transaction never enters a block and nobody pays anything for it. A frame’s revert is billed as usual. Invalid is free, which is why signature verification must be pulled forward to admission and intercepted there; otherwise it is a free DoS surface. This is exactly why protocol-scheme signatures are verified before any frame runs. The payment-line vocabulary still applies: invalid lives before the line, revert lives after it.

Third, the set of consensus-valid transactions is strictly larger than the set the public pool will propagate. A transaction can be perfectly legal at the consensus layer yet be politely declined by the public mempool because its validation prefix exceeds the hundred-thousand-gas budget, leaving only private channels straight to block builders. This mezzanine is no theoretical corner. The next article will show that today, every post-quantum transaction lives in it.

Summary

Compress the article back into one paragraph. EIP-8141 uses one hard fork to take the right to ask the validation question back into the protocol’s hands. The envelope keeps 2718’s one-byte dispatch, grounded in RLP’s five prefix rules and in the layering instinct that the type byte belongs to the protocol while the string shell belongs to storage. The transaction body becomes a list of at most 64 frames, mode setting identity and flags setting permission, with the atomic bit tying neighboring frames into all-or-nothing groups. The protocol knocks in the name of 0xaa, the predeploy 0x8141 checks the clock for it, and the account answers in the first person with one APPROVE instruction; authorization shifts from a middleman-construed return value to an action the protocol recognizes directly, and the prefund’s escrow transfer becomes a pre-deduction in the client’s own books. The signature grows from v, r, s on an envelope into a multi-scheme signature list, and the canonical signature hash carries the old rule, the signed thing must not contain the signature itself, into per-entry excision; empty msg locks, explicit msg testifies, and the format picks up parallel signing plus an aggregation ticket along the way. The mempool machine, with purely static checks and a hundred-thousand-gas prefix simulation, puts free failure back in its cage, at the price of drawing a mezzanine between consensus-valid and publicly-propagatable. Every 4337 role finds its counterpart here: EntryPoint dissolves into the frame loop, validateUserOp becomes the VERIFY frame, prefund becomes APPROVE’s pre-deduction, the 7562 sandbox becomes protocol-built-in trace rules, and the bundler vanishes into the block builder. The payment line from part one stands untouched; it has merely moved from userland into the kernel.

The next article puts a real post-quantum signature onto these rails: where it gets stopped and by whom, why the same signature is priced a hundredfold apart on two different lanes, and where an account’s ownership, recovery, and trust come from once the equation “address equals private key” has been torn up.