5/5
## Getting Started: Setting Up Your Account Abstraction Project Welcome to this lesson on Ethereum Account Abstraction (AA). Our primary objective is to develop smart contract wallets, also known as AA wallets. This project will be a significant step in your Web3 learning journey, covering the creation of two fundamental AA wallets: one for Ethereum, adhering to ERC-4337 principles, and another for zkSync, leveraging its native account abstraction capabilities. To begin, we'll configure our development environment using VS Code and its integrated terminal. The first crucial step is to ensure your Foundry toolkit is current. Foundry is a popular suite of tools for Ethereum application development. Execute the following command in your terminal to update or install Foundry: ```bash foundryup ``` This command downloads and installs the latest versions of Foundry's core components: `forge` (for compiling, testing, and deploying contracts), `cast` (for interacting with EVM chains), `anvil` (a local testnet), and `chisel` (an advanced Solidity REPL). Once Foundry is up-to-date, we'll initialize a new project. Navigate to your desired directory and run: ```bash forge init ``` This command scaffolds a standard Foundry project, creating essential directories like `src` (for contract source code), `script` (for deployment and interaction scripts), `test` (for contract tests), and `lib` (for external dependencies). It also generates a `foundry.toml` configuration file. To ensure a clean slate, we'll remove the default example contracts provided by `forge init`. Delete the following files: * `src/Counter.sol` * `script/Counter.s.sol` * `test/Counter.t.sol` Next, let's organize our project structure to accommodate contracts for different chains. Within the `src` directory, create two new subdirectories: * `src/ethereum`: This folder will house our Ethereum ERC-4337 implementation. * `src/zksync`: This folder will be for the zkSync native AA implementation. ## Defining Our Account Abstraction Project Objectives With the basic project structure in place, let's articulate our goals in the `README.md` file. Clear the existing content of `README.md` and add the following objectives: 1. Create a basic Account Abstraction wallet on Ethereum. 2. Create a basic Account Abstraction wallet on zkSync. 3. Deploy these wallets and send a UserOperation (or a standard transaction through them, as applicable). It's important to clarify a key distinction in our approach for Ethereum versus zkSync. For the Ethereum ERC-4337 component, we will *not* be sending a full UserOperation through the alternative mempool (alt-mempool) via a Bundler. Instead, we will focus on deploying the smart contract wallet and then demonstrating how to initiate a standard Ethereum transaction *from* this wallet. This approach still showcases the core functionality of interacting *through* the AA wallet but simplifies the setup by omitting the complexities of Bundler and alt-mempool interactions on Layer 1. However, for the zkSync portion, we *will* send a transaction leveraging its native account abstraction flow. This flow functions conceptually similarly to the ERC-4337 alt-mempool mechanism, although zkSync's native AA implementation has its own unique characteristics compared to ERC-4337. ## Crafting a Minimal Ethereum Account Contract Let's begin developing our Ethereum smart contract wallet. Create a new file named `MinimalAccount.sol` inside the `src/ethereum/` directory. Add the initial Solidity boilerplate to this file: ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.24; // The flow for ERC-4337 typically involves an EntryPoint contract // calling into this account contract. contract MinimalAccount { } ``` We've specified Solidity version `0.8.24`. The comment hints at the interaction model with the ERC-4337 `EntryPoint` contract, which we'll explore next. ## Understanding the ERC-4337 Interaction Flow To correctly implement our `MinimalAccount.sol`, we must understand the ERC-4337 standard. This Ethereum Improvement Proposal (EIP) outlines the architecture for account abstraction without requiring consensus-layer changes. You can find the full specification at `eips.ethereum.org/EIPS/eip-4337`. The ERC-4337 flow can be visualized as follows: 1. **Off-Chain User Action:** A user signs data, which forms a `UserOperation`. This `UserOperation` is essentially a pseudo-transaction that describes the action the user wants their smart contract wallet to perform. 2. **Alternative Mempool (Bundlers):** The signed `UserOperation` is sent to specialized nodes called Bundlers. Bundlers are actors in the ERC-4337 ecosystem that listen for `UserOperations` on an alternative, off-chain mempool. They validate these operations (checking for correctness, potential profitability, etc.). 3. **On-Chain Execution via EntryPoint:** Bundlers package valid `UserOperations` into a standard Ethereum transaction and send it to a globally deployed singleton contract called the `EntryPoint.sol`. 4. **EntryPoint Contract Logic:** The `EntryPoint` contract is the central orchestrator. It first verifies the `UserOperation`. This verification step involves calling a specific function on the user's smart contract wallet (our `MinimalAccount.sol`). If verification succeeds, the `EntryPoint` then executes the `UserOperation`, again by calling a function on the user's smart contract wallet. The `EntryPoint` also handles interactions with Paymasters (contracts that can sponsor gas fees) and Signature Aggregators (for more efficient signature schemes). 5. **Smart Contract Wallet (Account):** Our `MinimalAccount.sol` is the user's smart contract wallet. The `EntryPoint` interacts directly with this contract for both validating the `UserOperation` (e.g., checking the signature) and executing the intended action (e.g., calling another contract). 6. **Blockchain Inclusion:** The entire interaction, initiated by the Bundler's transaction to the `EntryPoint`, is ultimately included in a block on the Ethereum blockchain. The `UserOperation` struct, defined by ERC-4337, is crucial. It bundles all necessary data for an off-chain pseudo-transaction, including fields like `sender` (the smart contract wallet's address), `nonce`, `callData` (the action to execute), various gas limits, `paymasterAndData`, and the `signature`. This is the data structure passed around in the alt-mempool. When the `EntryPoint` contract interacts with an account contract on-chain, it uses a packed version of this `UserOperation`. ## Implementing the Core ERC-4337 Account Interface The ERC-4337 EIP mandates that any smart contract wallet (account contract) must implement a specific interface, often referred to as `IAccount`. The most critical function within this interface is `validateUserOp`. The `validateUserOp` function has the following signature: ```solidity function validateUserOp( PackedUserOperation calldata userOp, // The packed UserOperation data bytes32 userOpHash, // A hash of the userOp, used as the basis for the signature uint256 missingAccountFunds // Funds needed for the operation if the account hasn't pre-deposited enough into the EntryPoint ) external returns (uint256 validationData); // Returns data indicating validity and optional time constraints ``` **Purpose of `validateUserOp`:** This function is invoked by the `EntryPoint` contract *before* any execution takes place. The account contract (our `MinimalAccount.sol`) must use this function to: 1. Verify the user's signature, which is part of the `userOp` struct, against the `userOpHash`. 2. Validate the nonce to prevent replay attacks. 3. Perform any other necessary checks (e.g., account active, not locked). If `validateUserOp` completes successfully (doesn't revert), the `EntryPoint` proceeds to execute the operation. If it reverts, the operation is rejected. The `validationData` return value can be used to encode more complex validation logic, such as specifying time windows during which the `UserOperation` is valid (particularly useful for Paymaster interactions). A return value of `0` typically indicates successful validation without time constraints. To avoid manually defining all ERC-4337 interfaces, structs, and helper functions, we'll leverage a widely used reference implementation. The `eth-infinitism/account-abstraction` GitHub repository (`https://github.com/eth-infinitism/account-abstraction`) provides robust, community-vetted contracts for ERC-4337. For stability and compatibility with this lesson, we will install a specific version, `v0.7.0`, of this library. Use Foundry to install it as a project dependency: ```bash forge install eth-infinitism/account-abstraction@v0.7.0 --no-commit ``` * `forge install`: The Foundry command to add dependencies. * `eth-infinitism/account-abstraction`: The GitHub repository path. * `@v0.7.0`: Specifies the release tag. * `--no-commit`: Instructs Foundry not to automatically create a Git commit for this dependency addition. This command will download the library into your `lib/account-abstraction/` directory. Now, let's update `MinimalAccount.sol` to implement the `IAccount` interface. First, import the `IAccount` interface and the `PackedUserOperation` struct from the installed library: ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.24; import {IAccount} from "lib/account-abstraction/contracts/interfaces/IAccount.sol"; import {PackedUserOperation} from "lib/account-abstraction/contracts/interfaces/PackedUserOperation.sol"; // The flow for ERC-4337 typically involves an EntryPoint contract // calling into this account contract. contract MinimalAccount is IAccount { } ``` By declaring `contract MinimalAccount is IAccount`, we tell the Solidity compiler that our `MinimalAccount` contract promises to implement all functions defined in the `IAccount` interface. The primary function we need to implement is `validateUserOp`. Let's add a stub for this function to satisfy the interface requirement for now. You can find the exact signature by navigating to `lib/account-abstraction/contracts/interfaces/IAccount.sol` in your editor. ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.24; import {IAccount} from "lib/account-abstraction/contracts/interfaces/IAccount.sol"; import {PackedUserOperation} from "lib/account-abstraction/contracts/interfaces/PackedUserOperation.sol"; // The flow for ERC-4337 typically involves an EntryPoint contract // calling into this account contract. contract MinimalAccount is IAccount { function validateUserOp( PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds ) external view override returns (uint256 validationData) { // TODO: Implement actual validation logic (signature, nonce) return 0; // Placeholder for successful validation } } ``` Note: The `PackedUserOperation` is a struct, not a contract, primarily used for data structuring. Its definition can be found in `lib/account-abstraction/contracts/interfaces/PackedUserOperation.sol`. We've added `override` because we are implementing a function from an interface and `view` as we are not modifying state yet (this might change). ## What's Next? With the foundational setup complete and the `validateUserOp` function stubbed out, our immediate next step is to implement the core logic within this function. This involves handling signature verification and nonce management, which are central to the security and functionality of our ERC-4337 smart contract wallet.
A foundational setup guide to Setting Up Your Account Abstraction Project - Initialize your Foundry project, structure directories for Ethereum ERC-4337 and zkSync AA wallets, and create your first `MinimalAccount.sol`. You'll install ERC-4337 dependencies and implement the `IAccount` interface by stubbing `validateUserOp`.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Advanced smart contract development
How to develop a stablecoin
How to develop a DeFi protocol
How to develop a DAO
Advanced smart contracts testing
Fuzz testing
Manual verification
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Web3 engineer, educator, and Cyfrin co-founder. Patrick's smart contract development and security courses have helped hundreds of thousands of engineers kickstarting their careers into web3.
Guest lecturers:
Last updated on June 6, 2025
Duration: 36min
Duration: 3h 06min
Duration: 5h 02min
Duration: 6h 02min
Duration: 2h 47min
Duration: 1h 23min
Duration: 4h 28min
Duration: 1h 19min
Duration: 1h 10min
Course Overview
About the course
Advanced smart contract development
How to develop a stablecoin
How to develop a DeFi protocol
How to develop a DAO
Advanced smart contracts testing
Fuzz testing
Manual verification
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Web3 engineer, educator, and Cyfrin co-founder. Patrick's smart contract development and security courses have helped hundreds of thousands of engineers kickstarting their careers into web3.
Guest lecturers:
Last updated on June 6, 2025