LayerZeroFault
ERC-4337 & Safe CODE: AA10 Reviewed & Verified:

ERC-4337 Error AA10: Sender Already Constructed UserOp Fix

Verified by Victor Vance • Lead Technical Analyst & Systems Researcher
Quick Diagnostic Answer (Executive Summary) GSC Snippet Verified

ERC-4337 error AA10 "sender already constructed" is thrown by the EntryPoint contract when a UserOperation contains a non-empty initCode field for an account address that has already been deployed on-chain. Counterfactual smart accounts only use initCode on their initial creation transaction. If your frontend or client SDK fails to clear initCode on subsequent operations, EntryPoint aborts validation immediately to prevent factory re-entrancy. Set initCode to "0x" to resolve.

# Root Cause & Technical Architecture

Under the ERC-4337 specification (both v0.6 and v0.7 EntryPoint architectures), `initCode` specifies the factory address and initialization calldata used to deploy a counterfactual smart contract wallet. During the validation phase, EntryPoint checks if `sender.code.length > 0`. If code is already present at the sender address and `initCode.length > 0`, the EntryPoint triggers `revert("AA10 sender already constructed")` to prevent execution replay and unintended factory re-initialization.

# 4-Step Actionable Diagnostic Protocol

Step-by-Step Resolution
01

Check On-Chain Account Code Length Before Construction

Query the target sender address using eth_getCode. If the returned bytecode is not "0x", the account is already deployed and initCode must be cleared.

02

Configure Client SDK to Clear InitCode Dynamically

In Biconomy, ZeroDev, or Permissionless.js, ensure your wallet client checks account deployment status so subsequent userOps set initCode to "0x".

03

Reset Local Account Deployment Cache in Bundler Client

If the account was deployed in a recent block, stale RPC or bundler cache can report it as undeployed. Force an RPC cache refresh on your node.

04

Verify Factory Deterministic Create2 Address

Confirm the calculated sender address matches the exact CREATE2 salt and initialization parameters used by your account factory contract.

# Diagnostic Verification Code / Command

typescript
clear-initcode-fix.ts
// Viem / Permissionless.js: Check deployment status
import { createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';

const client = createPublicClient({ chain: mainnet, transport: http() });

async function prepareUserOp(senderAddress: `0x${string}`, initCode: `0x${string}`) {
  const code = await client.getBytecode({ address: senderAddress });
  const isDeployed = code && code !== '0x';

  // If already deployed, initCode MUST be empty ("0x") to avoid AA10 revert
  const finalInitCode = isDeployed ? '0x' : initCode;
  return { sender: senderAddress, initCode: finalInitCode };
}
Zero-Secrets Client Sandbox

Launch ERC-4337 UserOp Decoder

Execute client-side cryptographic simulation and parameter checks safely in your browser via native WebCrypto without sending private keys or mnemonics over the wire.

Open Sandbox →
Comprehensive In-Depth Guide

Read Full Investigation & Recovery Workflow

Explore complete architectural diagrams, edge-case failure trees, and detailed post-mortem analysis in our verified deep guide.

Read Guide →

# Related Diagnostic Codes in ERC-4337 & Safe

Related Hex Statuses

# Frequently Asked Questions

FAQ Schema

What is the purpose of initCode in ERC-4337?

initCode allows smart accounts to be deployed counterfactually on their very first user transaction, eliminating the need for a separate setup transaction.

Why does EntryPoint reject initCode if the wallet is already deployed?

Allowing initCode on an already-deployed wallet could allow attackers to call malicious factory code or trigger unexpected state overwrites during validation.

Does AA10 cause users to lose gas fees?

When rejected during bundler local simulation, no gas is spent. If included on-chain by a faulty bundler, the bundler absorbs the verification penalty.