@aztec/wallet-sdk 0.0.1-commit.d1f2d6c → 0.0.1-commit.d431d1c

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 (67) hide show
  1. package/README.md +294 -217
  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 +4 -9
  5. package/dest/crypto.d.ts +50 -59
  6. package/dest/crypto.d.ts.map +1 -1
  7. package/dest/crypto.js +108 -202
  8. package/dest/manager/index.d.ts +8 -2
  9. package/dest/manager/index.d.ts.map +1 -1
  10. package/dest/manager/index.js +6 -0
  11. package/dest/manager/types.d.ts +6 -88
  12. package/dest/manager/types.d.ts.map +1 -1
  13. package/dest/manager/types.js +1 -17
  14. package/dest/manager/wallet_manager.d.ts +7 -50
  15. package/dest/manager/wallet_manager.d.ts.map +1 -1
  16. package/dest/manager/wallet_manager.js +44 -174
  17. package/dest/providers/extension/extension_provider.d.ts +63 -0
  18. package/dest/providers/extension/extension_provider.d.ts.map +1 -0
  19. package/dest/providers/extension/extension_provider.js +124 -0
  20. package/dest/providers/extension/extension_wallet.d.ts +155 -0
  21. package/dest/providers/extension/extension_wallet.d.ts.map +1 -0
  22. package/dest/{extension/provider → providers/extension}/extension_wallet.js +95 -48
  23. package/dest/providers/extension/index.d.ts +6 -0
  24. package/dest/providers/extension/index.d.ts.map +1 -0
  25. package/dest/{extension/provider → providers/extension}/index.js +2 -0
  26. package/dest/types.d.ts +12 -43
  27. package/dest/types.d.ts.map +1 -1
  28. package/dest/types.js +2 -3
  29. package/package.json +9 -10
  30. package/src/base-wallet/base_wallet.ts +8 -15
  31. package/src/crypto.ts +113 -237
  32. package/src/manager/index.ts +10 -2
  33. package/src/manager/types.ts +5 -91
  34. package/src/manager/wallet_manager.ts +46 -192
  35. package/src/providers/extension/extension_provider.ts +167 -0
  36. package/src/{extension/provider → providers/extension}/extension_wallet.ts +110 -52
  37. package/src/providers/extension/index.ts +5 -0
  38. package/src/types.ts +10 -44
  39. package/dest/emoji_alphabet.d.ts +0 -35
  40. package/dest/emoji_alphabet.d.ts.map +0 -1
  41. package/dest/emoji_alphabet.js +0 -299
  42. package/dest/extension/handlers/background_connection_handler.d.ts +0 -158
  43. package/dest/extension/handlers/background_connection_handler.d.ts.map +0 -1
  44. package/dest/extension/handlers/background_connection_handler.js +0 -258
  45. package/dest/extension/handlers/content_script_connection_handler.d.ts +0 -56
  46. package/dest/extension/handlers/content_script_connection_handler.d.ts.map +0 -1
  47. package/dest/extension/handlers/content_script_connection_handler.js +0 -174
  48. package/dest/extension/handlers/index.d.ts +0 -12
  49. package/dest/extension/handlers/index.d.ts.map +0 -1
  50. package/dest/extension/handlers/index.js +0 -10
  51. package/dest/extension/handlers/internal_message_types.d.ts +0 -63
  52. package/dest/extension/handlers/internal_message_types.d.ts.map +0 -1
  53. package/dest/extension/handlers/internal_message_types.js +0 -22
  54. package/dest/extension/provider/extension_provider.d.ts +0 -107
  55. package/dest/extension/provider/extension_provider.d.ts.map +0 -1
  56. package/dest/extension/provider/extension_provider.js +0 -160
  57. package/dest/extension/provider/extension_wallet.d.ts +0 -131
  58. package/dest/extension/provider/extension_wallet.d.ts.map +0 -1
  59. package/dest/extension/provider/index.d.ts +0 -3
  60. package/dest/extension/provider/index.d.ts.map +0 -1
  61. package/src/emoji_alphabet.ts +0 -317
  62. package/src/extension/handlers/background_connection_handler.ts +0 -423
  63. package/src/extension/handlers/content_script_connection_handler.ts +0 -246
  64. package/src/extension/handlers/index.ts +0 -25
  65. package/src/extension/handlers/internal_message_types.ts +0 -69
  66. package/src/extension/provider/extension_provider.ts +0 -233
  67. package/src/extension/provider/index.ts +0 -7
package/README.md CHANGED
@@ -10,8 +10,6 @@ All types and utilities needed for wallet integration are exported from `@aztec/
10
10
  import type {
11
11
  DiscoveryRequest,
12
12
  DiscoveryResponse,
13
- KeyExchangeRequest,
14
- KeyExchangeResponse,
15
13
  WalletInfo,
16
14
  WalletMessage,
17
15
  WalletResponse,
@@ -24,298 +22,377 @@ Cryptographic utilities for secure channel establishment are exported from `@azt
24
22
  import type { EncryptedPayload, ExportedPublicKey } from '@aztec/wallet-sdk/crypto';
25
23
  import {
26
24
  decrypt,
27
- deriveSessionKeys,
25
+ deriveSharedKey,
28
26
  encrypt,
29
27
  exportPublicKey,
30
28
  generateKeyPair,
29
+ hashSharedSecret,
31
30
  hashToEmoji,
32
31
  importPublicKey,
33
32
  } from '@aztec/wallet-sdk/crypto';
34
33
  ```
35
34
 
36
- **For extension wallets**, pre-built connection handlers are available:
35
+ ## Overview
36
+
37
+ The Wallet SDK uses a **unified discovery and connection** model with **end-to-end encryption**:
38
+
39
+ 1. **dApp requests wallets** for a specific chain/version via `WalletManager.getAvailableWallets({ chainInfo })`
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
45
+
46
+ ### Key Features
47
+
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
51
+
52
+ ### Transport Mechanisms
53
+
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.
55
+
56
+ ## Discovery Protocol
57
+
58
+ ### 1. Listen for Discovery Requests
59
+
60
+ **Extension wallet (content script):**
61
+
62
+ ```typescript
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 {
70
+ return;
71
+ }
72
+
73
+ if (data.type === 'aztec-wallet-discovery') {
74
+ await handleDiscoveryRequest(data);
75
+ }
76
+ });
77
+ ```
78
+
79
+ ### 2. Discovery Message Format
80
+
81
+ Discovery messages have this structure:
82
+
83
+ ```typescript
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
89
+ }
90
+ ```
91
+
92
+ ### 3. Handle Discovery and Establish Secure Channel
93
+
94
+ When your wallet receives a discovery request:
95
+
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
100
+
101
+ **Extension wallet (background script):**
37
102
 
38
103
  ```typescript
39
104
  import {
40
- BackgroundConnectionHandler,
41
- ContentScriptConnectionHandler,
42
- } from '@aztec/wallet-sdk/extension/handlers';
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');
126
+ }
127
+
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);
131
+
132
+ // Compute verification hash for anti-MITM verification
133
+ const verificationHash = await hashSharedSecret(sharedKey);
134
+
135
+ // Store the session with verificationHash (emoji computed lazily for display)
136
+ sessions.set(request.requestId, { sharedKey, verificationHash, tabId });
137
+
138
+ const response: DiscoveryResponse = {
139
+ type: 'aztec-wallet-discovery-response',
140
+ requestId: request.requestId,
141
+ walletInfo: {
142
+ id: 'my-aztec-wallet',
143
+ name: 'My Aztec Wallet',
144
+ version: '1.0.0',
145
+ publicKey: walletPublicKey,
146
+ },
147
+ };
148
+
149
+ return { success: true, response };
150
+ }
43
151
  ```
44
152
 
45
- ## Overview
153
+ **Content script (creates MessageChannel and sends response):**
46
154
 
47
- The Wallet SDK uses a **two-phase connection model** with **end-to-end encryption**:
155
+ ```typescript
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();
48
177
 
49
- ### Phase 1: Discovery
178
+ // Send response with port2 to the page
179
+ window.postMessage(JSON.stringify(result.response), '*', [channel.port2]);
180
+ }
181
+ ```
50
182
 
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
183
+ ### 4. Discovery Response Format
55
184
 
56
- ### Phase 2: Secure Channel Establishment
185
+ ```typescript
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
+ }
57
191
 
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
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
198
+ }
199
+ ```
64
200
 
65
- ### Key Security Features
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.
66
202
 
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
203
+ ## Secure Communication
70
204
 
71
- ## Architecture for Extension Wallets
205
+ ### Architecture for Extension Wallets
72
206
 
73
207
  ```
74
208
  ┌─────────────┐ window.postMessage ┌─────────────────┐ browser.runtime ┌──────────────────┐
75
- │ dApp │◄──(discovery + port)────►│ Content Script │◄────────────────────►│ Background Script│
76
- │ (web page) │ │ (message relay)│ │ (crypto+state)
209
+ │ dApp │◄───(discovery only)─────►│ Content Script │◄────────────────────►│ Background Script│
210
+ │ (web page) │ │ (message relay)│ │ (decrypt+process)│
77
211
  └─────────────┘ └─────────────────┘ └──────────────────┘
78
212
  │ │
79
- MessagePort
80
- └──────────(key exchange + encrypted)──────┘
213
+ MessagePort (private channel)
214
+ └──────────(encrypted messages)────────────┘
81
215
  ```
82
216
 
83
- **Security model:**
217
+ **Security benefits:**
84
218
 
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
219
+ - Content script never has access to private keys or shared secrets
88
220
  - All cryptographic operations happen in the background script (service worker)
89
- - Anti-MITM verification (emoji grid) ensures both parties derived the same keys
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
90
223
 
91
- ## Using Pre-built Connection Handlers
224
+ ### Handle Encrypted Messages
92
225
 
93
- The SDK provides `BackgroundConnectionHandler` and `ContentScriptConnectionHandler` to handle the connection flow. These are the recommended way to build extension wallets.
94
-
95
- ### Background Script Setup
226
+ All wallet method calls arrive as encrypted payloads on the MessagePort:
96
227
 
97
228
  ```typescript
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
- },
134
-
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
- },
229
+ interface EncryptedPayload {
230
+ iv: string; // Base64-encoded initialization vector
231
+ ciphertext: string; // Base64-encoded encrypted data
232
+ }
233
+ ```
140
234
 
141
- // Called when a session is terminated
142
- onSessionTerminated: (requestId) => {
143
- console.log('Session terminated:', requestId);
144
- },
235
+ **Background script:**
145
236
 
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
- };
237
+ ```typescript
238
+ import { decrypt, encrypt } from '@aztec/wallet-sdk/crypto';
239
+
240
+ async function handleSecureMessage(requestId: string, encrypted: EncryptedPayload) {
241
+ const session = sessions.get(requestId);
242
+ if (!session) return;
243
+
244
+ try {
245
+ // Decrypt the incoming message
246
+ const message = await decrypt<WalletMessage>(session.sharedKey, encrypted);
247
+ const { type, messageId, args, chainInfo, walletId } = message;
248
+
249
+ // Process the wallet method call
250
+ const wallet = await getWalletForChain(chainInfo);
251
+ const result = await wallet[type](...args);
252
+
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
+ });
263
+ } catch (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
272
+ }
273
+ }
274
+ ```
152
275
 
153
- const handler = new BackgroundConnectionHandler(config, transport, callbacks);
276
+ ## Message Formats
154
277
 
155
- // Initialize the handler to start listening
156
- handler.initialize();
278
+ ### Wallet Method Request (Decrypted)
157
279
 
158
- // User approves connection from wallet UI
159
- function approveConnection(requestId: string) {
160
- handler.approveDiscovery(requestId);
280
+ ```typescript
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
161
288
  }
289
+ ```
162
290
 
163
- // User denies connection
164
- function denyConnection(requestId: string) {
165
- handler.rejectDiscovery(requestId);
166
- }
291
+ ### Wallet Method Response
167
292
 
168
- // Send response back to dApp
169
- async function sendWalletResponse(requestId: string, response: WalletResponse) {
170
- await handler.sendResponse(requestId, 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
171
299
  }
172
-
173
- // Clean up on tab close/navigate
174
- browser.tabs.onRemoved.addListener((tabId) => {
175
- handler.terminateForTab(tabId);
176
- });
177
300
  ```
178
301
 
179
- ### Content Script Setup
302
+ ## Anti-MITM Verification
180
303
 
181
- ```typescript
182
- import {
183
- ContentScriptConnectionHandler,
184
- type ContentScriptTransport,
185
- } from '@aztec/wallet-sdk/extension/handlers';
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.
186
305
 
187
- const transport: ContentScriptTransport = {
188
- sendToBackground: (message) => browser.runtime.sendMessage(message),
189
- addBackgroundListener: (handler) => browser.runtime.onMessage.addListener(handler),
190
- };
306
+ ```typescript
307
+ import { hashSharedSecret } from '@aztec/wallet-sdk/crypto';
191
308
 
192
- const handler = new ContentScriptConnectionHandler(transport);
309
+ // Compute verification hash from shared key
310
+ const verificationHash = await hashSharedSecret(sharedKey);
193
311
 
194
- // Start listening for discovery requests and background messages
195
- handler.start();
312
+ // Store verificationHash in session - this is the cryptographic proof
313
+ sessions.set(requestId, { sharedKey, verificationHash, tabId });
196
314
  ```
197
315
 
198
- ## Testing Your Integration (dApp Side)
199
-
200
- The `WalletManager` supports two patterns for consuming discovered wallets.
201
-
202
- ### Async Iterator Pattern
316
+ For user-friendly display, convert the hash to an emoji sequence:
203
317
 
204
318
  ```typescript
205
- import { Fr } from '@aztec/foundation/fields';
206
- import { WalletManager } from '@aztec/wallet-sdk/manager';
207
319
  import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
208
320
 
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
- });
321
+ // Convert to emoji only when displaying to the user
322
+ const emoji = hashToEmoji(verificationHash); // e.g., "🔵🦋🎯🐼"
323
+ ```
219
324
 
220
- // Iterate over discovered wallets as they're approved
221
- for await (const provider of discovery.wallets) {
222
- console.log(`Found: ${provider.name}`);
325
+ The dApp displays the same emoji sequence. If they match, the connection is secure.
223
326
 
224
- // Establish secure channel (key exchange)
225
- const pending = await provider.establishSecureChannel('my-dapp');
327
+ ## Session Management
226
328
 
227
- // Display verification emojis to user
228
- const emojis = hashToEmoji(pending.verificationHash);
229
- console.log('Verify this matches your wallet:', emojis);
329
+ Sessions should be cleaned up when:
230
330
 
231
- // User confirms emojis match
232
- const wallet = await pending.confirm();
331
+ - **Tab closes**: Browser tabs API `onRemoved` event
332
+ - **Tab navigates**: Browser tabs API `onUpdated` event with `status === 'loading'`
233
333
 
234
- // All calls are now encrypted
235
- const accounts = await wallet.getAccounts();
236
- console.log('Accounts:', accounts);
237
- }
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);
340
+ }
341
+ }
342
+ });
238
343
 
239
- // Cancel discovery when done or on cleanup
240
- discovery.cancel();
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
+ }
351
+ }
352
+ }
353
+ });
241
354
  ```
242
355
 
243
- ### Callback Pattern
356
+ ## Testing Your Integration
357
+
358
+ ### Using WalletManager
244
359
 
245
360
  ```typescript
246
361
  import { Fr } from '@aztec/foundation/fields';
247
- import { WalletManager, type WalletProvider } from '@aztec/wallet-sdk/manager';
248
- import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
249
-
250
- const discoveredProviders: WalletProvider[] = [];
362
+ import { WalletManager, hashToEmoji } from '@aztec/wallet-sdk/manager';
251
363
 
252
- const discovery = WalletManager.configure({
364
+ const manager = WalletManager.configure({
253
365
  extensions: { enabled: true },
254
- }).getAvailableWallets({
366
+ });
367
+
368
+ // Discover wallets (secure channel established automatically)
369
+ const wallets = await manager.getAvailableWallets({
255
370
  chainInfo: {
256
371
  chainId: new Fr(31337),
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
372
+ version: new Fr(0),
265
373
  },
374
+ timeout: 2000,
266
375
  });
267
376
 
268
- // Wait for discovery to complete (or cancel early with discovery.cancel())
269
- await discovery.done;
270
- console.log('Discovery complete, found:', discoveredProviders.length);
271
-
272
- // Connect to a selected provider
273
- async function connectToWallet(provider: WalletProvider) {
274
- const pending = await provider.establishSecureChannel('my-dapp');
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
+ }
275
382
 
276
- // Show verification UI
277
- const emojis = hashToEmoji(pending.verificationHash);
278
- showVerificationDialog(emojis);
383
+ // Connect and use
384
+ const walletProvider = wallets.find(w => w.id === 'my-aztec-wallet');
385
+ if (walletProvider) {
386
+ const wallet = await walletProvider.connect('my-app-id');
279
387
 
280
- // User confirms
281
- const wallet = await pending.confirm();
282
- return wallet;
388
+ // All calls are automatically encrypted
389
+ const accounts = await wallet.getAccounts();
390
+ console.log('Accounts:', accounts);
283
391
  }
284
392
  ```
285
393
 
286
- ### React Hook Example
287
-
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));
394
+ ## Reference Implementation
312
395
 
313
- return () => {
314
- discovery.cancel();
315
- discoveryRef.current = null;
316
- };
317
- }, [chainInfo.chainId.toString(), chainInfo.version.toString(), appId]);
396
+ For a complete reference implementation, see the demo wallet at:
318
397
 
319
- return { providers, isDiscovering, cancel: () => discoveryRef.current?.cancel() };
320
- }
321
- ```
398
+ - Repository: `~/repos/demo-wallet`
@@ -1,6 +1,5 @@
1
1
  import type { Account } from '@aztec/aztec.js/account';
2
2
  import type { CallIntent, IntentInnerHash } from '@aztec/aztec.js/authorization';
3
- import { type InteractionWaitOptions, type SendReturn } from '@aztec/aztec.js/contracts';
4
3
  import type { FeePaymentMethod } from '@aztec/aztec.js/fee';
5
4
  import type { Aliased, BatchResults, BatchedMethod, PrivateEvent, PrivateEventFilter, ProfileOptions, SendOptions, SimulateOptions, Wallet } from '@aztec/aztec.js/wallet';
6
5
  import { AccountFeePaymentMethodOptions } from '@aztec/entrypoints/account';
@@ -14,7 +13,7 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
14
13
  import { type ContractInstanceWithAddress } from '@aztec/stdlib/contract';
15
14
  import { GasSettings } from '@aztec/stdlib/gas';
16
15
  import type { AztecNode } from '@aztec/stdlib/interfaces/client';
17
- import type { TxExecutionRequest, TxProfileResult, TxSimulationResult, UtilitySimulationResult } from '@aztec/stdlib/tx';
16
+ import type { TxExecutionRequest, TxHash, TxProfileResult, TxReceipt, TxSimulationResult, UtilitySimulationResult } from '@aztec/stdlib/tx';
18
17
  import { ExecutionPayload } from '@aztec/stdlib/tx';
19
18
  /**
20
19
  * Options to configure fee payment for a transaction
@@ -84,9 +83,10 @@ export declare abstract class BaseWallet implements Wallet {
84
83
  registerContract(instance: ContractInstanceWithAddress, artifact?: ContractArtifact, secretKey?: Fr): Promise<ContractInstanceWithAddress>;
85
84
  simulateTx(executionPayload: ExecutionPayload, opts: SimulateOptions): Promise<TxSimulationResult>;
86
85
  profileTx(executionPayload: ExecutionPayload, opts: ProfileOptions): Promise<TxProfileResult>;
87
- sendTx<W extends InteractionWaitOptions = undefined>(executionPayload: ExecutionPayload, opts: SendOptions<W>): Promise<SendReturn<W>>;
86
+ sendTx(executionPayload: ExecutionPayload, opts: SendOptions): Promise<TxHash>;
88
87
  protected contextualizeError(err: Error, ...context: string[]): Error;
89
88
  simulateUtility(call: FunctionCall, authwits?: AuthWitness[]): Promise<UtilitySimulationResult>;
89
+ getTxReceipt(txHash: TxHash): Promise<TxReceipt>;
90
90
  getPrivateEvents<T>(eventDef: EventMetadataDefinition, eventFilter: PrivateEventFilter): Promise<PrivateEvent<T>[]>;
91
91
  getContractMetadata(address: AztecAddress): Promise<{
92
92
  instance: ContractInstanceWithAddress | undefined;
@@ -101,4 +101,4 @@ export declare abstract class BaseWallet implements Wallet {
101
101
  isContractClassPubliclyRegistered: boolean;
102
102
  }>;
103
103
  }
104
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzZV93YWxsZXQuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9iYXNlLXdhbGxldC9iYXNlX3dhbGxldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxPQUFPLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsZUFBZSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDakYsT0FBTyxFQUFFLEtBQUssc0JBQXNCLEVBQVcsS0FBSyxVQUFVLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNsRyxPQUFPLEtBQUssRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRTVELE9BQU8sS0FBSyxFQUNWLE9BQU8sRUFDUCxZQUFZLEVBQ1osYUFBYSxFQUNiLFlBQVksRUFDWixrQkFBa0IsRUFDbEIsY0FBYyxFQUNkLFdBQVcsRUFDWCxlQUFlLEVBQ2YsTUFBTSxFQUNQLE1BQU0sd0JBQXdCLENBQUM7QUFPaEMsT0FBTyxFQUFFLDhCQUE4QixFQUF3QyxNQUFNLDRCQUE0QixDQUFDO0FBQ2xILE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQy9ELE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUVwRCxPQUFPLEtBQUssRUFBRSxRQUFRLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN4RCxPQUFPLEtBQUssRUFBRSxHQUFHLEVBQXNCLE1BQU0sbUJBQW1CLENBQUM7QUFDakUsT0FBTyxFQUNMLEtBQUssZ0JBQWdCLEVBQ3JCLEtBQUssdUJBQXVCLEVBQzVCLEtBQUssWUFBWSxFQUVsQixNQUFNLG1CQUFtQixDQUFDO0FBQzNCLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQzlELE9BQU8sS0FBSyxFQUFFLFlBQVksRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ2hF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
104
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzZV93YWxsZXQuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9iYXNlLXdhbGxldC9iYXNlX3dhbGxldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxPQUFPLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsZUFBZSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDakYsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM1RCxPQUFPLEtBQUssRUFDVixPQUFPLEVBQ1AsWUFBWSxFQUNaLGFBQWEsRUFDYixZQUFZLEVBQ1osa0JBQWtCLEVBQ2xCLGNBQWMsRUFDZCxXQUFXLEVBQ1gsZUFBZSxFQUNmLE1BQU0sRUFDUCxNQUFNLHdCQUF3QixDQUFDO0FBT2hDLE9BQU8sRUFBRSw4QkFBOEIsRUFBd0MsTUFBTSw0QkFBNEIsQ0FBQztBQUNsSCxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUMvRCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFcEQsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDeEQsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFzQixNQUFNLG1CQUFtQixDQUFDO0FBQ2pFLE9BQU8sRUFDTCxLQUFLLGdCQUFnQixFQUNyQixLQUFLLHVCQUF1QixFQUM1QixLQUFLLFlBQVksRUFFbEIsTUFBTSxtQkFBbUIsQ0FBQztBQUMzQixPQUFPLEtBQUssRUFBRSxXQUFXLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUM5RCxPQUFPLEtBQUssRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUNoRSxPQUFPLEVBQ0wsS0FBSywyQkFBMkIsRUFHakMsTUFBTSx3QkFBd0IsQ0FBQztBQUVoQyxPQUFPLEVBQU8sV0FB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