This document describes the validation rules that ERC-4337 Account Abstraction protocol participants must follow for user transactions represented as UserOperation structs, alongside the rationale for each rule. Block builders and standalone bundlers enforce these rules off-chain.
Motivation
With Account Abstraction, transaction validation, gas payment, and execution are handled by EVM code rather than hard-coded protocol rules.
This provides several benefits:
These capabilities are unavailable in the traditional Externally Owned Account (EOA) model.
However, preserving network decentralization requires one fundamental rule: once admitted to the mempool, a transaction must guarantee fee payment to prevent denial-of-service (DoS) attacks.
The EOA model implicitly follows this rule. A valid transaction cannot become invalid without the account paying fees. For instance, the account’s balance can only be reduced by a higher-paying transaction.
This property ensures network sustainability. An attack involving a massive influx of transactions is economically prohibitive, as costs escalate with network congestion. While legitimate users can delay operations to avoid high fees, an attacker must pay increasingly exorbitant amounts to maintain the congestion.
To replicate this incentive structure in Account Abstraction systems, we propose a set of transaction validation rules.
These rules only apply to the validation phase of Account Abstraction transactions, not their entire executed code path.
For the interfaces of these contract-based accounts, see ERC-4337.
This document uses the term “UserOperation” for a transaction created by a smart contract account, following ERC-4337 terminology.
Notably, many of these rules can also be applicable in any other Account Abstraction framework that uses EVM code to perform transaction validation in a public mempool, and that treats validation and execution as distinct components of a transaction.
Specification
Validation Rule Types
We define two types of validation rules: network-wide rules and local rules.
A violation of any validation rule by a UserOperation results in the UserOperation being dropped from the mempool and excluded from a bundle.
A network-wide rule is a rule whose violation by a UserOperation damages the reputation of the peer bundler that sent this UserOperation into the P2P mempool.
A peer bundler with critically low reputation is eventually marked as a malicious spammer peer.
A local rule is enforced according to each bundler’s own local state. Because this state may differ between bundlers, there is no need for consensus on local rule violations.
Thus, the bundler that sent the violating UserOperation does not suffer P2P reputation damage from its peers.
Local rules are explicitly designated in the Local Rules section; every other rule in this document is a network-wide rule.
Constants
Title
Value
Comment
MIN_UNSTAKE_DELAY
86400
1 day, which provides a sufficient withdrawal delay to prevent most Sybil attacks
MIN_STAKE_VALUE
Adjustable per chain value
Recommended to be a non-trivial but not excessive amount, roughly $1000 equivalent in native tokens, sufficient to deter most Sybil attacks without being prohibitive
SAME_SENDER_MEMPOOL_COUNT
4
Maximum number of UserOperations allowed in the mempool from a single sender
SAME_UNSTAKED_ENTITY_MEMPOOL_COUNT
10
Maximum number of UserOperations in the mempool that reference the same unstaked entity
THROTTLED_ENTITY_MEMPOOL_COUNT
4
Number of UserOperations with a throttled entity that can stay in the mempool
THROTTLED_ENTITY_LIVE_BLOCKS
10
Number of blocks a UserOperation with a throttled entity can stay in the mempool
THROTTLED_ENTITY_BUNDLE_COUNT
4
Number of UserOperations with a throttled entity that can be added in a single bundle
MIN_INCLUSION_RATE_DENOMINATOR
10
A denominator of a formula for entity reputation calculation
THROTTLING_SLACK
10
Part of a reputation formula that allows entities to legitimately reject some transactions without being throttled
BAN_SLACK
50
Part of a reputation formula that allows throttled entities to reject some transactions without being throttled
BAN_OPS_SEEN_PENALTY
10000
A value to put into the opsSeen counter of an entity to declare it banned
MAX_OPS_ALLOWED_UNSTAKED_ENTITY
10000
Upper bound on opsIncluded used when calculating opsAllowed for an unstaked entity
PRE_VERIFICATION_OVERHEAD_GAS
50000
Gas used by the EntryPoint per UserOp that cannot be tracked on-chain
MAX_VERIFICATION_GAS
500000
Maximum gas verification functions may use
MAX_USEROP_SIZE
8192
Maximum size of a single packed and ABI-encoded UserOperation in bytes
MAX_CONTEXT_SIZE
2048
Maximum size of a context byte array returned by a paymaster in a single UserOperation in bytes
MAX_BUNDLE_SIZE
262144
Maximum size of an ABI-encoded bundle call to the handleOps function in bytes
MAX_BUNDLE_CONTEXT_SIZE
65536
Maximum total size of all context byte arrays returned by all paymasters in all UserOperations in a bundle in bytes
VALIDATION_GAS_SLACK
4000
An amount of gas that must be added to the estimations of verificationGasLimit and paymasterVerificationGasLimit
Validation Rules
Definitions
Validation Phase: There are up to three on-chain frames during the validation phase:
sender deployment frame (once per account)
sender validation (required)
paymaster validation frame (optional)
Execution Phase: There are up to two on-chain frames during the execution phase:
sender execution frame (required)
paymaster post-transaction frame (optional)
The validation rules only apply during the validation phase. Once a UserOperation is validated, it is guaranteed to pay. There are no restrictions on execution, neither on the account’s callData nor on the paymaster’s postOp.
Entity: A contract that is explicitly specified by the UserOperation.
Entities include the factory, paymaster, aggregator, and staked account, as discussed in the Entity-specific Rules section below.
Each validation frame is attributed to a single entity.
Entity contracts must have code deployed on-chain.
Canonical Mempool: The rules defined in this document apply to the main mempool shared by all bundlers on the network.
Staked Entity: An entity that has a locked stake of at least MIN_STAKE_VALUE
and an unstake delay of at least MIN_UNSTAKE_DELAY.
Associated storage: A storage slot of any smart contract is considered to be “associated” with address A if:
The slot value is A
The slot value was calculated as keccak(A||x)+n, where x is a bytes32 value, and n is a value in the range 0..128
Using an address: Accessing the code of a given address in any way.
This can be done by executing *CALL or EXTCODE* opcodes for a given address.
Spammer: A P2P peer bundler that attempts a DoS attack on the mempool by sending other peers a large number of invalid UserOperations.
Bundlers MUST detect and disconnect from such peers, as described in the Mempool Validation Rules section.
Reputation Definitions
opsSeen: A per-entity counter of how many times a unique valid UserOperation referencing this entity
was received by this bundler.
This includes UserOperations received via incoming RPC calls or through a P2P mempool protocol.
opsIncluded: A per-entity counter of how many times a unique valid UserOperation referencing this entity
appeared in an actual included UserOperation.
Calculation of this value is based on UserOperationEvents and is only counted for UserOperations that were
previously counted as opsSeen by this bundler.
Refresh rate: Both of the above values are updated every hour as value = value * 23 // 24.
Effectively, the value is reduced to 1% after 4 days.
inclusionRate: Ratio of opsIncluded to opsSeen.
Reputation Calculation
We define a value max_seen = opsSeen // MIN_INCLUSION_RATE_DENOMINATOR.
The reputation state of each entity is determined as follows:
The reputation refresh rate limits a malicious paymaster to processing at most BAN_SLACK * MIN_INCLUSION_RATE_DENOMINATOR / 24 non-paying UserOperations per hour. This affects only the P2P network, not the blockchain.
Running the Validation Rules
A block builder or a bundler should perform a full validation once before accepting a UserOperation into its mempool, and again before including it in a bundle/block.
The bundler should trace the validation phase of the UserOperation and apply all the rules defined in this document.
A bundler should also perform a full validation of the entire bundle before submission.
The validation rules prevent an unstaked entity from altering its behavior between simulation and execution of the UserOperation.
However, a malicious staked entity can detect that it is running as part of a bundle validation and cause a revert. Thus, a third tracing simulation of the entire bundle should be performed before submission.
Any failed UserOperation must be dropped from the bundle.
The bundler should update the reputation of the staked entity that violated the rules, considering it THROTTLED/BANNED as described in the General Reputation Rules section below.
Mempool Validation Rules
A UserOperation is broadcast over the P2P protocol with the following information:
The UserOperation itself.
The blockhash this UserOperation was originally verified against.
Once a UserOperation is received from another bundler, it should be verified locally by the receiving bundler.
A received UserOperation may fail one of several static checks, such as an invalid format, values below the minimum, or an outdated blockhash.
In this case, the bundler should drop this particular UserOperation but keep the connection.
The bundler should check the UserOperation against the nonces of last-included bundles and silently drop UserOperations with a nonce that was recently included.
This invalidation is likely attributable to a network race condition and should not cause a reputation change.
If a received UserOperation fails against the current block:
Retry the validation against the block the UserOperation was originally verified against.
If it succeeds, silently drop the UserOperation and keep the connection.
If it fails, mark the sender as a “spammer”: disconnect from that peer and block it permanently.
Opcode Rules
Opcodes that access information outside of storage and code, referred to here as the “environment”, are blocked:
[OP-012]GAS (0x5A) opcode is allowed, but only if followed immediately by *CALL instructions, otherwise it is blocked.
This is a common way to pass all remaining gas to an external call, meaning the actual value is consumed from the stack immediately and cannot be accessed by any other opcode.
[OP-013] any “unassigned” opcode.
[OP-020] Revert on “out of gas” is forbidden as it can “leak” the gas limit or the current call stack depth.
Contract Creation
[OP-031]CREATE2 is allowed exactly once in the deployment frame and must deploy code for the “sender” address.
This can be done either by the factory itself, or by a utility contract it calls.
[OP-032] If there is a factory, even an unstaked one, the sender contract is allowed to use the CREATE opcode.
This applies only to the sender contract itself, not via a utility contract.
Access to an address without deployed code is forbidden:
[OP-041] For EXTCODE* and *CALL opcodes.
[OP-042] Exception: access to the “sender” address is allowed.
This is only possible in factory code during the deployment frame.
Allowed access to the EntryPoint address:
[OP-051] May call EXTCODESIZE ISZERO.
This pattern is used to check that the destination has code before the depositTo function is called.
[OP-052] May call depositTo(sender) with any value from either the sender or the factory.
[OP-053] May call the fallback function from the sender with any value.
[OP-054] Any other access to the EntryPoint, whether via *CALL or EXT* opcodes, is forbidden.
[OP-055] May call incrementNonce() from the sender.
*CALL opcodes:
[OP-061]CALL with value is forbidden. The only exception is a call to the EntryPoint described above.
[OP-062] Precompiles:
Only known, accepted precompiles on the network that do not access anything in the blockchain state or environment are allowed.
The core precompiles 0x1–0x11.
The P256VERIFY secp256r1 precompile defined in EIP-7951.
[OP-070] Transient Storage slots defined in EIP-1153 and accessed using TLOAD (0x5c) and TSTORE (0x5d) opcodes
are treated exactly like persistent storage accessed via SLOAD/SSTORE.
[OP-080]BALANCE (0x31) and SELFBALANCE (0x47) are allowed only from a staked entity, otherwise they are blocked.
Code Rules
[COD-010] Between the first and second validations, the EXTCODEHASH value of any visited address,
entity, or referenced library may not be changed.
If the code is modified, the UserOperation is considered invalid.
Storage Rules
Storage access using the SLOAD, SSTORE, TLOAD, and TSTORE instructions is limited within each phase as follows:
[STO-010] Access to the “account” storage is always allowed.
Access to associated storage of the account in an external contract that is not an entity is allowed if either:
[STO-021] The account already exists.
[STO-022] There is an initCode and the factory contract is staked.
If the paymaster or factory entity is staked, then it is also allowed:
[STO-031] Access the entity’s own storage.
[STO-032] Read/Write access to storage slots that are associated with the entity, in any non-entity contract.
[STO-033] Read-only access to any storage in a non-entity contract.
Local Rules
Local storage rules protect the bundler against denial of service at the time of bundling. They do not affect mempool propagation and cannot cause a bundler to be marked as a “spammer”.
[STO-040] A UserOperation may not use a factory, paymaster, or aggregator address that is used as an “account” in another UserOperation in the mempool.
This means that paymaster, factory, or aggregator contracts cannot practically be an “account” contract as well.
[STO-041] A UserOperation may not use associated storage of either its account or a staked entity, in a contract that is a “sender” of another UserOperation in the mempool.
General Reputation Rules
The following reputation rules apply to all staked entities and to unstaked paymasters. All rules apply to all of these entities unless specified otherwise.
[GREP-010] A BANNED address is not allowed into the mempool.
Also, all existing UserOperations referencing this address are removed from the mempool.
[GREP-020] A THROTTLED address is limited to:
THROTTLED_ENTITY_MEMPOOL_COUNT entries in the mempool.
THROTTLED_ENTITY_BUNDLE_COUNTUserOperations in a bundle.
THROTTLED_ENTITY_LIVE_BLOCKS blocks of residency in the mempool.
[GREP-030] REMOVED
[GREP-040] If an entity fails bundle creation after passing the second validation, its opsSeen is set to BAN_OPS_SEEN_PENALTY and its opsIncluded to zero, causing it to become BANNED.
[GREP-050] When a UserOperation is replaced by submitting a new UserOperation with higher gas fees, and this causes an entity, such as a paymaster, to be replaced as well, the removed entity’s opsSeen counter is decremented by 1.
Staked Entities Reputation Rules
[SREP-010] The “canonical mempool” defines an entity as staked if it has at least MIN_STAKE_VALUE and an unstake delay of at least MIN_UNSTAKE_DELAY.
[SREP-020] MOVED TO GREP-010
[SREP-030] MOVED TO GREP-020
[SREP-040] An OK staked entity faces no limit under the reputation rules:
Allowed in unlimited numbers in the mempool.
Allowed in unlimited numbers in a bundle.
[SREP-050] MOVED TO GREP-040
Entity-specific Rules
[EREP-010] For each paymaster, the bundler must track the total gas that UserOperations using this paymaster may consume.
The bundler should not accept a new UserOperation with a paymaster into the mempool if the maximum total gas cost of all UserOperations in the mempool, including this new UserOperation, is above the deposit of that paymaster at the current gas price.
[EREP-011] REMOVED
[EREP-015] A paymaster should not have its opsSeen incremented because of a failure of the factory or account.
The second validation runs before a UserOperation is included in a bundle. If, at that point, the UserOperation fails because of a factory or account error, whether a FailOp revert or a validation rule violation, the paymaster’s opsSeen value is decremented by 1.
[EREP-016] An aggregator should not have its opsSeen incremented because of a failure of a factory, an account, or a paymaster.
The second validation runs before a UserOperation is included in a bundle. If, at that point, the UserOperation fails because of a factory, an account, or a paymaster error, whether a FailOp revert or a validation rule violation, the aggregator’s opsSeen value is decremented by 1.
[EREP-020] If a staked factory is used, its reputation is updated accordingly when the account violates any of the validation rules.
That is, if validateUserOp() is rejected for any reason in a UserOperation that has an initCode, this is treated as if the factory caused the failure, and its reputation is affected accordingly.
[EREP-030] If a staked account is used, its reputation is updated based on failures of other entities, such as a paymaster or aggregator, even if they are staked.
[EREP-040] An aggregator must be staked, regardless of storage usage.
[EREP-050] An unstaked paymaster may not return a context.
[EREP-055] A context’s size may not change between validation and bundle creation.
If bundle creation reverts and a paymaster’s context size was modified, that paymaster
is BANNED, regardless of whether the UserOperation that reverted used that paymaster or not.
Staked Factory Creation Rules
[EREP-060] If the factory is staked, either the factory itself or the sender may use the CREATE2 and CREATE opcodes.
The sender is allowed to use CREATE with an unstaked factory as well, per OP-032.
[EREP-061] A staked factory may also use a utility contract that calls the CREATE opcode.
[EREP-070] During bundle creation, if a staked entity reduces its validation gas by more than 10%
compared to the second validation, that entity is throttled, even if the UserOperation itself did not revert, since this might affect the gas calculation defined by EIP-7623.
The UnstakedReputation of an entity determines the maximum number of entries using this entity allowed in the mempool.
opsAllowed is a reputation-based calculation for an unstaked entity, representing how many UserOperations it is allowed to have in the mempool.
Rules:
[UREP-010] An unstaked sender that is not throttled or banned is only allowed to have SAME_SENDER_MEMPOOL_COUNTUserOperations in the mempool.
[UREP-020] For an unstaked paymaster that is not throttled or banned:
opsAllowed = SAME_UNSTAKED_ENTITY_MEMPOOL_COUNT + inclusionRate * min(opsIncluded, MAX_OPS_ALLOWED_UNSTAKED_ENTITY).
This defaults to SAME_UNSTAKED_ENTITY_MEMPOOL_COUNT for a new entity.
[UREP-030] REMOVED
Alt-mempools Rules
An alternate mempool is an agreed-upon rule that bundlers may opt into, in addition to the canonical mempool.
The alt-mempool “topic” is a unique identifier. By convention, this is the IPFS hash of the document that describes the specifics of this alt mempool, written in clear text and a YAML file.
[ALT-010] The bundler listens to the alt-mempool “topic” over the P2P protocol.
[ALT-020] The alt-mempool rules MUST be checked only when a canonical rule is violated.
That is, if validation follows the canonical rules above, it is not considered part of an alt-mempool.
[ALT-021] Such a UserOperation, since it violates the canonical rules, is checked against all the alt-mempools, and is considered part of all those alt-mempools.
[ALT-030] Bundlers SHOULD forward UserOperations to other bundlers only once, regardless of how many alt-mempools they share.
The receiving bundler validates the UserOperations, and, based on the above rules and its subscribed alt-mempools, decides which alt-mempools to propagate them to.
[ALT-040]opsIncluded and opsSeen of entities are kept per alt-mempool. That is, an entity can be considered throttled or banned in one mempool, while still active on another.
Alt-mempool Reputation
Alt-mempools are served by the same bundlers participating in the canonical mempool, but change the rules and may introduce denial-of-service attack vectors. To prevent them from taking the canonical mempool or other alt-mempools down with them, a reputation is managed for each. An alt-mempool that causes too many invalidations gets throttled. This limits the scope of the attack and lets the bundler continue doing its work for other mempools.
[AREP-010] Each alt-mempool has opsSeen and opsIncluded, much like entities. The opsSeen is incremented after UserOperation initial validation, when it is considered part of this mempool.
The opsIncluded is incremented after this UserOperation is included on-chain, whether by this bundler or another.
[AREP-020] The alt-mempool becomes THROTTLED/BANNED based on the Reputation Calculation.
[AREP-030] REMOVED
Authorizations
[AUTH-010] A UserOperation may only contain a single EIP-7702 authorization tuple.
[AUTH-020] An account with EIP-7702 delegation can only be used as the Sender of the UserOperation.
Using the authorized account as any other kind of UserOperation entity is not allowed.
[AUTH-030] An account with EIP-7702 delegation can only be accessed using *CALL or EXTCODE* opcodes, and only if it is the Sender of the current UserOperation.
[AUTH-040] If there are multiple UserOperations by the same sender with an authorization tuple in the mempool, they all MUST have the same delegate address.
Limitations
The validation rules attempt to guarantee a degree of isolation between individual UserOperations’ validations.
In order to prevent hitting the memory expansion limitations that the Ethereum EVM imposes when creating a bundle, UserOperations must meet the following limitations:
[LIM-010] Maximum size of a single packed and ABI-encoded UserOperation in bytes MUST not exceed MAX_USEROP_SIZE.
[LIM-020] Maximum size of a context byte array returned by a paymaster in a single UserOperation in bytes MUST not exceed MAX_CONTEXT_SIZE.
[LIM-030] The verificationGasLimit and paymasterVerificationGasLimit parameters MUST exceed the actual usage during validation of the UserOperation by VALIDATION_GAS_SLACK.
[LIM-040] Maximum size of an ABI-encoded bundle call to the handleOps function in bytes SHOULD not exceed MAX_BUNDLE_SIZE.
[LIM-050] Maximum total size of all context byte arrays returned by all paymasters in all UserOperations in a bundle in bytes SHOULD not exceed MAX_BUNDLE_CONTEXT_SIZE.
[LIM-060] The verificationGasLimit and paymasterVerificationGasLimit parameters MUST each be lower than MAX_VERIFICATION_GAS.
[LIM-070] The preVerificationGas parameter MUST be high enough to cover the calldata gas cost of serializing the UserOperation, plus PRE_VERIFICATION_OVERHEAD_GAS.
Rationale
All transactions initiated by EOAs have an implicit validation phase where balance, nonce, and signature are checked against the current state of the Ethereum blockchain.
Once a node has validated the transaction, only another transaction by the same EOA can modify the Ethereum state in a way that invalidates the first transaction.
With Account Abstraction, however, validation can also include arbitrary EVM code and rely on storage, which means that unrelated UserOperations or transactions may invalidate each other.
If not addressed, this would make maintaining a mempool of valid UserOperations and producing valid bundles computationally infeasible and susceptible to DoS attacks.
This document describes a set of validation rules that, if applied by a bundler before accepting a UserOperation into the mempool, prevent such attacks.
The high-level goal
The purpose of this specification is to define a consensus between nodes, whether bundlers or block builders, when processing incoming UserOperations from an external source.
This external source is either an end-user node submitting via the ERC-7769 RPC, or another node in the P2P network.
The protocol detects “spam” — large bursts of UserOperations that cannot be included on-chain and thus cannot pay fees.
The network is protected by throttling requests from such spammer nodes.
All network nodes must share the same definition of “spam”. If some nodes propagate UserOperations that others consider spam, the forgiving nodes risk being marked as spammers, potentially fracturing the network.
The processing flow of a UserOperation
First, a UserOperation is received, either via RPC or via the P2P protocol from another mempool node.
The node validates the UserOperation, adds it to its local mempool, and broadcasts it to its peers.
Finally, when building a block, a node collects UserOperations from the mempool, performs a second validation to ensure they remain valid as a bundle, and includes them in the next block.
The need for a second validation before submitting a block
A standard Ethereum transaction can be invalidated if replaced by another transaction with the same nonce. The replacement transaction must pay a higher gas price, satisfying the rule that mempool inclusion requires payment.
With contract-based accounts, a UserOperation’s validity may depend on mutable state. Other transactions can invalidate a previously valid UserOperation, necessitating a second validation before block inclusion.
Rationale for limiting opcodes
Validation occurs off-chain, before block creation. Certain opcodes access block-specific information that is not yet finalized.
Using these opcodes during validation enables scenarios where a UserOperation succeeds off-chain but consistently reverts on-chain, facilitating DoS attacks.
For example, require(block.number == 12345) might pass during mempool validation but fail when the transaction is eventually included in a later block.
Rationale for limiting storage access
Validation processes must not overlap, ensuring a single storage modification cannot invalidate a large number of mempool UserOperations. By restricting storage access to the account’s associated storage, bundlers can guarantee the inclusion of at least one UserOperation per account in a bundle.
A bundler MAY include multiple UserOperations of the same account in a bundle, but MUST first validate them together.
Rationale for requiring a stake
We want to allow globally-used contracts, such as paymasters, factories, and aggregators, to use storage not associated with the account, but still prevent them from spamming the mempool.
If a contract causes too many UserOperations to fail in their second validation after succeeding in their first, we can throttle its use in the mempool.
Requiring a stake prevents Sybil attacks by making it economically unviable to spawn numerous malicious paymasters to sustain a spam attack.
The validation rules allow nodes to detect and throttle contracts responsible for spam. The stake prevents the rapid recreation of these malicious entities. Because the stake serves only for off-chain detection, it is never slashed; however, the required lock-up period significantly increases the capital cost of an attack.
Definition of the mass invalidation attack
A series of actions constitutes a mass invalidation attack if a large number of UserOperations—having passed initial validation and propagated through the mempool—subsequently become invalid and ineligible for block inclusion.
There are three ways to execute such an attack:
Submitting UserOperations that pass initial validation but fail the second validation during bundle creation.
Submitting UserOperations that are valid in isolation but become invalid when bundled together.
Front-running valid UserOperations with an economically viable state change that invalidates them.
To prevent these attacks, the validation code is sandboxed. It is isolated from other UserOperations, external storage changes, and environmental information like the current block timestamp.
What is not considered a mass invalidation attack
A UserOperation that fails initial validation without entering the mempool is not considered an attack. Nodes are expected to implement standard security measures, throttling requests based on API keys, IP addresses, or P2P peer scoring, to prevent spam.
Furthermore, if invalidating NUserOperations costs an attacker N * X (where X is sufficiently large), the attack is not considered economically viable.
The minimum change to cause an invalidation is a storage modification, which costs 5,000 gas.
Assuming a node can process 2,000 invalid UserOperations per block, the cost of a DoS attack is 10,000,000 gas per block.
While this cost is already prohibitive, the rules defined in this document impose further measures to increase the cost of an attack.
Security Considerations
This document describes the security considerations that bundlers must take to protect themselves, and the entire mempool network,
from denial-of-service attacks.