--- name: account-abstraction description: "Help users implement ERC-4337 Account Abstraction with Nethereum — send UserOperations, use smart accounts, route contract calls through a bundler, enable gasless transactions, or work with AA in .NET/C#. Use this skill whenever the user mentions account abstraction, ERC-4337, UserOperations, smart accounts, bundler integration, gasless UX, or anything involving AA on EVM chains with Nethereum." user-invocable: true --- # Account Abstraction (ERC-4337) ERC-4337 replaces EOA transactions with **UserOperations** — signed intents executed by smart contract wallets through a Bundler and EntryPoint contract. Use Account Abstraction when you need: batched calls, gas sponsorship (paymasters), social recovery, session keys, or modular validation logic. ## When to Use This - User wants to send transactions from a **smart contract wallet** instead of an EOA - User needs **gasless transactions** (paymaster sponsors gas) - User wants to **batch multiple calls** into one atomic operation - User is building on ERC-4337 infrastructure - User mentions UserOperations, bundlers, EntryPoint, or smart accounts ## Packages ```bash dotnet add package Nethereum.Web3 dotnet add package Nethereum.AccountAbstraction dotnet add package Nethereum.AccountAbstraction.SimpleAccount # for SimpleAccount factory ``` ## The Simple Way: AAContractHandler Switch any existing typed contract service to route through Account Abstraction. Every call becomes a UserOperation automatically: ```csharp using Nethereum.AccountAbstraction; using Nethereum.JsonRpc.Client; using Nethereum.Signer; using Nethereum.Web3; using Nethereum.Web3.Accounts; var web3 = new Web3(new Account(privateKey), rpcUrl); var erc20Service = new StandardTokenService(web3, tokenAddress); // The signer key and bundler service - ChangeContractHandlerToAA takes an EthECKey and an // IAccountAbstractionBundlerService, not raw strings. var signerKey = new EthECKey(privateKey); var bundlerService = new AccountAbstractionBundlerService(new RpcClient(new Uri(bundlerUrl))); // One line: switch to AA erc20Service.ChangeContractHandlerToAA( accountAddress, // smart account address signerKey, // signer key bundlerService, // bundler service entryPointAddress // EntryPoint contract address ); // Use the service exactly as before — AA is transparent var receipt = await erc20Service.TransferRequestAndWaitForReceiptAsync( new TransferFunction { To = recipient, Value = amount }); // Check AA-specific fields Console.WriteLine($"UserOp Hash: {receipt.UserOpHash}"); Console.WriteLine($"UserOp Success: {receipt.UserOpSuccess}"); ``` ### With Factory for First-Time Deployment If the smart account hasn't been deployed yet: ```csharp erc20Service.ChangeContractHandlerToAA( accountAddress, signerKey, bundlerService, entryPointAddress, new FactoryConfig { FactoryAddress = factoryAddress, Owner = ownerAddress, Salt = 0 }); ``` ### Configuring Gas and Paymaster `ChangeContractHandlerToAA` returns the `AAContractHandler`, so gas and paymaster settings chain off it directly: ```csharp var handler = erc20Service.ChangeContractHandlerToAA( accountAddress, signerKey, bundlerService, entryPointAddress); // Configure gas, paymaster, etc. handler.WithPaymaster(paymasterAddress); handler.WithGasConfig(new AAGasConfig { CallGasMultiplier = 1.2m, VerificationGasMultiplier = 1.1m }); ``` ## Manual UserOperation Flow When you need full control over UserOperation construction: ```csharp using Nethereum.AccountAbstraction; using Nethereum.AccountAbstraction.SimpleAccount; using Nethereum.Signer; // 1. Build the inner call var executeFunction = new ExecuteFunction { Target = recipientAddress, Value = Web3.Convert.ToWei(0.01m), Data = new byte[0] }; // 2. Create UserOperation var userOp = new UserOperation { Sender = smartAccountAddress, CallData = executeFunction.GetCallData(), MaxFeePerGas = Web3.Convert.ToWei(30, Nethereum.Util.UnitConversion.EthUnit.Gwei), MaxPriorityFeePerGas = Web3.Convert.ToWei(1, Nethereum.Util.UnitConversion.EthUnit.Gwei) }; // 3. Estimate gas via bundler var bundlerClient = new Nethereum.JsonRpc.Client.RpcClient(new Uri(bundlerUrl)); var bundlerService = new AccountAbstractionBundlerService(bundlerClient); var gasEstimate = await bundlerService.EstimateUserOperationGas .SendRequestAsync(userOp, entryPointAddress); userOp.CallGasLimit = gasEstimate.CallGasLimit?.Value; userOp.VerificationGasLimit = gasEstimate.VerificationGasLimit?.Value; userOp.PreVerificationGas = gasEstimate.PreVerificationGas?.Value; // 4. Sign var entryPointService = new EntryPointService(web3, entryPointAddress); var key = new EthECKey(privateKey); var packedUserOp = await entryPointService.SignAndInitialiseUserOperationAsync(userOp, key); // 5. Send to bundler var userOpHash = await bundlerService.SendUserOperation .SendRequestAsync(userOp, entryPointAddress); ``` ## EntryPoint Versions ```csharp EntryPointAddresses.V08 // EIP-712 userOpHash, EIP-7702 support EntryPointAddresses.V09 // 0x433709009B8330FDa32311DF1C2AFA402eD8D009 (latest) // V06/V07 constants exist for reference but are rejected by the handler: // they use the legacy keccak userOpHash, not the EIP-712 hash this library signs. EntryPointAddresses.Latest // Alias for V09 — recommended ``` ## Key Types - **`UserOperation`** — UserOp fields (Sender, CallData as byte[], gas fields as BigInteger?) - **`AAContractHandler`** — routes contract service calls through AA automatically - **`AATransactionReceipt`** — extends TransactionReceipt with UserOpHash, UserOpSuccess, ActualGasCost, Paymaster, Sender - **`FactoryConfig`** — factory address + owner + salt for first-time account deployment - **`PaymasterConfig`** — paymaster address + optional data provider callback - **`AAGasConfig`** — gas buffers, multipliers, receipt polling config - **`AccountAbstractionBundlerService`** — constructor takes `IClient`, NOT a URL string - **`EntryPointService`** — hash and sign UserOperations - **`BatchCall`** — for batching multiple calls in one UserOperation ## Decision Guide | Scenario | Approach | |----------|----------| | Existing contract service + AA | `ChangeContractHandlerToAA` (simple path) | | Full control over UserOp fields | Manual UserOperation construction | | First-time account deployment | Add `FactoryConfig` to handler or set `InitCode` on UserOp | | Gasless transactions | Add `PaymasterConfig` via `handler.WithPaymaster()` | | Multiple calls in one tx | `handler.BatchExecuteAsync()` with `ToBatchCall()` | For full documentation, see: https://docs.nethereum.com/docs/account-abstraction/overview