@aztec/wallet-sdk 0.0.1-commit.c7c42ec → 0.0.1-commit.f295ac2

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 (40) hide show
  1. package/README.md +241 -267
  2. package/dest/base-wallet/base_wallet.d.ts +17 -7
  3. package/dest/base-wallet/base_wallet.d.ts.map +1 -1
  4. package/dest/base-wallet/base_wallet.js +32 -13
  5. package/dest/crypto.d.ts +183 -0
  6. package/dest/crypto.d.ts.map +1 -0
  7. package/dest/crypto.js +300 -0
  8. package/dest/manager/index.d.ts +4 -3
  9. package/dest/manager/index.d.ts.map +1 -1
  10. package/dest/manager/index.js +2 -0
  11. package/dest/manager/types.d.ts +22 -1
  12. package/dest/manager/types.d.ts.map +1 -1
  13. package/dest/manager/wallet_manager.d.ts +1 -1
  14. package/dest/manager/wallet_manager.d.ts.map +1 -1
  15. package/dest/manager/wallet_manager.js +34 -15
  16. package/dest/providers/extension/extension_provider.d.ts +53 -7
  17. package/dest/providers/extension/extension_provider.d.ts.map +1 -1
  18. package/dest/providers/extension/extension_provider.js +81 -13
  19. package/dest/providers/extension/extension_wallet.d.ts +140 -8
  20. package/dest/providers/extension/extension_wallet.d.ts.map +1 -1
  21. package/dest/providers/extension/extension_wallet.js +268 -46
  22. package/dest/providers/extension/index.d.ts +6 -4
  23. package/dest/providers/extension/index.d.ts.map +1 -1
  24. package/dest/providers/extension/index.js +2 -0
  25. package/dest/types.d.ts +92 -0
  26. package/dest/types.d.ts.map +1 -0
  27. package/dest/types.js +10 -0
  28. package/package.json +11 -9
  29. package/src/base-wallet/base_wallet.ts +40 -19
  30. package/src/crypto.ts +375 -0
  31. package/src/manager/index.ts +4 -8
  32. package/src/manager/types.ts +22 -0
  33. package/src/manager/wallet_manager.ts +43 -16
  34. package/src/providers/extension/extension_provider.ts +112 -17
  35. package/src/providers/extension/extension_wallet.ts +310 -55
  36. package/src/providers/extension/index.ts +5 -3
  37. package/src/{providers/types.ts → types.ts} +33 -6
  38. package/dest/providers/types.d.ts +0 -67
  39. package/dest/providers/types.d.ts.map +0 -1
  40. package/dest/providers/types.js +0 -3
@@ -36,6 +36,11 @@ export interface WalletManagerConfig {
36
36
  */
37
37
  export type WalletProviderType = 'extension' | 'web' | 'embedded';
38
38
 
39
+ /**
40
+ * Callback type for wallet disconnect events at the provider level.
41
+ */
42
+ export type ProviderDisconnectionCallback = () => void;
43
+
39
44
  /**
40
45
  * A wallet provider that can connect to create a wallet instance.
41
46
  * Chain information is already baked in from the discovery process.
@@ -56,6 +61,23 @@ export interface WalletProvider {
56
61
  * @param appId - Application identifier for the requesting dapp
57
62
  */
58
63
  connect(appId: string): Promise<Wallet>;
64
+ /**
65
+ * Disconnects the current wallet and cleans up resources.
66
+ * After calling this, the wallet returned from connect() should no longer be used.
67
+ * @returns A promise that resolves when disconnection is complete
68
+ */
69
+ disconnect?(): Promise<void>;
70
+ /**
71
+ * Registers a callback to be invoked when the wallet disconnects unexpectedly.
72
+ * @param callback - Function to call when wallet disconnects
73
+ * @returns A function to unregister the callback
74
+ */
75
+ onDisconnect?(callback: ProviderDisconnectionCallback): () => void;
76
+ /**
77
+ * Returns whether the provider's wallet connection has been disconnected.
78
+ * @returns true if the wallet is no longer connected
79
+ */
80
+ isDisconnected?(): boolean;
59
81
  }
60
82
 
61
83
  /**
@@ -1,5 +1,11 @@
1
1
  import { ExtensionProvider, ExtensionWallet } from '../providers/extension/index.js';
2
- import type { DiscoverWalletsOptions, ExtensionWalletConfig, WalletManagerConfig, WalletProvider } from './types.js';
2
+ import type {
3
+ DiscoverWalletsOptions,
4
+ ExtensionWalletConfig,
5
+ ProviderDisconnectionCallback,
6
+ WalletManagerConfig,
7
+ WalletProvider,
8
+ } from './types.js';
3
9
 
4
10
  /**
5
11
  * Manager for wallet discovery, configuration, and connection
@@ -35,27 +41,51 @@ export class WalletManager {
35
41
  const providers: WalletProvider[] = [];
36
42
  const { chainInfo } = options;
37
43
 
38
- // Discover extension wallets
39
44
  if (this.config.extensions?.enabled) {
40
- const extensions = await ExtensionProvider.discoverExtensions(chainInfo, options.timeout);
45
+ const discoveredWallets = await ExtensionProvider.discoverExtensions(chainInfo, options.timeout);
41
46
  const extensionConfig = this.config.extensions;
42
47
 
43
- for (const ext of extensions) {
44
- // Apply allow/block lists
45
- if (!this.isExtensionAllowed(ext.id, extensionConfig)) {
48
+ for (const { info, port, sharedKey } of discoveredWallets) {
49
+ if (!this.isExtensionAllowed(info.id, extensionConfig)) {
46
50
  continue;
47
51
  }
48
52
 
49
- providers.push({
50
- id: ext.id,
53
+ let extensionWallet: ExtensionWallet | null = null;
54
+
55
+ const provider: WalletProvider = {
56
+ id: info.id,
51
57
  type: 'extension',
52
- name: ext.name,
53
- icon: ext.icon,
58
+ name: info.name,
59
+ icon: info.icon,
54
60
  metadata: {
55
- version: ext.version,
61
+ version: info.version,
62
+ verificationHash: info.verificationHash,
63
+ },
64
+ connect: (appId: string) => {
65
+ extensionWallet = ExtensionWallet.create(info, chainInfo, port, sharedKey, appId);
66
+ return Promise.resolve(extensionWallet.getWallet());
67
+ },
68
+ disconnect: async () => {
69
+ if (extensionWallet) {
70
+ await extensionWallet.disconnect();
71
+ extensionWallet = null;
72
+ }
56
73
  },
57
- connect: (appId: string) => Promise.resolve(ExtensionWallet.create(chainInfo, appId, ext.id)),
58
- });
74
+ onDisconnect: (callback: ProviderDisconnectionCallback) => {
75
+ if (extensionWallet) {
76
+ return extensionWallet.onDisconnect(callback);
77
+ }
78
+ return () => {};
79
+ },
80
+ isDisconnected: () => {
81
+ if (extensionWallet) {
82
+ return extensionWallet.isDisconnected();
83
+ }
84
+ return true;
85
+ },
86
+ };
87
+
88
+ providers.push(provider);
59
89
  }
60
90
  }
61
91
 
@@ -70,17 +100,14 @@ export class WalletManager {
70
100
  * @param config - Extension wallet configuration containing allow/block lists
71
101
  */
72
102
  private isExtensionAllowed(extensionId: string, config: ExtensionWalletConfig): boolean {
73
- // Check block list first
74
103
  if (config.blockList && config.blockList.includes(extensionId)) {
75
104
  return false;
76
105
  }
77
106
 
78
- // If allow list exists, extension must be in it
79
107
  if (config.allowList && config.allowList.length > 0) {
80
108
  return config.allowList.includes(extensionId);
81
109
  }
82
110
 
83
- // If no allow list, extension is allowed (unless blocked)
84
111
  return true;
85
112
  }
86
113
  }
@@ -2,34 +2,95 @@ import type { ChainInfo } from '@aztec/aztec.js/account';
2
2
  import { jsonStringify } from '@aztec/foundation/json-rpc';
3
3
  import { promiseWithResolvers } from '@aztec/foundation/promise';
4
4
 
5
- import type { DiscoveryRequest, DiscoveryResponse, WalletInfo } from '../types.js';
5
+ import { deriveSharedKey, exportPublicKey, generateKeyPair, hashSharedSecret, importPublicKey } from '../../crypto.js';
6
+ import { type DiscoveryRequest, type DiscoveryResponse, type WalletInfo, WalletMessageType } from '../../types.js';
6
7
 
7
8
  /**
8
- * Provider for discovering and managing Aztec wallet extensions
9
+ * A discovered wallet with its secure channel components.
10
+ * Returned by {@link ExtensionProvider.discoverExtensions}.
11
+ */
12
+ export interface DiscoveredWallet {
13
+ /** Basic wallet information (id, name, icon, version, publicKey, verificationHash) */
14
+ info: WalletInfo;
15
+ /** The MessagePort for private communication with the wallet */
16
+ port: MessagePort;
17
+ /** The derived AES-256-GCM shared key for encryption */
18
+ sharedKey: CryptoKey;
19
+ }
20
+
21
+ /**
22
+ * Internal type for discovery response with MessagePort
23
+ * @internal
24
+ */
25
+ interface DiscoveryResponseWithPort extends DiscoveryResponse {
26
+ /** The MessagePort transferred from the wallet */
27
+ port?: MessagePort;
28
+ }
29
+
30
+ /**
31
+ * Provider for discovering Aztec wallet extensions.
32
+ *
33
+ * This class handles the discovery phase of wallet communication:
34
+ * 1. Broadcasts a discovery request with the dApp's public key
35
+ * 2. Receives responses from installed wallets with their public keys
36
+ * 3. Derives shared secrets and computes verification hashes
37
+ * 4. Returns discovered wallets with their secure channel components
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const wallets = await ExtensionProvider.discoverExtensions(chainInfo);
42
+ * // Display wallets to user with optional emoji verification
43
+ * for (const wallet of wallets) {
44
+ * const emoji = hashToEmoji(wallet.info.verificationHash!);
45
+ * console.log(`${wallet.info.name}: ${emoji}`);
46
+ * }
47
+ * // User selects a wallet after verifying
48
+ * const wallet = await ExtensionWallet.create(wallets[0], chainInfo, 'my-app');
49
+ * ```
9
50
  */
10
51
  export class ExtensionProvider {
11
- private static discoveredExtensions: Map<string, WalletInfo> = new Map();
12
52
  private static discoveryInProgress = false;
13
53
 
14
54
  /**
15
- * Discovers all installed Aztec wallet extensions
55
+ * Discovers all installed Aztec wallet extensions and establishes secure channel components.
56
+ *
57
+ * This method:
58
+ * 1. Generates an ECDH key pair for this discovery session
59
+ * 2. Broadcasts a discovery request with the dApp's public key
60
+ * 3. Receives responses from wallets with their public keys and MessagePorts
61
+ * 4. Derives shared secrets and computes verification hashes
62
+ *
16
63
  * @param chainInfo - Chain information to check if extensions support this network
17
64
  * @param timeout - How long to wait for extensions to respond (ms)
18
- * @returns Array of discovered extension information
65
+ * @returns Array of discovered wallets with their secure channel components
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const wallets = await ExtensionProvider.discoverExtensions({
70
+ * chainId: Fr(31337),
71
+ * version: Fr(0)
72
+ * });
73
+ * // Access wallet info and secure channel
74
+ * const { info, port, sharedKey } = wallets[0];
75
+ * ```
19
76
  */
20
- static async discoverExtensions(chainInfo: ChainInfo, timeout: number = 1000): Promise<WalletInfo[]> {
21
- // If discovery is in progress, wait for it to complete
77
+ static async discoverExtensions(chainInfo: ChainInfo, timeout: number = 1000): Promise<DiscoveredWallet[]> {
78
+ // If discovery is already in progress, wait and return empty
79
+ // (caller should retry or handle appropriately)
22
80
  if (this.discoveryInProgress) {
23
81
  await new Promise(resolve => setTimeout(resolve, timeout));
24
- return Array.from(this.discoveredExtensions.values());
82
+ return [];
25
83
  }
26
84
 
27
85
  this.discoveryInProgress = true;
28
- this.discoveredExtensions.clear();
29
86
 
30
- const { promise, resolve } = promiseWithResolvers<WalletInfo[]>();
87
+ // Generate key pair for this discovery session
88
+ const keyPair = await generateKeyPair();
89
+ const exportedPublicKey = await exportPublicKey(keyPair.publicKey);
90
+
91
+ const { promise, resolve } = promiseWithResolvers<DiscoveredWallet[]>();
31
92
  const requestId = globalThis.crypto.randomUUID();
32
- const responses: WalletInfo[] = [];
93
+ const responses: DiscoveredWallet[] = [];
33
94
 
34
95
  // Set up listener for discovery responses
35
96
  const handleMessage = (event: MessageEvent) => {
@@ -37,26 +98,60 @@ export class ExtensionProvider {
37
98
  return;
38
99
  }
39
100
 
40
- let data: DiscoveryResponse;
101
+ let data: DiscoveryResponseWithPort;
41
102
  try {
42
103
  data = JSON.parse(event.data);
43
104
  } catch {
44
105
  return;
45
106
  }
46
107
 
47
- if (data.type === 'aztec-wallet-discovery-response' && data.requestId === requestId) {
48
- responses.push(data.walletInfo);
49
- this.discoveredExtensions.set(data.walletInfo.id, data.walletInfo);
108
+ if (data.type === WalletMessageType.DISCOVERY_RESPONSE && data.requestId === requestId) {
109
+ // Get the MessagePort from the event
110
+ const port = event.ports?.[0];
111
+ if (!port) {
112
+ return;
113
+ }
114
+
115
+ // Derive shared key from wallet's public key
116
+ const walletPublicKey = data.walletInfo.publicKey;
117
+ if (!walletPublicKey) {
118
+ return;
119
+ }
120
+
121
+ void (async () => {
122
+ try {
123
+ const importedWalletKey = await importPublicKey(walletPublicKey);
124
+ const sharedKey = await deriveSharedKey(keyPair.privateKey, importedWalletKey);
125
+
126
+ // Compute verification hash
127
+ const verificationHash = await hashSharedSecret(sharedKey);
128
+
129
+ // Create wallet info with verification hash
130
+ const walletInfo: WalletInfo = {
131
+ ...data.walletInfo,
132
+ verificationHash,
133
+ };
134
+
135
+ responses.push({
136
+ info: walletInfo,
137
+ port,
138
+ sharedKey,
139
+ });
140
+ } catch {
141
+ // Failed to derive key, skip this wallet
142
+ }
143
+ })();
50
144
  }
51
145
  };
52
146
 
53
147
  window.addEventListener('message', handleMessage);
54
148
 
55
- // Send discovery message
149
+ // Send discovery message with our public key
56
150
  const discoveryMessage: DiscoveryRequest = {
57
- type: 'aztec-wallet-discovery',
151
+ type: WalletMessageType.DISCOVERY,
58
152
  requestId,
59
153
  chainInfo,
154
+ publicKey: exportedPublicKey,
60
155
  };
61
156
  window.postMessage(jsonStringify(discoveryMessage), '*');
62
157