A complete ERC-4337 Account Abstraction implementation that allows you to deploy multiple ERC-20 tokens in a single UserOperation. Built with Foundry for smart contracts and React + Vite for the frontend.
- Overview
- Architecture
- Prerequisites
- Installation
- Setup
- Usage
- How It Works
- Contract Addresses
- Troubleshooting
This project demonstrates ERC-4337 Account Abstraction by:
- β Creating a Smart Contract Wallet (SmartAccount) for each user
- β Deploying multiple ERC-20 tokens in a single transaction
- β Using ERC-1167 minimal proxies for gas efficiency
- β Manual UserOperation construction (no bundler SDKs)
- β Complete frontend UI with MetaMask integration
Key Features:
- Batch deploy multiple tokens in one go
- Gas-efficient token deployment using clones
- Tokens automatically owned by your SmartAccount
- Beautiful React frontend with real-time status updates
- Auto-switches to Sepolia testnet
-
EntryPoint (
0x0000000071727De22E5E9d8BAf0edAc6f37da032)- Official ERC-4337 EntryPoint (deployed by Ethereum Foundation)
- Validates and executes UserOperations
- Same address on all networks
-
SmartAccountFactory (
0x8838EA1d2188f63f9187573A77d4b0B31193086D)- Creates SmartAccount instances using CREATE2
- Predictable addresses (can calculate before deployment)
-
SmartAccount (created per user)
- Your smart contract wallet
- Validates signatures and executes calls
- Only EntryPoint can call
execute()
-
BatchTokenFactory (
0xa667A04fBe2FDFD3d16c14C60EC1C300e7190d85)- Deploys multiple ERC-20 tokens using ERC-1167 clones
- Gas-efficient batch deployment
-
MinimalERC20 (
0xaFd482eE83aD8b292b95529E6D375345d61575Db)- Cloneable ERC-20 implementation
- Used as template for all tokens
-
Frontend (React + Vite + Tailwind CSS)
- User interface for token deployment
- MetaMask integration
- UserOperation construction and signing
-
Smart Contracts (Foundry)
- All contract logic
- Tests and deployment scripts
Before you begin, ensure you have:
- Node.js v18 or higher
- Foundry (for Solidity compilation)
- MetaMask browser extension
- Sepolia ETH (for gas fees)
curl -L https://foundry.paradigm.xyz | bash
foundryupVerify installation:
forge --versiongit clone <your-repo-url>
cd 4337Backend (Root):
npm installFrontend:
cd frontend
npm install
cd ..Foundry Dependencies:
forge install foundry-rs/forge-stdCreate a .env file in the root directory:
# RPC Configuration
RPC_URL=https://rpc.sepolia.org
# Your wallet private key (with 0x prefix)
DEPLOYER_PRIVATE_KEY=0x...
SMART_ACCOUNT_OWNER_KEY=0x...
# EntryPoint (official ERC-4337 address)
ENTRYPOINT_ADDRESS=0x0000000071727De22E5E9d8BAf0edAc6f37da032
# Deposit Configuration
DEPOSIT_AMOUNT=0.1Deploy all contracts to Sepolia testnet:
forge script script/Deploy.s.sol --rpc-url $RPC_URL --broadcastThis deploys:
- MinimalERC20 implementation
- BatchTokenFactory
- SmartAccountFactory
Note: EntryPoint is already deployed (official ERC-4337 address).
After deployment, update deployments.json with the deployed addresses:
{
"entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"minimalERC20Implementation": "0x...",
"batchTokenFactory": "0x...",
"smartAccountFactory": "0x..."
}Also update frontend/src/utils/constants.ts with the same addresses.
cd frontend
npm run devThe frontend will start at http://localhost:5173
-
Open the Frontend
- Navigate to
http://localhost:5173 - The app will automatically prompt you to connect MetaMask
- Navigate to
-
Connect MetaMask
- Click "Connect MetaMask"
- Approve the connection
- The app will automatically switch to Sepolia testnet
-
Create SmartAccount (if needed)
- If you don't have a SmartAccount, click "Create Account"
- Approve the transaction in MetaMask
- Wait for confirmation (~15 seconds)
-
Fund Your SmartAccount
β οΈ IMPORTANT: Your SmartAccount needs ETH to pay for gas!- Send at least 0.02 ETH to your SmartAccount address
- Use MetaMask: Send β Paste SmartAccount address β Send 0.02 ETH
-
Add Tokens to Deploy
- Fill in token details:
- Name: e.g., "MyAwesomeToken"
- Symbol: e.g., "MAT"
- Initial Supply: e.g., "1000000"
- Click "Add Token"
- Repeat for multiple tokens
- Fill in token details:
-
Deploy Tokens
- Click "Deploy X Tokens via ERC-4337"
- You'll see two MetaMask popups:
- First: "Sign Message" (for UserOperation)
- Second: "Confirm Transaction" (to submit to EntryPoint)
- Approve both
- Wait for confirmation
- β Tokens deployed!
If you prefer command line:
# Create SmartAccount
npm run create-account
# Deploy tokens (requires scripts - currently removed)
# Use frontend instead!1. User clicks "Create Account"
β
2. Frontend calls SmartAccountFactory.createAccount()
β
3. SmartAccountFactory deploys SmartAccount using CREATE2
β
4. SmartAccount constructor sets:
- owner = Your wallet address
- entryPoint = Official EntryPoint
- nonce = 0
β
5. β
SmartAccount created!
Contract Calls:
YOUR WALLETβSmartAccountFactory.createAccount()SmartAccountFactoryβ CreatesSmartAccount(constructor)
1. User fills token form and clicks "Deploy Tokens"
β
2. Frontend prepares:
- Token parameters
- Encodes BatchTokenFactory call
- Encodes SmartAccount.execute() call
β
3. Frontend builds UserOperation:
- sender: SmartAccount address
- callData: execute(BatchTokenFactory, ...)
- signature: (empty, will add)
β
4. Frontend signs UserOperation:
- Hashes UserOperation
- Signs with YOUR wallet (MetaMask)
- Adds signature to UserOperation
β
5. Frontend submits to EntryPoint:
- entryPoint.handleOps([userOp], beneficiary)
- MetaMask popup for transaction approval
β
6. EntryPoint processes:
- Calls SmartAccount.validateUserOp() β
- Validates signature matches owner
- Calls SmartAccount.execute() β
β
7. SmartAccount executes:
- Calls BatchTokenFactory.batchDeployTokens()
β
8. BatchTokenFactory deploys tokens:
- Clones MinimalERC20 (ERC-1167)
- Initializes each clone
- Sets SmartAccount as owner
β
9. β
Tokens deployed!
Contract Calls (in order):
YOUR WALLETβEntryPoint.handleOps([userOp])EntryPointβSmartAccount.validateUserOp()(validates signature)EntryPointβSmartAccount.execute()(executes call)SmartAccountβBatchTokenFactory.batchDeployTokens()BatchTokenFactoryβ_clone()Γ 2 (creates token proxies)BatchTokenFactoryβMinimalERC20.initialize()Γ 2 (initializes tokens)
UserOperation:
- Special transaction format for ERC-4337
- Contains: sender (SmartAccount), callData, signature
- Goes through EntryPoint instead of directly to blockchain
EntryPoint:
- Security layer that validates all UserOperations
- Only EntryPoint can call
SmartAccount.execute() - Prevents unauthorized access
SmartAccount:
- Your smart contract wallet
- Validates signatures before executing
- Can execute arbitrary calls (like deploying tokens)
ERC-1167 Minimal Proxies:
- Gas-efficient way to deploy multiple contracts
- One implementation, many tiny proxies
- Each proxy has its own storage
All contracts are deployed on Sepolia. Update these in frontend/src/utils/constants.ts:
export const ADDRESSES = {
entryPoint: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', // Official
minimalERC20Implementation: '0xaFd482eE83aD8b292b95529E6D375345d61575Db',
batchTokenFactory: '0xa667A04fBe2FDFD3d16c14C60EC1C300e7190d85',
smartAccountFactory: '0x8838EA1d2188f63f9187573A77d4b0B31193086D',
};Verify on Etherscan:
- EntryPoint: https://sepolia.etherscan.io/address/0x0000000071727De22E5E9d8BAf0edAc6f37da032
- BatchTokenFactory: https://sepolia.etherscan.io/address/0xa667A04fBe2FDFD3d16c14C60EC1C300e7190d85
- SmartAccountFactory: https://sepolia.etherscan.io/address/0x8838EA1d2188f63f9187573A77d4b0B31193086D
"MetaMask is not installed"
- Install MetaMask browser extension
- Refresh the page
"Failed to switch network"
- Manually add Sepolia to MetaMask:
- Network Name: Sepolia
- RPC URL: https://rpc.sepolia.org
- Chain ID: 11155111
- Currency: ETH
"Bad address checksum"
- Update contract addresses in
frontend/src/utils/constants.ts - Make sure addresses match
deployments.json - Use checksummed addresses (with proper capitalization)
"Missing revert data" or "CALL_EXCEPTION"
β οΈ Your SmartAccount needs ETH!- Send at least 0.02 ETH to your SmartAccount address
- Check balance in the UI
- Try again after funding
"Transaction was rejected"
- You rejected the transaction in MetaMask
- Try again and approve the transaction
"Source not found"
forge install foundry-rs/forge-std"Failed parsing private key"
- Make sure private key has
0xprefix in.env - Example:
DEPLOYER_PRIVATE_KEY=0x1234...
"Insufficient funds"
- Make sure your wallet has Sepolia ETH
- Get testnet ETH from: https://sepoliafaucet.com/
"SmartAccount not found"
- Create SmartAccount first using the frontend
- Or use:
npm run create-account
"Nonce mismatch"
- Wait a few seconds and try again
- The nonce might have changed
4337/
βββ contracts/ # Smart contracts
β βββ account/
β β βββ SmartAccount.sol
β β βββ SmartAccountFactory.sol
β β βββ interfaces/
β βββ tokens/
β β βββ MinimalERC20.sol
β β βββ BatchTokenFactory.sol
β βββ erc4337/
β βββ IEntryPoint.sol
βββ script/ # Deployment scripts
β βββ Deploy.s.sol
βββ test/ # Foundry tests
β βββ SmartAccount.t.sol
β βββ BatchTokenFactory.t.sol
βββ frontend/ # React frontend
β βββ src/
β β βββ App.tsx # Main app
β β βββ components/ # UI components
β β βββ utils/ # Web3 utilities
β βββ package.json
βββ deployments.json # Deployed addresses
βββ foundry.toml # Foundry config
βββ .env # Environment variables
βββ README.md # This file
- ERC-4337 Specification: https://eips.ethereum.org/EIPS/eip-4337
- OpenZeppelin Account Abstraction: https://docs.openzeppelin.com/contracts/5.x/account-abstraction
- ERC-1167 Minimal Proxies: https://eips.ethereum.org/EIPS/eip-1167
- Foundry Documentation: https://book.getfoundry.sh/
MIT
This is experimental software for educational purposes. Always audit smart contracts before deploying with real funds. Use at your own risk.
Built with β€οΈ for the ERC-4337 ecosystem