This project presents a robust implementation of an Account Abstraction (EIP-4337) compatible smart wallet, developed using Solidity and Foundry. It enables enhanced user experiences on the Ethereum Virtual Machine (EVM) by abstracting away the complexities of traditional wallet management, supporting both direct owner-initiated transactions and bundler-driven UserOperation execution.
- EIP-4337 Compliance: Fully integrates with the ERC-4337 standard for secure and flexible account abstraction.
- Flexible Transaction Execution: Supports direct transaction execution by the owner and indirect execution via an
EntryPointcontract forUserOperations. - ECDSA Signature Validation: Verifies
UserOperationsignatures using industry-standard ECDSA cryptography for strong security. - Gas Fee Management: Includes logic for prefunding gas fees to the
EntryPointcontract, essential for sponsored transactions. - Multi-Network Configuration: Provides flexible deployment configurations for local development (Anvil), Ethereum Sepolia, and zkSync Sepolia testnets.
- Foundry Development Workflow: Leverages Foundry's powerful toolkit for streamlined smart contract development, testing, and deployment.
- Modular Design: Separates concerns into distinct contracts and scripts for clarity and maintainability.
The repository is organized to clearly delineate contracts, deployment scripts, and configuration:
.
βββ lib/
βββ script/
β βββ DeployAA_Account.s.sol # Script to deploy the AA_Contract and HelperConfig.
β βββ HelperConfig.s.sol # Manages network configurations and deploys EntryPoint mocks.
β βββ SendPackedUserOp.s.sol # Utility to generate and sign PackedUserOperations.
βββ src/
β βββ ethereum/
β β βββ AA_Contract.sol # The core EIP-4337 compatible smart account.
β βββ zksync/
β βββ ZkSmartWallet.sol # Placeholder for zkSync native account abstraction.
βββ foundry.toml # Foundry project configuration.
βββ remappings.txt # Solidity import remappings.
βββ ...other Foundry files (cache, broadcast)
Before you begin, ensure you have the following installed:
- Git: For cloning the repository.
# Check if Git is installed git --version - Foundry: A blazing-fast, portable, and modular toolkit for Ethereum application development written in Rust.
# Install Foundry curl -L https://foundry.paradigm.xyz | bash foundryup # If targeting zkSync Era, you might need specific Foundry ZKsync tools: # curl -L https://raw.githubusercontent.com/matter-labs/foundry-zksync/main/foundryup-zksync/foundryup-zksync -o foundryup-zksync # chmod +x foundryup-zksync # ./foundryup-zksync
- Clone the Repository:
git clone https://github.com/olujimiAdebakin/Account_Abstraction_contract.git cd Account_Abstraction_contract - Install Foundry Dependencies:
forge install
- Build Contracts:
forge build
To interact with public testnets like Sepolia, you need to set up environment variables.
Create a .env file in the root directory and populate it with:
PRIVATE_KEY: The private key of your Ethereum account (without0xprefix). This account will be used for deploying contracts and signing transactions. Example:PRIVATE_KEY=your_burner_wallet_private_key_hereETH_SEPOLIA_RPC_URL: Your RPC URL for the Ethereum Sepolia network (e.g., from Alchemy or Infura). Example:ETH_SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_ALCHEMY_API_KEYZKSYNC_SEPOLIA_RPC_URL: Your RPC URL for the zkSync Sepolia network. (Optional, if only targeting Ethereum) Example:ZKSYNC_SEPOLIA_RPC_URL=https://sepolia.era.zksync.dev
You can deploy the AA_Contract to your local Anvil instance or a testnet using Foundry scripts.
-
Deploy to Local Anvil: First, start an Anvil instance in a separate terminal:
anvil
Then, deploy the contract:
forge script script/DeployAA_Account.s.sol --rpc-url http://127.0.0.1:8545 --broadcast --ffi
This will deploy a mock
EntryPointand yourAA_Contractto your local Anvil chain. -
Deploy to Ethereum Sepolia: Ensure your
PRIVATE_KEYandETH_SEPOLIA_RPC_URLare set in your.envfile.source .env forge script script/DeployAA_Account.s.sol --rpc-url $ETH_SEPOLIA_RPC_URL --broadcast --verify -vvvv
The
--verifyflag attempts to verify the contract on Etherscan. You might need to set up Etherscan API keys.
Run unit and integration tests using forge test:
forge testTo run tests with detailed verbosity:
forge test -vvvvThe AA_Contract acts as a smart wallet that can be controlled by its owner or by an EntryPoint contract for EIP-4337 UserOperations.
This section details the primary interface of the AA_Contract that external entities (like dApps, bundlers, or the owner) interact with.
Interaction occurs directly with the deployed AA_Contract address on the blockchain.
Sepolia Deployment Example: 0x0780FbC5eb9BfA684154A8f0220aC59707256b41 (as observed in broadcast/DeployAA_Account.s.sol/11155111/run-latest.json)
Executes a low-level call from the smart account to a target address.
Request:
This function is typically called by the EntryPoint contract or the contract's owner.
function execute(address dest, uint256 value, bytes calldata functionData) external;dest:address- The address of the contract or EOA to call.value:uint256- The amount of native token (ETH) to send with the call, in wei.functionData:bytes- The encoded calldata for the function to be executed ondest.
Response:
Success implies the internal call completed without reverting. No explicit return value from the execute function itself, but state changes occur on dest.
Errors:
AA_Account_NotFromEntryPointOrOwner(): Ifmsg.senderis neither theEntryPointnor the contractowner.AA_Account__CallFailed(bytes reason): If the low-level call todestreverts, thereason(raw revert data) is included.
Validates an EIP-4337 UserOperation, checking its signature and handling prefunding. This function is called exclusively by the EntryPoint contract.
Request:
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds) external returns (uint256 validationData);userOp:PackedUserOperation calldata- The fullUserOperationstruct.sender:address- The address of the smart account.nonce:uint256- The nonce for the operation (for replay protection).initCode:bytes- Code to deploy the account if it's not yet deployed (empty for deployed accounts).callData:bytes- The data for the execution call (e.g.,executecall toAA_Contract).accountGasLimits:bytes32- PackedverificationGasLimitandcallGasLimit.preVerificationGas:uint128- Gas required forvalidateUserOpand transaction overhead.gasFees:bytes32- PackedmaxFeePerGasandmaxPriorityFeePerGas.paymasterAndData:bytes- Data for paymaster, if used.signature:bytes- Signature ofuserOpHashby the account owner.
userOpHash:bytes32- The unique hash of theUserOperationto be signed.missingAccountFunds:uint256- The amount of ETH required to prefund the operation to theEntryPoint.
Response:
returns (uint256 validationData)validationData:uint256- ReturnsSIG_VALIDATION_SUCCESS(0) if validation passes and prefunding is handled. Otherwise, specific error codes (e.g.,SIG_VALIDATION_FAILED(1)) are returned based on EIP-4337 specification.
Errors:
AA_Account_NotFromEntryPoint(): Ifmsg.senderis not theEntryPointcontract.- Implicit (handled by
EntryPoint): Signature mismatch, insufficient prefund (thoughEntryPointdeterminesmissingAccountFunds).
Retrieves the address of the EntryPoint contract configured for this smart account.
Request:
function getEntryPoint() external view returns (address);No parameters.
Response:
returns (address entryPointAddress)entryPointAddress:address- The address of theEntryPointcontract this account interacts with.
| Technology | Description | Link |
|---|---|---|
| Solidity | Smart contract programming language for Ethereum. | Solidity Lang |
| Foundry | Fast, portable, and modular toolkit for EVM dev. | [Foundry Docs](https://book.getfoundry.sh/ |
| EIP-4337 | Account Abstraction standard for smart accounts. | EIP-4337 |
| OpenZeppelin | Secure smart contract libraries. | [OpenZeppelin](https://openzeppelin.com/ |
| zkSync Era | Layer 2 scaling solution with native account abstraction. | zkSync Era |
Contributions are welcome! If you have suggestions for improvements or find a bug, please follow these steps:
- Fork the repository.
- Clone your forked repository.
- Create a new branch for your feature or bug fix:
git checkout -b feature/your-feature-nameorbugfix/fix-bug-name. - Make your changes and test them thoroughly.
- Commit your changes with clear, concise messages.
- Push your branch to your forked repository.
- Open a Pull Request against the
mainbranch of this repository, describing your changes in detail.
This project is licensed under the MIT License.
Adebakin Olujimi
- LinkedIn: YourLinkedInProfile
- Twitter: YourTwitterHandle