DEV Community

ohmygod
ohmygod

Posted on

Simulation-Execution Divergence: The Systemic Risk Threatening Every ERC-4337 Bundler

Account abstraction is eating Ethereum. With ERC-4337 adoption accelerating — over 30 million UserOperations processed in Q1 2026 alone — bundlers have quietly become critical infrastructure. They're the entities that simulate, batch, and submit UserOperations on behalf of users. And they have a systemic vulnerability that most teams are ignoring.

The problem is simulation-execution divergence: a class of attacks where a UserOperation passes off-chain simulation but fails (or behaves differently) when executed on-chain. The bundler pays gas either way. At scale, this is an existential threat to bundler economics.

How Bundlers Actually Work

Before we dig into the attack surface, let's establish the mental model:

  1. User constructs and signs a UserOperation off-chain
  2. Bundler receives it and runs local simulation via eth_call against the EntryPoint
  3. If simulation passes, bundler includes it in a bundle with other UserOps
  4. Bundler submits the bundle on-chain via handleOps()
  5. EntryPoint validates each UserOp, then executes them sequentially
  6. Bundler gets reimbursed from each account's prefunded deposit

The critical assumption: what passes in simulation will also pass on-chain. When that assumption breaks, the bundler bleeds money.

Attack Vector 1: State Manipulation Between Simulation and Execution

The most straightforward divergence attack exploits the time gap between simulation and on-chain inclusion:

1. Attacker submits valid UserOperation to bundler mempool
2. Bundler simulates → passes ✓
3. Attacker observes bundler's pending handleOps() transaction
4. Attacker front-runs with a transaction that changes relevant state
5. UserOperation now fails during on-chain execution
6. Bundler pays gas, gets no reimbursement
Enter fullscreen mode Exit fullscreen mode

This was demonstrated in practice by TrustSec's February 2026 disclosure through the Account Abstraction bug bounty program. The attack is particularly effective because:

  • ERC-4337 mempools are often public — UserOps can be observed before execution
  • Bundler transactions sit in the public Ethereum mempool — they can be front-run
  • The cost to the attacker is just the gas for the state-change transaction — typically a few dollars
  • The cost to the bundler scales with the gas limit of the failed UserOp — potentially hundreds of dollars per griefing attempt

The Asymmetry Problem

This creates a devastating economic asymmetry. If griefing a bundler costs the attacker $2 in gas but costs the bundler $50 in unreimbursed fees, a well-funded attacker can systematically bankrupt competing bundlers or simply cause chaos in the AA ecosystem.

Attack Vector 2: Non-Deterministic Validation Logic

ERC-4337 restricts what opcodes and storage slots can be accessed during validateUserOp() — specifically banning TIMESTAMP, BLOCKHASH, NUMBER, and similar environment-dependent opcodes. But enforcement happens at the bundler's simulation layer, not at the protocol level.

This means:

  • Custom validation modules (especially in ERC-7579 modular accounts) might introduce subtle non-determinism that passes simple opcode checks
  • Cross-contract reads during validation can return different values if the referenced contract's state changes between blocks
  • Paymaster validation adds another layer where state can diverge
// Looks deterministic, but isn't
function validateUserOp(
    UserOperation calldata op,
    bytes32 userOpHash,
    uint256 missingAccountFunds
) external returns (uint256) {
    // This oracle price could change between simulation and execution
    uint256 tokenPrice = IPriceOracle(oracle).getPrice(token);
    uint256 requiredTokens = missingAccountFunds * 1e18 / tokenPrice;
    require(IERC20(token).balanceOf(address(this)) >= requiredTokens);
    // ... signature verification ...
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

The oracle call is the subtle killer here. Between simulation and execution (even a single block later), the oracle price can shift enough to flip the require from pass to fail.

Attack Vector 3: The Paymaster Drain

Paymasters — contracts that sponsor gas on behalf of users — introduce a particularly nasty divergence scenario that Trail of Bits recently highlighted:

  1. Paymaster validates and agrees to sponsor the UserOp during validatePaymasterUserOp()
  2. The EntryPoint debits the paymaster's deposit during validation
  3. The UserOp executes and the paymaster's postOp() is called for bookkeeping
  4. If postOp() reverts, the payment from validation is NOT reversed

An attacker can exploit this by:

1. Create a UserOp that passes paymaster validation
2. During execution, manipulate state so postOp() will revert
   (e.g., withdraw tokens that postOp() expects to collect)
3. Paymaster pays gas but can't charge the user back
4. Repeat until paymaster deposit is drained
Enter fullscreen mode Exit fullscreen mode

This isn't theoretical — it's a structural weakness in how the EntryPoint handles the validation→execution→postOp lifecycle.

Attack Vector 4: Calldata Encoding Hash Collisions

NIOLabs' 2025 disclosure revealed that the EntryPoint's pack() function — which uses Yul assembly to copy calldata fields into memory for hashing — relies on .offset fields that can be manipulated with malformed calldata.

The impact:

  • Hash collisions: Two different UserOperations can produce the same userOpHash
  • Divergent hashes: The same UserOperation can produce different hashes depending on encoding

For bundlers, this means:

  • Mempool deduplication logic can be confused
  • Signature validation in simulation might pass with one hash while on-chain execution uses another
  • Bundle inclusion logic can be manipulated
// The vulnerable pack() pattern
function pack(UserOperation calldata userOp) internal pure returns (bytes memory) {
    // Uses userOp.callData.offset instead of properly decoding
    // Malformed calldata can make this copy different bytes than expected
    assembly {
        let ofs := calldataload(add(userOp, 0x40)) // callData offset
        let len := calldataload(add(calldataload(0x04), ofs))
        // ...
    }
}
Enter fullscreen mode Exit fullscreen mode

The fix is straightforward: use abi.encode() instead of abi.encodePacked() for variable-length arguments, and validate calldata offsets manually when using Yul.

The Compounding Risk: ERC-7702 Meets ERC-4337

ERC-7702 (SET_CODE_TX) allows EOAs to temporarily delegate to smart contract code. When combined with ERC-4337, this creates new divergence vectors:

  • An EOA can delegate to an AA-compatible implementation in one transaction, submit UserOps, then revoke delegation
  • The bundler simulates against the current delegation state, but delegation can change before execution
  • Initialization frontrunning becomes possible: an attacker who spots a 7702 delegation can frontrun the initialize() call

The interaction between these two standards is still poorly understood, and most bundler implementations don't adequately account for 7702 delegation changes.

Defensive Measures for Bundler Operators

1. Private Transaction Submission

Use Flashbots Protect, MEV Blocker, or similar private mempool services to submit handleOps() transactions. This eliminates the front-running vector entirely.

# Instead of public mempool submission
curl -X POST https://protect.flashbots.net \\
  -H 'Content-Type: application/json' \\
  -d '{\"jsonrpc\":\"2.0\",\"method\":\"eth_sendPrivateTransaction\",...}'
Enter fullscreen mode Exit fullscreen mode

2. Just-In-Time Simulation

Re-simulate the entire bundle immediately before submission, using the latest block state. This narrows the window for state manipulation:

simulate(bundle, latestBlock)  submit immediately if passes
Enter fullscreen mode Exit fullscreen mode

Some advanced bundlers are experimenting with simulation at the pending state (including mempool transactions) to further reduce divergence risk.

3. Reputation and Staking Systems

Track sender/paymaster reliability:

{
  "sender": "0xabc...",
  "successRate": 0.97,
  "failedOps": 3,
  "totalOps": 100,
  "status": "trusted"
}
Enter fullscreen mode Exit fullscreen mode

Throttle or ban entities whose UserOperations frequently fail on-chain after passing simulation. ERC-4337's staking mechanism provides economic backing — slash stakes for repeated failures.

4. Gas Limit Caps and Sanity Checks

  • Set maximum preVerificationGas relative to actual calldata size
  • Cap total gas per UserOp to limit worst-case loss
  • Reject UserOps with suspiciously high gas parameters even if they pass simulation

5. Paymaster Whitelisting

Only accept UserOps from paymasters that:

  • Have sufficient stake in the EntryPoint
  • Have a proven track record
  • Implement non-reverting postOp() patterns
  • Use per-user escrow rather than shared pools

The Bigger Picture

Simulation-execution divergence isn't just a bug — it's a fundamental tension in any system where off-chain prediction must match on-chain execution in a mutable-state environment. The EVM's shared state model means that any transaction between simulation and execution can potentially invalidate assumptions.

ERC-4337 attempted to mitigate this with strict validation rules (ERC-7562), but the reality is messier:

  • Not all bundlers enforce the rules equally
  • Modular account standards (ERC-7579) introduce validation complexity that rules can't fully anticipate
  • Cross-chain AA (with bridged UserOps) adds network-level divergence risks

As account abstraction moves toward native protocol integration (Pectra and beyond), the bundler layer needs the same security scrutiny we give to smart contracts. Today's bundlers are tomorrow's sequencers — and they deserve the same paranoia.


Key Takeaways:

  1. Bundler economics depend on simulation accuracy — divergence attacks break that assumption
  2. State manipulation, non-deterministic validation, paymaster drains, and calldata encoding flaws are four distinct attack vectors
  3. Private transaction submission and just-in-time simulation are the most effective immediate mitigations
  4. ERC-7702 + ERC-4337 interactions create poorly-understood new attack surface
  5. Bundler security deserves dedicated audit attention — not just the smart accounts they serve

References: TrustSec ERC-4337 griefing disclosure (Feb 2026), NIOLabs calldata encoding vulnerability (2025), Trail of Bits smart account audit patterns (Mar 2026), Ethereum Foundation ERC-4337 bug bounty program.

Top comments (0)