EIP-draft: Transaction Event Manifest — a signed, protocol-enforced declaration of what a tx may emit

The problem

When a user signs a transaction today, they commit to calldata, not to outcomes. The wallet simulates and shows what it expects; nothing in the protocol binds execution to that expectation. A malicious frontend, a proxy upgrade, state drift, or a routing change between sign and inclusion can produce a different outcome, and the user has no recourse.

Existing mitigations (protocol-specific slippage params, wallet-side simulation, off-chain screening, ERC-7730 clear-signing) are all above the protocol and none generalize.

Why this can’t be solved above the protocol

I’ve been working on an intent-based state-transition framework — ERC TBD: Intent-Based State Transition — from the framework layer through to dapp contracts for months. I keep hitting the same wall:

In the EVM, there is no way to guarantee that a piece of code MUST run before the transaction ends. A RETURN anywhere in the call stack ends that frame, and there is no consensus-level hook that runs after the outermost call. Any verifier I place “at the end” can be skipped by routing the user’s call through a different path and returning before reaching it. The framework can require the verifier by convention, not by consensus.

The EL’s end-of-execution check is the only place where a piece of code is guaranteed to run regardless of how the call stack unwound. That’s the gap this idea fills.

The proposed primitive

A new transaction type carries a signed event manifest in an extended access_list: per address, a declaration of what log events the transaction must emit ([item, ...], with per-topic and per-data-word value predicates), may emit anything ([]), or must not emit (0x). The EVM checks the manifest during and at the end of execution; mismatch reverts the transaction.

Predicates admit exact / range / union / wildcard forms, so manifests can express slippage tolerances and recipient sets, not just point equality. The same primitive composes naturally with ERC-7683 (cross-chain outcome commitment), ERC-7730 (the rendered surface and the on-chain check are bit-for-bit the same artifact), ERC-4337, and EIP-7702.

As a side benefit, the global emitter whitelist is a strictly richer execution-graph hint than the legacy access list: every contract the transaction touches via a state-changing path shows up, so proposers running EIP-7928-style parallel execution get tighter conflict prediction for free.

Full draft (with grammar, matching rules, gas accounting, test cases, and rationale): EIPS/eip-draft_event_manifest.md

Questions I’d value feedback on

  1. Is enforcement-via-events the right shape? Events are ABI-typed and stable across implementation changes, which is why I chose them over storage post-state predicates. Are there outcomes you’d want to bind that events don’t expose?
  2. Greedy matching vs. bipartite. The draft uses greedy (single pass per LOG), at the cost of requiring wallets to order “specific before broad.” Acceptable, or argues for end-of-execution maximum matching?
  3. Dynamic-data events. Currently every data word must be declared (count derived from a simulation trace), making bytes/string events fragile to state drift. Worth a tail-wildcard form now, or defer?
  4. ERC-4337 bundler economics. For a UserOperation’s manifest to reach the EVM, the bundler has to submit the new tx type and pay intrinsic gas for the manifest bytes. Deal-breaker or workable?

The draft is a working document, not a PR to ethereum/EIPs yet — I’d like to converge on the core primitive here before formalizing.