@aztec/wallet-sdk 0.0.1-commit.d431d1c → 0.0.1-commit.e310a4c8
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.
- package/README.md +217 -294
- package/dest/base-wallet/base_wallet.d.ts +19 -6
- package/dest/base-wallet/base_wallet.d.ts.map +1 -1
- package/dest/base-wallet/base_wallet.js +25 -9
- package/dest/crypto.d.ts +59 -50
- package/dest/crypto.d.ts.map +1 -1
- package/dest/crypto.js +202 -108
- package/dest/emoji_alphabet.d.ts +35 -0
- package/dest/emoji_alphabet.d.ts.map +1 -0
- package/dest/emoji_alphabet.js +299 -0
- package/dest/extension/handlers/background_connection_handler.d.ts +158 -0
- package/dest/extension/handlers/background_connection_handler.d.ts.map +1 -0
- package/dest/extension/handlers/background_connection_handler.js +258 -0
- package/dest/extension/handlers/content_script_connection_handler.d.ts +56 -0
- package/dest/extension/handlers/content_script_connection_handler.d.ts.map +1 -0
- package/dest/extension/handlers/content_script_connection_handler.js +174 -0
- package/dest/extension/handlers/index.d.ts +12 -0
- package/dest/extension/handlers/index.d.ts.map +1 -0
- package/dest/extension/handlers/index.js +10 -0
- package/dest/extension/handlers/internal_message_types.d.ts +63 -0
- package/dest/extension/handlers/internal_message_types.d.ts.map +1 -0
- package/dest/extension/handlers/internal_message_types.js +22 -0
- package/dest/extension/provider/extension_provider.d.ts +107 -0
- package/dest/extension/provider/extension_provider.d.ts.map +1 -0
- package/dest/extension/provider/extension_provider.js +160 -0
- package/dest/extension/provider/extension_wallet.d.ts +131 -0
- package/dest/extension/provider/extension_wallet.d.ts.map +1 -0
- package/dest/{providers/extension → extension/provider}/extension_wallet.js +48 -95
- package/dest/extension/provider/index.d.ts +3 -0
- package/dest/extension/provider/index.d.ts.map +1 -0
- package/dest/{providers/extension → extension/provider}/index.js +0 -2
- package/dest/manager/index.d.ts +2 -8
- package/dest/manager/index.d.ts.map +1 -1
- package/dest/manager/index.js +0 -6
- package/dest/manager/types.d.ts +88 -6
- package/dest/manager/types.d.ts.map +1 -1
- package/dest/manager/types.js +17 -1
- package/dest/manager/wallet_manager.d.ts +50 -7
- package/dest/manager/wallet_manager.d.ts.map +1 -1
- package/dest/manager/wallet_manager.js +174 -44
- package/dest/types.d.ts +43 -12
- package/dest/types.d.ts.map +1 -1
- package/dest/types.js +3 -2
- package/package.json +10 -9
- package/src/base-wallet/base_wallet.ts +35 -17
- package/src/crypto.ts +237 -113
- package/src/emoji_alphabet.ts +317 -0
- package/src/extension/handlers/background_connection_handler.ts +423 -0
- package/src/extension/handlers/content_script_connection_handler.ts +246 -0
- package/src/extension/handlers/index.ts +25 -0
- package/src/extension/handlers/internal_message_types.ts +69 -0
- package/src/extension/provider/extension_provider.ts +233 -0
- package/src/{providers/extension → extension/provider}/extension_wallet.ts +52 -110
- package/src/extension/provider/index.ts +7 -0
- package/src/manager/index.ts +2 -10
- package/src/manager/types.ts +91 -5
- package/src/manager/wallet_manager.ts +192 -46
- package/src/types.ts +44 -10
- package/dest/providers/extension/extension_provider.d.ts +0 -63
- package/dest/providers/extension/extension_provider.d.ts.map +0 -1
- package/dest/providers/extension/extension_provider.js +0 -124
- package/dest/providers/extension/extension_wallet.d.ts +0 -155
- package/dest/providers/extension/extension_wallet.d.ts.map +0 -1
- package/dest/providers/extension/index.d.ts +0 -6
- package/dest/providers/extension/index.d.ts.map +0 -1
- package/src/providers/extension/extension_provider.ts +0 -167
- package/src/providers/extension/index.ts +0 -5
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ 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,
|
|
13
15
|
WalletInfo,
|
|
14
16
|
WalletMessage,
|
|
15
17
|
WalletResponse,
|
|
@@ -22,377 +24,298 @@ Cryptographic utilities for secure channel establishment are exported from `@azt
|
|
|
22
24
|
import type { EncryptedPayload, ExportedPublicKey } from '@aztec/wallet-sdk/crypto';
|
|
23
25
|
import {
|
|
24
26
|
decrypt,
|
|
25
|
-
|
|
27
|
+
deriveSessionKeys,
|
|
26
28
|
encrypt,
|
|
27
29
|
exportPublicKey,
|
|
28
30
|
generateKeyPair,
|
|
29
|
-
hashSharedSecret,
|
|
30
31
|
hashToEmoji,
|
|
31
32
|
importPublicKey,
|
|
32
33
|
} from '@aztec/wallet-sdk/crypto';
|
|
33
34
|
```
|
|
34
35
|
|
|
35
|
-
|
|
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):**
|
|
36
|
+
**For extension wallets**, pre-built connection handlers are available:
|
|
102
37
|
|
|
103
38
|
```typescript
|
|
104
39
|
import {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
}
|
|
40
|
+
BackgroundConnectionHandler,
|
|
41
|
+
ContentScriptConnectionHandler,
|
|
42
|
+
} from '@aztec/wallet-sdk/extension/handlers';
|
|
151
43
|
```
|
|
152
44
|
|
|
153
|
-
|
|
45
|
+
## Overview
|
|
154
46
|
|
|
155
|
-
|
|
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();
|
|
47
|
+
The Wallet SDK uses a **two-phase connection model** with **end-to-end encryption**:
|
|
177
48
|
|
|
178
|
-
|
|
179
|
-
window.postMessage(JSON.stringify(result.response), '*', [channel.port2]);
|
|
180
|
-
}
|
|
181
|
-
```
|
|
49
|
+
### Phase 1: Discovery
|
|
182
50
|
|
|
183
|
-
|
|
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
|
|
184
55
|
|
|
185
|
-
|
|
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
|
-
}
|
|
56
|
+
### Phase 2: Secure Channel Establishment
|
|
191
57
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
```
|
|
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
|
|
200
64
|
|
|
201
|
-
|
|
65
|
+
### Key Security Features
|
|
202
66
|
|
|
203
|
-
|
|
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
|
|
204
70
|
|
|
205
|
-
|
|
71
|
+
## Architecture for Extension Wallets
|
|
206
72
|
|
|
207
73
|
```
|
|
208
74
|
┌─────────────┐ window.postMessage ┌─────────────────┐ browser.runtime ┌──────────────────┐
|
|
209
|
-
│ dApp
|
|
210
|
-
│ (web page) │ │ (message relay)│ │ (
|
|
75
|
+
│ dApp │◄──(discovery + port)────►│ Content Script │◄────────────────────►│ Background Script│
|
|
76
|
+
│ (web page) │ │ (message relay)│ │ (crypto+state) │
|
|
211
77
|
└─────────────┘ └─────────────────┘ └──────────────────┘
|
|
212
78
|
│ │
|
|
213
|
-
│
|
|
214
|
-
└──────────(encrypted
|
|
79
|
+
│ MessagePort │
|
|
80
|
+
└──────────(key exchange + encrypted)──────┘
|
|
215
81
|
```
|
|
216
82
|
|
|
217
|
-
**Security
|
|
83
|
+
**Security model:**
|
|
218
84
|
|
|
219
|
-
-
|
|
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
|
|
220
88
|
- All cryptographic operations happen in the background script (service worker)
|
|
221
|
-
-
|
|
222
|
-
- Only discovery uses `window.postMessage`; all wallet calls are encrypted on the MessagePort
|
|
89
|
+
- Anti-MITM verification (emoji grid) ensures both parties derived the same keys
|
|
223
90
|
|
|
224
|
-
|
|
91
|
+
## Using Pre-built Connection Handlers
|
|
225
92
|
|
|
226
|
-
|
|
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
|
|
227
96
|
|
|
228
97
|
```typescript
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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';
|
|
234
105
|
|
|
235
|
-
|
|
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
|
+
},
|
|
236
134
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
```
|
|
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
|
+
},
|
|
275
140
|
|
|
276
|
-
|
|
141
|
+
// Called when a session is terminated
|
|
142
|
+
onSessionTerminated: (requestId) => {
|
|
143
|
+
console.log('Session terminated:', requestId);
|
|
144
|
+
},
|
|
277
145
|
|
|
278
|
-
|
|
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
|
+
};
|
|
279
152
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
153
|
+
const handler = new BackgroundConnectionHandler(config, transport, callbacks);
|
|
154
|
+
|
|
155
|
+
// Initialize the handler to start listening
|
|
156
|
+
handler.initialize();
|
|
157
|
+
|
|
158
|
+
// User approves connection from wallet UI
|
|
159
|
+
function approveConnection(requestId: string) {
|
|
160
|
+
handler.approveDiscovery(requestId);
|
|
288
161
|
}
|
|
289
|
-
```
|
|
290
162
|
|
|
291
|
-
|
|
163
|
+
// User denies connection
|
|
164
|
+
function denyConnection(requestId: string) {
|
|
165
|
+
handler.rejectDiscovery(requestId);
|
|
166
|
+
}
|
|
292
167
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
result?: unknown; // Method result (if successful)
|
|
297
|
-
error?: unknown; // Error (if failed)
|
|
298
|
-
walletId: string; // Your wallet's ID
|
|
168
|
+
// Send response back to dApp
|
|
169
|
+
async function sendWalletResponse(requestId: string, response: WalletResponse) {
|
|
170
|
+
await handler.sendResponse(requestId, response);
|
|
299
171
|
}
|
|
300
|
-
```
|
|
301
172
|
|
|
302
|
-
|
|
173
|
+
// Clean up on tab close/navigate
|
|
174
|
+
browser.tabs.onRemoved.addListener((tabId) => {
|
|
175
|
+
handler.terminateForTab(tabId);
|
|
176
|
+
});
|
|
177
|
+
```
|
|
303
178
|
|
|
304
|
-
|
|
179
|
+
### Content Script Setup
|
|
305
180
|
|
|
306
181
|
```typescript
|
|
307
|
-
import {
|
|
182
|
+
import {
|
|
183
|
+
ContentScriptConnectionHandler,
|
|
184
|
+
type ContentScriptTransport,
|
|
185
|
+
} from '@aztec/wallet-sdk/extension/handlers';
|
|
186
|
+
|
|
187
|
+
const transport: ContentScriptTransport = {
|
|
188
|
+
sendToBackground: (message) => browser.runtime.sendMessage(message),
|
|
189
|
+
addBackgroundListener: (handler) => browser.runtime.onMessage.addListener(handler),
|
|
190
|
+
};
|
|
308
191
|
|
|
309
|
-
|
|
310
|
-
const verificationHash = await hashSharedSecret(sharedKey);
|
|
192
|
+
const handler = new ContentScriptConnectionHandler(transport);
|
|
311
193
|
|
|
312
|
-
//
|
|
313
|
-
|
|
194
|
+
// Start listening for discovery requests and background messages
|
|
195
|
+
handler.start();
|
|
314
196
|
```
|
|
315
197
|
|
|
316
|
-
|
|
198
|
+
## Testing Your Integration (dApp Side)
|
|
199
|
+
|
|
200
|
+
The `WalletManager` supports two patterns for consuming discovered wallets.
|
|
201
|
+
|
|
202
|
+
### Async Iterator Pattern
|
|
317
203
|
|
|
318
204
|
```typescript
|
|
205
|
+
import { Fr } from '@aztec/foundation/fields';
|
|
206
|
+
import { WalletManager } from '@aztec/wallet-sdk/manager';
|
|
319
207
|
import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
|
|
320
208
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
+
});
|
|
324
219
|
|
|
325
|
-
|
|
220
|
+
// Iterate over discovered wallets as they're approved
|
|
221
|
+
for await (const provider of discovery.wallets) {
|
|
222
|
+
console.log(`Found: ${provider.name}`);
|
|
326
223
|
|
|
327
|
-
|
|
224
|
+
// Establish secure channel (key exchange)
|
|
225
|
+
const pending = await provider.establishSecureChannel('my-dapp');
|
|
328
226
|
|
|
329
|
-
|
|
227
|
+
// Display verification emojis to user
|
|
228
|
+
const emojis = hashToEmoji(pending.verificationHash);
|
|
229
|
+
console.log('Verify this matches your wallet:', emojis);
|
|
330
230
|
|
|
331
|
-
|
|
332
|
-
|
|
231
|
+
// User confirms emojis match
|
|
232
|
+
const wallet = await pending.confirm();
|
|
333
233
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (session.tabId === tabId) {
|
|
339
|
-
sessions.delete(requestId);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
});
|
|
234
|
+
// All calls are now encrypted
|
|
235
|
+
const accounts = await wallet.getAccounts();
|
|
236
|
+
console.log('Accounts:', accounts);
|
|
237
|
+
}
|
|
343
238
|
|
|
344
|
-
//
|
|
345
|
-
|
|
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
|
-
});
|
|
239
|
+
// Cancel discovery when done or on cleanup
|
|
240
|
+
discovery.cancel();
|
|
354
241
|
```
|
|
355
242
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
### Using WalletManager
|
|
243
|
+
### Callback Pattern
|
|
359
244
|
|
|
360
245
|
```typescript
|
|
361
246
|
import { Fr } from '@aztec/foundation/fields';
|
|
362
|
-
import { WalletManager,
|
|
247
|
+
import { WalletManager, type WalletProvider } from '@aztec/wallet-sdk/manager';
|
|
248
|
+
import { hashToEmoji } from '@aztec/wallet-sdk/crypto';
|
|
363
249
|
|
|
364
|
-
const
|
|
365
|
-
extensions: { enabled: true },
|
|
366
|
-
});
|
|
250
|
+
const discoveredProviders: WalletProvider[] = [];
|
|
367
251
|
|
|
368
|
-
|
|
369
|
-
|
|
252
|
+
const discovery = WalletManager.configure({
|
|
253
|
+
extensions: { enabled: true },
|
|
254
|
+
}).getAvailableWallets({
|
|
370
255
|
chainInfo: {
|
|
371
256
|
chainId: new Fr(31337),
|
|
372
|
-
version: new Fr(
|
|
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
|
|
373
265
|
},
|
|
374
|
-
timeout: 2000,
|
|
375
266
|
});
|
|
376
267
|
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
console.log(`${provider.name}: ${emoji}`);
|
|
381
|
-
}
|
|
268
|
+
// Wait for discovery to complete (or cancel early with discovery.cancel())
|
|
269
|
+
await discovery.done;
|
|
270
|
+
console.log('Discovery complete, found:', discoveredProviders.length);
|
|
382
271
|
|
|
383
|
-
// Connect
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const wallet = await walletProvider.connect('my-app-id');
|
|
272
|
+
// Connect to a selected provider
|
|
273
|
+
async function connectToWallet(provider: WalletProvider) {
|
|
274
|
+
const pending = await provider.establishSecureChannel('my-dapp');
|
|
387
275
|
|
|
388
|
-
//
|
|
389
|
-
const
|
|
390
|
-
|
|
276
|
+
// Show verification UI
|
|
277
|
+
const emojis = hashToEmoji(pending.verificationHash);
|
|
278
|
+
showVerificationDialog(emojis);
|
|
279
|
+
|
|
280
|
+
// User confirms
|
|
281
|
+
const wallet = await pending.confirm();
|
|
282
|
+
return wallet;
|
|
391
283
|
}
|
|
392
284
|
```
|
|
393
285
|
|
|
394
|
-
|
|
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));
|
|
395
312
|
|
|
396
|
-
|
|
313
|
+
return () => {
|
|
314
|
+
discovery.cancel();
|
|
315
|
+
discoveryRef.current = null;
|
|
316
|
+
};
|
|
317
|
+
}, [chainInfo.chainId.toString(), chainInfo.version.toString(), appId]);
|
|
397
318
|
|
|
398
|
-
|
|
319
|
+
return { providers, isDiscovering, cancel: () => discoveryRef.current?.cancel() };
|
|
320
|
+
}
|
|
321
|
+
```
|