Application-layer review of EIP-8105 & EIP-8184: header split, representation gap, and an unsourced Motivation claim

Submission for the “Encrypt The Mempool!” POIDH bounty (poidh.xyz/mainnet/bounty/11). Wallet address (used to add funds to the pool): 0x934C80e3a3e9136eE3558950385B66Ac5e7D9bf7 Name/pseudonym: Crezno Quotes are from the specs as they stand on ethereum/EIPs master as of 2026-07-24.

Scope

The consensus-layer mechanics of both proposals have been reviewed thoroughly in this venue already — key-withholding economics and PTC bribery by Bonus (topic 897), and a spec-level pass covering block-validity ambiguities, key-vote algebra, the DEM nonce, and the ordering fixpoint by vellichorlabs (topic 906). On Magicians, dataalways argued that wallets will not adopt LUCID because it worsens inclusion time, execution quality, and MEV refunds.

I do not re-tread any of that. This review looks at the layer above: what these designs do to the applications, wallets, and tooling that would have to carry them. Three findings.

Part 1. A LUCID decrypted transaction reads one block’s header while residing in the next. That split is deliberate and well-motivated, but neither EIP enumerates which header-derived values a decrypted transaction observes, and the consequences reach contracts and indexers that never opted in. I also examine — and then largely dismiss — the PREVRANDAO attack the split appears to enable, because I think the obvious reading of it is wrong and it is better to say so than to let it stand.

Part 2. Distinct from dataalways’s objection. His point is that wallets won’t want this. Mine is narrower and more tractable: neither EIP says how a wallet, explorer, indexer, or accounting tool should represent one of these transactions to a human. ERC-4337 created a weaker version of this problem and the ecosystem is still paying for it.

Part 3. EIP-8105’s Motivation asserts a regulatory conclusion with no citation and no supporting argument, in a section whose job is to justify the change. I argue it should be sourced or narrowed.


Part 1 — A decrypted transaction reads one block’s header and lives in another, and neither EIP says what that means

1.1 The split

LUCID executes decrypted transactions against the header of the commitment block:

The decrypted_transactions are executed against the header of the block in which their ST ticket was committed to. This prevents builders from being able to inject information into the block after having witnessed decrypted transactions. (Rationale, “Probabilistic front-running”, point 3)

The reasoning is sound and I think the choice is right. Pinning the context to the commitment block closes a real channel: a builder who could pick the execution environment after seeing decrypted contents would have an information-injection lever, and this removes it structurally rather than economically.

But the plaintext transaction does not reside in block n. It resides in block n+1:

The first transactions (white rectangle) are decrypted STs, previously committed to in block $n-1$. (Specification, “Overview: from submission to execution”)

So a decrypted transaction is in one block and reads another block’s header. Nothing else in Ethereum does this.

  SLOT n                            |  SLOT n+1
  --------------------------------- | ---------------------------------
   ST ticket charged, payload n      |
   published. header_n now fixed:    |
                                     |   PT physically resides here
     NUMBER     = n                  |   and is logged here,
     TIMESTAMP  = t_n                |   but executes reading header_n
     PREVRANDAO = randao_n   --------+->
                                     |   receipt.blockNumber  = n+1
                                     |   NUMBER opcode        = n
                                     |          ^ these disagree

EIP-8105 makes the opposite choice — decrypted transactions read the block they are in:

Decrypted transactions are executed as a regular transaction under the execution context of the inclusion block (e.g., for block.slot and block.coinbase). (Specification, “Transaction Execution”)

so it does not have the split, and pays for that by letting the builder of block n+1 construct the environment after observing which keys were released — the exact lever LUCID’s point 3 closes. Both choices are defensible. Neither EIP documents the cost of the side it picked.

1.2 What the split does to contracts that never opted in

The e.g. in 8105’s sentence, and the absence of any enumeration in LUCID’s, is the problem. Both EIPs gesture at the execution context without listing it. Every header-derived opcode needs a defined value, and several have application-visible consequences:

  • NUMBER returns n while the transaction is in block n+1. A contract that stores block.number records the wrong block. Any “blocks elapsed since” calculation is off by one for every LUCID transaction and correct for every other one — which is worse than being uniformly wrong, because it is intermittent.
  • TIMESTAMP returns t_n, roughly 12 seconds stale. For Uniswap-style deadline checks this is directionally harmless (stricter, not looser). For oracle staleness guards of the form require(block.timestamp - updatedAt <= MAX_AGE) it is the wrong direction: the check silently accepts a price feed 12 seconds older than the author specified. On a 12-second slot that is one whole extra slot of staleness tolerance, granted without the contract’s knowledge.
  • Logs disagree with themselves. The log’s blockNumber is n+1 because that is where the receipt lives. Any block number written into the event payload is n. Indexers that reconcile the two — and many do, as a sanity check — will see a mismatch on exactly the transactions that used the encrypted mempool.
  • BLOCKHASH behaviour is undefined at the boundary. With NUMBER reporting n, is blockhash(n) the completed block n’s hash, or 0 per the usual “current block” rule? Block n is complete by execution time, so both answers are arguable. Two clients could reasonably differ, and that is a consensus split, not a UX wart.

None of this is exotic. It is the ordinary consequence of a design where a transaction’s residence and its context diverge, and it lands on contracts that were deployed years ago and cannot opt out — the sender chooses the inclusion path on their behalf.

1.3 The PREVRANDAO case, and why I think it is not an attack

The obvious next step is to claim the split creates a free option on block randomness. PREVRANDAO in the PT returns randao_n, which is public in beacon block n, and the spec has the decryptor act only afterwards:

After $T_2$ (Keys) – Each key publisher observes the ST-commitments in the beacon block. […] It propagates the key(s) that reveal the STs. (Specification, “Overview”)

So the decryptor knows the randomness before choosing to reveal, and under trustless self-decryption that decryptor is the user. The apparent attack: commit a raffle entry, look at the outcome, unseal only if you win.

I do not think this holds, and I want to say so explicitly rather than leave it implied. A searcher today can submit a private bundle that reads PREVRANDAO and reverts when unfavourable. Reverting bundles are dropped and cost nothing. That is already a free option on block randomness, available now and widely used. LUCID’s version costs the forfeited tob_fee plus the ticket’s sunk gas, which is charged in block n regardless of whether the key is ever released. On this axis LUCID is more expensive for the attacker than the status quo, not less.

The only genuine delta is permissionlessness: withholding needs no builder relationship, whereas revert-sampling needs a venue that honours revert protection. That is a real difference but a small one, and it is priced.

I raise it because it is the first thing a reader will think when they see §1.1, and because the tob_fee mechanism deserves credit for pricing something that is currently free. The finding in §1.2 stands on its own without it.

1.4 Proposed fixes

  1. Enumerate the execution context normatively, in one place. For a decrypted transaction, state the value of every header-derived opcode — NUMBER, TIMESTAMP, PREVRANDAO, COINBASE, GASLIMIT, BASEFEE, BLOCKHASH, CHAINID — and which block each is drawn from. LUCID currently says “the header”; 8105 says “the execution context of the inclusion block (e.g., …)”. Neither is implementable without a client author guessing, and guesses diverge. This is a table, and writing it costs an afternoon.
  2. Resolve BLOCKHASH at the boundary explicitly. Define blockhash(n) for a decrypted transaction executing in block n+1 with NUMBER == n. Any answer is fine; silence is not.
  3. Add the NUMBER/receipt.blockNumber divergence to Backwards Compatibility, with the log-reconciliation consequence spelled out, as EIP-1559 did for effectiveGasPrice.
  4. Note the staleness-guard interaction in Security Considerations. One sentence: decrypted transactions observe a timestamp one slot old, so time-bounded checks evaluate against a stale clock. Integrators can then decide whether they care.
  5. For EIP-8105: add that the builder of the inclusion block constructs the execution context after observing key releases, and state which fields that gives it meaningful influence over.

Part 2 — One user action, two blocks, two identities, and no defined way to render it

2.1 What actually lands on chain

Under LUCID, a single user swap produces two artifacts in two different blocks:

In block n — an ST ticket. Signed by the sender, consumes their execution nonce, pays the entire cost of the operation. No to, no value, no calldata. Read by any existing tool, it is an expensive no-op.

In block n+1 — the plaintext transaction. The thing that actually swaps. Per spec it must carry:

max_priority_fee_per_gas = 0, max_fee_per_gas = 0 (Specification, “Ciphertext envelope”)

and it may be signed by an entirely different account:

the revealed plaintext_tx may be signed by a different account than the ticket signer (Rationale, “Probabilistic front-running”, point 1)

The correspondence is positional and cryptographic rather than referential: commitments and decrypted transactions are order-preserving, and reveal_commitment is a hash_tree_root over a container built from the PT bytes plus the ticket’s from and nonce. A client following the spec can reconstruct it. What does not exist is a field to join on — nothing in either artifact points at the other by hash, which is what every explorer, subgraph, and SQL-backed pipeline in the ecosystem actually consumes.

Worth noting that EIP-8105 handles this better. Its decrypted transaction type carries envelope_signer as an explicit field, which at minimum names the paying account directly rather than requiring reconstruction. It is not a full bidirectional link, but it is a real one, and LUCID would be improved by adopting something equivalent.

2.2 What this breaks

  • Block explorers. Etherscan’s account view is built on “transactions where from = X.” A user who swaps via LUCID sees a fee-paying no-op under their own address, and the swap under an address they may not control. The hash their wallet showed them is not the hash of the transaction that did the work.
  • Gas accounting, everywhere. Any pipeline computing cost as gasUsed × effectiveGasPrice reports zero for the PT, because the fee fields are mandated to zero. Dune dashboards, receipt consumers, wallet fee displays, and cost attribution all undercount. The real cost sits on a ticket in the previous block.
  • Accounting and tax software. Cost basis attaches to the swap; the fee attaches to a different transaction, in a different block, from a different address.
  • Safe and other smart accounts. Safe’s transaction service matches on-chain activity to queued proposals via the executing address. A PT signed by a different account, with a different hash, in a later block than the fee-paying artifact, has no path through that logic.
  • Pre-flight simulation. A wallet’s “this will fail” check simulates against latest state. A PT executes ToB in n+1 after up to seven other decrypted commitments, against a state no wallet can construct at signing time. Combined with on-chain reverts — which LUCID’s own User welfare section concedes the public path cannot avoid — wallets lose the main defence they currently have against user-visible failure.

2.3 Why this is distinct from the economic adoption objection

dataalways argued wallets will decline LUCID because it worsens inclusion time, execution quality, and MEV refunds. Those are preference arguments — a wallet could weigh them and ship anyway, and Shutter’s “adoption comes slowly, then all at once” is a coherent reply.

Representation is not a preference. A wallet that wants to ship encrypted transactions still cannot render the result coherently, because the protocol defines no canonical view of what happened. That is a specification gap, and unlike the economic objection it is cheap to close.

2.4 The 4337 precedent

ERC-4337 introduced a strictly weaker version of the same split: a UserOperation with its own hash, bundled inside a transaction sent by a different party, fees paid via an EntryPoint rather than by the acting account. The result was years of ecosystem work — bespoke explorer support, a separate userOpHash lookup namespace, indexer special-casing, persistent user confusion about which hash to paste where. Much of it remains incomplete.

4337 was survivable because it was opt-in application-layer tooling. LUCID and 8105 are core EIPs: post-fork, every indexer, explorer, and accounting tool must handle this shape or silently produce wrong numbers. The lesson is that when the canonical representation is not specified up front, each tool invents its own and they disagree.

2.5 Proposed fixes

  1. Bidirectional linkage in the receipts. Put the corresponding ticket_hash in the PT’s receipt and the PT hash in the ticket’s receipt. Both are 32 bytes, both known at execution time, and this converts reconstruction into a one-hop lookup for every tool that exists today.
  2. Define what effectiveGasPrice reports on a PT receipt. 0 is accurate and useless. Either surface the ticket’s effective price or add an explicit feePayer field. Silence guarantees divergent client behaviour.
  3. Add a JSON-RPC method returning the joined view — ticket, PT, reveal status — so wallets and explorers are not each reimplementing RevealCommitmentPreimage.
  4. Write a real Backwards Compatibility section. LUCID’s current text — implementers “must add support for the new transaction types, commitment rules, and key-propagation flows, etc.” — is doing enormous work in one sentence. Enumerate what breaks for indexers, explorers, receipt consumers, and accounting tools.

Part 3 — EIP-8105’s Motivation makes a regulatory claim it does not support

3.1 The claim

It also aims to reduce regulatory risks of block builders and other protocol participants by temporarily blinding them. (Motivation)

This is a legal and policy assertion, in the Motivation of a core EIP, with no citation and no supporting argument. It is load-bearing: Motivation sections exist to justify why a change should happen, and this one will be read by client teams allocating implementation effort and by institutional participants deciding whether this path is usable.

I am not a lawyer and I take no position on what any regulator would conclude. That is precisely the objection — I cannot evaluate the claim, and the EIP gives me nothing to evaluate it with. Whether reduced visibility mitigates or aggravates exposure is jurisdiction-specific and contested; “we built a system we cannot inspect” is not self-evidently a defence, and in some regimes deliberate non-inspection is treated as aggravating. The EIP asserts the favourable reading as settled.

3.2 A related tension in the adjacent sentence

The goal is not to improve user privacy (e.g., transaction confidentiality) as transactions are publicly revealed eventually. (Motivation)

The disclaimer rests on everything being revealed eventually. But per Part 2, the payer/actor correspondence is not straightforwardly revealed — contents become public while the linkage requires reconstruction and appears nowhere as a field. That is closer to a privacy property than the disclaimer allows, and it also sits awkwardly with the word temporarily in the first claim: the builder is blind until key release, but the on-chain record stays unjoined indefinitely. Those are different durations merged into one word.

3.3 Proposed fixes

  1. Source the regulatory claim or narrow it. If there is analysis behind it, cite it. If not, a narrower version motivates the EIP just as well and needs no legal argument: blinding builders removes their ability to discriminate between transactions, which is a censorship-resistance property and is straightforwardly true.
  2. Say what “temporarily” means — blind until when, with respect to which observer.
  3. State whether durable payer/actor unlinkability is intended. If yes, it deserves analysis; if no, note it as a side effect. Worth adding that the receipt linkage proposed in §2.5 restores auditability without weakening MEV protection, since by reveal time everything it would expose is already public.

What both EIPs get right

Pinning the execution context to the commitment block is the correct instinct — Part 1 is an argument about documenting it, not about reversing it. LUCID’s tob_fee refund-on-reveal is the first serious attempt I have seen to price reveal optionality rather than assume it away, and §1.3 is a case where that pricing does real work: it makes an attack that is free today into one that costs money. 8105’s scheme-agnostic registry is the right call given that no current construction meets the requirements its own Motivation lists, and its envelope_signer field is a small piece of design that LUCID should copy. Two teams converging in public rather than lobbying against each other for a headliner slot is how competing core EIPs ought to behave.

Questions for the authors

To the LUCID authors: is there an intended normative list of header-derived values a decrypted transaction observes? Specifically — does NUMBER return n or n+1, and what is blockhash(n) for a transaction executing in n+1 with NUMBER == n? I could not determine either from the text, and both look like places two client teams could implement differently in good faith.

To the LUCID authors, separately: was the one-slot timestamp staleness weighed against time-bounded checks in deployed contracts, particularly oracle freshness guards? I am not claiming it is severe — only that it is invisible from the current text.

To the EIP-8105 authors: the inclusion-block context means the builder of block n+1 constructs the environment decrypted transactions execute in, after observing which keys were released. Which header fields does that give it meaningful influence over, and should it sit in Security Considerations alongside the existing key-provider pre-state discussion?

To the EIP-8105 authors, separately: what supports the Motivation’s claim about reduced regulatory risk? I ask genuinely — if there is analysis behind it, citing it would be valuable, since institutional readers will rely on it. If not, would you consider narrowing it to the censorship-resistance property?

To both teams: is there an intended canonical representation of a single encrypted-mempool user action for wallets, explorers, and indexers, or is that out of scope for the core EIP? If out of scope, which body specifies it, and on what timeline relative to the fork? The 4337 experience suggests leaving it to emergent convention costs years.

Continuing the discussion from Application-layer review of EIP-8105 & EIP-8184: header split, representation gap, and an unsourced Motivation claim:

Revision 2 (2026-07-30) — self-decryption requires wallet liveness that most wallets cannot provide

One new finding, staying in the application lane, plus a short note on how this review sits alongside the others now in this category.


§4 — LUCID’s trustless path requires the sender’s client to be live inside a sub-slot window

4.1 The claim being examined

LUCID’s headline differentiator is that a sender need not trust anyone:

The design remains open to different off-protocol decryption schemes, including trustless self-decryption, rather than coupling Ethereum to a single enshrined cryptographic construction. (Abstract)

Self-decryption is what makes the trustlessness claim more than aspirational — the sender generates k_dem, commits to keccak256(k_dem), and releases the key themselves. No key publisher, no trust graph, no reputational dependency. It is the cleanest answer to the Motivation’s concern about users being pushed into “opaque bilateral relationships.”

My finding is that this path carries an operational requirement the spec does not state, and that the requirement excludes most of the wallet population the Motivation is written about.

4.2 The requirement

The key must be released after the sender can confirm their commitment landed, and before the PTC’s key vote. From the timeline:

**After T2T_2 T2​ (Keys)** – Each key publisher observes the ST-commitments in the beacon block. It confirms correct data for its own ST-commitment […] and that the beacon block is attested to. It propagates the key(s) that reveal the STs. The key(s) are flooded P2P and observed by attesters of the next block before their deadline at T4T_4 T4​. (Specification, “Overview”)

So the sender must, within a window bounded by T2T_2 T2​ and T4T_4 T4​ inside slot n:

  1. Observe beacon block n
  2. Identify their own ST-commitment within it
  3. Verify gas_obligation and aggregate ToB gas are as expected
  4. Broadcast k_dem to a libp2p topic in time to propagate to the next slot’s PTC

The spec is explicit that wallets are the intended actor here. Its own RPC additions say so:

Update /eth/v2/events endpoint to notify wallets that their ST has been included, and it is safe to publish the key. Add /eth/v1/beacon/key endpoint to relay the key to the libp2p topic. (Specification, “Beacon Chain RPC API Changes”)

That is a wallet holding a live subscription to a beacon-node event stream, waking on an event, doing a verification step, and publishing — inside a few seconds, roughly twelve seconds after the user pressed Send.

4.3 Why most wallets cannot do this

This is a poor fit for how keys are actually custodied today:

  • Mobile wallets are subject to OS background-execution limits. iOS suspends an app shortly after backgrounding and does not guarantee wake-ups on a seconds-scale schedule. A user who submits and then switches apps — the overwhelmingly common behaviour — may not have a process alive at T2T_2 T2​.
  • Browser-extension wallets run service workers that idle out. Closing the tab, closing the browser, or sleeping the laptop ends the process. Users routinely submit and then close the tab, because today that is safe.
  • Hardware wallets are typically disconnected the moment signing completes. There is no persistent process at all.
  • Air-gapped and multisig flows are structurally incompatible: the signer that produced the ST may never be online again in that slot by design.

4.4 Why the failure mode is not benign

If the window is missed, the outcome is not “transaction delayed.” The ticket is charged in block n regardless of whether the key ever appears. The sender pays the base fee on the full declared gas_limit, the key_publication_fee, and forfeits tob_fee — and the transaction never executes.

So the retail failure mode is: submit a swap, close your laptop, pay for nothing. There is no in-protocol distinction between a malicious withholder and a user whose phone went to sleep; both are recorded identically. And because LUCID has no expiry_slot (a gap vellichorlabs also flags), the sender’s only recovery is to burn the nonce with a plaintext transaction — which is exactly the block-validity dilemma of their §1.1.

4.5 The consequence for the trustlessness claim

Self-decryption is therefore realistically available to always-on infrastructure: searchers, solvers, institutional desks, anyone already running a node or a keeper. It is not realistically available to a mobile or extension wallet without a persistent backend service.

But a wallet that runs a persistent backend to release keys on the user’s behalf is a key publisher, in everything but name. It sees the plaintext, it chooses whether to release, and the user depends on its liveness and honesty. That is the trusted intermediary the Motivation is written against, re-entering through an operational side door rather than a design decision.

I want to be careful about the strength of this claim. It does not mean the trustless path is fictional — it is real, and for the sophisticated senders LUCID’s Rationale spends most of its time on (autogenous MEV, self-decrypting arbitrageurs) the liveness requirement is trivially met, because those parties are already online. The finding is narrower: the trustless path and the retail user are largely disjoint populations, and the EIP presents self-decryption without noting that. Given that “wallets will adopt this” is load-bearing for both EIPs’ adoption story, and given dataalways’s scepticism on exactly that point in the Magicians thread, the gap seems worth stating.

4.6 Proposed fixes

  1. State the requirement in Security Considerations. Give the release window explicitly, in seconds relative to slot start, and say plainly that a self-decrypting sender must maintain a live process and event subscription for its duration. One paragraph. Wallet teams can then make an informed architectural choice instead of discovering this in testnet.
  2. Add an optional fallback publisher. Let the ST ticket name a fallback_key_publisher permitted to release k_dem only after a sub-deadline the sender did not meet. The sender would share k_dem with the fallback at submission time, accepting a weaker confidentiality guarantee against that one party in exchange for bounded downside. This keeps the fully trustless path intact for those who can use it, and gives everyone else a graceful degradation rather than a total loss. It is opt-in and requires no change to the key-release mechanism itself.
  3. Add expiry_slot — already requested by vellichorlabs for staleness reasons. It matters here too: it gives an offline sender a defined end state rather than an indefinitely floating charged ticket.
  4. Document that self-decryption is unsuitable for mobile and extension wallets absent a persistent backend, and that a wallet-operated release service is a key publisher for trust-model purposes. Better said in the EIP than discovered by users.

Note on how this review sits alongside the others

Five submissions now exist in this category and they are largely complementary rather than competing, so it seems useful to say where this one does and does not add.

Bonus (897) established that key-withholding is priced optionality and that a fixed penalty may not outprice a sufficiently valuable suppression. vellichorlabs (906) produced the spec-level audit — the key-vote overlap, the DEM nonce reuse, the executable fixpoint, the CR inversion — and is on a fourth revision. Pascaline (922) has gone further into formal territory, and their Finding 2 in particular — that EIP-8105 and EIP-8141 both hardcode transaction type 0x06 — is the kind of concrete, checkable defect that ought to be actioned immediately regardless of how this bounty resolves. Dee13 (919) addresses backrun monetization, including a treatment of the self-decrypting sender capturing their own backrun.

I should note two convergences honestly. Pascaline’s Finding 11 (envelope-signer/payload-signer trust versus 8105’s own privacy framing) reaches the same tension as my Part 3, arrived at independently and posted within minutes of it; and their application-category analysis overlaps the territory of my Part 2. Where I think this review still adds something distinct: the header/execution-block divergence and its consequences for deployed contracts and indexers (Part 1), the absence of a canonical representation for wallets and explorers (Part 2), and the liveness requirement above (§4). Those are application- and tooling-layer concerns, and they are cheap to close — mostly by writing down what is currently implicit.

The volume of substantive review this bounty has produced in three weeks is itself an argument for the mechanism. Several of these findings are things a client team would otherwise have hit during implementation.