Skip to content

TheBossMickael/erc4337

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

11 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ERC-4337 Account Abstraction β€” from scratch

The three ERC-4337 components β€” smart account, paymaster, and a minimal bundler β€” implemented from scratch (no SDK) to understand account abstraction end to end. V3 adds passkeys: tap a button, approve Face ID / Touch ID / Windows Hello, and a real gasless transaction happens on Sepolia β€” no wallet extension, no seed phrase, no gas. The account even deploys itself on first use (counterfactual factory + initCode).

Status: V3 β€” shipped, deployed + verified on Sepolia, and hosted live.

β–Ά Live demo

erc4337.onrender.com β€” open it, tap Increment the counter, and approve your device's biometric prompt. The first tap registers a passkey and prompts twice (register, then sign); later taps prompt once. You sign no Ethereum transaction, install no wallet, and pay no gas β€” that is the whole point of ERC-4337 account abstraction.

Requires a browser/device with a passkey authenticator (Face ID, Touch ID, Windows Hello, or a security key) and a secure (HTTPS) context. Works in Chrome, Edge, Firefox and Safari on desktop, and in Safari on iOS. On iPhone/iPad, use Safari β€” Apple restricts passkey access to Safari there, so third-party iOS browsers (Chrome, Firefox, Edge) return a permission error. Hosted on Render's free tier β€” the first visit after a while may take ~30-50s to wake the server.

What this is

A hands-on implementation of ERC-4337. Instead of an SDK (Pimlico, Alchemy, permissionless.js…), each piece is built by hand to understand the full machinery: how a smart account validates a UserOperation, how a paymaster sponsors gas, how a bundler turns UserOperations into a real handleOps transaction, and how a factory deploys the account on the fly.

The project evolves in milestones that keep the same architecture (a BaseAccount Template Method) and swap only the validation logic:

  • V1 β€” single-owner ECDSA account (auth by possession: you hold a key).
  • V2 β€” auth by knowledge: 3 secret questions derive the signing key in the browser; the account stores only the derived address.
  • V3 (current) β€” passkeys / WebAuthn (P-256): the device's authenticator signs; the account stores only the public key (x, y). Plus a CREATE2 factory for counterfactual deployment.

The EntryPoint is the only piece not written here β€” the canonical singleton deployed by the Ethereum Foundation (0x4337084d9e255ff0702461cf8895ce9e3b5ff108 on Sepolia).

V3 β€” Passkeys + counterfactual factory

Two additions, both proving the BaseAccount refactor holds β€” a new scheme touches only two hooks:

  1. PasskeyAccount β€” validated by a WebAuthn passkey (P-256 / secp256r1). The private key is generated and held by the authenticator (Secure Enclave / TPM / security key) and never leaves the device. On-chain verification uses OpenZeppelin's audited WebAuthn + P256 libraries (EIP-7951 precompile at 0x100, with a Solidity fallback).
  2. AccountFactory β€” CREATE2, idempotent. The account address is computed off-chain (factory.getAddress) before it exists, and the real deployment is bundled into the account's first UserOp via initCode. This removed V2's /deploy endpoint and its server-side deployer key: deployment is now paid out of the normal handleOps flow.
Browser (passkey signs userOpHash with Face ID / Touch ID / Windows Hello)
   β”‚  POST /rpc (eth_sendUserOperation) β€” first op carries initCode (factory + createAccount)
   β–Ό
Bundler ──simulate (eth_call), then handleOps tx──▢ EntryPoint
                                        β”œβ”€(first op)─▢ AccountFactory.createAccount (CREATE2 deploy)
                                        β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ά PasskeyAccount.validate()  (WebAuthn + P-256)
                                        └──────────▢ PasskeyAccount.execute() ──▢ Counter
                                        (gas sponsored by the Paymaster's deposit)

The user never signs an Ethereum transaction β€” the bundler does that (and pays), reimbursed by the Paymaster. Deep dive: docs/v3-passkeys-factory.md.

Contracts

A Template Method hierarchy isolates the one thing that changes between auth schemes:

BaseAccount (abstract)          validateUserOp / execute / prefund / deposit  +  _validateSignature (hook)
 β”œβ”€ SmartAccount (V1)           secp256k1 Β· _validateSignature = ECDSA recover == s_owner
 β”œβ”€ SecretQuestionAccount (V2)  secp256k1 Β· _validateSignature = ECDSA recover == s_signerAddress
 └─ PasskeyAccount (V3)         secp256r1 Β· _validateSignature = WebAuthn.verify(userOpHash, (x, y))

AccountFactory (V3)  CREATE2, idempotent β€” counterfactual deployment via initCode

Tech stack

  • Solidity + Foundry β€” contracts, tests, deployment
  • TypeScript + Node.js + viem β€” bundler + counterfactual UserOp flow
  • React + Vite β€” frontend (single fused passkey button)
  • OpenZeppelin β€” WebAuthn + P256 (V3), ECDSA (V1/V2), Create2
  • @noble/curves β€” off-chain DER/low-s parsing
  • WebAuthn (navigator.credentials) β€” passkey registration & signing
  • Sepolia testnet Β· Render hosting

Project structure

contracts/   Foundry β€” BaseAccount, SmartAccount, SecretQuestionAccount, PasskeyAccount, AccountFactory, Paymaster, Counter, tests
bundler/     Node.js β€” JSON-RPC bundler (/rpc) + serves the frontend build
frontend/    React + Vite β€” passkey wallet (single fused button) + WebAuthn library
docs/        Architecture & technical documentation
render.yaml  Single-service hosting (bundler serves the frontend)
Makefile     Common commands (build, test, deploy-v3, vector, bundler, front)

Getting started

Prerequisites: Foundry, Node.js β‰₯ 18.

git clone --recursive https://github.com/TheBossMickael/erc4337.git   # forge-std + OpenZeppelin are git submodules
cd erc4337

make install     # submodules + bundler & frontend dependencies
make build       # compile the contracts
make test        # Solidity unit tests (local EVM)
make test-fork SEPOLIA_RPC_URL="<your RPC>"   # integration test vs the real EntryPoint (+ P-256 precompile)

Testing

  • Solidity unit tests (local EVM, mocked EntryPoint): signature validation, access control, execution, paymaster, deposits β€” for SmartAccount, SecretQuestionAccount, PasskeyAccount, plus AccountFactory (idempotence, CREATE2 determinism).
  • Fork tests against the real EntryPoint v0.8 on a Sepolia fork (V1, V2, and V3's real P-256 precompile at 0x100) β€” the actual handleOps flow without spending sETH.
  • Locked WebAuthn vector cross-checked on both sides: the same P-256 assertion is asserted on-chain (WebAuthnVector.sol) and off-chain (webauthn.test.ts). Foundry has no P-256 signing cheatcode, so the vector is generated once by make vector and locked β€” if the format changes, both worlds break.
  • Frontend Vitest: SPKIβ†’(x,y), DERβ†’(r,s) with low-s normalization, and the naked-tuple signature encoding.

Deployed on Sepolia

V3 (current)

Contract Address (verified on Etherscan)
AccountFactory 0x28d7…Ac1a
Paymaster 0x93ba…d314
Counter 0x3921…b48d

PasskeyAccounts are counterfactual β€” each is created lazily inside its owner's first UserOp, so there is no single "demo account" address (one passkey = one account).

V2 β€” auth by knowledge

Contract Address (verified on Etherscan)
SecretQuestionAccount (demo) 0xE4c8…324C
Paymaster 0x73B4…C57D
Counter 0xb73D…5F1D

V1 β€” single-owner ECDSA

Contract Address (verified on Etherscan)
SmartAccount 0x6f12…Aa87
Paymaster 0x317E…9e2c
Counter 0x12dF…d81d

Hosting

Hosted live on Render as a single web service β€” erc4337.onrender.com. Config: render.yaml. WebAuthn requires HTTPS, which the Render URL provides.

Documentation

Recommended reading order β€” each builds on the previous one:

  1. Architecture β€” the full path of a transaction, who pays the gas
  2. Contracts β€” BaseAccount, the account hierarchy, signature convention
  3. Bundler β€” JSON-RPC server, simulation, userOpHash, serialization
  4. V2 β€” auth by knowledge β€” key derivation from answers
  5. V3 β€” passkeys + factory β€” WebAuthn/P-256, counterfactual deployment
  6. Limitations β€” deliberate simplifications (V1, V2 & V3)

Roadmap

  • V1 β€” ECDSA validation, gasless via paymaster, minimal bundler βœ…
  • V2 β€” auth by knowledge (secret questions), React frontend, deployed + hosted βœ…
  • V3 (current) β€” passkeys / WebAuthn (P-256) + counterfactual factory, deployed + hosted βœ…

License

MIT β€” see LICENSE.

About

β›“οΈπŸ” Account abstraction (ERC-4337) built from scratch for learning: custom smart wallet, gas-sponsoring paymaster, and a minimal TypeScript bundler.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors