@aztec/wallet-sdk 0.0.1-commit.96bb3f7 → 0.0.1-commit.bf2612ae

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 (69) hide show
  1. package/README.md +218 -355
  2. package/dest/base-wallet/base_wallet.d.ts +19 -10
  3. package/dest/base-wallet/base_wallet.d.ts.map +1 -1
  4. package/dest/base-wallet/base_wallet.js +38 -16
  5. package/dest/crypto.d.ts +73 -27
  6. package/dest/crypto.d.ts.map +1 -1
  7. package/dest/crypto.js +219 -41
  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/{providers/extension → extension/provider}/index.js +0 -1
  32. package/dest/manager/index.d.ts +2 -7
  33. package/dest/manager/index.d.ts.map +1 -1
  34. package/dest/manager/index.js +0 -4
  35. package/dest/manager/types.d.ts +108 -5
  36. package/dest/manager/types.d.ts.map +1 -1
  37. package/dest/manager/types.js +17 -1
  38. package/dest/manager/wallet_manager.d.ts +50 -7
  39. package/dest/manager/wallet_manager.d.ts.map +1 -1
  40. package/dest/manager/wallet_manager.js +178 -29
  41. package/dest/types.d.ts +55 -15
  42. package/dest/types.d.ts.map +1 -1
  43. package/dest/types.js +10 -2
  44. package/package.json +11 -10
  45. package/src/base-wallet/base_wallet.ts +55 -28
  46. package/src/crypto.ts +263 -47
  47. package/src/emoji_alphabet.ts +317 -0
  48. package/src/extension/handlers/background_connection_handler.ts +423 -0
  49. package/src/extension/handlers/content_script_connection_handler.ts +246 -0
  50. package/src/extension/handlers/index.ts +25 -0
  51. package/src/extension/handlers/internal_message_types.ts +69 -0
  52. package/src/extension/provider/extension_provider.ts +233 -0
  53. package/src/extension/provider/extension_wallet.ts +321 -0
  54. package/src/extension/provider/index.ts +7 -0
  55. package/src/manager/index.ts +3 -9
  56. package/src/manager/types.ts +112 -4
  57. package/src/manager/wallet_manager.ts +204 -31
  58. package/src/types.ts +57 -14
  59. package/dest/providers/extension/extension_provider.d.ts +0 -17
  60. package/dest/providers/extension/extension_provider.d.ts.map +0 -1
  61. package/dest/providers/extension/extension_provider.js +0 -56
  62. package/dest/providers/extension/extension_wallet.d.ts +0 -95
  63. package/dest/providers/extension/extension_wallet.d.ts.map +0 -1
  64. package/dest/providers/extension/extension_wallet.js +0 -225
  65. package/dest/providers/extension/index.d.ts +0 -5
  66. package/dest/providers/extension/index.d.ts.map +0 -1
  67. package/src/providers/extension/extension_provider.ts +0 -72
  68. package/src/providers/extension/extension_wallet.ts +0 -275
  69. package/src/providers/extension/index.ts +0 -11
package/README.md CHANGED
@@ -4,19 +4,18 @@ 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
- ConnectRequest,
13
11
  DiscoveryRequest,
14
12
  DiscoveryResponse,
13
+ KeyExchangeRequest,
14
+ KeyExchangeResponse,
15
15
  WalletInfo,
16
16
  WalletMessage,
17
17
  WalletResponse,
18
- } from '@aztec/wallet-sdk/manager';
19
- import { ChainInfoSchema, WalletSchema, jsonStringify } from '@aztec/wallet-sdk/manager';
18
+ } from '@aztec/wallet-sdk/types';
20
19
  ```
21
20
 
22
21
  Cryptographic utilities for secure channel establishment are exported from `@aztec/wallet-sdk/crypto`:
@@ -25,434 +24,298 @@ Cryptographic utilities for secure channel establishment are exported from `@azt
25
24
  import type { EncryptedPayload, ExportedPublicKey } from '@aztec/wallet-sdk/crypto';
26
25
  import {
27
26
  decrypt,
28
- deriveSharedKey,
27
+ deriveSessionKeys,
29
28
  encrypt,
30
29
  exportPublicKey,
31
30
  generateKeyPair,
31
+ hashToEmoji,
32
32
  importPublicKey,
33
33
  } from '@aztec/wallet-sdk/crypto';
34
34
  ```
35
35
 
36
- ## Overview
37
-
38
- The Wallet SDK uses a **request-based discovery** model with **end-to-end encryption**:
39
-
40
- 1. **dApp requests wallets** for a specific chain/version via `WalletManager.getAvailableWallets({ chainInfo })`
41
- 2. **SDK broadcasts** a discovery message with chain information
42
- 3. **Your wallet responds** with its ECDH public key ONLY if it supports that specific network
43
- 4. **dApp receives** only compatible wallets
44
- 5. **dApp establishes secure channel** via ECDH key exchange (see [Secure Channel](#secure-channel))
45
- 6. **All subsequent communication** is encrypted using AES-256-GCM
46
-
47
- ### Transport Mechanisms
48
-
49
- 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:
50
-
51
- - **Extension wallets**: Use `window.postMessage` (examples shown throughout this guide)
52
- - **Web wallets**: Could use WebSockets, HTTP, or other protocols (see comments in examples for hypothetical WebSocket usage)
53
- - **Mobile wallets**: Could use deep links, app-to-app communication, or custom protocols
54
-
55
- The message format remains the same regardless of transport - only the delivery mechanism changes.
56
-
57
- ## Discovery Protocol
58
-
59
- ### 1. Listen for Discovery Requests
60
-
61
- **Extension wallet example:**
36
+ **For extension wallets**, pre-built connection handlers are available:
62
37
 
63
38
  ```typescript
64
- window.addEventListener('message', event => {
65
- if (event.source !== window) {
66
- return;
67
- }
68
-
69
- const data = JSON.parse(event.data);
70
-
71
- if (data.type === 'aztec-wallet-discovery') {
72
- handleDiscovery(data);
73
- }
74
- });
75
-
76
- // Using WebSocket:
77
- // websocket.on('message', (message) => {
78
- // const data = JSON.parse(message);
79
- // if (data.type === 'aztec-wallet-discovery') {
80
- // handleDiscovery(data);
81
- // }
82
- // });
39
+ import {
40
+ BackgroundConnectionHandler,
41
+ ContentScriptConnectionHandler,
42
+ } from '@aztec/wallet-sdk/extension/handlers';
83
43
  ```
84
44
 
85
- ### 2. Discovery Message Format
86
-
87
- Discovery messages have this structure:
45
+ ## Overview
88
46
 
89
- ```typescript
90
- {
91
- type: 'aztec-wallet-discovery',
92
- requestId: string, // UUID for tracking this request
93
- chainInfo: {
94
- chainId: Fr, // Chain ID
95
- version: Fr // Protocol version
96
- }
97
- }
98
- ```
47
+ The Wallet SDK uses a **two-phase connection model** with **end-to-end encryption**:
99
48
 
100
- ### 3. Check Network Support
49
+ ### Phase 1: Discovery
101
50
 
102
- Before responding, verify your wallet supports the requested network:
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
103
55
 
104
- ```typescript
105
- import { ChainInfoSchema } from '@aztec/wallet-sdk/manager';
56
+ ### Phase 2: Secure Channel Establishment
106
57
 
107
- function handleDiscovery(message: any) {
108
- const { requestId, chainInfo } = message;
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
109
64
 
110
- // Parse and validate chain info
111
- const { chainId, version } = ChainInfoSchema.parse(chainInfo);
65
+ ### Key Security Features
112
66
 
113
- // Check if your wallet supports this network
114
- const isSupported = checkNetworkSupport(chainId, version);
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
115
70
 
116
- if (!isSupported) {
117
- // Do NOT respond if you don't support this network
118
- return;
119
- }
71
+ ## Architecture for Extension Wallets
120
72
 
121
- // Respond if supported
122
- respondToDiscovery(requestId);
123
- }
124
73
  ```
125
-
126
- ### 4. Respond to Discovery
127
-
128
- If your wallet supports the network, respond with your wallet information:
129
-
130
- **Extension wallet example:**
131
-
132
- ```typescript
133
- import { jsonStringify } from '@aztec/wallet-sdk/manager';
134
-
135
- // Your wallet should generate a key pair on initialization.
136
- // This keypair should be recreated each session
137
- let walletKeyPair: CryptoKeyPair;
138
-
139
- async function initializeWallet() {
140
- // Generate ECDH key pair for secure channel establishment
141
- walletKeyPair = await generateKeyPair();
142
- }
143
-
144
- async function respondToDiscovery(requestId: string) {
145
- // Export the public key for sharing with dApps
146
- const publicKey = await exportPublicKey(walletKeyPair.publicKey);
147
-
148
- const response = {
149
- type: 'aztec-wallet-discovery-response',
150
- requestId,
151
- walletInfo: {
152
- id: 'my-aztec-wallet', // Unique wallet identifier
153
- name: 'My Aztec Wallet', // Display name
154
- icon: 'https://example.com/icon.png', // Optional icon URL
155
- version: '1.0.0', // Wallet version
156
- publicKey, // ECDH public key for secure channel (required)
157
- },
158
- };
159
-
160
- // Send as JSON string via window.postMessage
161
- window.postMessage(jsonStringify(response), '*');
162
- }
163
-
164
- // Using WebSocket:
165
- // websocket.send(jsonStringify(response));
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)──────┘
166
81
  ```
167
82
 
168
- **Important Notes:**
169
-
170
- - Both the SDK and wallets send messages as JSON strings (using `jsonStringify`)
171
- - Both the SDK and wallets must parse incoming JSON strings
172
- - Always use `jsonStringify` from `@aztec/foundation/json-rpc` for sending messages
173
- - Always parse incoming messages with `JSON.parse` and the proper schemas
174
- - The `publicKey` field is required for secure channel establishment
175
-
176
- ## Secure Channel
83
+ **Security model:**
177
84
 
178
- After discovery, the dApp establishes a secure encrypted channel with your wallet using ECDH key exchange and AES-256-GCM encryption. This ensures all wallet method calls and responses are encrypted end-to-end.
179
-
180
- ### Security Model
181
-
182
- - **ECDH Key Exchange**: Uses P-256 (secp256r1) elliptic curve for key agreement
183
- - **AES-256-GCM Encryption**: All messages after channel establishment are encrypted
184
- - **Per-Session Keys**: Each connection derives a unique shared secret
185
- - **MessageChannel (Extension wallets)**: Uses a private MessagePort for communication, not visible to other page scripts
186
-
187
- ### 1. Handle Connection Requests
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
188
90
 
189
- When a dApp connects, it sends a `ConnectRequest` containing its ECDH public key:
91
+ ## Using Pre-built Connection Handlers
190
92
 
191
- ```typescript
192
- interface ConnectRequest {
193
- type: 'aztec-wallet-connect';
194
- walletId: string; // Your wallet's ID
195
- appId: string; // Application identifier
196
- publicKey: ExportedPublicKey; // dApp's ECDH public key
197
- }
198
- ```
93
+ The SDK provides `BackgroundConnectionHandler` and `ContentScriptConnectionHandler` to handle the connection flow. These are the recommended way to build extension wallets.
199
94
 
200
- **Extension wallet example:**
95
+ ### Background Script Setup
201
96
 
202
97
  ```typescript
203
- import { decrypt, deriveSharedKey, encrypt, importPublicKey } from '@aztec/wallet-sdk/crypto';
204
-
205
- // Store connections by appId
206
- const connections = new Map<string, { sharedKey: CryptoKey }>();
207
-
208
- window.addEventListener('message', async event => {
209
- if (event.source !== window) return;
210
-
211
- const data = JSON.parse(event.data);
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
132
+ }
133
+ },
212
134
 
213
- if (data.type === 'aztec-wallet-connect') {
214
- await handleConnect(data, event.ports[0]);
215
- }
216
- });
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
+ },
217
140
 
218
- async function handleConnect(request: ConnectRequest, port: MessagePort) {
219
- // Import dApp's public key
220
- const dappPublicKey = await importPublicKey(request.publicKey);
141
+ // Called when a session is terminated
142
+ onSessionTerminated: (requestId) => {
143
+ console.log('Session terminated:', requestId);
144
+ },
221
145
 
222
- // Derive shared secret using our private key and dApp's public key
223
- const sharedKey = await deriveSharedKey(walletKeyPair.privateKey, dappPublicKey);
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
+ };
224
152
 
225
- // Store the connection
226
- connections.set(request.appId, { sharedKey });
153
+ const handler = new BackgroundConnectionHandler(config, transport, callbacks);
227
154
 
228
- // Set up encrypted message handler on the MessagePort
229
- port.onmessage = async event => {
230
- await handleEncryptedMessage(request.appId, event.data);
231
- };
155
+ // Initialize the handler to start listening
156
+ handler.initialize();
232
157
 
233
- port.start();
158
+ // User approves connection from wallet UI
159
+ function approveConnection(requestId: string) {
160
+ handler.approveDiscovery(requestId);
234
161
  }
235
- ```
236
-
237
- ### 2. Handle Encrypted Messages
238
-
239
- All wallet method calls arrive as encrypted payloads:
240
162
 
241
- ```typescript
242
- interface EncryptedPayload {
243
- iv: string; // Base64-encoded initialization vector
244
- ciphertext: string; // Base64-encoded encrypted data
163
+ // User denies connection
164
+ function denyConnection(requestId: string) {
165
+ handler.rejectDiscovery(requestId);
245
166
  }
246
- ```
247
-
248
- Decrypt incoming messages and encrypt responses:
249
167
 
250
- ```typescript
251
- async function handleEncryptedMessage(appId: string, encrypted: EncryptedPayload) {
252
- const connection = connections.get(appId);
253
- if (!connection) {
254
- console.error('Unknown connection');
255
- return;
256
- }
257
-
258
- try {
259
- // Decrypt the incoming message
260
- const message = await decrypt<WalletMessage>(connection.sharedKey, encrypted);
261
-
262
- const { type, messageId, args, chainInfo, walletId } = message;
263
-
264
- // Process the wallet method call
265
- const wallet = await getWalletForChain(chainInfo);
266
- const result = await wallet[type](...args);
267
-
268
- // Create response
269
- const response: WalletResponse = {
270
- messageId,
271
- result,
272
- walletId,
273
- };
274
-
275
- // Encrypt and send the response
276
- const encryptedResponse = await encrypt(connection.sharedKey, response);
277
- sendEncryptedResponse(appId, encryptedResponse);
278
- } catch (error) {
279
- // Send encrypted error response
280
- const errorResponse: WalletResponse = {
281
- messageId: message?.messageId ?? '',
282
- error: { message: error.message },
283
- walletId: message?.walletId ?? '',
284
- };
285
-
286
- const encryptedError = await encrypt(connection.sharedKey, errorResponse);
287
- sendEncryptedResponse(appId, encryptedError);
288
- }
168
+ // Send response back to dApp
169
+ async function sendWalletResponse(requestId: string, response: WalletResponse) {
170
+ await handler.sendResponse(requestId, response);
289
171
  }
290
- ```
291
-
292
- ### 3. Extension Wallet Architecture
293
-
294
- For browser extension wallets, the recommended architecture separates concerns:
295
172
 
173
+ // Clean up on tab close/navigate
174
+ browser.tabs.onRemoved.addListener((tabId) => {
175
+ handler.terminateForTab(tabId);
176
+ });
296
177
  ```
297
- ┌─────────────┐ window.postMessage ┌─────────────────┐ browser.runtime ┌──────────────────┐
298
- │ dApp │◄────────────────────────►│ Content Script │◄────────────────────►│ Background Script│
299
- │ (web page) │ (discovery only) │ (message relay)│ (encrypted msgs) │ (decrypt+process)│
300
- └─────────────┘ └─────────────────┘ └──────────────────┘
301
- │ │
302
- │ MessagePort (private channel) │
303
- └────────────────────────────────────────────┘
304
- (encrypted wallet method calls)
305
- ```
306
-
307
- **Security benefits:**
308
-
309
- - Content script never has access to private keys or shared secrets
310
- - All cryptographic operations happen in the background script (service worker)
311
- - MessagePort provides a private channel not visible to other page scripts
312
- - Only the initial connection handshake uses `window.postMessage`
313
178
 
314
- ## Message Format
315
-
316
- ### Wallet Method Request
317
-
318
- After discovery, dApps will call wallet methods. These arrive as:
179
+ ### Content Script Setup
319
180
 
320
181
  ```typescript
321
- {
322
- type: string, // Wallet method name from the Wallet interface
323
- messageId: string, // UUID for tracking this request
324
- args: unknown[], // Method arguments
325
- chainInfo: {
326
- chainId: Fr, // Same chain that was used in discovery
327
- version: Fr
328
- },
329
- appId: string, // Application identifier
330
- walletId: string // Your wallet's ID (from discovery response)
331
- }
332
- ```
333
-
334
- Example method calls:
335
-
336
- - `type: 'getAccounts'` - Get list of accounts
337
- - `type: 'getChainInfo'` - Get chain information
338
- - `type: 'sendTx'` - Send a transaction
339
- - `type: 'registerContract'` - Register a contract instance
182
+ import {
183
+ ContentScriptConnectionHandler,
184
+ type ContentScriptTransport,
185
+ } from '@aztec/wallet-sdk/extension/handlers';
340
186
 
341
- ### Wallet Method Response
187
+ const transport: ContentScriptTransport = {
188
+ sendToBackground: (message) => browser.runtime.sendMessage(message),
189
+ addBackgroundListener: (handler) => browser.runtime.onMessage.addListener(handler),
190
+ };
342
191
 
343
- Your wallet must respond with:
192
+ const handler = new ContentScriptConnectionHandler(transport);
344
193
 
345
- ```typescript
346
- {
347
- messageId: string, // MUST match the request's messageId
348
- result?: unknown, // Method result (if successful)
349
- error?: unknown, // Error (if failed)
350
- walletId: string // Your wallet's ID
351
- }
194
+ // Start listening for discovery requests and background messages
195
+ handler.start();
352
196
  ```
353
197
 
354
- ## Parsing Messages
198
+ ## Testing Your Integration (dApp Side)
355
199
 
356
- ### Using Zod Schemas
200
+ The `WalletManager` supports two patterns for consuming discovered wallets.
357
201
 
358
- Use the provided Zod schemas to parse and validate incoming messages:
202
+ ### Async Iterator Pattern
359
203
 
360
204
  ```typescript
361
- import { ChainInfoSchema, WalletSchema } from '@aztec/wallet-sdk/manager';
362
-
363
- // Parse chain info
364
- const chainInfo = ChainInfoSchema.parse(message.chainInfo);
365
-
366
- // Validate result against expected schema for a method
367
- const accountsResult = await wallet.getAccounts(...args);
368
- // The SDK handles schema validation on the client side
369
- ```
370
-
371
- 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.
372
-
373
- ## Error Handling
374
-
375
- ### Error Response Format
376
-
377
- Always send error responses with this structure:
205
+ import { Fr } from '@aztec/foundation/fields';
206
+ import { WalletManager } from '@aztec/wallet-sdk/manager';
207
+ import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
378
208
 
379
- ```typescript
380
- {
381
- messageId: string, // Match the request
382
- error: {
383
- message: string, // Error message
384
- code?: string, // Optional error code
385
- stack?: string // Optional stack trace
209
+ const discovery = WalletManager.configure({
210
+ extensions: { enabled: true },
211
+ }).getAvailableWallets({
212
+ chainInfo: {
213
+ chainId: new Fr(31337),
214
+ version: new Fr(1),
386
215
  },
387
- walletId: string
388
- }
389
- ```
390
-
391
- ### Common Error Scenarios
216
+ appId: 'my-dapp',
217
+ timeout: 60000,
218
+ });
392
219
 
393
- Common errors to handle within the encrypted message handler:
220
+ // Iterate over discovered wallets as they're approved
221
+ for await (const provider of discovery.wallets) {
222
+ console.log(`Found: ${provider.name}`);
394
223
 
395
- - **Network not supported**: Chain info doesn't match wallet's supported networks
396
- - **Unknown method**: The requested wallet method doesn't exist
397
- - **Invalid arguments**: Method arguments fail validation
398
- - **User rejection**: User declined the transaction or action
224
+ // Establish secure channel (key exchange)
225
+ const pending = await provider.establishSecureChannel('my-dapp');
399
226
 
400
- ### User Rejection Handling
227
+ // Display verification emojis to user
228
+ const emojis = hashToEmoji(pending.verificationHash);
229
+ console.log('Verify this matches your wallet:', emojis);
401
230
 
402
- If a user rejects an action:
231
+ // User confirms emojis match
232
+ const wallet = await pending.confirm();
403
233
 
404
- ```typescript
405
- {
406
- messageId: 'abc-123',
407
- error: {
408
- message: 'User rejected the request',
409
- code: 'USER_REJECTED'
410
- },
411
- walletId: 'my-wallet'
234
+ // All calls are now encrypted
235
+ const accounts = await wallet.getAccounts();
236
+ console.log('Accounts:', accounts);
412
237
  }
413
- ```
414
238
 
415
- ## Testing Your Integration
416
-
417
- ### WalletManager
239
+ // Cancel discovery when done or on cleanup
240
+ discovery.cancel();
241
+ ```
418
242
 
419
- In a dApp using the Wallet SDK:
243
+ ### Callback Pattern
420
244
 
421
245
  ```typescript
422
- import { Fr } from '@aztec/foundation/curves/bn254';
423
- 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';
424
249
 
425
- const manager = WalletManager.configure({
426
- extensions: { enabled: true },
427
- });
250
+ const discoveredProviders: WalletProvider[] = [];
428
251
 
429
- // Discover wallets
430
- const wallets = await manager.getAvailableWallets({
252
+ const discovery = WalletManager.configure({
253
+ extensions: { enabled: true },
254
+ }).getAvailableWallets({
431
255
  chainInfo: {
432
256
  chainId: new Fr(31337),
433
- 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
434
265
  },
435
- timeout: 2000,
436
266
  });
437
267
 
438
- 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);
439
271
 
440
- // Connect to your wallet
441
- const walletProvider = wallets.find(w => w.id === 'my-aztec-wallet');
442
- if (walletProvider) {
443
- 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');
444
275
 
445
- // Test wallet methods from the Wallet interface
446
- const accounts = await wallet.getAccounts();
447
- console.log('Accounts:', accounts);
276
+ // Show verification UI
277
+ const emojis = hashToEmoji(pending.verificationHash);
278
+ showVerificationDialog(emojis);
448
279
 
449
- const chainInfo = await wallet.getChainInfo();
450
- console.log('Chain info:', chainInfo);
280
+ // User confirms
281
+ const wallet = await pending.confirm();
282
+ return wallet;
451
283
  }
452
284
  ```
453
285
 
454
- ## Reference Implementation
286
+ ### React Hook Example
455
287
 
456
- For a complete reference implementation, see the demo wallet at:
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]);
457
318
 
458
- - Repository: `~/repos/demo-wallet`
319
+ return { providers, isDiscovering, cancel: () => discoveryRef.current?.cancel() };
320
+ }
321
+ ```