Account Abstraction (ERC-4337) Signatures

ERC-4337 ("Account Abstraction without Ethereum Protocol Changes") introduces a parallel transaction lifecycle: users sign UserOperation structs rather than transactions; bundlers package them into handleOps calls to a singleton EntryPoint contract; smart-contract wallets (account abstraction wallets) validate and execute them. The validation step is where signature verification lives, and the rules are subtly different from EOA-signed transactions.

Pectra introduces native account abstraction via EIP-7702 (delegation from EOAs to contract code), which interacts with but does not replace 4337. Both schemes are live and audit-relevant in 2026.

The UserOperation Lifecycle

1. User signs UserOperation off-chain → submits to alt-mempool
2. Bundler picks up UserOperation
3. Bundler simulates: calls EntryPoint.simulateValidation
4. If valid: bundler includes in handleOps()
5. EntryPoint calls account.validateUserOp(userOp, userOpHash, missingAccountFunds)
6. EntryPoint calls account.execute() (via the userOp.callData)
7. If a paymaster is set: EntryPoint calls paymaster.validatePaymasterUserOp

Each handoff is a place where signature verification can go wrong.

validateUserOp — the Heart of 4337 Validation

The wallet's validateUserOp function is responsible for:

  • Verifying the signature on the userOpHash.
  • Optionally incrementing a nonce.
  • Returning a packed validationData that encodes (signature validity, time validity, aggregator address).
function validateUserOp(
    UserOperation calldata userOp,
    bytes32 userOpHash,
    uint256 missingAccountFunds
) external returns (uint256 validationData);

The userOpHash is computed by the EntryPoint from the entire UserOperation, including the chain ID and EntryPoint address — domain separation is structural. The wallet's responsibility is just to verify the signature against the right signer.

Standard Pitfalls

Pitfall 1: Signature Doesn't Cover the Full UserOp

The userOpHash covers the UserOperation's fields except the signature itself (the signature would be circular). A naïve implementation that re-hashes only some fields can let an attacker modify other fields (callData, gas limits, paymaster) without invalidating the signature.

Wallets should accept the EntryPoint-supplied userOpHash as the canonical hash and not re-derive it from selected fields.

Pitfall 2: Bundler-Settable Fields Treated as Signed

Some fields of the UserOperation are intentionally not covered by the signature (or are covered loosely) so the bundler can adjust them — gas limits, in particular, may be tuned by the bundler before submission. A wallet that strictly compares "submitted call" to "signed intent" can refuse valid UserOps that bundlers have legitimately tuned. Conversely, a wallet that doesn't validate critical parameters at execution time can be made to execute attacker-tuned operations.

The line between "user authorized this exact operation" and "user authorized this kind of operation, with bundler-tuned gas" is a design choice the wallet must make and document.

Pitfall 3: Missing Validation of userOp.sender

The EntryPoint passes the UserOperation to account.validateUserOp, where account == userOp.sender. The wallet should verify that it is the sender (or trust the EntryPoint to have routed correctly). Most implementations get this right because the EntryPoint is the only direct caller.

Pitfall 4: Replay Across EntryPoints

The userOpHash includes the EntryPoint address as part of the hash construction. A wallet that supports multiple EntryPoint versions (the spec has evolved across releases) must verify the calling EntryPoint matches what the user signed for. Otherwise a signature for one EntryPoint can be replayed via another.

Pitfall 5: Time-Range Validation Encoding

The validationData return value packs:

  • Bits 0..159: aggregator address (or 0 for no aggregator).
  • Bits 160..207: validUntil (48 bits).
  • Bits 208..255: validAfter (48 bits).

Misencoding any of these fields causes the EntryPoint to interpret the wallet's response incorrectly. Common bugs:

  • Forgetting to include validAfter / validUntil, leaving the bits at zero (which means "any time").
  • Confusing the aggregator-address slot with the validity-time slots.
  • Returning 1 for "invalid" when the spec expects a specific packing.

Use a reference implementation (eth-infinitism's BaseAccount) and modify only what's necessary.

Signature Aggregation

ERC-4337 supports signature aggregation: multiple UserOperations from different wallets can be verified by a single aggregator-contract call. This is primarily for BLS aggregation (each wallet's signature is a BLS share; the aggregator verifies the combined signature).

Audit considerations for aggregated UserOps:

  • The aggregator's validateSignatures must verify every UserOperation it claims to aggregate.
  • A wallet's validateUserOp returns its aggregator address; the EntryPoint must verify this matches the aggregator submitting the bundle.
  • Aggregator code is often relatively new and less tested; treat it as high-risk.

In practice, most 4337 deployments today do not use aggregation — they use per-UserOp ECDSA verification. Aggregation is the future state but uncommon in current audit scope.

Paymasters and Their Signatures

A paymaster sponsors gas for a UserOperation. The wallet's owner doesn't need ETH; the paymaster pays the EntryPoint and is reimbursed (or eats the cost) according to its own policy.

function validatePaymasterUserOp(
    UserOperation calldata userOp,
    bytes32 userOpHash,
    uint256 maxCost
) external returns (bytes memory context, uint256 validationData);

Common paymaster architectures:

  • Sponsorship paymaster: The paymaster operator signs an off-chain authorization that this specific UserOp is sponsored. The paymaster contract verifies the operator's signature over the userOpHash + sponsorship parameters.
  • Token paymaster: Accepts payment in ERC-20s instead of ETH. Internally swaps to ETH or holds the tokens.
  • Permit-based paymaster: Accepts an EIP-2612 / Permit2 signature in the paymaster data to draw tokens from the user.

Paymaster Signature Pitfalls

Sponsorship Signature Reuse

A sponsorship signature that doesn't include the specific UserOperation hash can be replayed across multiple UserOps. The signature should bind to:

  • The userOpHash (or equivalent commitment to the operation).
  • A validity window (validAfter, validUntil).
  • An (optional) nonce or one-shot identifier.

Cross-Paymaster Replay

A signature for paymaster A should not be valid for paymaster B. The paymaster's address must be in the signed message — usually by including it in the EIP-712 domain separator or as an explicit field.

Misencoded paymasterAndData

The first 20 bytes of paymasterAndData must be the paymaster contract's address; the rest is paymaster-specific. A wallet that ignores this layout, or a paymaster that doesn't validate its own address is at byte 0..20, can be tricked.

Token Paymaster Front-Running

A token paymaster that pulls tokens via Permit2 is exposed to the same front-running issues as any Permit2 integration. The signed permit grants spending authority; another party can submit the UserOp first, consuming the permit on a different paymaster's behalf if cross-paymaster replay isn't prevented.

EIP-7702: Set Code on EOA

EIP-7702 (Pectra) lets an EOA authorize a piece of contract code to execute on its behalf for the duration of a transaction. This is not the same as becoming a contract, but it lets EOAs adopt 4337-like behavior without migrating to a smart-contract wallet.

The authorization is a signed message — and like all signed authorizations, it has audit-relevant pitfalls:

  • Replay across chains if the chain ID is omitted from the signed authorization. EIP-7702 includes chain_id explicitly; correct implementations use it.
  • Authorization for "any" code is dangerous. The signed message specifies the exact code address; any signature that omits this is a critical bug.
  • Nonce consumption. Each authorization is one-shot; the nonce must be tracked correctly to prevent replay.
  • Interaction with existing contract storage at the EOA. EIP-7702 doesn't migrate state; if the EOA's "delegated" code reads/writes storage, it's reading the EOA's storage (which is normally zero).
  • Composing with 4337. An EOA delegated via 7702 to a 4337-compatible code address can act as a 4337 wallet. Audits of such systems must check both layers.

EIP-7702 is new (Pectra-era). Best practices are still settling; treat all 7702-using code as relatively high-risk and benefit from specialist review.

Audit Checklist for 4337 Wallets

For any account abstraction wallet under audit:

  • validateUserOp accepts the userOpHash from the EntryPoint and verifies the signature against the wallet's authorized signer(s).
  • Nonce handling: 4337 supports two-dimensional nonces (nonceKey, nonceValue); the wallet uses them correctly to support concurrent UserOps where appropriate.
  • Time validity packed correctly into validationData.
  • EntryPoint address is validated (either explicitly or by trusting only one EntryPoint).
  • EIP-1271 support for signature verification by other contracts.
  • No execution code that lets msg.sender (always the EntryPoint) be bypassed for sensitive operations.
  • Recovery / social-recovery mechanisms are themselves correctly signature-protected.
  • Upgrade paths (if the wallet is upgradeable) follow the rules in §4.12.

For any paymaster:

  • Sponsorship signatures (or equivalent) bind to the specific UserOp, paymaster, and time window.
  • Token-payment flows handle ERC-20 transfer failure gracefully (no silent over-charge).
  • The paymaster's deposit at the EntryPoint cannot be drained by malicious UserOps.
  • If using Permit2, the recipient validation from §4.14.4 is enforced.

For any EIP-7702-using contract:

  • Authorization signatures include chain ID and the correct code address.
  • Storage assumptions are explicit (EOA storage is zero; the delegated code shouldn't assume any existing state).
  • Interaction with 4337 EntryPoint, if any, is double-validated.

Account abstraction increases the attack surface in exchange for substantial UX wins. A 2026 audit that handles AA correctly is the difference between a wallet that gets adopted and one that gets drained.