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_transactionsare 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.slotandblock.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:
NUMBERreturnsnwhile the transaction is in blockn+1. A contract that storesblock.numberrecords 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.TIMESTAMPreturnst_n, roughly 12 seconds stale. For Uniswap-styledeadlinechecks this is directionally harmless (stricter, not looser). For oracle staleness guards of the formrequire(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
blockNumberisn+1because that is where the receipt lives. Any block number written into the event payload isn. 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. BLOCKHASHbehaviour is undefined at the boundary. WithNUMBERreportingn, isblockhash(n)the completed blockn’s hash, or0per the usual “current block” rule? Blocknis 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
- 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. - Resolve
BLOCKHASHat the boundary explicitly. Defineblockhash(n)for a decrypted transaction executing in blockn+1withNUMBER == n. Any answer is fine; silence is not. - Add the
NUMBER/receipt.blockNumberdivergence to Backwards Compatibility, with the log-reconciliation consequence spelled out, as EIP-1559 did foreffectiveGasPrice. - 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.
- 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_txmay 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 × effectiveGasPricereports 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+1after 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
- Bidirectional linkage in the receipts. Put the corresponding
ticket_hashin 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. - Define what
effectiveGasPricereports on a PT receipt.0is accurate and useless. Either surface the ticket’s effective price or add an explicitfeePayerfield. Silence guarantees divergent client behaviour. - Add a JSON-RPC method returning the joined view — ticket, PT, reveal status — so wallets and explorers are not each reimplementing
RevealCommitmentPreimage. - 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
- 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.
- Say what “temporarily” means — blind until when, with respect to which observer.
- 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.