Understanding Account Abstraction (AA) and How Bundlers Work — A Deep Dive with Minimal Bundler
Ethereum has come a long way from its inception, and one of the most talked-about advancements in recent years is Account Abstraction (AA).
But what does AA actually do? How do Bundlers and Paymasters work? And most importantly, why does this change how transactions work?
In this post, we’ll break it all down with a real implementation: a minimal Bundler built using Biconomy’s AA infrastructure.
💡 The Problem with EOAs
Ethereum started with Externally Owned Accounts (EOAs) — wallets controlled by a private key.
But EOAs have some major limitations:
❌ Lose your key? Say goodbye to your funds.
❌ You always need ETH to pay for gas.
❌ No custom transaction rules, just simple to, value, and data fields.
Account Abstraction (AA) fixes these problems by allowing users to use smart contract wallets instead of EOAs.
🔥 What is Account Abstraction (AA)?
Instead of EOAs, AA introduces smart contract wallets, which can:
✅ Allow multi-signatures.
✅ Let you pay gas in any token (or have someone else pay it for you).
✅ Enable social recovery and custom authentication logic.
But to make this work, we need a new way to send transactions, and that’s where Bundlers and Paymasters come in.
🚀 How Transactions Work in Account Abstraction
A normal Ethereum transaction goes directly to the mempool, waiting to be picked up by miners or validators.
With AA, the flow is different:
The user creates a UserOperation.Sends it to a Bundler, NOT the mempool.The Bundler validates and submits it to the network.
But what exactly does a Bundler do?
🔄 What is a Bundler?
A Bundler is like a miner, but specifically for AA transactions.
It does the following:
Accepts UserOperations.Validates them by checking: 🔹 Signatures 🔹 Gas limits and balances 🔹 The validateUserOp() function in the smart contract wallet.Packs UserOperations into a regular Ethereum transaction and sends it to the blockchain.
Without a Bundler, AA wallets wouldn’t work, as normal transactions require an EOA sender.
But who pays for gas? That’s where Paymasters come in.
💰 What is a Paymaster?
A Paymaster is a smart contract that sponsors gas fees.
It allows:
🔹 Users to pay gas in any ERC-20 token.
🔹 DApps to sponsor transactions for better UX.
🔹 Gasless transactions where users don’t pay at all.
Without a Paymaster, AA wallets still need ETH for gas, which brings us back to the original problem.
🛠️ Building a Minimal Bundler (with Code)
To understand this process better, I built Minimal Bundler — a simple Bundler implementation that works with Biconomy’s AA infrastructure.
It does the following:
Accepts ERC-4337 UserOperations.Uses two different EOAs to send transactions (to avoid failures).Works on Sepolia testnet.
Key Code: Handling a UserOperation
Here’s how Minimal Bundler processes a UserOperation:
import {
createWalletClient,
createPublicClient,
encodeFunctionData,
http,
} from “viem”;
import { sepolia } from “viem/chains”;
import { privateKeyToAccount } from “viem/accounts”;
import ENTRY_POINT_ABI from “../../abis/EntryPointAbi.json”;
import { env } from “../../config/env”;const ENTRY_POINT_ADDRESS = “0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789”;const walletClient1 = createWalletClient({
chain: sepolia,
transport: http(env.RPC_URL),
account: privateKeyToAccount(`0x${env.PRIVATE_KEY_1}`),
});const walletClient2 = createWalletClient({
chain: sepolia,
transport: http(env.RPC_URL),
account: privateKeyToAccount(`0x${env.PRIVATE_KEY_2}`),
});const publicClient = createPublicClient({
chain: sepolia,
transport: http(env.RPC_URL),
});let pick = 0;function pickWalletClient() {
pick = 1 – pick; // flip 0 -> 1, 1 -> 0
return pick === 0 ? walletClient1 : walletClient2;
}export async function handleSingleUserOp(userOp: any) {
try {
const walletClient = pickWalletClient();
const [beneficiary] = await walletClient.getAddresses(); const data = encodeFunctionData({
abi: ENTRY_POINT_ABI,
functionName: “handleOps”,
args: [[userOp], beneficiary],
}); console.log(“🔹 Encoded handleOps calldata”); const txHash = await walletClient.sendTransaction({
to: ENTRY_POINT_ADDRESS,
data,
}); console.log(`Transaction sent: ${txHash}`); return { transactionHash: txHash };
} catch (err) {
console.error(“Error in handleSingleUserOp:”, err);
throw err;
}
}
This function:
1️⃣ Picks a wallet (to prevent stuck transactions).
2️⃣ Encodes a UserOperation for the EntryPoint contract.
3️⃣ Sends it as a transaction.
Check the Minimal Bundler repo to see the full implementation: GitHub
🛠️ Testing the Bundler
API Endpoint: eth_sendUserOperation
You can test by sending a UserOperation request:
{
“jsonrpc”: “2.0”,
“method”: “eth_sendUserOperation”,
“params”: [{
“sender”: “0x…”,
“nonce”: “0x…”,
“initCode”: “0x…”,
“callData”: “0x…”,
“signature”: “0x…”,
“paymasterAndData”: “0x”,
“maxFeePerGas”: “0x…”,
“maxPriorityFeePerGas”: “0x…”,
“verificationGasLimit”: “0x…”,
“callGasLimit”: “0x…”,
“preVerificationGas”: “0x…”
}, “0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789”],
“id”: 1
}
🎯 Conclusion
Account Abstraction is a game-changer for Ethereum UX. Bundlers and Paymasters make it possible to: ✔️ Remove the need for EOAs. ✔️ Allow gas payments in any token. ✔️ Enable more flexible authentication.
Want to see it in action? Try Minimal Bundler now! 🚀
References:
GitHub RepoBiconomyBiconomy SDK Examples
My Links:
Understanding Account Abstraction (AA) and How Bundlers Work — A Deep Dive with Minimal Bundler was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.