@aztec/wallet-sdk 0.0.1-commit.fcb71a6 → 0.0.1-commit.fffb133c

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 (73) hide show
  1. package/README.md +229 -332
  2. package/dest/base-wallet/base_wallet.d.ts +21 -11
  3. package/dest/base-wallet/base_wallet.d.ts.map +1 -1
  4. package/dest/base-wallet/base_wallet.js +45 -19
  5. package/dest/crypto.d.ts +192 -0
  6. package/dest/crypto.d.ts.map +1 -0
  7. package/dest/crypto.js +394 -0
  8. package/dest/emoji_alphabet.d.ts +35 -0
  9. package/dest/emoji_alphabet.d.ts.map +1 -0
  10. package/dest/emoji_alphabet.js +299 -0
  11. package/dest/extension/handlers/background_connection_handler.d.ts +158 -0
  12. package/dest/extension/handlers/background_connection_handler.d.ts.map +1 -0
  13. package/dest/extension/handlers/background_connection_handler.js +258 -0
  14. package/dest/extension/handlers/content_script_connection_handler.d.ts +56 -0
  15. package/dest/extension/handlers/content_script_connection_handler.d.ts.map +1 -0
  16. package/dest/extension/handlers/content_script_connection_handler.js +174 -0
  17. package/dest/extension/handlers/index.d.ts +12 -0
  18. package/dest/extension/handlers/index.d.ts.map +1 -0
  19. package/dest/extension/handlers/index.js +10 -0
  20. package/dest/extension/handlers/internal_message_types.d.ts +63 -0
  21. package/dest/extension/handlers/internal_message_types.d.ts.map +1 -0
  22. package/dest/extension/handlers/internal_message_types.js +22 -0
  23. package/dest/extension/provider/extension_provider.d.ts +107 -0
  24. package/dest/extension/provider/extension_provider.d.ts.map +1 -0
  25. package/dest/extension/provider/extension_provider.js +160 -0
  26. package/dest/extension/provider/extension_wallet.d.ts +131 -0
  27. package/dest/extension/provider/extension_wallet.d.ts.map +1 -0
  28. package/dest/extension/provider/extension_wallet.js +271 -0
  29. package/dest/extension/provider/index.d.ts +3 -0
  30. package/dest/extension/provider/index.d.ts.map +1 -0
  31. package/dest/manager/index.d.ts +2 -7
  32. package/dest/manager/index.d.ts.map +1 -1
  33. package/dest/manager/index.js +0 -4
  34. package/dest/manager/types.d.ts +108 -5
  35. package/dest/manager/types.d.ts.map +1 -1
  36. package/dest/manager/types.js +17 -1
  37. package/dest/manager/wallet_manager.d.ts +50 -7
  38. package/dest/manager/wallet_manager.d.ts.map +1 -1
  39. package/dest/manager/wallet_manager.js +178 -29
  40. package/dest/types.d.ts +123 -0
  41. package/dest/types.d.ts.map +1 -0
  42. package/dest/types.js +11 -0
  43. package/package.json +13 -10
  44. package/src/base-wallet/base_wallet.ts +65 -30
  45. package/src/crypto.ts +499 -0
  46. package/src/emoji_alphabet.ts +317 -0
  47. package/src/extension/handlers/background_connection_handler.ts +423 -0
  48. package/src/extension/handlers/content_script_connection_handler.ts +246 -0
  49. package/src/extension/handlers/index.ts +25 -0
  50. package/src/extension/handlers/internal_message_types.ts +69 -0
  51. package/src/extension/provider/extension_provider.ts +233 -0
  52. package/src/extension/provider/extension_wallet.ts +321 -0
  53. package/src/extension/provider/index.ts +7 -0
  54. package/src/manager/index.ts +3 -15
  55. package/src/manager/types.ts +112 -4
  56. package/src/manager/wallet_manager.ts +204 -31
  57. package/src/types.ts +132 -0
  58. package/dest/providers/extension/extension_provider.d.ts +0 -17
  59. package/dest/providers/extension/extension_provider.d.ts.map +0 -1
  60. package/dest/providers/extension/extension_provider.js +0 -56
  61. package/dest/providers/extension/extension_wallet.d.ts +0 -23
  62. package/dest/providers/extension/extension_wallet.d.ts.map +0 -1
  63. package/dest/providers/extension/extension_wallet.js +0 -96
  64. package/dest/providers/extension/index.d.ts +0 -4
  65. package/dest/providers/extension/index.d.ts.map +0 -1
  66. package/dest/providers/types.d.ts +0 -67
  67. package/dest/providers/types.d.ts.map +0 -1
  68. package/dest/providers/types.js +0 -3
  69. package/src/providers/extension/extension_provider.ts +0 -72
  70. package/src/providers/extension/extension_wallet.ts +0 -124
  71. package/src/providers/extension/index.ts +0 -3
  72. package/src/providers/types.ts +0 -71
  73. /package/dest/{providers/extension → extension/provider}/index.js +0 -0
package/README.md CHANGED
@@ -4,421 +4,318 @@ 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,
13
+ KeyExchangeRequest,
14
+ KeyExchangeResponse,
14
15
  WalletInfo,
15
16
  WalletMessage,
16
17
  WalletResponse,
17
- } from '@aztec/wallet-sdk/manager';
18
- import { ChainInfoSchema, WalletSchema, jsonStringify } from '@aztec/wallet-sdk/manager';
18
+ } from '@aztec/wallet-sdk/types';
19
19
  ```
20
20
 
21
- ## Overview
22
-
23
- The Wallet SDK uses a **request-based discovery** model:
24
-
25
- 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
30
-
31
- ### Transport Mechanisms
32
-
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:
34
-
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
38
-
39
- The message format remains the same regardless of transport - only the delivery mechanism changes.
40
-
41
- ## Discovery Protocol
42
-
43
- ### 1. Listen for Discovery Requests
44
-
45
- **Extension wallet example:**
21
+ Cryptographic utilities for secure channel establishment are exported from `@aztec/wallet-sdk/crypto`:
46
22
 
47
23
  ```typescript
48
- window.addEventListener('message', event => {
49
- if (event.source !== window) {
50
- return;
51
- }
52
-
53
- const data = JSON.parse(event.data);
54
-
55
- if (data.type === 'aztec-wallet-discovery') {
56
- handleDiscovery(data);
57
- }
58
- });
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
- ```
68
-
69
- ### 2. Discovery Message Format
70
-
71
- Discovery messages have this structure:
72
-
73
- ```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
- }
81
- }
24
+ import type { EncryptedPayload, ExportedPublicKey } from '@aztec/wallet-sdk/crypto';
25
+ import {
26
+ decrypt,
27
+ deriveSessionKeys,
28
+ encrypt,
29
+ exportPublicKey,
30
+ generateKeyPair,
31
+ hashToEmoji,
32
+ importPublicKey,
33
+ } from '@aztec/wallet-sdk/crypto';
82
34
  ```
83
35
 
84
- ### 3. Check Network Support
85
-
86
- Before responding, verify your wallet supports the requested network:
36
+ **For extension wallets**, pre-built connection handlers are available:
87
37
 
88
38
  ```typescript
89
- import { ChainInfoSchema } from '@aztec/wallet-sdk/manager';
90
-
91
- function handleDiscovery(message: any) {
92
- const { requestId, chainInfo } = message;
93
-
94
- // Parse and validate chain info
95
- const { chainId, version } = ChainInfoSchema.parse(chainInfo);
96
-
97
- // Check if your wallet supports this network
98
- const isSupported = checkNetworkSupport(chainId, version);
99
-
100
- if (!isSupported) {
101
- // Do NOT respond if you don't support this network
102
- return;
103
- }
104
-
105
- // Respond if supported
106
- respondToDiscovery(requestId);
107
- }
39
+ import {
40
+ BackgroundConnectionHandler,
41
+ ContentScriptConnectionHandler,
42
+ } from '@aztec/wallet-sdk/extension/handlers';
108
43
  ```
109
44
 
110
- ### 4. Respond to Discovery
45
+ ## Overview
111
46
 
112
- If your wallet supports the network, respond with your wallet information:
47
+ The Wallet SDK uses a **two-phase connection model** with **end-to-end encryption**:
113
48
 
114
- **Extension wallet example:**
49
+ ### Phase 1: Discovery
115
50
 
116
- ```typescript
117
- import { jsonStringify } from '@aztec/wallet-sdk/manager';
118
-
119
- function respondToDiscovery(requestId: string) {
120
- const response = {
121
- type: 'aztec-wallet-discovery-response',
122
- requestId,
123
- 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
128
- },
129
- };
130
-
131
- // Send as JSON string via window.postMessage
132
- window.postMessage(jsonStringify(response), '*');
133
- }
51
+ 1. **dApp broadcasts** a discovery request with chain information (NO public keys)
52
+ 2. **Your wallet shows** a pending connection request to the user
53
+ 3. **User approves** the connection request
54
+ 4. **Your wallet responds** with basic wallet info and a MessagePort
134
55
 
135
- // Using WebSocket:
136
- // websocket.send(jsonStringify(response));
137
- ```
56
+ ### Phase 2: Secure Channel Establishment
138
57
 
139
- **Important Notes:**
58
+ 5. **dApp initiates key exchange** by sending its ECDH public key over the MessagePort
59
+ 6. **Wallet generates** ephemeral key pair and derives session keys using HKDF
60
+ 7. **Both parties compute** the same verification hash independently
61
+ 8. **User verifies** the has matches on both sides. A util for conversion to an emoji grid is provided
62
+ 9. **User confirms** the connection in the dApp
63
+ 10. **All subsequent communication** is encrypted using AES-256-GCM
140
64
 
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
65
+ ### Key Security Features
145
66
 
146
- ## Message Format
67
+ - **User approval required**: Wallet never reveals itself without explicit user consent
68
+ - **Ephemeral keys**: New key pairs generated for each session
69
+ - **Anti-MITM verification**: 3x3 emoji grid (72 bits of security) for visual confirmation
147
70
 
148
- ### Wallet Method Request
71
+ ## Architecture for Extension Wallets
149
72
 
150
- After discovery, dApps will call wallet methods. These arrive as:
151
-
152
- ```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)
163
- }
164
73
  ```
165
-
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:
176
-
177
- ```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
183
- }
74
+ ┌─────────────┐ window.postMessage ┌─────────────────┐ browser.runtime ┌──────────────────┐
75
+ │ dApp │◄──(discovery + port)────►│ Content Script │◄────────────────────►│ Background Script│
76
+ │ (web page) │ │ (message relay)│ │ (crypto+state) │
77
+ └─────────────┘ └─────────────────┘ └──────────────────┘
78
+ │ │
79
+ │ MessagePort │
80
+ └──────────(key exchange + encrypted)──────┘
184
81
  ```
185
82
 
186
- ## Handling Wallet Methods
83
+ **Security model:**
187
84
 
188
- ### 1. Set Up Message Listener
85
+ - The MessagePort is transferred via `window.postMessage` - other scripts on the page could intercept it
86
+ - **Security comes from encryption**: After key exchange, all communication is AES-256-GCM encrypted
87
+ - Content script never has access to private keys or session secrets
88
+ - All cryptographic operations happen in the background script (service worker)
89
+ - Anti-MITM verification (emoji grid) ensures both parties derived the same keys
189
90
 
190
- **Extension wallet example:**
91
+ ## Using Pre-built Connection Handlers
191
92
 
192
- ```typescript
193
- window.addEventListener('message', event => {
194
- if (event.source !== window) {
195
- return;
196
- }
197
-
198
- let data;
199
- try {
200
- data = JSON.parse(event.data);
201
- } catch {
202
- return; // Not a valid JSON message
203
- }
204
-
205
- // Handle discovery
206
- if (data.type === 'aztec-wallet-discovery') {
207
- handleDiscovery(data);
208
- return;
209
- }
210
-
211
- // Handle wallet methods
212
- if (data.messageId && data.type && data.walletId === 'my-aztec-wallet') {
213
- handleWalletMethod(data);
214
- }
215
- });
216
-
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
- // });
226
- ```
93
+ The SDK provides `BackgroundConnectionHandler` and `ContentScriptConnectionHandler` to handle the connection flow. These are the recommended way to build extension wallets.
227
94
 
228
- ### 2. Route to Wallet Implementation
95
+ ### Background Script Setup
229
96
 
230
97
  ```typescript
231
- import { ChainInfoSchema } from '@aztec/wallet-sdk/manager';
232
-
233
- async function handleWalletMethod(message: any) {
234
- const { type, messageId, args, chainInfo, appId, walletId } = message;
235
-
236
- 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}`);
98
+ import {
99
+ BackgroundConnectionHandler,
100
+ type BackgroundConnectionConfig,
101
+ type BackgroundConnectionCallbacks,
102
+ type BackgroundTransport,
103
+ } from '@aztec/wallet-sdk/extension/handlers';
104
+ import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
105
+
106
+ // Configuration for your wallet
107
+ const config: BackgroundConnectionConfig = {
108
+ walletId: 'my-aztec-wallet',
109
+ walletName: 'My Aztec Wallet',
110
+ walletVersion: '1.0.0',
111
+ walletIcon: 'https://example.com/icon.png',
112
+ };
113
+
114
+ // Transport for browser extension APIs
115
+ const transport: BackgroundTransport = {
116
+ sendToTab: (tabId, message) => browser.tabs.sendMessage(tabId, message),
117
+ addContentListener: (handler) => browser.runtime.onMessage.addListener(handler),
118
+ };
119
+
120
+ // Event callbacks (all optional)
121
+ const callbacks: BackgroundConnectionCallbacks = {
122
+ // Called when a new discovery request is received
123
+ onPendingDiscovery: (discovery) => {
124
+ // Show pending connection in wallet UI
125
+ // Check if wallet supports this network (chainId AND version)
126
+ const supported = supportedNetworks.some(
127
+ n => n.chainId === discovery.chainInfo.chainId.toString() &&
128
+ n.version === discovery.chainInfo.version.toString()
129
+ );
130
+ if (supported) {
131
+ // Show the user so they can approve or reject
246
132
  }
133
+ },
247
134
 
248
- // Call the wallet method
249
- const result = await wallet[type](...args);
135
+ // Called when key exchange completes and session is ready
136
+ onSessionEstablished: (session) => {
137
+ // Display verification emojis for user reference
138
+ console.log('Session emojis:', hashToEmoji(session.verificationHash));
139
+ },
250
140
 
251
- // Send success response
252
- sendResponse(messageId, walletId, result);
253
- } catch (error) {
254
- // Send error response
255
- sendError(messageId, walletId, error);
256
- }
257
- }
258
- ```
141
+ // Called when a session is terminated
142
+ onSessionTerminated: (requestId) => {
143
+ console.log('Session terminated:', requestId);
144
+ },
259
145
 
260
- ### 3. Send Response
146
+ // Called when a decrypted wallet message is received
147
+ onWalletMessage: (session, message) => {
148
+ // Forward to your wallet backend
149
+ wallet.postMessage(message);
150
+ },
151
+ };
261
152
 
262
- **Extension wallet example:**
153
+ const handler = new BackgroundConnectionHandler(config, transport, callbacks);
263
154
 
264
- ```typescript
265
- import { jsonStringify } from '@aztec/wallet-sdk/manager';
155
+ // Initialize the handler to start listening
156
+ handler.initialize();
266
157
 
267
- function sendResponse(messageId: string, walletId: string, result: unknown) {
268
- const response = {
269
- messageId,
270
- result,
271
- walletId,
272
- };
158
+ // User approves connection from wallet UI
159
+ function approveConnection(requestId: string) {
160
+ handler.approveDiscovery(requestId);
161
+ }
273
162
 
274
- // Send as JSON string
275
- window.postMessage(jsonStringify(response), '*');
163
+ // User denies connection
164
+ function denyConnection(requestId: string) {
165
+ handler.rejectDiscovery(requestId);
276
166
  }
277
167
 
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
- };
287
-
288
- window.postMessage(jsonStringify(response), '*');
168
+ // Send response back to dApp
169
+ async function sendWalletResponse(requestId: string, response: WalletResponse) {
170
+ await handler.sendResponse(requestId, response);
289
171
  }
290
172
 
291
- // Using WebSocket:
292
- // websocket.send(jsonStringify({ messageId, result, walletId }));
173
+ // Clean up on tab close/navigate
174
+ browser.tabs.onRemoved.addListener((tabId) => {
175
+ handler.terminateForTab(tabId);
176
+ });
293
177
  ```
294
178
 
295
- ## Parsing Messages
296
-
297
- ### Using Zod Schemas
298
-
299
- Use the provided Zod schemas to parse and validate incoming messages:
179
+ ### Content Script Setup
300
180
 
301
181
  ```typescript
302
- import { ChainInfoSchema, WalletSchema } from '@aztec/wallet-sdk/manager';
182
+ import {
183
+ ContentScriptConnectionHandler,
184
+ type ContentScriptTransport,
185
+ } from '@aztec/wallet-sdk/extension/handlers';
303
186
 
304
- // Parse chain info
305
- const chainInfo = ChainInfoSchema.parse(message.chainInfo);
187
+ const transport: ContentScriptTransport = {
188
+ sendToBackground: (message) => browser.runtime.sendMessage(message),
189
+ addBackgroundListener: (handler) => browser.runtime.onMessage.addListener(handler),
190
+ };
306
191
 
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
310
- ```
311
-
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
192
+ const handler = new ContentScriptConnectionHandler(transport);
315
193
 
316
- ### Error Response Format
194
+ // Start listening for discovery requests and background messages
195
+ handler.start();
196
+ ```
317
197
 
318
- Always send error responses with this structure:
198
+ ## Testing Your Integration (dApp Side)
319
199
 
320
- ```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
- }
330
- ```
200
+ The `WalletManager` supports two patterns for consuming discovered wallets.
331
201
 
332
- ### Common Error Scenarios
202
+ ### Async Iterator Pattern
333
203
 
334
204
  ```typescript
335
- import { ChainInfoSchema } from '@aztec/wallet-sdk/manager';
205
+ import { Fr } from '@aztec/foundation/fields';
206
+ import { WalletManager } from '@aztec/wallet-sdk/manager';
207
+ import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
336
208
 
337
- async function handleWalletMethod(message: any) {
338
- const { type, messageId, args, chainInfo, walletId } = message;
209
+ const discovery = WalletManager.configure({
210
+ extensions: { enabled: true },
211
+ }).getAvailableWallets({
212
+ chainInfo: {
213
+ chainId: new Fr(31337),
214
+ version: new Fr(1),
215
+ },
216
+ appId: 'my-dapp',
217
+ timeout: 60000,
218
+ });
339
219
 
340
- try {
341
- // 1. Parse and validate chain info
342
- const parsedChainInfo = ChainInfoSchema.parse(chainInfo);
220
+ // Iterate over discovered wallets as they're approved
221
+ for await (const provider of discovery.wallets) {
222
+ console.log(`Found: ${provider.name}`);
343
223
 
344
- // 2. Check network support
345
- if (!isNetworkSupported(parsedChainInfo)) {
346
- throw new Error('Network not supported by wallet');
347
- }
224
+ // Establish secure channel (key exchange)
225
+ const pending = await provider.establishSecureChannel('my-dapp');
348
226
 
349
- // 3. Get wallet instance
350
- const wallet = await getWalletForChain(parsedChainInfo);
227
+ // Display verification emojis to user
228
+ const emojis = hashToEmoji(pending.verificationHash);
229
+ console.log('Verify this matches your wallet:', emojis);
351
230
 
352
- // 4. Validate method exists
353
- if (typeof wallet[type] !== 'function') {
354
- throw new Error(`Unknown wallet method: ${type}`);
355
- }
231
+ // User confirms emojis match
232
+ const wallet = await pending.confirm();
356
233
 
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
- }
234
+ // All calls are now encrypted
235
+ const accounts = await wallet.getAccounts();
236
+ console.log('Accounts:', accounts);
363
237
  }
364
- ```
365
238
 
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
- }
239
+ // Cancel discovery when done or on cleanup
240
+ discovery.cancel();
379
241
  ```
380
242
 
381
- ## Testing Your Integration
382
-
383
- ### WalletManager
384
-
385
- In a dApp using the Wallet SDK:
243
+ ### Callback Pattern
386
244
 
387
245
  ```typescript
388
- import { Fr } from '@aztec/foundation/curves/bn254';
389
- import { WalletManager } from '@aztec/wallet-sdk/manager';
246
+ import { Fr } from '@aztec/foundation/fields';
247
+ import { WalletManager, type WalletProvider } from '@aztec/wallet-sdk/manager';
248
+ import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
390
249
 
391
- const manager = WalletManager.configure({
392
- extensions: { enabled: true },
393
- });
250
+ const discoveredProviders: WalletProvider[] = [];
394
251
 
395
- // Discover wallets
396
- const wallets = await manager.getAvailableWallets({
252
+ const discovery = WalletManager.configure({
253
+ extensions: { enabled: true },
254
+ }).getAvailableWallets({
397
255
  chainInfo: {
398
256
  chainId: new Fr(31337),
399
- version: new Fr(0),
257
+ version: new Fr(1),
258
+ },
259
+ appId: 'my-dapp',
260
+ timeout: 60000,
261
+ // Callback fires as each wallet is discovered
262
+ onWalletDiscovered: (provider) => {
263
+ discoveredProviders.push(provider);
264
+ updateUI(); // Your UI update function
400
265
  },
401
- timeout: 2000,
402
266
  });
403
267
 
404
- console.log('Discovered wallets:', wallets);
268
+ // Wait for discovery to complete (or cancel early with discovery.cancel())
269
+ await discovery.done;
270
+ console.log('Discovery complete, found:', discoveredProviders.length);
405
271
 
406
- // Connect to your wallet
407
- const walletProvider = wallets.find(w => w.id === 'my-aztec-wallet');
408
- if (walletProvider) {
409
- const wallet = await walletProvider.connect('test-app');
272
+ // Connect to a selected provider
273
+ async function connectToWallet(provider: WalletProvider) {
274
+ const pending = await provider.establishSecureChannel('my-dapp');
410
275
 
411
- // Test wallet methods from the Wallet interface
412
- const accounts = await wallet.getAccounts();
413
- console.log('Accounts:', accounts);
276
+ // Show verification UI
277
+ const emojis = hashToEmoji(pending.verificationHash);
278
+ showVerificationDialog(emojis);
414
279
 
415
- const chainInfo = await wallet.getChainInfo();
416
- console.log('Chain info:', chainInfo);
280
+ // User confirms
281
+ const wallet = await pending.confirm();
282
+ return wallet;
417
283
  }
418
284
  ```
419
285
 
420
- ## Reference Implementation
421
-
422
- For a complete reference implementation, see the demo wallet at:
286
+ ### React Hook Example
423
287
 
424
- - Repository: `~/repos/demo-wallet`
288
+ ```typescript
289
+ function useWalletDiscovery(chainInfo: ChainInfo, appId: string) {
290
+ const [providers, setProviders] = useState<WalletProvider[]>([]);
291
+ const [isDiscovering, setIsDiscovering] = useState(true);
292
+ const discoveryRef = useRef<DiscoverySession | null>(null);
293
+
294
+ useEffect(() => {
295
+ setProviders([]);
296
+ setIsDiscovering(true);
297
+
298
+ const discovery = WalletManager.configure({
299
+ extensions: { enabled: true },
300
+ }).getAvailableWallets({
301
+ chainInfo,
302
+ appId,
303
+ timeout: 60000,
304
+ onWalletDiscovered: (provider) => {
305
+ setProviders(prev => [...prev, provider]);
306
+ },
307
+ });
308
+
309
+ discoveryRef.current = discovery;
310
+
311
+ discovery.done.then(() => setIsDiscovering(false));
312
+
313
+ return () => {
314
+ discovery.cancel();
315
+ discoveryRef.current = null;
316
+ };
317
+ }, [chainInfo.chainId.toString(), chainInfo.version.toString(), appId]);
318
+
319
+ return { providers, isDiscovering, cancel: () => discoveryRef.current?.cancel() };
320
+ }
321
+ ```