@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.
- package/README.md +294 -217
- package/dest/base-wallet/base_wallet.d.ts +4 -4
- package/dest/base-wallet/base_wallet.d.ts.map +1 -1
- package/dest/base-wallet/base_wallet.js +4 -9
- package/dest/crypto.d.ts +50 -59
- package/dest/crypto.d.ts.map +1 -1
- package/dest/crypto.js +108 -202
- package/dest/manager/index.d.ts +8 -2
- package/dest/manager/index.d.ts.map +1 -1
- package/dest/manager/index.js +6 -0
- package/dest/manager/types.d.ts +6 -88
- package/dest/manager/types.d.ts.map +1 -1
- package/dest/manager/types.js +1 -17
- package/dest/manager/wallet_manager.d.ts +7 -50
- package/dest/manager/wallet_manager.d.ts.map +1 -1
- package/dest/manager/wallet_manager.js +44 -174
- package/dest/providers/extension/extension_provider.d.ts +63 -0
- package/dest/providers/extension/extension_provider.d.ts.map +1 -0
- package/dest/providers/extension/extension_provider.js +124 -0
- package/dest/providers/extension/extension_wallet.d.ts +155 -0
- package/dest/providers/extension/extension_wallet.d.ts.map +1 -0
- package/dest/{extension/provider → providers/extension}/extension_wallet.js +95 -48
- package/dest/providers/extension/index.d.ts +6 -0
- package/dest/providers/extension/index.d.ts.map +1 -0
- package/dest/{extension/provider → providers/extension}/index.js +2 -0
- package/dest/types.d.ts +12 -43
- package/dest/types.d.ts.map +1 -1
- package/dest/types.js +2 -3
- package/package.json +9 -10
- package/src/base-wallet/base_wallet.ts +8 -15
- package/src/crypto.ts +113 -237
- package/src/manager/index.ts +10 -2
- package/src/manager/types.ts +5 -91
- package/src/manager/wallet_manager.ts +46 -192
- package/src/providers/extension/extension_provider.ts +167 -0
- package/src/{extension/provider → providers/extension}/extension_wallet.ts +110 -52
- package/src/providers/extension/index.ts +5 -0
- package/src/types.ts +10 -44
- package/dest/emoji_alphabet.d.ts +0 -35
- package/dest/emoji_alphabet.d.ts.map +0 -1
- package/dest/emoji_alphabet.js +0 -299
- package/dest/extension/handlers/background_connection_handler.d.ts +0 -158
- package/dest/extension/handlers/background_connection_handler.d.ts.map +0 -1
- package/dest/extension/handlers/background_connection_handler.js +0 -258
- package/dest/extension/handlers/content_script_connection_handler.d.ts +0 -56
- package/dest/extension/handlers/content_script_connection_handler.d.ts.map +0 -1
- package/dest/extension/handlers/content_script_connection_handler.js +0 -174
- package/dest/extension/handlers/index.d.ts +0 -12
- package/dest/extension/handlers/index.d.ts.map +0 -1
- package/dest/extension/handlers/index.js +0 -10
- package/dest/extension/handlers/internal_message_types.d.ts +0 -63
- package/dest/extension/handlers/internal_message_types.d.ts.map +0 -1
- package/dest/extension/handlers/internal_message_types.js +0 -22
- package/dest/extension/provider/extension_provider.d.ts +0 -107
- package/dest/extension/provider/extension_provider.d.ts.map +0 -1
- package/dest/extension/provider/extension_provider.js +0 -160
- package/dest/extension/provider/extension_wallet.d.ts +0 -131
- package/dest/extension/provider/extension_wallet.d.ts.map +0 -1
- package/dest/extension/provider/index.d.ts +0 -3
- package/dest/extension/provider/index.d.ts.map +0 -1
- package/src/emoji_alphabet.ts +0 -317
- package/src/extension/handlers/background_connection_handler.ts +0 -423
- package/src/extension/handlers/content_script_connection_handler.ts +0 -246
- package/src/extension/handlers/index.ts +0 -25
- package/src/extension/handlers/internal_message_types.ts +0 -69
- package/src/extension/provider/extension_provider.ts +0 -233
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
153
|
+
**Content script (creates MessageChannel and sends response):**
|
|
46
154
|
|
|
47
|
-
|
|
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
|
-
|
|
178
|
+
// Send response with port2 to the page
|
|
179
|
+
window.postMessage(JSON.stringify(result.response), '*', [channel.port2]);
|
|
180
|
+
}
|
|
181
|
+
```
|
|
50
182
|
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
205
|
+
### Architecture for Extension Wallets
|
|
72
206
|
|
|
73
207
|
```
|
|
74
208
|
┌─────────────┐ window.postMessage ┌─────────────────┐ browser.runtime ┌──────────────────┐
|
|
75
|
-
│ dApp
|
|
76
|
-
│ (web page) │ │ (message relay)│ │ (
|
|
209
|
+
│ dApp │◄───(discovery only)─────►│ Content Script │◄────────────────────►│ Background Script│
|
|
210
|
+
│ (web page) │ │ (message relay)│ │ (decrypt+process)│
|
|
77
211
|
└─────────────┘ └─────────────────┘ └──────────────────┘
|
|
78
212
|
│ │
|
|
79
|
-
│
|
|
80
|
-
└──────────(
|
|
213
|
+
│ MessagePort (private channel) │
|
|
214
|
+
└──────────(encrypted messages)────────────┘
|
|
81
215
|
```
|
|
82
216
|
|
|
83
|
-
**Security
|
|
217
|
+
**Security benefits:**
|
|
84
218
|
|
|
85
|
-
-
|
|
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
|
-
-
|
|
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
|
-
|
|
224
|
+
### Handle Encrypted Messages
|
|
92
225
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
### Background Script Setup
|
|
226
|
+
All wallet method calls arrive as encrypted payloads on the MessagePort:
|
|
96
227
|
|
|
97
228
|
```typescript
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
142
|
-
onSessionTerminated: (requestId) => {
|
|
143
|
-
console.log('Session terminated:', requestId);
|
|
144
|
-
},
|
|
235
|
+
**Background script:**
|
|
145
236
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
276
|
+
## Message Formats
|
|
154
277
|
|
|
155
|
-
|
|
156
|
-
handler.initialize();
|
|
278
|
+
### Wallet Method Request (Decrypted)
|
|
157
279
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
164
|
-
function denyConnection(requestId: string) {
|
|
165
|
-
handler.rejectDiscovery(requestId);
|
|
166
|
-
}
|
|
291
|
+
### Wallet Method Response
|
|
167
292
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
302
|
+
## Anti-MITM Verification
|
|
180
303
|
|
|
181
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
addBackgroundListener: (handler) => browser.runtime.onMessage.addListener(handler),
|
|
190
|
-
};
|
|
306
|
+
```typescript
|
|
307
|
+
import { hashSharedSecret } from '@aztec/wallet-sdk/crypto';
|
|
191
308
|
|
|
192
|
-
|
|
309
|
+
// Compute verification hash from shared key
|
|
310
|
+
const verificationHash = await hashSharedSecret(sharedKey);
|
|
193
311
|
|
|
194
|
-
//
|
|
195
|
-
|
|
312
|
+
// Store verificationHash in session - this is the cryptographic proof
|
|
313
|
+
sessions.set(requestId, { sharedKey, verificationHash, tabId });
|
|
196
314
|
```
|
|
197
315
|
|
|
198
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
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
|
-
|
|
225
|
-
const pending = await provider.establishSecureChannel('my-dapp');
|
|
327
|
+
## Session Management
|
|
226
328
|
|
|
227
|
-
|
|
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
|
-
|
|
232
|
-
|
|
331
|
+
- **Tab closes**: Browser tabs API `onRemoved` event
|
|
332
|
+
- **Tab navigates**: Browser tabs API `onUpdated` event with `status === 'loading'`
|
|
233
333
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
//
|
|
240
|
-
|
|
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
|
-
|
|
356
|
+
## Testing Your Integration
|
|
357
|
+
|
|
358
|
+
### Using WalletManager
|
|
244
359
|
|
|
245
360
|
```typescript
|
|
246
361
|
import { Fr } from '@aztec/foundation/fields';
|
|
247
|
-
import { WalletManager,
|
|
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
|
|
364
|
+
const manager = WalletManager.configure({
|
|
253
365
|
extensions: { enabled: true },
|
|
254
|
-
})
|
|
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(
|
|
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
|
-
//
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
//
|
|
281
|
-
const
|
|
282
|
-
|
|
388
|
+
// All calls are automatically encrypted
|
|
389
|
+
const accounts = await wallet.getAccounts();
|
|
390
|
+
console.log('Accounts:', accounts);
|
|
283
391
|
}
|
|
284
392
|
```
|
|
285
393
|
|
|
286
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
104
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzZV93YWxsZXQuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9iYXNlLXdhbGxldC9iYXNlX3dhbGxldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxPQUFPLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsZUFBZSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDakYsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM1RCxPQUFPLEtBQUssRUFDVixPQUFPLEVBQ1AsWUFBWSxFQUNaLGFBQWEsRUFDYixZQUFZLEVBQ1osa0JBQWtCLEVBQ2xCLGNBQWMsRUFDZCxXQUFXLEVBQ1gsZUFBZSxFQUNmLE1BQU0sRUFDUCxNQUFNLHdCQUF3QixDQUFDO0FBT2hDLE9BQU8sRUFBRSw4QkFBOEIsRUFBd0MsTUFBTSw0QkFBNEIsQ0FBQztBQUNsSCxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUMvRCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFcEQsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDeEQsT0FBTyxLQUFLLEVBQUUsR0FBRyxFQUFzQixNQUFNLG1CQUFtQixDQUFDO0FBQ2pFLE9BQU8sRUFDTCxLQUFLLGdCQUFnQixFQUNyQixLQUFLLHVCQUF1QixFQUM1QixLQUFLLFlBQVksRUFFbEIsTUFBTSxtQkFBbUIsQ0FBQztBQUMzQixPQUFPLEtBQUssRUFBRSxXQUFXLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUM5RCxPQUFPLEtBQUssRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUNoRSxPQUFPLEVBQ0wsS0FBSywyQkFBMkIsRUFHakMsTUFBTSx3QkFBd0IsQ0FBQztBQUVoQyxPQUFPLEVBQU8sV0FB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
|