Building a Wallet Extension for Aztec
In this tutorial, you'll build a fully functional Chrome extension wallet that can:
- Create and store encrypted accounts
- Deploy account contracts using SponsoredFPC (no fee tokens needed)
- Connect to dApps using the Aztec wallet SDK protocol
- Approve and sign transactions with a popup UI
This is a standalone tutorial that complements the Webapp Tutorial. While the webapp tutorial uses an embedded wallet for simplicity, this tutorial shows how to build a real browser extension wallet like MetaMask or Rabby.
What You'll Learn
- Extension Architecture - Service workers, offscreen documents, and message passing
- Wallet SDK Protocol - Discovery, ECDH key exchange, and secure messaging
- PXE Integration - Running a Private eXecution Environment in a browser extension
- Account Management - Key derivation, encrypted storage, and Schnorr signatures
- Transaction Handling - Signing, proofs, and SponsoredFPC fee payment
- Approval UIs - React popups for connection and transaction approval
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ Content Script │
│ - Injected into every page │
│ - Relays messages between page and background │
└──────────────────────┬──────────────────────────────────────┘
│ chrome.runtime messages
┌──────────────────────▼──────────────────────────────────────┐
│ Service Worker (Background) │
│ - Handles wallet SDK protocol │
│ - Routes wallet method calls to offscreen document │
│ - Manages popup for user approvals │
└──────────────────────┬──────────────────────────────────────┘
│ chrome.runtime messages
┌──────────────────────▼──────────────────────────────────────┐
│ Offscreen Document │
│ - Runs PXE instance (long-lived, supports WASM) │
│ - Implements wallet methods with SponsoredFPC │
│ - Manages account creation and signing │
└──────────────────────────────────────────────────────────────┘
Why This Architecture?
Service workers in Manifest V3 have a 5-minute inactivity timeout and limited WASM support. Since the PXE needs persistent state and long-running proof generation, the extension uses an offscreen document that:
- Runs longer than service workers
- Supports IndexedDB for PXE storage
- Handles WASM-based proof generation
- Maintains state across requests
The service worker handles the lightweight protocol layer (discovery, key exchange) and routes heavier operations to the offscreen document.
Prerequisites
Before starting, you should be familiar with:
- TypeScript and React basics
- Chrome extension development (Manifest V3)
- The Aztec concepts (accounts, transactions, PXE)
You'll also need:
- Node.js 22+
- Chrome browser
- A local Aztec network running (
aztec start --local-network) - The webapp-tutorial project set up (for testing)
Project Structure
We'll build on the existing test-extension/ in the webapp tutorial:
test-extension/
├── manifest.json # Chrome extension manifest
├── popup/
│ ├── popup.html # Popup UI HTML
│ └── popup.css # Popup styles
├── src/
│ ├── background.ts # Service worker - protocol + routing
│ ├── content-script.ts # Page <-> background relay
│ ├── config.ts # Constants and configuration
│ ├── account-utils.ts # Shared account instantiation logic
│ ├── aztec-imports.ts # Lazy import caching for Aztec modules
│ ├── utils.ts # Chrome runtime helpers and utilities
│ ├── offscreen/
│ │ ├── offscreen.html # Offscreen document HTML
│ │ └── offscreen.ts # PXE host + OffscreenWallet (BaseWallet subclass)
│ ├── popup/
│ │ └── popup.tsx # React popup component
│ └── wallet/
│ ├── wallet-impl.ts # ExtensionWalletManager - secret generation and encrypted storage
│ └── storage.ts # Encrypted key storage with CryptoKey pattern
└── dist/ # Compiled output
Tutorial Sections
- Architecture - Understanding service worker limitations and offscreen documents
- Wallet Protocol - Implementing discovery, key exchange, and secure messaging
- PXE Integration - Running PXE in an extension and extending BaseWallet
- Account Management - Key derivation, encrypted storage, and SchnorrAccountContract
- Transaction Handling - The sendTx flow, proofs, and SponsoredFPC
- Approval UI - Building React popups for user confirmations
- Testing - Loading the extension and testing with the Pod Racing dApp
Quick Start
If you want to try the completed wallet before reading the tutorial:
git clone https://github.com/AztecProtocol/aztec-packages.git
cd aztec-packages
git checkout v5.0.0-rc.1
cd docs/examples/webapp-tutorial
./setup.sh
node esbuild.extension.mjs
Then load it in Chrome:
- Make sure your local Aztec network is running (
aztec start --local-network) - Open
chrome://extensions/ - Enable Developer mode (toggle in top-right corner)
- Click Load unpacked and select the
test-extensionfolder - Start the dApp with
yarn devand openhttp://localhost:5173 - Select "Browser Wallet" to connect
Key Differences from Embedded Wallet
| Feature | Embedded Wallet | Extension Wallet |
|---|---|---|
| Location | Runs in dApp's page | Runs in extension context |
| Storage | localStorage (accessible to dApp) | chrome.storage (isolated) |
| Security | Keys visible to dApp | Keys encrypted, never exposed |
| UX | No approval needed | Popup for approvals |
| Setup | Just import library | User installs extension |
| Network | Local development | Local network (or any network) |
Next Steps
Start with Architecture to understand why this multi-component design is needed, then work through each section to build your wallet.
- Webapp Tutorial - Build a dApp that connects to this wallet
- BaseWallet Source - The class you extend