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:
- User constructs and signs a
UserOperationoff-chain - Bundler receives it and runs local simulation via
eth_callagainst theEntryPoint - If simulation passes, bundler includes it in a bundle with other UserOps
- Bundler submits the bundle on-chain via
handleOps() - EntryPoint validates each UserOp, then executes them sequentially
- 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
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;
}
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:
- Paymaster validates and agrees to sponsor the UserOp during
validatePaymasterUserOp() - The EntryPoint debits the paymaster's deposit during validation
- The UserOp executes and the paymaster's
postOp()is called for bookkeeping - 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
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))
// ...
}
}
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\",...}'
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
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"
}
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
preVerificationGasrelative 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:
- Bundler economics depend on simulation accuracy — divergence attacks break that assumption
- State manipulation, non-deterministic validation, paymaster drains, and calldata encoding flaws are four distinct attack vectors
- Private transaction submission and just-in-time simulation are the most effective immediate mitigations
- ERC-7702 + ERC-4337 interactions create poorly-understood new attack surface
- 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)