Aztec Wallet SDK Integration
Overview
Use this skill for wallet connectivity and wallet-provider implementation work with @aztec/wallet-sdk.
Primary scope:
- dApp wallet discovery with
WalletManager
- secure channel establishment (
PendingConnection, key exchange, verification hash)
- extension-specific provider usage (
ExtensionProvider, ExtensionWallet)
- extension relay handlers (
BackgroundConnectionHandler, ContentScriptConnectionHandler)
- encrypted message protocol and session lifecycle handling
- wallet implementation extension via
BaseWallet
Out of scope:
- Noir/Aztec.nr contract authoring (use
aztec-contracts)
- contract deployment workflows (use
aztec-deployment)
- broad Aztec.js app development beyond wallet-connection concerns (use
aztec-js)
Required Repository State
Use the upstream repository and pin:
- Repo:
https://github.com/AztecProtocol/aztec-packages
- Tag:
v4.2.0
- Commit:
f8c89cf4345df6c4ca9e66ea9b738e96070abc5a
- Source root:
yarn-project/wallet-sdk
Checkout example:
git clone https://github.com/AztecProtocol/aztec-packages.git
cd aztec-packages
git checkout v4.2.0
git status
Expected status includes HEAD detached at v4.2.0.
Operating Rules
- Follow the two-phase protocol strictly: discovery first, then key exchange.
- Do not call wallet methods until
PendingConnection.confirm() succeeds.
- Treat verification-hash comparison as mandatory before confirming a connection.
- Keep extension private keys and derived session keys in background context only.
- Keep content scripts as message relays only; no crypto or session-key state there.
- Handle disconnect control messages (
WalletMessageType.DISCONNECT) and cleanup all in-flight requests.
- Use explicit
chainInfo and appId on discovery and message flows.
- Respect allow/block list policies when exposing extension wallets.
- For custom wallet implementations, extend
BaseWallet and implement account lookup and capability behavior explicitly.
Quick Start
# Install core SDK deps (choose one package manager)
scripts/install_wallet_sdk_deps.sh npm
import { Fr } from '@aztec/aztec.js/fields';
import { WalletManager } from '@aztec/wallet-sdk/manager';
import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
const discovery = WalletManager.configure({
extensions: { enabled: true },
}).getAvailableWallets({
chainInfo: { chainId: new Fr(31337), version: new Fr(1) },
appId: 'my-dapp',
timeout: 60000,
});
for await (const provider of discovery.wallets) {
const pending = await provider.establishSecureChannel('my-dapp');
console.log('Verify:', hashToEmoji(pending.verificationHash));
const wallet = await pending.confirm();
const accounts = await wallet.getAccounts();
console.log(accounts);
}
Core Workflows
1. Discover Wallet Providers (dApp Side)
- Configure manager with
WalletManager.configure({ extensions: { enabled: true } }).
- Start discovery with
getAvailableWallets({ chainInfo, appId, timeout, onWalletDiscovered? }).
- Consume discovered providers using either:
- async iteration via
discovery.wallets
- callback via
onWalletDiscovered
- Cancel discovery on route/network changes via
discovery.cancel().
2. Establish and Verify Secure Channel
- Call
provider.establishSecureChannel(appId).
- Display
pending.verificationHash using hashToEmoji(...).
- Require user confirmation that wallet UI and dApp UI match.
- Call
pending.confirm() only after user confirmation.
- Call
pending.cancel() if verification fails.
3. Use Low-Level Extension Provider (When Needed)
- Use
ExtensionProvider.discoverWallets(chainInfo, options) when bypassing manager abstractions.
- Handle
DiscoveredWallet entries through onWalletDiscovered callback.
- Promote a discovered wallet to connected state with
discovered.establishSecureChannel().
- Enforce key exchange timeout handling and retry policy.
4. Implement Extension Wallet Transport
Background script:
- Instantiate
BackgroundConnectionHandler(config, transport, callbacks).
- Call
initialize() once.
- All
BackgroundConnectionCallbacks fields are optional:
onPendingDiscovery — queues approval UI for incoming discovery requests.
onSessionEstablished — notified when key exchange completes and a session is live.
onSessionTerminated — notified when a session ends (disconnect or tab close).
onWalletMessage — receives decrypted wallet calls; call sendResponse with result.
- On user approval/rejection, call
approveDiscovery(requestId) or rejectDiscovery(requestId).
- Forward decrypted wallet calls from
onWalletMessage to wallet backend.
- Return encrypted responses through
sendResponse(sessionId, walletResponse).
- Declare
handler with let before defining callbacks that reference it (avoids temporal deadzone).
Content script:
- Instantiate
ContentScriptConnectionHandler(transport).
- Call
start() once.
- Relay discovery, key exchange, encrypted messages, and disconnect notifications.
5. Encrypted Protocol and Message Types
- Discovery/control types use
WalletMessageType:
DISCOVERY
DISCOVERY_RESPONSE
KEY_EXCHANGE_REQUEST
KEY_EXCHANGE_RESPONSE
DISCONNECT
- Use
generateKeyPair, exportPublicKey, importPublicKey, deriveSessionKeys for key exchange.
- Use
encrypt/decrypt for all post-handshake payloads.
- Keep request correlation by
requestId/messageId and validate walletId on responses.
6. Build Custom Wallets with BaseWallet
- Extend
BaseWallet for wallet implementations backed by PXE + Aztec node.
- Implement:
getAccountFromAddress(address)
getAccounts()
- Use built-ins for common wallet operations:
simulateTx, profileTx, sendTx
registerContract, registerSender
createAuthWit, getPrivateEvents
getContractMetadata, getContractClassMetadata
- Override capabilities handling with
requestCapabilities(...) for external wallets.
BaseWallet.sendTx returns { receipt?, txHash?, offchainEffects, offchainMessages }. Custom Wallet / BaseWallet implementations must call extractOffchainOutput(provenTx.getOffchainEffects(), provenTx.publicInputs.constants.anchorBlockHeader.globalVariables.timestamp) (the anchor-block timestamp argument is mandatory in v4.2.0) and spread the result into the returned object — the OffchainMessage.anchorBlockTimestamp field added in this release is set from that argument.
7. Session Lifecycle and Disconnect Handling
- Register disconnect hooks via
provider.onDisconnect(callback) — returns an unsubscribe function.
- Check
provider.isDisconnected() to test current session state without polling.
- On disconnect, clear app state and stop using prior wallet handles.
- Call
provider.disconnect() on app shutdown or wallet switch.
- In background handler, terminate stale sessions with
terminateSession(...)/terminateForTab(...)/clearAll().
8. Test and Validation Flow
- Use wallet-sdk unit tests in
yarn-project/wallet-sdk/src/**/*.test.ts as behavior references.
- Validate mixed simulation path behavior and private-event decoding patterns from
base_wallet.test.ts.
- Add integration checks for:
- discovery timeout behavior
- key exchange timeout (
2s in DiscoveredWallet)
- disconnect propagation and in-flight rejection handling
Tooling / Commands
# preflight checks (node + package manager + optional node URL + optional source path)
scripts/preflight_wallet_sdk.sh [node-url] [wallet-sdk-dir]
# install wallet-sdk dependencies
scripts/install_wallet_sdk_deps.sh <npm|yarn|pnpm> [version]
# run a TS wallet-sdk example with tsx (tsx must be installed: npm install -D tsx)
scripts/run_wallet_sdk_example.sh <entry-file.ts> [-- <extra-args...>]
# summarize package entrypoints/exports from aztec-packages checkout
scripts/summarize_wallet_sdk_exports.sh <aztec-packages-dir>
# run wallet-sdk package tests from aztec-packages checkout
scripts/run_wallet_sdk_tests.sh <aztec-packages-dir> [test-name-pattern]
Edge Cases and Failure Handling
- Wallet discovery yields no providers:
check user approval path and discovery timeout (default
60000ms).
- Key exchange fails with timeout:
DiscoveredWallet.establishSecureChannel() enforces a 2000ms timeout; retry from approved discovery state.
- Verification hash mismatch:
cancel the pending connection and do not call
confirm().
- Wallet responses ignored unexpectedly:
validate
walletId, messageId, and per-session routing state.
- Calls fail after disconnect:
expected;
ExtensionWallet rejects in-flight and future calls once disconnected.
- Extension appears in discovery but not manager output:
check
allowList/blockList filtering in manager config.
PendingDiscovery.appName is always undefined at this pin:
DiscoveryRequest does not carry an appName field, so BackgroundConnectionHandler never populates it. Do not rely on it for UI labelling; use appId and origin instead.
Next Steps / Related Files
- Use
reference.md for pinned source corpus and API/file map.
- Use
patterns.md for reusable dApp and extension integration snippets.
- Use
scripts/ for repeatable setup, inspection, and test workflows.
1---2name: aztec-wallet-sdk3description: Use this skill when integrating Aztec wallet connectivity with @aztec/wallet-sdk, including discovery/session flows, secure-channel key exchange, extension handlers, encrypted messaging, and BaseWallet implementations.4license: Proprietary. LICENSE.txt has complete terms5---67# Aztec Wallet SDK Integration89## Overview1011Use this skill for wallet connectivity and wallet-provider implementation work with `@aztec/wallet-sdk`.1213Primary scope:1415- dApp wallet discovery with `WalletManager`16- secure channel establishment (`PendingConnection`, key exchange, verification hash)17- extension-specific provider usage (`ExtensionProvider`, `ExtensionWallet`)18- extension relay handlers (`BackgroundConnectionHandler`, `ContentScriptConnectionHandler`)19- encrypted message protocol and session lifecycle handling20- wallet implementation extension via `BaseWallet`2122Out of scope:2324- Noir/Aztec.nr contract authoring (use `aztec-contracts`)25- contract deployment workflows (use `aztec-deployment`)26- broad Aztec.js app development beyond wallet-connection concerns (use `aztec-js`)2728## Required Repository State2930Use the upstream repository and pin:3132- Repo: `https://github.com/AztecProtocol/aztec-packages`33- Tag: `v4.2.0`34- Commit: `f8c89cf4345df6c4ca9e66ea9b738e96070abc5a`35- Source root: `yarn-project/wallet-sdk`3637Checkout example:3839```bash40git clone https://github.com/AztecProtocol/aztec-packages.git41cd aztec-packages42git checkout v4.2.043git status44```4546Expected status includes `HEAD detached at v4.2.0`.4748## Operating Rules4950- Follow the two-phase protocol strictly: discovery first, then key exchange.51- Do not call wallet methods until `PendingConnection.confirm()` succeeds.52- Treat verification-hash comparison as mandatory before confirming a connection.53- Keep extension private keys and derived session keys in background context only.54- Keep content scripts as message relays only; no crypto or session-key state there.55- Handle disconnect control messages (`WalletMessageType.DISCONNECT`) and cleanup all in-flight requests.56- Use explicit `chainInfo` and `appId` on discovery and message flows.57- Respect allow/block list policies when exposing extension wallets.58- For custom wallet implementations, extend `BaseWallet` and implement account lookup and capability behavior explicitly.5960## Quick Start6162```bash63# Install core SDK deps (choose one package manager)64scripts/install_wallet_sdk_deps.sh npm65```6667```typescript68import { Fr } from '@aztec/aztec.js/fields';69import { WalletManager } from '@aztec/wallet-sdk/manager';70import { hashToEmoji } from '@aztec/wallet-sdk/crypto';7172const discovery = WalletManager.configure({73 extensions: { enabled: true },74}).getAvailableWallets({75 chainInfo: { chainId: new Fr(31337), version: new Fr(1) },76 appId: 'my-dapp',77 timeout: 60000,78});7980for await (const provider of discovery.wallets) {81 const pending = await provider.establishSecureChannel('my-dapp');82 console.log('Verify:', hashToEmoji(pending.verificationHash));83 const wallet = await pending.confirm();84 const accounts = await wallet.getAccounts();85 console.log(accounts);86}87```8889## Core Workflows9091### 1. Discover Wallet Providers (dApp Side)9293- Configure manager with `WalletManager.configure({ extensions: { enabled: true } })`.94- Start discovery with `getAvailableWallets({ chainInfo, appId, timeout, onWalletDiscovered? })`.95- Consume discovered providers using either:96- async iteration via `discovery.wallets`97- callback via `onWalletDiscovered`98- Cancel discovery on route/network changes via `discovery.cancel()`.99100### 2. Establish and Verify Secure Channel101102- Call `provider.establishSecureChannel(appId)`.103- Display `pending.verificationHash` using `hashToEmoji(...)`.104- Require user confirmation that wallet UI and dApp UI match.105- Call `pending.confirm()` only after user confirmation.106- Call `pending.cancel()` if verification fails.107108### 3. Use Low-Level Extension Provider (When Needed)109110- Use `ExtensionProvider.discoverWallets(chainInfo, options)` when bypassing manager abstractions.111- Handle `DiscoveredWallet` entries through `onWalletDiscovered` callback.112- Promote a discovered wallet to connected state with `discovered.establishSecureChannel()`.113- Enforce key exchange timeout handling and retry policy.114115### 4. Implement Extension Wallet Transport116117Background script:118119- Instantiate `BackgroundConnectionHandler(config, transport, callbacks)`.120- Call `initialize()` once.121- All `BackgroundConnectionCallbacks` fields are optional:122 - `onPendingDiscovery` — queues approval UI for incoming discovery requests.123 - `onSessionEstablished` — notified when key exchange completes and a session is live.124 - `onSessionTerminated` — notified when a session ends (disconnect or tab close).125 - `onWalletMessage` — receives decrypted wallet calls; call `sendResponse` with result.126- On user approval/rejection, call `approveDiscovery(requestId)` or `rejectDiscovery(requestId)`.127- Forward decrypted wallet calls from `onWalletMessage` to wallet backend.128- Return encrypted responses through `sendResponse(sessionId, walletResponse)`.129- Declare `handler` with `let` before defining callbacks that reference it (avoids temporal deadzone).130131Content script:132133- Instantiate `ContentScriptConnectionHandler(transport)`.134- Call `start()` once.135- Relay discovery, key exchange, encrypted messages, and disconnect notifications.136137### 5. Encrypted Protocol and Message Types138139- Discovery/control types use `WalletMessageType`:140- `DISCOVERY`141- `DISCOVERY_RESPONSE`142- `KEY_EXCHANGE_REQUEST`143- `KEY_EXCHANGE_RESPONSE`144- `DISCONNECT`145- Use `generateKeyPair`, `exportPublicKey`, `importPublicKey`, `deriveSessionKeys` for key exchange.146- Use `encrypt`/`decrypt` for all post-handshake payloads.147- Keep request correlation by `requestId`/`messageId` and validate `walletId` on responses.148149### 6. Build Custom Wallets with `BaseWallet`150151- Extend `BaseWallet` for wallet implementations backed by PXE + Aztec node.152- Implement:153- `getAccountFromAddress(address)`154- `getAccounts()`155- Use built-ins for common wallet operations:156- `simulateTx`, `profileTx`, `sendTx`157- `registerContract`, `registerSender`158- `createAuthWit`, `getPrivateEvents`159- `getContractMetadata`, `getContractClassMetadata`160- Override capabilities handling with `requestCapabilities(...)` for external wallets.161- `BaseWallet.sendTx` returns `{ receipt?, txHash?, offchainEffects, offchainMessages }`. Custom `Wallet` / `BaseWallet` implementations must call `extractOffchainOutput(provenTx.getOffchainEffects(), provenTx.publicInputs.constants.anchorBlockHeader.globalVariables.timestamp)` (the anchor-block timestamp argument is mandatory in v4.2.0) and spread the result into the returned object — the `OffchainMessage.anchorBlockTimestamp` field added in this release is set from that argument.162163### 7. Session Lifecycle and Disconnect Handling164165- Register disconnect hooks via `provider.onDisconnect(callback)` — returns an unsubscribe function.166- Check `provider.isDisconnected()` to test current session state without polling.167- On disconnect, clear app state and stop using prior wallet handles.168- Call `provider.disconnect()` on app shutdown or wallet switch.169- In background handler, terminate stale sessions with `terminateSession(...)`/`terminateForTab(...)`/`clearAll()`.170171### 8. Test and Validation Flow172173- Use wallet-sdk unit tests in `yarn-project/wallet-sdk/src/**/*.test.ts` as behavior references.174- Validate mixed simulation path behavior and private-event decoding patterns from `base_wallet.test.ts`.175- Add integration checks for:176- discovery timeout behavior177- key exchange timeout (`2s` in `DiscoveredWallet`)178- disconnect propagation and in-flight rejection handling179180## Tooling / Commands181182```bash183# preflight checks (node + package manager + optional node URL + optional source path)184scripts/preflight_wallet_sdk.sh [node-url] [wallet-sdk-dir]185186# install wallet-sdk dependencies187scripts/install_wallet_sdk_deps.sh <npm|yarn|pnpm> [version]188189# run a TS wallet-sdk example with tsx (tsx must be installed: npm install -D tsx)190scripts/run_wallet_sdk_example.sh <entry-file.ts> [-- <extra-args...>]191192# summarize package entrypoints/exports from aztec-packages checkout193scripts/summarize_wallet_sdk_exports.sh <aztec-packages-dir>194195# run wallet-sdk package tests from aztec-packages checkout196scripts/run_wallet_sdk_tests.sh <aztec-packages-dir> [test-name-pattern]197```198199## Edge Cases and Failure Handling200201- Wallet discovery yields no providers:202check user approval path and discovery timeout (default `60000ms`).203- Key exchange fails with timeout:204`DiscoveredWallet.establishSecureChannel()` enforces a `2000ms` timeout; retry from approved discovery state.205- Verification hash mismatch:206cancel the pending connection and do not call `confirm()`.207- Wallet responses ignored unexpectedly:208validate `walletId`, `messageId`, and per-session routing state.209- Calls fail after disconnect:210expected; `ExtensionWallet` rejects in-flight and future calls once disconnected.211- Extension appears in discovery but not manager output:212check `allowList`/`blockList` filtering in manager config.213- `PendingDiscovery.appName` is always `undefined` at this pin:214`DiscoveryRequest` does not carry an `appName` field, so `BackgroundConnectionHandler` never populates it. Do not rely on it for UI labelling; use `appId` and `origin` instead.215216## Next Steps / Related Files217218- Use `reference.md` for pinned source corpus and API/file map.219- Use `patterns.md` for reusable dApp and extension integration snippets.220- Use `scripts/` for repeatable setup, inspection, and test workflows.