EC4337: Account Abstraction¶
Account abstraction is the way to onboard the next billion users to Ethereum? Great, but how?
The problem is, ERC4337 is not straight forward.
Even for seasoned blockchain developers, its very hard to understand the implementation and its details.
There are a number of services that abstract the complexity away, like stackup and alchemy are offering easy-to-use javascript libraries. However, that is only partly helpful.
If you try to build something for a company, you need to understand the underlaying technology - there's money at risk and you need to know what that risk is.
This guide is going deep into the weeds of erc4337. It is not directed at Ethereum beginners, its definitely advanced - meant for people who are already solidity developers. If you want to get started with Ethereum, you don't need to make my own other tutorials, you could come from any other free or paid course. However, just doin the crypto zombies and then coming here will probably not be enough.
Let me walk you through what we're building:
First, we build a small chat application the normal way. For the contracts we will use a simple contract and test and deploy it using Foundry. For the frontend we'll be building something from scratch using NextJS, Tailwindcss, Rainbowkit with Wagmi and Viem. I intentionally do not want to use HardHat or web3js here, as foundry and is much faster than hardhat and viem is (in my personal opinion) much easier to use than web3js.
Interactive Tutorials¶
I will try to put most of the things into codesandbox mini containers embedded into this site. If you don't know what they are: It's like SSH'ing into docker container that is spun up just for you while visiting the page. If you don't know what that is, then you are probably not the right audience for this tutorial anyways.
So without further ado, lets dive into what we're building. Here is a Link to the codesandbox
The Result¶
The final application can be seen here: https://erc4337-chatter-nextjs-app.vercel.app/
The result will be a chat-like application. There will be 4 different functions here to chat:
- Through your normal wallet - you connect your wallet and when you send a transaction you pay for it.
- Through Stackup. When you send a transaction through an on-chain walletI will use their pay-as-you-go paymaster
- Through Candide with a naive Paymaster that simply pays for everything
- Through Alchemy Accountkit without any paymaster attached.
You can find that in the code sample above.
Code Changes
A lot of things changed since I recorded that video (yep - a week ago was that). There's a new abstractionkit version, a new viem version, a new wagmi version. The ecosystem is moving fast. So, below I am outlining the most prominent example using account abstraction.
What is Account Abstraction?¶
Alright, let's break it down simply. ERC-4337 is about making Ethereum accounts smarter and easier to use.
In Ethereum, you have two types of accounts:
- Externally Owned Accounts (EOAs) - These are your regular accounts controlled by private keys. You do something (like send Ether) by signing a transaction with your private key.
- Contract Accounts - These are controlled by code (smart contracts).
Now, EOAs are limited. You need the private key for every action, and if you lose it, you're out of luck. They can't do anything fancy on their own.
ERC-4337 lets you replace EOAs with smart contract accounts without needing to change the core of Ethereum.
Here's what that enables:
Smart Wallets: You can have a wallet where spending rules, multi-signatures, or daily limits are baked into the code.
Recovery: Lose your key? No problem. The smart contract can have rules to get back control, like verifying your identity in another way.
Sponsored Transactions: Normally, you pay gas for transactions with ETH. With ERC-4337, someone else could pay the gas for you. This is good for dApps wanting to offer free transactions to users.
Batch Transactions: One transaction could trigger multiple actions, saving on fees and making complex operations easier.
ERC-4337 does this through a special contract called "The Entrypoint." This Entrypoint acts like a traffic director. It routes transactions from these smart contract accounts and can handle the gas fees however the account's code specifies.
Let's check out how that works exactly by converting a normal web3 app into an erc4337 app...
Step 1 - The Contracts¶
Install Foundry¶
To get started with Foundry, its actually pretty simple. Fire up a console and follow along with this getting started guide
What I do first is download a bash script for the installation of foundry:
curl -L https://foundry.paradigm.xyz | bash
and then from there, its usually just foundryup
, but inbetween you might need to source your shell profile to get the path into the shell.
Codesandbox
In the codesandbox example above, foundry is already installed
Init Project¶
The next step is to initialize a new project. Since foundry installs a few tools (forge, cast, anvil, and chisel), we will use forge to init a new project:
forge init chatter-contracts
Then we can delete the existing files in the src, test and scripts folder:
rm src/* test/* scripts/*
Create Chatter.sol¶
Let's create our Smart Contract first. Create a new file and add this to src/Chatter.sol
:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Chatter {
event Message(address indexed sender, string message);
function sendMessage(string calldata message) public {
emit Message(msg.sender, message);
}
}
Let's also add a test in test/Chatter.t.sol
:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test, console2} from "forge-std/Test.sol";
import {Chatter} from "../src/Chatter.sol";
contract ChatterTest is Test {
Chatter public chat;
event Message(address indexed sender, string message);
function setUp() public {
chat = new Chatter();
}
function test_message() public {
vm.expectEmit(true, false, false, true);
emit Message(address(this), "hello 123");
chat.sendMessage("hello 123");
}
}
If you run forge test
on the terminal, it should execute the test. The test is setting up a new chat smart contract in the setUp()
function, and then with vm.expectEmit(...)
we can instruct the vm to assert an event is thrown. Which event? The one in the next line. Then we call chat.sendMessage() and it will hopefully emit this event and the test will pass.
Chatter solidity file
chatter test https://book.getfoundry.sh/forge/cheatcodes chatter script
Step 2: Rainbowallet and layout¶
https://www.rainbowkit.com/docs/installation npm install @rainbow-me/rainbowkit wagmi viem https://github.com/rainbow-me/rainbowkit/blob/main/examples/with-next-app/next.config.js
Jazzicon https://github.com/raugfer/jazzicon?tab=readme-ov-file
https://docs.stackup.sh/docs/account-abstraction
https://github.com/stackup-wallet/account-abstraction/blob/develop/contracts/samples/SimpleAccount.sol#L100
https://github.com/stackup-wallet/userop.js/blob/main/src/preset/middleware/signature.ts
https://mumbai.polygonscan.com/address/0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789#code
https://safe.global
https://github.com/eth-infinitism/bundler-spec-tests/blob/master/tests/single/bundle/test_bundle.py https://www.stackup.sh/blog/a-guide-to-the-top-erc-4337-bundlers-features-performance-and-more https://github.com/silius-rs/silius
https://www.alchemy.com/account-abstraction-infrastructure https://accountkit.alchemy.com/smart-accounts/accounts/guides/using-your-own.html https://docs.safe.global/safe-smart-account/supported-networks/v1.4.1
https://www.candide.dev https://docs.candide.dev/wallet/guides/send-gasless-tx/#step-5-get-paymaster-data https://github.com/candidelabs/abstractionkit/blob/safe/src/utils.ts https://blockpi.io/pricing https://docs.candide.dev/wallet/abstractionkit/safe-account/
https://hackmd.io/@stackup/H1oIvV-qi
Stackup¶
npm install userop