@aztec/wallet-sdk 0.0.1-commit.fce3e4f → 3.0.0-devnet.2-patch.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +240 -266
  2. package/dest/base-wallet/base_wallet.d.ts +4 -4
  3. package/dest/base-wallet/base_wallet.d.ts.map +1 -1
  4. package/dest/base-wallet/base_wallet.js +15 -6
  5. package/dest/crypto.d.ts +183 -0
  6. package/dest/crypto.d.ts.map +1 -0
  7. package/dest/crypto.js +300 -0
  8. package/dest/manager/index.d.ts +4 -3
  9. package/dest/manager/index.d.ts.map +1 -1
  10. package/dest/manager/index.js +2 -0
  11. package/dest/manager/types.d.ts +22 -1
  12. package/dest/manager/types.d.ts.map +1 -1
  13. package/dest/manager/wallet_manager.d.ts +1 -1
  14. package/dest/manager/wallet_manager.d.ts.map +1 -1
  15. package/dest/manager/wallet_manager.js +34 -15
  16. package/dest/providers/extension/extension_provider.d.ts +53 -7
  17. package/dest/providers/extension/extension_provider.d.ts.map +1 -1
  18. package/dest/providers/extension/extension_provider.js +81 -13
  19. package/dest/providers/extension/extension_wallet.d.ts +140 -8
  20. package/dest/providers/extension/extension_wallet.d.ts.map +1 -1
  21. package/dest/providers/extension/extension_wallet.js +268 -46
  22. package/dest/providers/extension/index.d.ts +6 -4
  23. package/dest/providers/extension/index.d.ts.map +1 -1
  24. package/dest/providers/extension/index.js +2 -0
  25. package/dest/types.d.ts +92 -0
  26. package/dest/types.d.ts.map +1 -0
  27. package/dest/types.js +10 -0
  28. package/package.json +12 -10
  29. package/src/base-wallet/base_wallet.ts +17 -11
  30. package/src/crypto.ts +375 -0
  31. package/src/manager/index.ts +4 -8
  32. package/src/manager/types.ts +22 -0
  33. package/src/manager/wallet_manager.ts +43 -16
  34. package/src/providers/extension/extension_provider.ts +112 -17
  35. package/src/providers/extension/extension_wallet.ts +310 -55
  36. package/src/providers/extension/index.ts +5 -3
  37. package/src/{providers/types.ts → types.ts} +33 -6
  38. package/dest/providers/types.d.ts +0 -67
  39. package/dest/providers/types.d.ts.map +0 -1
  40. package/dest/providers/types.js +0 -3
package/README.md CHANGED
@@ -4,66 +4,76 @@ This guide explains how to integrate your wallet with the Aztec Wallet SDK, enab
4
4
 
5
5
  ## Available Types
6
6
 
7
- All types and utilities needed for wallet integration are exported from `@aztec/wallet-sdk/manager`:
7
+ All types and utilities needed for wallet integration are exported from `@aztec/wallet-sdk/types`:
8
8
 
9
9
  ```typescript
10
10
  import type {
11
- ChainInfo,
12
11
  DiscoveryRequest,
13
12
  DiscoveryResponse,
14
13
  WalletInfo,
15
14
  WalletMessage,
16
15
  WalletResponse,
17
- } from '@aztec/wallet-sdk/manager';
18
- import { ChainInfoSchema, WalletSchema, jsonStringify } from '@aztec/wallet-sdk/manager';
16
+ } from '@aztec/wallet-sdk/types';
17
+ ```
18
+
19
+ Cryptographic utilities for secure channel establishment are exported from `@aztec/wallet-sdk/crypto`:
20
+
21
+ ```typescript
22
+ import type { EncryptedPayload, ExportedPublicKey } from '@aztec/wallet-sdk/crypto';
23
+ import {
24
+ decrypt,
25
+ deriveSharedKey,
26
+ encrypt,
27
+ exportPublicKey,
28
+ generateKeyPair,
29
+ hashSharedSecret,
30
+ hashToEmoji,
31
+ importPublicKey,
32
+ } from '@aztec/wallet-sdk/crypto';
19
33
  ```
20
34
 
21
35
  ## Overview
22
36
 
23
- The Wallet SDK uses a **request-based discovery** model:
37
+ The Wallet SDK uses a **unified discovery and connection** model with **end-to-end encryption**:
24
38
 
25
39
  1. **dApp requests wallets** for a specific chain/version via `WalletManager.getAvailableWallets({ chainInfo })`
26
- 2. **SDK broadcasts** a discovery message with chain information
27
- 3. **Your wallet responds** ONLY if it supports that specific network
28
- 4. **dApp receives** only compatible wallets
29
- 5. **dApp calls wallet methods** which your wallet handles and responds to
40
+ 2. **SDK broadcasts** a discovery message with chain information and the dApp's ECDH public key
41
+ 3. **Your wallet responds** with its ECDH public key and a MessagePort ONLY if it supports that network
42
+ 4. **Both parties derive** the same shared secret via ECDH key exchange
43
+ 5. **SDK receives** discovered wallets with secure channel already established (port + sharedKey)
44
+ 6. **All subsequent communication** is encrypted using AES-256-GCM over the private MessagePort
30
45
 
31
- ### Transport Mechanisms
46
+ ### Key Features
32
47
 
33
- This guide uses **browser extension wallets** as the primary example, which communicate via `window.postMessage`. However, the same message protocol can be used with other transport mechanisms:
48
+ - **No separate connection step**: The secure channel is established during discovery
49
+ - **MessagePort transferred immediately**: The discovery response includes a MessagePort for private communication
50
+ - **Anti-MITM verification**: Both parties can display emoji verification codes derived from the shared secret
34
51
 
35
- - **Extension wallets**: Use `window.postMessage` (examples shown throughout this guide)
36
- - **Web wallets**: Could use WebSockets, HTTP, or other protocols (see comments in examples for hypothetical WebSocket usage)
37
- - **Mobile wallets**: Could use deep links, app-to-app communication, or custom protocols
52
+ ### Transport Mechanisms
38
53
 
39
- The message format remains the same regardless of transport - only the delivery mechanism changes.
54
+ This guide uses **browser extension wallets** as the primary example, which communicate via `window.postMessage` for discovery and MessageChannel for secure communication. The same message protocol can be adapted for other transport mechanisms.
40
55
 
41
56
  ## Discovery Protocol
42
57
 
43
58
  ### 1. Listen for Discovery Requests
44
59
 
45
- **Extension wallet example:**
60
+ **Extension wallet (content script):**
46
61
 
47
62
  ```typescript
48
- window.addEventListener('message', event => {
49
- if (event.source !== window) {
63
+ window.addEventListener('message', async (event) => {
64
+ if (event.source !== window) return;
65
+
66
+ let data: DiscoveryRequest;
67
+ try {
68
+ data = JSON.parse(event.data);
69
+ } catch {
50
70
  return;
51
71
  }
52
72
 
53
- const data = JSON.parse(event.data);
54
-
55
73
  if (data.type === 'aztec-wallet-discovery') {
56
- handleDiscovery(data);
74
+ await handleDiscoveryRequest(data);
57
75
  }
58
76
  });
59
-
60
- // Using WebSocket:
61
- // websocket.on('message', (message) => {
62
- // const data = JSON.parse(message);
63
- // if (data.type === 'aztec-wallet-discovery') {
64
- // handleDiscovery(data);
65
- // }
66
- // });
67
77
  ```
68
78
 
69
79
  ### 2. Discovery Message Format
@@ -71,328 +81,291 @@ window.addEventListener('message', event => {
71
81
  Discovery messages have this structure:
72
82
 
73
83
  ```typescript
74
- {
75
- type: 'aztec-wallet-discovery',
76
- requestId: string, // UUID for tracking this request
77
- chainInfo: {
78
- chainId: Fr, // Chain ID
79
- version: Fr // Protocol version
80
- }
84
+ interface DiscoveryRequest {
85
+ type: 'aztec-wallet-discovery';
86
+ requestId: string; // UUID for tracking this request
87
+ chainInfo: ChainInfo; // Chain ID and protocol version
88
+ publicKey: ExportedPublicKey; // dApp's ECDH public key for key exchange
81
89
  }
82
90
  ```
83
91
 
84
- ### 3. Check Network Support
85
-
86
- Before responding, verify your wallet supports the requested network:
87
-
88
- ```typescript
89
- import { ChainInfoSchema } from '@aztec/wallet-sdk/manager';
92
+ ### 3. Handle Discovery and Establish Secure Channel
90
93
 
91
- function handleDiscovery(message: any) {
92
- const { requestId, chainInfo } = message;
94
+ When your wallet receives a discovery request:
93
95
 
94
- // Parse and validate chain info
95
- const { chainId, version } = ChainInfoSchema.parse(chainInfo);
96
+ 1. Check if you support the requested network
97
+ 2. Derive the shared secret from the dApp's public key
98
+ 3. Create a MessageChannel for secure communication
99
+ 4. Respond with your wallet info and transfer one end of the channel
96
100
 
97
- // Check if your wallet supports this network
98
- const isSupported = checkNetworkSupport(chainId, version);
101
+ **Extension wallet (background script):**
99
102
 
100
- if (!isSupported) {
101
- // Do NOT respond if you don't support this network
102
- return;
103
+ ```typescript
104
+ import {
105
+ deriveSharedKey,
106
+ exportPublicKey,
107
+ generateKeyPair,
108
+ hashSharedSecret,
109
+ importPublicKey,
110
+ } from '@aztec/wallet-sdk/crypto';
111
+
112
+ // Generate key pair on wallet initialization (per session)
113
+ let walletKeyPair = await generateKeyPair();
114
+ let walletPublicKey = await exportPublicKey(walletKeyPair.publicKey);
115
+
116
+ // Store sessions by requestId
117
+ const sessions = new Map<string, { sharedKey: CryptoKey; verificationHash: string; tabId: number }>();
118
+
119
+ async function handleDiscovery(
120
+ request: DiscoveryRequest,
121
+ tabId: number
122
+ ): Promise<{ success: true; response: DiscoveryResponse }> {
123
+ // Check network support
124
+ if (!supportsNetwork(request.chainInfo)) {
125
+ throw new Error('Network not supported');
103
126
  }
104
127
 
105
- // Respond if supported
106
- respondToDiscovery(requestId);
107
- }
108
- ```
109
-
110
- ### 4. Respond to Discovery
128
+ // Import dApp's public key and derive shared secret
129
+ const dAppPublicKey = await importPublicKey(request.publicKey);
130
+ const sharedKey = await deriveSharedKey(walletKeyPair.privateKey, dAppPublicKey);
111
131
 
112
- If your wallet supports the network, respond with your wallet information:
132
+ // Compute verification hash for anti-MITM verification
133
+ const verificationHash = await hashSharedSecret(sharedKey);
113
134
 
114
- **Extension wallet example:**
135
+ // Store the session with verificationHash (emoji computed lazily for display)
136
+ sessions.set(request.requestId, { sharedKey, verificationHash, tabId });
115
137
 
116
- ```typescript
117
- import { jsonStringify } from '@aztec/wallet-sdk/manager';
118
-
119
- function respondToDiscovery(requestId: string) {
120
- const response = {
138
+ const response: DiscoveryResponse = {
121
139
  type: 'aztec-wallet-discovery-response',
122
- requestId,
140
+ requestId: request.requestId,
123
141
  walletInfo: {
124
- id: 'my-aztec-wallet', // Unique wallet identifier
125
- name: 'My Aztec Wallet', // Display name
126
- icon: 'https://example.com/icon.png', // Optional icon URL
127
- version: '1.0.0', // Wallet version
142
+ id: 'my-aztec-wallet',
143
+ name: 'My Aztec Wallet',
144
+ version: '1.0.0',
145
+ publicKey: walletPublicKey,
128
146
  },
129
147
  };
130
148
 
131
- // Send as JSON string via window.postMessage
132
- window.postMessage(jsonStringify(response), '*');
149
+ return { success: true, response };
133
150
  }
134
-
135
- // Using WebSocket:
136
- // websocket.send(jsonStringify(response));
137
151
  ```
138
152
 
139
- **Important Notes:**
140
-
141
- - Both the SDK and wallets send messages as JSON strings (using `jsonStringify`)
142
- - Both the SDK and wallets must parse incoming JSON strings
143
- - Always use `jsonStringify` from `@aztec/foundation/json-rpc` for sending messages
144
- - Always parse incoming messages with `JSON.parse` and the proper schemas
145
-
146
- ## Message Format
147
-
148
- ### Wallet Method Request
149
-
150
- After discovery, dApps will call wallet methods. These arrive as:
153
+ **Content script (creates MessageChannel and sends response):**
151
154
 
152
155
  ```typescript
153
- {
154
- type: string, // Wallet method name from the Wallet interface
155
- messageId: string, // UUID for tracking this request
156
- args: unknown[], // Method arguments
157
- chainInfo: {
158
- chainId: Fr, // Same chain that was used in discovery
159
- version: Fr
160
- },
161
- appId: string, // Application identifier
162
- walletId: string // Your wallet's ID (from discovery response)
156
+ async function handleDiscoveryRequest(request: DiscoveryRequest) {
157
+ // Forward to background script for key derivation
158
+ const result = await browser.runtime.sendMessage({
159
+ type: 'aztec-wallet-discovery',
160
+ content: request,
161
+ });
162
+
163
+ if (!result?.success) return;
164
+
165
+ // Create MessageChannel for secure communication
166
+ const channel = new MessageChannel();
167
+
168
+ // Set up relay from page to background
169
+ channel.port1.onmessage = (event) => {
170
+ browser.runtime.sendMessage({
171
+ type: 'secure-message',
172
+ requestId: request.requestId,
173
+ content: event.data, // Encrypted payload
174
+ });
175
+ };
176
+ channel.port1.start();
177
+
178
+ // Send response with port2 to the page
179
+ window.postMessage(JSON.stringify(result.response), '*', [channel.port2]);
163
180
  }
164
181
  ```
165
182
 
166
- Example method calls:
167
-
168
- - `type: 'getAccounts'` - Get list of accounts
169
- - `type: 'getChainInfo'` - Get chain information
170
- - `type: 'sendTx'` - Send a transaction
171
- - `type: 'registerContract'` - Register a contract instance
172
-
173
- ### Wallet Method Response
174
-
175
- Your wallet must respond with:
183
+ ### 4. Discovery Response Format
176
184
 
177
185
  ```typescript
178
- {
179
- messageId: string, // MUST match the request's messageId
180
- result?: unknown, // Method result (if successful)
181
- error?: unknown, // Error (if failed)
182
- walletId: string // Your wallet's ID
186
+ interface DiscoveryResponse {
187
+ type: 'aztec-wallet-discovery-response';
188
+ requestId: string; // Must match the request
189
+ walletInfo: WalletInfo; // Wallet info including public key
190
+ }
191
+
192
+ interface WalletInfo {
193
+ id: string; // Unique wallet identifier
194
+ name: string; // Display name
195
+ icon?: string; // Optional icon URL
196
+ version: string; // Wallet version
197
+ publicKey: ExportedPublicKey; // ECDH public key for key exchange
183
198
  }
184
199
  ```
185
200
 
186
- ## Handling Wallet Methods
201
+ **Important:** The response is sent via `window.postMessage` with a MessagePort transferred as the third argument. The SDK receives the port and uses it for all subsequent encrypted communication.
187
202
 
188
- ### 1. Set Up Message Listener
203
+ ## Secure Communication
189
204
 
190
- **Extension wallet example:**
205
+ ### Architecture for Extension Wallets
191
206
 
192
- ```typescript
193
- window.addEventListener('message', event => {
194
- if (event.source !== window) {
195
- return;
196
- }
207
+ ```
208
+ ┌─────────────┐ window.postMessage ┌─────────────────┐ browser.runtime ┌──────────────────┐
209
+ │ dApp │◄───(discovery only)─────►│ Content Script │◄────────────────────►│ Background Script│
210
+ │ (web page) │ │ (message relay)│ │ (decrypt+process)│
211
+ └─────────────┘ └─────────────────┘ └──────────────────┘
212
+ │ │
213
+ │ MessagePort (private channel) │
214
+ └──────────(encrypted messages)────────────┘
215
+ ```
197
216
 
198
- let data;
199
- try {
200
- data = JSON.parse(event.data);
201
- } catch {
202
- return; // Not a valid JSON message
203
- }
217
+ **Security benefits:**
204
218
 
205
- // Handle discovery
206
- if (data.type === 'aztec-wallet-discovery') {
207
- handleDiscovery(data);
208
- return;
209
- }
219
+ - Content script never has access to private keys or shared secrets
220
+ - All cryptographic operations happen in the background script (service worker)
221
+ - MessagePort provides a private channel not visible to other page scripts
222
+ - Only discovery uses `window.postMessage`; all wallet calls are encrypted on the MessagePort
210
223
 
211
- // Handle wallet methods
212
- if (data.messageId && data.type && data.walletId === 'my-aztec-wallet') {
213
- handleWalletMethod(data);
214
- }
215
- });
224
+ ### Handle Encrypted Messages
225
+
226
+ All wallet method calls arrive as encrypted payloads on the MessagePort:
216
227
 
217
- // Using WebSocket:
218
- // websocket.on('message', (message) => {
219
- // const data = JSON.parse(message);
220
- // if (data.type === 'aztec-wallet-discovery') {
221
- // handleDiscovery(data);
222
- // } else if (data.messageId && data.type) {
223
- // handleWalletMethod(data);
224
- // }
225
- // });
228
+ ```typescript
229
+ interface EncryptedPayload {
230
+ iv: string; // Base64-encoded initialization vector
231
+ ciphertext: string; // Base64-encoded encrypted data
232
+ }
226
233
  ```
227
234
 
228
- ### 2. Route to Wallet Implementation
235
+ **Background script:**
229
236
 
230
237
  ```typescript
231
- import { ChainInfoSchema } from '@aztec/wallet-sdk/manager';
238
+ import { decrypt, encrypt } from '@aztec/wallet-sdk/crypto';
232
239
 
233
- async function handleWalletMethod(message: any) {
234
- const { type, messageId, args, chainInfo, appId, walletId } = message;
240
+ async function handleSecureMessage(requestId: string, encrypted: EncryptedPayload) {
241
+ const session = sessions.get(requestId);
242
+ if (!session) return;
235
243
 
236
244
  try {
237
- // Parse and validate chain info
238
- const parsedChainInfo = ChainInfoSchema.parse(chainInfo);
239
-
240
- // Get the wallet instance for this chain
241
- const wallet = await getWalletForChain(parsedChainInfo);
242
-
243
- // Verify the method exists on the Wallet interface
244
- if (typeof wallet[type] !== 'function') {
245
- throw new Error(`Unknown wallet method: ${type}`);
246
- }
245
+ // Decrypt the incoming message
246
+ const message = await decrypt<WalletMessage>(session.sharedKey, encrypted);
247
+ const { type, messageId, args, chainInfo, walletId } = message;
247
248
 
248
- // Call the wallet method
249
+ // Process the wallet method call
250
+ const wallet = await getWalletForChain(chainInfo);
249
251
  const result = await wallet[type](...args);
250
252
 
251
- // Send success response
252
- sendResponse(messageId, walletId, result);
253
+ // Create and encrypt response
254
+ const response: WalletResponse = { messageId, result, walletId };
255
+ const encryptedResponse = await encrypt(session.sharedKey, response);
256
+
257
+ // Send back through content script
258
+ browser.tabs.sendMessage(session.tabId, {
259
+ type: 'secure-response',
260
+ requestId,
261
+ content: encryptedResponse,
262
+ });
253
263
  } catch (error) {
254
- // Send error response
255
- sendError(messageId, walletId, error);
264
+ // Send encrypted error response
265
+ const errorResponse: WalletResponse = {
266
+ messageId: message?.messageId ?? '',
267
+ error: { message: error.message },
268
+ walletId: message?.walletId ?? '',
269
+ };
270
+ const encryptedError = await encrypt(session.sharedKey, errorResponse);
271
+ // ... send error response
256
272
  }
257
273
  }
258
274
  ```
259
275
 
260
- ### 3. Send Response
276
+ ## Message Formats
261
277
 
262
- **Extension wallet example:**
278
+ ### Wallet Method Request (Decrypted)
263
279
 
264
280
  ```typescript
265
- import { jsonStringify } from '@aztec/wallet-sdk/manager';
266
-
267
- function sendResponse(messageId: string, walletId: string, result: unknown) {
268
- const response = {
269
- messageId,
270
- result,
271
- walletId,
272
- };
273
-
274
- // Send as JSON string
275
- window.postMessage(jsonStringify(response), '*');
281
+ interface WalletMessage {
282
+ type: string; // Wallet method name (e.g., 'getAccounts', 'sendTx')
283
+ messageId: string; // UUID for tracking this request
284
+ args: unknown[]; // Method arguments
285
+ chainInfo: ChainInfo;
286
+ appId: string; // Application identifier
287
+ walletId: string; // Your wallet's ID
276
288
  }
289
+ ```
277
290
 
278
- function sendError(messageId: string, walletId: string, error: Error) {
279
- const response = {
280
- messageId,
281
- error: {
282
- message: error.message,
283
- stack: error.stack,
284
- },
285
- walletId,
286
- };
291
+ ### Wallet Method Response
287
292
 
288
- window.postMessage(jsonStringify(response), '*');
293
+ ```typescript
294
+ interface WalletResponse {
295
+ messageId: string; // Must match the request
296
+ result?: unknown; // Method result (if successful)
297
+ error?: unknown; // Error (if failed)
298
+ walletId: string; // Your wallet's ID
289
299
  }
290
-
291
- // Using WebSocket:
292
- // websocket.send(jsonStringify({ messageId, result, walletId }));
293
300
  ```
294
301
 
295
- ## Parsing Messages
296
-
297
- ### Using Zod Schemas
302
+ ## Anti-MITM Verification
298
303
 
299
- Use the provided Zod schemas to parse and validate incoming messages:
304
+ Both the dApp and wallet independently compute a `verificationHash` from the shared secret. If both parties compute the same hash, they know there's no man-in-the-middle attack.
300
305
 
301
306
  ```typescript
302
- import { ChainInfoSchema, WalletSchema } from '@aztec/wallet-sdk/manager';
307
+ import { hashSharedSecret } from '@aztec/wallet-sdk/crypto';
303
308
 
304
- // Parse chain info
305
- const chainInfo = ChainInfoSchema.parse(message.chainInfo);
309
+ // Compute verification hash from shared key
310
+ const verificationHash = await hashSharedSecret(sharedKey);
306
311
 
307
- // Validate result against expected schema for a method
308
- const accountsResult = await wallet.getAccounts(...args);
309
- // The SDK handles schema validation on the client side
312
+ // Store verificationHash in session - this is the cryptographic proof
313
+ sessions.set(requestId, { sharedKey, verificationHash, tabId });
310
314
  ```
311
315
 
312
- The Wallet SDK automatically validates return values using `WalletSchema` on the client side, so your wallet implementation should return values that match the `Wallet` interface specification.
313
-
314
- ## Error Handling
315
-
316
- ### Error Response Format
317
-
318
- Always send error responses with this structure:
316
+ For user-friendly display, convert the hash to an emoji sequence:
319
317
 
320
318
  ```typescript
321
- {
322
- messageId: string, // Match the request
323
- error: {
324
- message: string, // Error message
325
- code?: string, // Optional error code
326
- stack?: string // Optional stack trace
327
- },
328
- walletId: string
329
- }
319
+ import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
320
+
321
+ // Convert to emoji only when displaying to the user
322
+ const emoji = hashToEmoji(verificationHash); // e.g., "🔵🦋🎯🐼"
330
323
  ```
331
324
 
332
- ### Common Error Scenarios
325
+ The dApp displays the same emoji sequence. If they match, the connection is secure.
333
326
 
334
- ```typescript
335
- import { ChainInfoSchema } from '@aztec/wallet-sdk/manager';
327
+ ## Session Management
336
328
 
337
- async function handleWalletMethod(message: any) {
338
- const { type, messageId, args, chainInfo, walletId } = message;
329
+ Sessions should be cleaned up when:
339
330
 
340
- try {
341
- // 1. Parse and validate chain info
342
- const parsedChainInfo = ChainInfoSchema.parse(chainInfo);
331
+ - **Tab closes**: Browser tabs API `onRemoved` event
332
+ - **Tab navigates**: Browser tabs API `onUpdated` event with `status === 'loading'`
343
333
 
344
- // 2. Check network support
345
- if (!isNetworkSupported(parsedChainInfo)) {
346
- throw new Error('Network not supported by wallet');
334
+ ```typescript
335
+ // Clean up when tab closes
336
+ browser.tabs.onRemoved.addListener((tabId) => {
337
+ for (const [requestId, session] of sessions) {
338
+ if (session.tabId === tabId) {
339
+ sessions.delete(requestId);
347
340
  }
341
+ }
342
+ });
348
343
 
349
- // 3. Get wallet instance
350
- const wallet = await getWalletForChain(parsedChainInfo);
351
-
352
- // 4. Validate method exists
353
- if (typeof wallet[type] !== 'function') {
354
- throw new Error(`Unknown wallet method: ${type}`);
344
+ // Clean up when tab navigates
345
+ browser.tabs.onUpdated.addListener((tabId, changeInfo) => {
346
+ if (changeInfo.status === 'loading') {
347
+ for (const [requestId, session] of sessions) {
348
+ if (session.tabId === tabId) {
349
+ sessions.delete(requestId);
350
+ }
355
351
  }
356
-
357
- // 5. Execute method
358
- const result = await wallet[type](...args);
359
- sendResponse(messageId, walletId, result);
360
- } catch (error) {
361
- sendError(messageId, walletId, error);
362
352
  }
363
- }
364
- ```
365
-
366
- ### User Rejection Handling
367
-
368
- If a user rejects an action:
369
-
370
- ```typescript
371
- {
372
- messageId: 'abc-123',
373
- error: {
374
- message: 'User rejected the request',
375
- code: 'USER_REJECTED'
376
- },
377
- walletId: 'my-wallet'
378
- }
353
+ });
379
354
  ```
380
355
 
381
356
  ## Testing Your Integration
382
357
 
383
- ### WalletManager
384
-
385
- In a dApp using the Wallet SDK:
358
+ ### Using WalletManager
386
359
 
387
360
  ```typescript
388
361
  import { Fr } from '@aztec/foundation/fields';
389
- import { WalletManager } from '@aztec/wallet-sdk/manager';
362
+ import { WalletManager, hashToEmoji } from '@aztec/wallet-sdk/manager';
390
363
 
391
364
  const manager = WalletManager.configure({
392
365
  extensions: { enabled: true },
393
366
  });
394
367
 
395
- // Discover wallets
368
+ // Discover wallets (secure channel established automatically)
396
369
  const wallets = await manager.getAvailableWallets({
397
370
  chainInfo: {
398
371
  chainId: new Fr(31337),
@@ -401,19 +374,20 @@ const wallets = await manager.getAvailableWallets({
401
374
  timeout: 2000,
402
375
  });
403
376
 
404
- console.log('Discovered wallets:', wallets);
377
+ // Each wallet provider has verification info
378
+ for (const provider of wallets) {
379
+ const emoji = hashToEmoji(provider.metadata.verificationHash);
380
+ console.log(`${provider.name}: ${emoji}`);
381
+ }
405
382
 
406
- // Connect to your wallet
383
+ // Connect and use
407
384
  const walletProvider = wallets.find(w => w.id === 'my-aztec-wallet');
408
385
  if (walletProvider) {
409
- const wallet = await walletProvider.connect('test-app');
386
+ const wallet = await walletProvider.connect('my-app-id');
410
387
 
411
- // Test wallet methods from the Wallet interface
388
+ // All calls are automatically encrypted
412
389
  const accounts = await wallet.getAccounts();
413
390
  console.log('Accounts:', accounts);
414
-
415
- const chainInfo = await wallet.getChainInfo();
416
- console.log('Chain info:', chainInfo);
417
391
  }
418
392
  ```
419
393