@aztec/wallet-sdk 0.0.1-commit.fff30aa → 0.0.1-private.3d175340d2
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/dest/base-wallet/base_wallet.d.ts +24 -5
- package/dest/base-wallet/base_wallet.d.ts.map +1 -1
- package/dest/base-wallet/base_wallet.js +55 -12
- package/dest/base-wallet/utils.d.ts +5 -3
- package/dest/base-wallet/utils.d.ts.map +1 -1
- package/dest/base-wallet/utils.js +8 -4
- package/dest/extension/handlers/background_connection_handler.d.ts +12 -2
- package/dest/extension/handlers/background_connection_handler.d.ts.map +1 -1
- package/dest/extension/handlers/background_connection_handler.js +44 -8
- package/dest/extension/handlers/content_script_connection_handler.d.ts +2 -1
- package/dest/extension/handlers/content_script_connection_handler.d.ts.map +1 -1
- package/dest/extension/handlers/content_script_connection_handler.js +19 -0
- package/dest/extension/handlers/internal_message_types.d.ts +3 -1
- package/dest/extension/handlers/internal_message_types.d.ts.map +1 -1
- package/dest/extension/handlers/internal_message_types.js +3 -1
- package/dest/extension/provider/extension_wallet.d.ts +26 -3
- package/dest/extension/provider/extension_wallet.d.ts.map +1 -1
- package/dest/extension/provider/extension_wallet.js +80 -9
- package/dest/iframe/handlers/iframe_connection_handler.d.ts +6 -2
- package/dest/iframe/handlers/iframe_connection_handler.d.ts.map +1 -1
- package/dest/iframe/handlers/iframe_connection_handler.js +18 -7
- package/dest/iframe/provider/iframe_wallet.d.ts +20 -3
- package/dest/iframe/provider/iframe_wallet.d.ts.map +1 -1
- package/dest/iframe/provider/iframe_wallet.js +79 -10
- package/dest/types.d.ts +52 -2
- package/dest/types.d.ts.map +1 -1
- package/dest/types.js +25 -0
- package/package.json +8 -8
- package/src/base-wallet/base_wallet.ts +62 -7
- package/src/base-wallet/utils.ts +9 -1
- package/src/extension/handlers/background_connection_handler.ts +42 -9
- package/src/extension/handlers/content_script_connection_handler.ts +18 -0
- package/src/extension/handlers/internal_message_types.ts +2 -0
- package/src/extension/provider/extension_wallet.ts +94 -8
- package/src/iframe/handlers/iframe_connection_handler.ts +21 -8
- package/src/iframe/provider/iframe_wallet.ts +103 -9
- package/src/types.ts +59 -0
|
@@ -8,10 +8,12 @@ export declare const InternalMessageType: {
|
|
|
8
8
|
readonly KEY_EXCHANGE_REQUEST: "key-exchange-request";
|
|
9
9
|
readonly SECURE_MESSAGE: "secure-message";
|
|
10
10
|
readonly DISCONNECT_REQUEST: "disconnect-request";
|
|
11
|
+
readonly PING: "ping";
|
|
11
12
|
readonly DISCOVERY_APPROVED: "discovery-approved";
|
|
12
13
|
readonly KEY_EXCHANGE_RESPONSE: "key-exchange-response";
|
|
13
14
|
readonly SECURE_RESPONSE: "secure-response";
|
|
14
15
|
readonly SESSION_DISCONNECTED: "session-disconnected";
|
|
16
|
+
readonly PONG: "pong";
|
|
15
17
|
};
|
|
16
18
|
/**
|
|
17
19
|
* Message origins for internal extension communication.
|
|
@@ -60,4 +62,4 @@ export interface MessageSender {
|
|
|
60
62
|
url?: string;
|
|
61
63
|
};
|
|
62
64
|
}
|
|
63
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
65
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJuYWxfbWVzc2FnZV90eXBlcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2V4dGVuc2lvbi9oYW5kbGVycy9pbnRlcm5hbF9tZXNzYWdlX3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7O0dBSUc7QUFDSCxlQUFPLE1BQU0sbUJBQW1COzs7Ozs7Ozs7OztDQWF0QixDQUFDO0FBRVg7O0dBRUc7QUFDSCxlQUFPLE1BQU0sYUFBYTs7O0NBR2hCLENBQUM7QUFFWCxxQ0FBcUM7QUFDckMsTUFBTSxNQUFNLGlCQUFpQixHQUFHLENBQUMsT0FBTyxhQUFhLENBQUMsQ0FBQyxNQUFNLE9BQU8sYUFBYSxDQUFDLENBQUM7QUFFbkY7O0dBRUc7QUFDSCxNQUFNLFdBQVcsb0JBQW9CO0lBQ25DLGlDQUFpQztJQUNqQyxNQUFNLEVBQUUsT0FBTyxhQUFhLENBQUMsY0FBYyxDQUFDO0lBQzVDLG9CQUFvQjtJQUNwQixJQUFJLEVBQUUsTUFBTSxDQUFDO0lBQ2IsbUNBQW1DO0lBQ25DLFNBQVMsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUNuQixnQ0FBZ0M7SUFDaEMsT0FBTyxDQUFDLEVBQUUsT0FBTyxDQUFDO0NBQ25CO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLFdBQVcsaUJBQWlCO0lBQ2hDLGlDQUFpQztJQUNqQyxNQUFNLEVBQUUsT0FBTyxhQUFhLENBQUMsVUFBVSxDQUFDO0lBQ3hDLG9CQUFvQjtJQUNwQixJQUFJLEVBQUUsTUFBTSxDQUFDO0lBQ2IsMEJBQTBCO0lBQzFCLFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsZ0NBQWdDO0lBQ2hDLE9BQU8sQ0FBQyxFQUFFLE9BQU8sQ0FBQztDQUNuQjtBQUVEOztHQUVHO0FBQ0gsTUFBTSxXQUFXLGFBQWE7SUFDNUIsb0NBQW9DO0lBQ3BDLEdBQUcsQ0FBQyxFQUFFO1FBQ0osc0JBQXNCO1FBQ3RCLEVBQUUsQ0FBQyxFQUFFLE1BQU0sQ0FBQztRQUNaLGVBQWU7UUFDZixHQUFHLENBQUMsRUFBRSxNQUFNLENBQUM7S0FDZCxDQUFDO0NBQ0gifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal_message_types.d.ts","sourceRoot":"","sources":["../../../src/extension/handlers/internal_message_types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,mBAAmB
|
|
1
|
+
{"version":3,"file":"internal_message_types.d.ts","sourceRoot":"","sources":["../../../src/extension/handlers/internal_message_types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;CAatB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,aAAa;;;CAGhB,CAAC;AAEX,qCAAqC;AACrC,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAEnF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,iCAAiC;IACjC,MAAM,EAAE,OAAO,aAAa,CAAC,cAAc,CAAC;IAC5C,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,MAAM,EAAE,OAAO,aAAa,CAAC,UAAU,CAAC;IACxC,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,GAAG,CAAC,EAAE;QACJ,sBAAsB;QACtB,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,eAAe;QACf,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;CACH"}
|
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
KEY_EXCHANGE_REQUEST: 'key-exchange-request',
|
|
9
9
|
SECURE_MESSAGE: 'secure-message',
|
|
10
10
|
DISCONNECT_REQUEST: 'disconnect-request',
|
|
11
|
+
PING: 'ping',
|
|
11
12
|
// Background → Content script
|
|
12
13
|
DISCOVERY_APPROVED: 'discovery-approved',
|
|
13
14
|
KEY_EXCHANGE_RESPONSE: 'key-exchange-response',
|
|
14
15
|
SECURE_RESPONSE: 'secure-response',
|
|
15
|
-
SESSION_DISCONNECTED: 'session-disconnected'
|
|
16
|
+
SESSION_DISCONNECTED: 'session-disconnected',
|
|
17
|
+
PONG: 'pong'
|
|
16
18
|
};
|
|
17
19
|
/**
|
|
18
20
|
* Message origins for internal extension communication.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ChainInfo } from '@aztec/aztec.js/account';
|
|
2
2
|
import { type Wallet } from '@aztec/aztec.js/wallet';
|
|
3
|
-
import { type DisconnectCallback } from '../../types.js';
|
|
3
|
+
import { type DisconnectCallback, type HeartbeatOptions, type WalletSdkLogger } from '../../types.js';
|
|
4
4
|
/**
|
|
5
5
|
* A wallet implementation that communicates with browser extension wallets
|
|
6
6
|
* using an encrypted MessageChannel.
|
|
@@ -42,6 +42,11 @@ export declare class ExtensionWallet {
|
|
|
42
42
|
private inFlight;
|
|
43
43
|
private disconnected;
|
|
44
44
|
private disconnectCallbacks;
|
|
45
|
+
private heartbeatTimer;
|
|
46
|
+
private lastInboundAt;
|
|
47
|
+
private log;
|
|
48
|
+
private heartbeatIntervalMs;
|
|
49
|
+
private heartbeatDeadAfterMs;
|
|
45
50
|
/**
|
|
46
51
|
* Private constructor - use {@link ExtensionWallet.create} to instantiate.
|
|
47
52
|
* @param chainInfo - The chain information (chainId and version)
|
|
@@ -49,6 +54,8 @@ export declare class ExtensionWallet {
|
|
|
49
54
|
* @param extensionId - The unique identifier of the target wallet extension
|
|
50
55
|
* @param port - The MessagePort for private communication with the wallet
|
|
51
56
|
* @param sharedKey - The derived AES-256-GCM shared key for encryption
|
|
57
|
+
* @param logger - Optional logger; defaults to a no-op logger
|
|
58
|
+
* @param heartbeatOptions - Optional heartbeat tuning (mostly useful for tests)
|
|
52
59
|
*/
|
|
53
60
|
private constructor();
|
|
54
61
|
/**
|
|
@@ -60,6 +67,8 @@ export declare class ExtensionWallet {
|
|
|
60
67
|
* @param sharedKey - The derived AES-256-GCM shared key for encryption
|
|
61
68
|
* @param chainInfo - The chain information (chainId and version) for request context
|
|
62
69
|
* @param appId - Application identifier used to identify the requesting dApp to the wallet
|
|
70
|
+
* @param logger - Optional logger; defaults to a no-op logger to keep extension/page bundles small
|
|
71
|
+
* @param heartbeatOptions - Optional override for heartbeat tuning (mostly useful for tests)
|
|
63
72
|
* @returns A Wallet interface where all method calls are encrypted
|
|
64
73
|
*
|
|
65
74
|
* @example
|
|
@@ -77,10 +86,24 @@ export declare class ExtensionWallet {
|
|
|
77
86
|
* const accounts = await wallet.getAccounts();
|
|
78
87
|
* ```
|
|
79
88
|
*/
|
|
80
|
-
static create(extensionId: string, port: MessagePort, sharedKey: CryptoKey, chainInfo: ChainInfo, appId: string): ExtensionWallet;
|
|
89
|
+
static create(extensionId: string, port: MessagePort, sharedKey: CryptoKey, chainInfo: ChainInfo, appId: string, logger?: WalletSdkLogger, heartbeatOptions?: HeartbeatOptions): ExtensionWallet;
|
|
81
90
|
asWallet(): Wallet;
|
|
82
91
|
private handleEncryptedResponse;
|
|
83
92
|
private postMessage;
|
|
93
|
+
/**
|
|
94
|
+
* Start the heartbeat probe loop while at least one request is in flight.
|
|
95
|
+
* Idempotent — calling while already running is a no-op.
|
|
96
|
+
*
|
|
97
|
+
* Heartbeat is opt-in via wire protocol: PINGs are unencrypted control messages
|
|
98
|
+
* (like DISCONNECT). Older wallets that do not understand PING simply drop it,
|
|
99
|
+
* which is safe — we only declare disconnect when **no** inbound traffic of any
|
|
100
|
+
* kind (PONG, encrypted response, DISCONNECT) arrives within the dead window.
|
|
101
|
+
* A wallet that is processing a slow request will reset the timer when it
|
|
102
|
+
* eventually responds, so this never causes false disconnects on legacy peers.
|
|
103
|
+
*/
|
|
104
|
+
private startHeartbeat;
|
|
105
|
+
private maybeStopHeartbeat;
|
|
106
|
+
private heartbeatTick;
|
|
84
107
|
/**
|
|
85
108
|
* Handles wallet disconnection.
|
|
86
109
|
* Rejects all pending requests and notifies registered callbacks.
|
|
@@ -126,4 +149,4 @@ export declare class ExtensionWallet {
|
|
|
126
149
|
*/
|
|
127
150
|
disconnect(): Promise<void>;
|
|
128
151
|
}
|
|
129
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
152
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXh0ZW5zaW9uX3dhbGxldC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2V4dGVuc2lvbi9wcm92aWRlci9leHRlbnNpb25fd2FsbGV0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3pELE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx3QkFBd0IsQ0FBQztBQU9uRSxPQUFPLEVBR0wsS0FBSyxrQkFBa0IsRUFDdkIsS0FBSyxnQkFBZ0IsRUFLckIsS0FBSyxlQUFlLEVBQ3JCLE1BQU0sZ0JBQWdCLENBQUM7QUFheEI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQThCRztBQUNILHFCQUFhLGVBQWU7SUFzQnhCLE9BQU8sQ0FBQyxTQUFTO0lBQ2pCLE9BQU8sQ0FBQyxLQUFLO0lBQ2IsT0FBTyxDQUFDLFdBQVc7SUFDbkIsT0FBTyxDQUFDLElBQUk7SUFDWixPQUFPLENBQUMsU0FBUztJQXpCbkIsc0VBQXNFO0lBQ3RFLE9BQU8sQ0FBQyxRQUFRLENBQW9EO0lBQ3BFLE9BQU8sQ0FBQyxZQUFZLENBQVM7SUFDN0IsT0FBTyxDQUFDLG1CQUFtQixDQUE0QjtJQUN2RCxPQUFPLENBQUMsY0FBYyxDQUErQztJQUNyRSxPQUFPLENBQUMsYUFBYSxDQUFLO0lBQzFCLE9BQU8sQ0FBQyxHQUFHLENBQWtCO0lBQzdCLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBUztJQUNwQyxPQUFPLENBQUMsb0JBQW9CLENBQVM7SUFFckM7Ozs7Ozs7OztPQVNHO0lBQ0gsT0FBTyxlQVlOO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQTJCRztJQUNILE1BQU0sQ0FBQyxNQUFNLENBQ1gsV0FBVyxFQUFFLE1BQU0sRUFDbkIsSUFBSSxFQUFFLFdBQVcsRUFDakIsU0FBUyxFQUFFLFNBQVMsRUFDcEIsU0FBUyxFQUFFLFNBQVMsRUFDcEIsS0FBSyxFQUFFLE1BQU0sRUFDYixNQUFNLENBQUMsRUFBRSxlQUFlLEVBQ3hCLGdCQUFnQixDQUFDLEVBQUUsZ0JBQWdCLEdBQ2xDLGVBQWUsQ0FvQ2pCO0lBRUQsUUFBUSxJQUFJLE1BQU0sQ0FJakI7WUFVYSx1QkFBdUI7WUFnRHZCLFdBQVc7SUEyQnpCOzs7Ozs7Ozs7O09BVUc7SUFDSCxPQUFPLENBQUMsY0FBYztJQVF0QixPQUFPLENBQUMsa0JBQWtCO0lBTzFCLE9BQU8sQ0FBQyxhQUFhO0lBdUJyQjs7OztPQUlHO0lBQ0gsT0FBTyxDQUFDLGdCQUFnQjtJQStCeEI7Ozs7Ozs7Ozs7Ozs7OztPQWVHO0lBQ0gsWUFBWSxDQUFDLFFBQVEsRUFBRSxrQkFBa0IsR0FBRyxNQUFNLElBQUksQ0FRckQ7SUFFRDs7OztPQUlHO0lBQ0gsY0FBYyxJQUFJLE9BQU8sQ0FFeEI7SUFFRDs7Ozs7Ozs7Ozs7OztPQWFHO0lBRUcsVUFBVSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FhaEM7Q0FDRiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension_wallet.d.ts","sourceRoot":"","sources":["../../../src/extension/provider/extension_wallet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,wBAAwB,CAAC;AAOnE,OAAO,
|
|
1
|
+
{"version":3,"file":"extension_wallet.d.ts","sourceRoot":"","sources":["../../../src/extension/provider/extension_wallet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,wBAAwB,CAAC;AAOnE,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EAKrB,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAaxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,eAAe;IAsBxB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,SAAS;IAzBnB,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAoD;IACpE,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,mBAAmB,CAA4B;IACvD,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,oBAAoB,CAAS;IAErC;;;;;;;;;OASG;IACH,OAAO,eAYN;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,MAAM,CAAC,MAAM,CACX,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,eAAe,EACxB,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,eAAe,CAoCjB;IAED,QAAQ,IAAI,MAAM,CAIjB;YAUa,uBAAuB;YAgDvB,WAAW;IA2BzB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;IAuBrB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IA+BxB;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI,CAQrD;IAED;;;;OAIG;IACH,cAAc,IAAI,OAAO,CAExB;IAED;;;;;;;;;;;;;OAaG;IAEG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAahC;CACF"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { WalletSchema } from '@aztec/aztec.js/wallet';
|
|
2
2
|
import { jsonStringify } from '@aztec/foundation/json-rpc';
|
|
3
3
|
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
4
|
-
import { schemaHasMethod } from '@aztec/foundation/schemas';
|
|
4
|
+
import { getSchemaReturnType, schemaHasMethod } from '@aztec/foundation/schemas';
|
|
5
5
|
import { decrypt, encrypt } from '../../crypto.js';
|
|
6
|
-
import { WalletMessageType } from '../../types.js';
|
|
6
|
+
import { DEFAULT_HEARTBEAT_DEAD_AFTER_MS, DEFAULT_HEARTBEAT_INTERVAL_MS, NOOP_LOGGER, WalletMessageType } from '../../types.js';
|
|
7
7
|
/**
|
|
8
8
|
* A wallet implementation that communicates with browser extension wallets
|
|
9
9
|
* using an encrypted MessageChannel.
|
|
@@ -43,6 +43,11 @@ import { WalletMessageType } from '../../types.js';
|
|
|
43
43
|
/** Map of pending requests awaiting responses, keyed by message ID */ inFlight;
|
|
44
44
|
disconnected;
|
|
45
45
|
disconnectCallbacks;
|
|
46
|
+
heartbeatTimer;
|
|
47
|
+
lastInboundAt;
|
|
48
|
+
log;
|
|
49
|
+
heartbeatIntervalMs;
|
|
50
|
+
heartbeatDeadAfterMs;
|
|
46
51
|
/**
|
|
47
52
|
* Private constructor - use {@link ExtensionWallet.create} to instantiate.
|
|
48
53
|
* @param chainInfo - The chain information (chainId and version)
|
|
@@ -50,7 +55,9 @@ import { WalletMessageType } from '../../types.js';
|
|
|
50
55
|
* @param extensionId - The unique identifier of the target wallet extension
|
|
51
56
|
* @param port - The MessagePort for private communication with the wallet
|
|
52
57
|
* @param sharedKey - The derived AES-256-GCM shared key for encryption
|
|
53
|
-
|
|
58
|
+
* @param logger - Optional logger; defaults to a no-op logger
|
|
59
|
+
* @param heartbeatOptions - Optional heartbeat tuning (mostly useful for tests)
|
|
60
|
+
*/ constructor(chainInfo, appId, extensionId, port, sharedKey, logger, heartbeatOptions){
|
|
54
61
|
this.chainInfo = chainInfo;
|
|
55
62
|
this.appId = appId;
|
|
56
63
|
this.extensionId = extensionId;
|
|
@@ -59,6 +66,11 @@ import { WalletMessageType } from '../../types.js';
|
|
|
59
66
|
this.inFlight = new Map();
|
|
60
67
|
this.disconnected = false;
|
|
61
68
|
this.disconnectCallbacks = [];
|
|
69
|
+
this.heartbeatTimer = null;
|
|
70
|
+
this.lastInboundAt = 0;
|
|
71
|
+
this.log = logger ?? NOOP_LOGGER;
|
|
72
|
+
this.heartbeatIntervalMs = heartbeatOptions?.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
73
|
+
this.heartbeatDeadAfterMs = heartbeatOptions?.deadAfterMs ?? DEFAULT_HEARTBEAT_DEAD_AFTER_MS;
|
|
62
74
|
}
|
|
63
75
|
/**
|
|
64
76
|
* Creates a Wallet that communicates with a browser extension
|
|
@@ -69,6 +81,8 @@ import { WalletMessageType } from '../../types.js';
|
|
|
69
81
|
* @param sharedKey - The derived AES-256-GCM shared key for encryption
|
|
70
82
|
* @param chainInfo - The chain information (chainId and version) for request context
|
|
71
83
|
* @param appId - Application identifier used to identify the requesting dApp to the wallet
|
|
84
|
+
* @param logger - Optional logger; defaults to a no-op logger to keep extension/page bundles small
|
|
85
|
+
* @param heartbeatOptions - Optional override for heartbeat tuning (mostly useful for tests)
|
|
72
86
|
* @returns A Wallet interface where all method calls are encrypted
|
|
73
87
|
*
|
|
74
88
|
* @example
|
|
@@ -85,12 +99,13 @@ import { WalletMessageType } from '../../types.js';
|
|
|
85
99
|
*
|
|
86
100
|
* const accounts = await wallet.getAccounts();
|
|
87
101
|
* ```
|
|
88
|
-
*/ static create(extensionId, port, sharedKey, chainInfo, appId) {
|
|
89
|
-
const wallet = new ExtensionWallet(chainInfo, appId, extensionId, port, sharedKey);
|
|
102
|
+
*/ static create(extensionId, port, sharedKey, chainInfo, appId, logger, heartbeatOptions) {
|
|
103
|
+
const wallet = new ExtensionWallet(chainInfo, appId, extensionId, port, sharedKey, logger, heartbeatOptions);
|
|
90
104
|
// Set up message handler for encrypted responses and unencrypted control messages
|
|
91
105
|
wallet.port.onmessage = (event)=>{
|
|
92
106
|
const data = event.data;
|
|
93
|
-
//
|
|
107
|
+
// Any inbound traffic counts as proof of liveness.
|
|
108
|
+
wallet.lastInboundAt = Date.now();
|
|
94
109
|
if (data && typeof data === 'object' && 'type' in data && data.type === WalletMessageType.DISCONNECT) {
|
|
95
110
|
wallet.handleDisconnect();
|
|
96
111
|
return;
|
|
@@ -109,7 +124,7 @@ import { WalletMessageType } from '../../types.js';
|
|
|
109
124
|
type: prop.toString(),
|
|
110
125
|
args
|
|
111
126
|
});
|
|
112
|
-
return WalletSchema[prop.toString()]
|
|
127
|
+
return getSchemaReturnType(WalletSchema[prop.toString()]).parseAsync(result);
|
|
113
128
|
};
|
|
114
129
|
} else {
|
|
115
130
|
return target[prop];
|
|
@@ -152,8 +167,12 @@ import { WalletMessageType } from '../../types.js';
|
|
|
152
167
|
resolve(result);
|
|
153
168
|
}
|
|
154
169
|
this.inFlight.delete(messageId);
|
|
155
|
-
|
|
156
|
-
} catch
|
|
170
|
+
this.maybeStopHeartbeat();
|
|
171
|
+
} catch (err) {
|
|
172
|
+
this.log.warn('Failed to decrypt wallet response', {
|
|
173
|
+
err
|
|
174
|
+
});
|
|
175
|
+
}
|
|
157
176
|
}
|
|
158
177
|
/**
|
|
159
178
|
* Sends an encrypted wallet method call over the secure MessageChannel.
|
|
@@ -190,9 +209,57 @@ import { WalletMessageType } from '../../types.js';
|
|
|
190
209
|
resolve,
|
|
191
210
|
reject
|
|
192
211
|
});
|
|
212
|
+
this.startHeartbeat();
|
|
193
213
|
return promise;
|
|
194
214
|
}
|
|
195
215
|
/**
|
|
216
|
+
* Start the heartbeat probe loop while at least one request is in flight.
|
|
217
|
+
* Idempotent — calling while already running is a no-op.
|
|
218
|
+
*
|
|
219
|
+
* Heartbeat is opt-in via wire protocol: PINGs are unencrypted control messages
|
|
220
|
+
* (like DISCONNECT). Older wallets that do not understand PING simply drop it,
|
|
221
|
+
* which is safe — we only declare disconnect when **no** inbound traffic of any
|
|
222
|
+
* kind (PONG, encrypted response, DISCONNECT) arrives within the dead window.
|
|
223
|
+
* A wallet that is processing a slow request will reset the timer when it
|
|
224
|
+
* eventually responds, so this never causes false disconnects on legacy peers.
|
|
225
|
+
*/ startHeartbeat() {
|
|
226
|
+
if (this.heartbeatTimer !== null || this.disconnected) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
this.lastInboundAt = Date.now();
|
|
230
|
+
this.heartbeatTimer = setInterval(()=>this.heartbeatTick(), this.heartbeatIntervalMs);
|
|
231
|
+
}
|
|
232
|
+
maybeStopHeartbeat() {
|
|
233
|
+
if (this.inFlight.size === 0 && this.heartbeatTimer !== null) {
|
|
234
|
+
clearInterval(this.heartbeatTimer);
|
|
235
|
+
this.heartbeatTimer = null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
heartbeatTick() {
|
|
239
|
+
if (this.disconnected || this.inFlight.size === 0) {
|
|
240
|
+
this.maybeStopHeartbeat();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const idleMs = Date.now() - this.lastInboundAt;
|
|
244
|
+
if (idleMs >= this.heartbeatDeadAfterMs) {
|
|
245
|
+
this.log.warn('Wallet channel unresponsive — declaring disconnect', {
|
|
246
|
+
idleMs,
|
|
247
|
+
inFlight: this.inFlight.size
|
|
248
|
+
});
|
|
249
|
+
this.handleDisconnect();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
this.port.postMessage({
|
|
254
|
+
type: WalletMessageType.PING
|
|
255
|
+
});
|
|
256
|
+
} catch (err) {
|
|
257
|
+
this.log.warn('Failed to send heartbeat PING', {
|
|
258
|
+
err
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
196
263
|
* Handles wallet disconnection.
|
|
197
264
|
* Rejects all pending requests and notifies registered callbacks.
|
|
198
265
|
* @internal
|
|
@@ -201,6 +268,10 @@ import { WalletMessageType } from '../../types.js';
|
|
|
201
268
|
return;
|
|
202
269
|
}
|
|
203
270
|
this.disconnected = true;
|
|
271
|
+
if (this.heartbeatTimer !== null) {
|
|
272
|
+
clearInterval(this.heartbeatTimer);
|
|
273
|
+
this.heartbeatTimer = null;
|
|
274
|
+
}
|
|
204
275
|
if (this.port) {
|
|
205
276
|
this.port.onmessage = null;
|
|
206
277
|
this.port.close();
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { ChainInfo } from '@aztec/aztec.js/account';
|
|
17
17
|
import type { Wallet } from '@aztec/aztec.js/wallet';
|
|
18
|
+
import { type WalletSdkLogger } from '../../types.js';
|
|
18
19
|
/**
|
|
19
20
|
* A pending discovery request from a dApp (before user approval).
|
|
20
21
|
*/
|
|
@@ -57,6 +58,8 @@ export interface IframeConnectionConfig {
|
|
|
57
58
|
walletIcon?: string;
|
|
58
59
|
/** Origins allowed to connect. If empty or undefined, all origins are allowed (dev mode). */
|
|
59
60
|
allowedOrigins?: string[];
|
|
61
|
+
/** Logger used for diagnostics. */
|
|
62
|
+
logger: WalletSdkLogger;
|
|
60
63
|
}
|
|
61
64
|
/**
|
|
62
65
|
* Event callbacks for the iframe connection handler.
|
|
@@ -85,7 +88,7 @@ export interface IframeConnectionCallbacks {
|
|
|
85
88
|
* @example
|
|
86
89
|
* ```typescript
|
|
87
90
|
* const handler = new IframeConnectionHandler(
|
|
88
|
-
* { walletId: 'my-wallet', walletName: 'My Wallet', walletVersion: '1.0.0' },
|
|
91
|
+
* { walletId: 'my-wallet', walletName: 'My Wallet', walletVersion: '1.0.0', logger: console },
|
|
89
92
|
* {
|
|
90
93
|
* onPendingDiscovery: (session) => showApprovalUI(session),
|
|
91
94
|
* getWallet: (appId, chainInfo) => createWalletForApp(appId, chainInfo),
|
|
@@ -109,10 +112,11 @@ export declare class IframeConnectionHandler {
|
|
|
109
112
|
getPendingSessions(): PendingSession[];
|
|
110
113
|
private handleMessage;
|
|
111
114
|
private handleMessageAsync;
|
|
115
|
+
private handlePing;
|
|
112
116
|
private handleDiscoveryRequest;
|
|
113
117
|
private handleKeyExchangeRequest;
|
|
114
118
|
private handleSecureMessage;
|
|
115
119
|
private postToParent;
|
|
116
120
|
private postToOrigin;
|
|
117
121
|
}
|
|
118
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
122
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWZyYW1lX2Nvbm5lY3Rpb25faGFuZGxlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2lmcmFtZS9oYW5kbGVycy9pZnJhbWVfY29ubmVjdGlvbl9oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDekQsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFjckQsT0FBTyxFQUE4RCxLQUFLLGVBQWUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRWxIOztHQUVHO0FBQ0gsTUFBTSxXQUFXLGNBQWM7SUFDN0IsZ0NBQWdDO0lBQ2hDLFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsNkJBQTZCO0lBQzdCLEtBQUssRUFBRSxNQUFNLENBQUM7SUFDZCx3Q0FBd0M7SUFDeEMsTUFBTSxFQUFFLE1BQU0sQ0FBQztJQUNmLHNCQUFzQjtJQUN0QixNQUFNLEVBQUUsU0FBUyxHQUFHLFVBQVUsQ0FBQztDQUNoQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxXQUFXLGFBQWE7SUFDNUIsMkRBQTJEO0lBQzNELFNBQVMsRUFBRSxNQUFNLENBQUM7SUFDbEIsOENBQThDO0lBQzlDLFNBQVMsRUFBRSxTQUFTLENBQUM7SUFDckIsMENBQTBDO0lBQzFDLGdCQUFnQixFQUFFLE1BQU0sQ0FBQztJQUN6Qix1Q0FBdUM7SUFDdkMsTUFBTSxFQUFFLE1BQU0sQ0FBQztJQUNmLDZCQUE2QjtJQUM3QixLQUFLLEVBQUUsTUFBTSxDQUFDO0NBQ2Y7QUFFRDs7R0FFRztBQUNILE1BQU0sV0FBVyxzQkFBc0I7SUFDckMsK0JBQStCO0lBQy9CLFFBQVEsRUFBRSxNQUFNLENBQUM7SUFDakIsa0NBQWtDO0lBQ2xDLFVBQVUsRUFBRSxNQUFNLENBQUM7SUFDbkIsNEJBQTRCO0lBQzVCLGFBQWEsRUFBRSxNQUFNLENBQUM7SUFDdEIsK0JBQStCO0lBQy9CLFVBQVUsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUNwQiw2RkFBNkY7SUFDN0YsY0FBYyxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFDMUIsbUNBQW1DO0lBQ25DLE1BQU0sRUFBRSxlQUFlLENBQUM7Q0FDekI7QUFFRDs7R0FFRztBQUNILE1BQU0sV0FBVyx5QkFBeUI7SUFDeEMsa0ZBQWdGO0lBQ2hGLGtCQUFrQixDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsY0FBYyxLQUFLLElBQUksQ0FBQztJQUN2RCxtRUFBbUU7SUFDbkUsb0JBQW9CLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxhQUFhLEtBQUssSUFBSSxDQUFDO0lBQ3hELDBDQUEwQztJQUMxQyxtQkFBbUIsQ0FBQyxFQUFFLENBQUMsU0FBUyxFQUFFLE1BQU0sS0FBSyxJQUFJLENBQUM7SUFDbEQsMkZBQXlGO0lBQ3pGLGtCQUFrQixDQUFDLEVBQUUsQ0FBQyxnQkFBZ0IsRUFBRSxNQUFNLEtBQUssSUFBSSxDQUFDO0lBQ3hEOzs7T0FHRztJQUNILFNBQVMsRUFBRSxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLFNBQVMsS0FBSyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7Q0FDckU7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFDSCxxQkFBYSx1QkFBdUI7SUFNaEMsT0FBTyxDQUFDLE1BQU07SUFDZCxPQUFPLENBQUMsU0FBUztJQU5uQixPQUFPLENBQUMsZUFBZSxDQUFxQztJQUM1RCxPQUFPLENBQUMsY0FBYyxDQUFvQztJQUMxRCxPQUFPLENBQUMsR0FBRyxDQUFrQjtJQUU3QixZQUNVLE1BQU0sRUFBRSxzQkFBc0IsRUFDOUIsU0FBUyxFQUFFLHlCQUF5QixFQUc3QztJQUVELEtBQUssSUFBSSxJQUFJLENBSVo7SUFFRCxJQUFJLElBQUksSUFBSSxDQUVYO0lBRUQsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLE1BQU0sR0FBRyxJQUFJLENBa0J4QztJQUVELGVBQWUsQ0FBQyxTQUFTLEVBQUUsTUFBTSxHQUFHLElBQUksQ0FFdkM7SUFFRCxnQkFBZ0IsQ0FBQyxTQUFTLEVBQUUsTUFBTSxHQUFHLElBQUksQ0FVeEM7SUFFRCxrQkFBa0IsSUFBSSxjQUFjLEVBQUUsQ0FFckM7SUFFRCxPQUFPLENBQUMsYUFBYSxDQUVuQjtZQUVZLGtCQUFrQjtJQStCaEMsT0FBTyxDQUFDLFVBQVU7SUFRbEIsT0FBTyxDQUFDLHNCQUFzQjtZQVNoQix3QkFBd0I7WUE2Q3hCLG1CQUFtQjtJQXFEakMsT0FBTyxDQUFDLFlBQVk7SUFNcEIsT0FBTyxDQUFDLFlBQVk7Q0FLckIifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iframe_connection_handler.d.ts","sourceRoot":"","sources":["../../../src/iframe/handlers/iframe_connection_handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"iframe_connection_handler.d.ts","sourceRoot":"","sources":["../../../src/iframe/handlers/iframe_connection_handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAcrD,OAAO,EAA8D,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAElH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,MAAM,EAAE,SAAS,GAAG,UAAU,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,SAAS,EAAE,SAAS,CAAC;IACrB,0CAA0C;IAC1C,gBAAgB,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,+BAA+B;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6FAA6F;IAC7F,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,mCAAmC;IACnC,MAAM,EAAE,eAAe,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,kFAAgF;IAChF,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IACvD,mEAAmE;IACnE,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IACxD,0CAA0C;IAC1C,mBAAmB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,2FAAyF;IACzF,kBAAkB,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD;;;OAGG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACrE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,uBAAuB;IAMhC,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,SAAS;IANnB,OAAO,CAAC,eAAe,CAAqC;IAC5D,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,GAAG,CAAkB;IAE7B,YACU,MAAM,EAAE,sBAAsB,EAC9B,SAAS,EAAE,yBAAyB,EAG7C;IAED,KAAK,IAAI,IAAI,CAIZ;IAED,IAAI,IAAI,IAAI,CAEX;IAED,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAkBxC;IAED,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEvC;IAED,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAUxC;IAED,kBAAkB,IAAI,cAAc,EAAE,CAErC;IAED,OAAO,CAAC,aAAa,CAEnB;YAEY,kBAAkB;IA+BhC,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,sBAAsB;YAShB,wBAAwB;YA6CxB,mBAAmB;IAqDjC,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,YAAY;CAKrB"}
|
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
*
|
|
13
13
|
* The wallet announces itself by posting WALLET_READY as soon as the handler starts,
|
|
14
14
|
* so the dApp knows it can send a discovery request.
|
|
15
|
-
*/ import {
|
|
16
|
-
import { WalletSchema } from '@aztec/aztec.js/wallet';
|
|
15
|
+
*/ import { WalletSchema } from '@aztec/aztec.js/wallet';
|
|
17
16
|
import { jsonStringify } from '@aztec/foundation/json-rpc';
|
|
18
|
-
import { parseWithOptionals, schemaHasMethod } from '@aztec/foundation/schemas';
|
|
17
|
+
import { getSchemaParameters, parseWithOptionals, schemaHasMethod } from '@aztec/foundation/schemas';
|
|
19
18
|
import { decrypt, deriveSessionKeys, encrypt, exportPublicKey, generateKeyPair, importPublicKey } from '../../crypto.js';
|
|
20
19
|
import { WalletMessageType } from '../../types.js';
|
|
21
20
|
/**
|
|
@@ -27,7 +26,7 @@ import { WalletMessageType } from '../../types.js';
|
|
|
27
26
|
* @example
|
|
28
27
|
* ```typescript
|
|
29
28
|
* const handler = new IframeConnectionHandler(
|
|
30
|
-
* { walletId: 'my-wallet', walletName: 'My Wallet', walletVersion: '1.0.0' },
|
|
29
|
+
* { walletId: 'my-wallet', walletName: 'My Wallet', walletVersion: '1.0.0', logger: console },
|
|
31
30
|
* {
|
|
32
31
|
* onPendingDiscovery: (session) => showApprovalUI(session),
|
|
33
32
|
* getWallet: (appId, chainInfo) => createWalletForApp(appId, chainInfo),
|
|
@@ -46,10 +45,10 @@ import { WalletMessageType } from '../../types.js';
|
|
|
46
45
|
this.callbacks = callbacks;
|
|
47
46
|
this.pendingSessions = new Map();
|
|
48
47
|
this.activeSessions = new Map();
|
|
49
|
-
this.log = createLogger('wallet:iframe-handler');
|
|
50
48
|
this.handleMessage = (event)=>{
|
|
51
49
|
void this.handleMessageAsync(event);
|
|
52
50
|
};
|
|
51
|
+
this.log = config.logger;
|
|
53
52
|
}
|
|
54
53
|
start() {
|
|
55
54
|
window.addEventListener('message', this.handleMessage);
|
|
@@ -120,8 +119,21 @@ import { WalletMessageType } from '../../types.js';
|
|
|
120
119
|
case WalletMessageType.DISCONNECT:
|
|
121
120
|
this.terminateSession(msg.sessionId);
|
|
122
121
|
break;
|
|
122
|
+
case WalletMessageType.PING:
|
|
123
|
+
this.handlePing(msg.sessionId);
|
|
124
|
+
break;
|
|
123
125
|
}
|
|
124
126
|
}
|
|
127
|
+
handlePing(sessionId) {
|
|
128
|
+
const session = this.activeSessions.get(sessionId);
|
|
129
|
+
if (!session) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this.postToOrigin(session.origin, {
|
|
133
|
+
type: WalletMessageType.PONG,
|
|
134
|
+
sessionId
|
|
135
|
+
});
|
|
136
|
+
}
|
|
125
137
|
handleDiscoveryRequest(msg, origin) {
|
|
126
138
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
127
139
|
const { requestId, appId } = msg;
|
|
@@ -191,8 +203,7 @@ import { WalletMessageType } from '../../types.js';
|
|
|
191
203
|
if (!schemaHasMethod(WalletSchema, type)) {
|
|
192
204
|
throw new Error(`Unknown wallet method: ${type}`);
|
|
193
205
|
}
|
|
194
|
-
|
|
195
|
-
const sanitizedArgs = await parseWithOptionals(args, WalletSchema[type].parameters());
|
|
206
|
+
const sanitizedArgs = await parseWithOptionals(args, getSchemaParameters(WalletSchema[type]));
|
|
196
207
|
result = await wallet[type](...sanitizedArgs);
|
|
197
208
|
} catch (err) {
|
|
198
209
|
error = err instanceof Error ? err.message : String(err);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { ChainInfo } from '@aztec/aztec.js/account';
|
|
10
10
|
import { type Wallet } from '@aztec/aztec.js/wallet';
|
|
11
|
-
import { type DisconnectCallback } from '../../types.js';
|
|
11
|
+
import { type DisconnectCallback, type HeartbeatOptions, type WalletSdkLogger } from '../../types.js';
|
|
12
12
|
/**
|
|
13
13
|
* A wallet implementation that communicates with a web wallet loaded in an iframe
|
|
14
14
|
* using encrypted postMessage.
|
|
@@ -35,6 +35,11 @@ export declare class IframeWallet {
|
|
|
35
35
|
private disconnected;
|
|
36
36
|
private disconnectCallbacks;
|
|
37
37
|
private messageListener;
|
|
38
|
+
private heartbeatTimer;
|
|
39
|
+
private lastInboundAt;
|
|
40
|
+
private log;
|
|
41
|
+
private heartbeatIntervalMs;
|
|
42
|
+
private heartbeatDeadAfterMs;
|
|
38
43
|
private constructor();
|
|
39
44
|
/**
|
|
40
45
|
* Creates a proxied IframeWallet that implements the {@link Wallet} interface.
|
|
@@ -50,9 +55,11 @@ export declare class IframeWallet {
|
|
|
50
55
|
* @param sharedKey - AES-256-GCM key derived from ECDH key exchange
|
|
51
56
|
* @param chainInfo - Network information (chainId and version)
|
|
52
57
|
* @param appId - Application identifier for the requesting dApp
|
|
58
|
+
* @param logger - Optional logger; defaults to a no-op logger to keep extension/page bundles small
|
|
59
|
+
* @param heartbeatOptions - Optional override for heartbeat tuning (mostly useful for tests)
|
|
53
60
|
* @returns A proxied IframeWallet — call `.asWallet()` to get the typed `Wallet`
|
|
54
61
|
*/
|
|
55
|
-
static create(walletId: string, sessionId: string, iframeWindow: Window, walletOrigin: string, sharedKey: CryptoKey, chainInfo: ChainInfo, appId: string): IframeWallet;
|
|
62
|
+
static create(walletId: string, sessionId: string, iframeWindow: Window, walletOrigin: string, sharedKey: CryptoKey, chainInfo: ChainInfo, appId: string, logger?: WalletSdkLogger, heartbeatOptions?: HeartbeatOptions): IframeWallet;
|
|
56
63
|
/**
|
|
57
64
|
* Returns this wallet as a typed {@link Wallet} interface.
|
|
58
65
|
* When accessed through the Proxy (via `create()`), returns the proxy itself.
|
|
@@ -60,9 +67,19 @@ export declare class IframeWallet {
|
|
|
60
67
|
asWallet(): Wallet;
|
|
61
68
|
private handleEncryptedResponse;
|
|
62
69
|
private postMessage;
|
|
70
|
+
/**
|
|
71
|
+
* Start liveness probing while at least one request is in flight. PINGs are
|
|
72
|
+
* unencrypted control messages — older wallet handlers that don't understand
|
|
73
|
+
* them simply drop them, but any inbound traffic (PONG, encrypted response,
|
|
74
|
+
* disconnect notice) resets the idle timer, so a slow-but-alive legacy wallet
|
|
75
|
+
* never trips a false disconnect.
|
|
76
|
+
*/
|
|
77
|
+
private startHeartbeat;
|
|
78
|
+
private maybeStopHeartbeat;
|
|
79
|
+
private heartbeatTick;
|
|
63
80
|
private handleDisconnect;
|
|
64
81
|
onDisconnect(callback: DisconnectCallback): () => void;
|
|
65
82
|
isDisconnected(): boolean;
|
|
66
83
|
disconnect(): void;
|
|
67
84
|
}
|
|
68
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
85
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWZyYW1lX3dhbGxldC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2lmcmFtZS9wcm92aWRlci9pZnJhbWVfd2FsbGV0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O0dBT0c7QUFDSCxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsS0FBSyxNQUFNLEVBQWdCLE1BQU0sd0JBQXdCLENBQUM7QUFPbkUsT0FBTyxFQUdMLEtBQUssa0JBQWtCLEVBQ3ZCLEtBQUssZ0JBQWdCLEVBS3JCLEtBQUssZUFBZSxFQUNyQixNQUFNLGdCQUFnQixDQUFDO0FBYXhCOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxxQkFBYSxZQUFZO0lBWXJCLE9BQU8sQ0FBQyxTQUFTO0lBQ2pCLE9BQU8sQ0FBQyxLQUFLO0lBQ2IsT0FBTyxDQUFDLFFBQVE7SUFDaEIsT0FBTyxDQUFDLFNBQVM7SUFDakIsT0FBTyxDQUFDLFlBQVk7SUFDcEIsT0FBTyxDQUFDLFlBQVk7SUFDcEIsT0FBTyxDQUFDLFNBQVM7SUFqQm5CLE9BQU8sQ0FBQyxRQUFRLENBQW9EO0lBQ3BFLE9BQU8sQ0FBQyxZQUFZLENBQVM7SUFDN0IsT0FBTyxDQUFDLG1CQUFtQixDQUE0QjtJQUN2RCxPQUFPLENBQUMsZUFBZSxDQUE0QztJQUNuRSxPQUFPLENBQUMsY0FBYyxDQUErQztJQUNyRSxPQUFPLENBQUMsYUFBYSxDQUFLO0lBQzFCLE9BQU8sQ0FBQyxHQUFHLENBQWtCO0lBQzdCLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBUztJQUNwQyxPQUFPLENBQUMsb0JBQW9CLENBQVM7SUFFckMsT0FBTyxlQWNOO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7O09BaUJHO0lBQ0gsTUFBTSxDQUFDLE1BQU0sQ0FDWCxRQUFRLEVBQUUsTUFBTSxFQUNoQixTQUFTLEVBQUUsTUFBTSxFQUNqQixZQUFZLEVBQUUsTUFBTSxFQUNwQixZQUFZLEVBQUUsTUFBTSxFQUNwQixTQUFTLEVBQUUsU0FBUyxFQUNwQixTQUFTLEVBQUUsU0FBUyxFQUNwQixLQUFLLEVBQUUsTUFBTSxFQUNiLE1BQU0sQ0FBQyxFQUFFLGVBQWUsRUFDeEIsZ0JBQWdCLENBQUMsRUFBRSxnQkFBZ0IsR0FDbEMsWUFBWSxDQXNEZDtJQUVEOzs7T0FHRztJQUNILFFBQVEsSUFBSSxNQUFNLENBRWpCO1lBRWEsdUJBQXVCO1lBMEJ2QixXQUFXO0lBMkJ6Qjs7Ozs7O09BTUc7SUFDSCxPQUFPLENBQUMsY0FBYztJQVF0QixPQUFPLENBQUMsa0JBQWtCO0lBTzFCLE9BQU8sQ0FBQyxhQUFhO0lBdUJyQixPQUFPLENBQUMsZ0JBQWdCO0lBK0J4QixZQUFZLENBQUMsUUFBUSxFQUFFLGtCQUFrQixHQUFHLE1BQU0sSUFBSSxDQVFyRDtJQUVELGNBQWMsSUFBSSxPQUFPLENBRXhCO0lBRUQsVUFBVSxJQUFJLElBQUksQ0FNakI7Q0FDRiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iframe_wallet.d.ts","sourceRoot":"","sources":["../../../src/iframe/provider/iframe_wallet.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,wBAAwB,CAAC;AAOnE,OAAO,
|
|
1
|
+
{"version":3,"file":"iframe_wallet.d.ts","sourceRoot":"","sources":["../../../src/iframe/provider/iframe_wallet.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,wBAAwB,CAAC;AAOnE,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EAKrB,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAaxB;;;;;;;;;;;;;GAaG;AACH,qBAAa,YAAY;IAYrB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,SAAS;IAjBnB,OAAO,CAAC,QAAQ,CAAoD;IACpE,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,mBAAmB,CAA4B;IACvD,OAAO,CAAC,eAAe,CAA4C;IACnE,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,oBAAoB,CAAS;IAErC,OAAO,eAcN;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,MAAM,CACX,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,eAAe,EACxB,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,YAAY,CAsDd;IAED;;;OAGG;IACH,QAAQ,IAAI,MAAM,CAEjB;YAEa,uBAAuB;YA0BvB,WAAW;IA2BzB;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,gBAAgB;IA+BxB,YAAY,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,IAAI,CAQrD;IAED,cAAc,IAAI,OAAO,CAExB;IAED,UAAU,IAAI,IAAI,CAMjB;CACF"}
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
*/ import { WalletSchema } from '@aztec/aztec.js/wallet';
|
|
9
9
|
import { jsonStringify } from '@aztec/foundation/json-rpc';
|
|
10
10
|
import { promiseWithResolvers } from '@aztec/foundation/promise';
|
|
11
|
-
import { schemaHasMethod } from '@aztec/foundation/schemas';
|
|
11
|
+
import { getSchemaReturnType, schemaHasMethod } from '@aztec/foundation/schemas';
|
|
12
12
|
import { decrypt, encrypt } from '../../crypto.js';
|
|
13
|
-
import { WalletMessageType } from '../../types.js';
|
|
13
|
+
import { DEFAULT_HEARTBEAT_DEAD_AFTER_MS, DEFAULT_HEARTBEAT_INTERVAL_MS, NOOP_LOGGER, WalletMessageType } from '../../types.js';
|
|
14
14
|
/**
|
|
15
15
|
* A wallet implementation that communicates with a web wallet loaded in an iframe
|
|
16
16
|
* using encrypted postMessage.
|
|
@@ -36,7 +36,12 @@ import { WalletMessageType } from '../../types.js';
|
|
|
36
36
|
disconnected;
|
|
37
37
|
disconnectCallbacks;
|
|
38
38
|
messageListener;
|
|
39
|
-
|
|
39
|
+
heartbeatTimer;
|
|
40
|
+
lastInboundAt;
|
|
41
|
+
log;
|
|
42
|
+
heartbeatIntervalMs;
|
|
43
|
+
heartbeatDeadAfterMs;
|
|
44
|
+
constructor(chainInfo, appId, walletId, sessionId, iframeWindow, walletOrigin, sharedKey, logger, heartbeatOptions){
|
|
40
45
|
this.chainInfo = chainInfo;
|
|
41
46
|
this.appId = appId;
|
|
42
47
|
this.walletId = walletId;
|
|
@@ -48,6 +53,11 @@ import { WalletMessageType } from '../../types.js';
|
|
|
48
53
|
this.disconnected = false;
|
|
49
54
|
this.disconnectCallbacks = [];
|
|
50
55
|
this.messageListener = null;
|
|
56
|
+
this.heartbeatTimer = null;
|
|
57
|
+
this.lastInboundAt = 0;
|
|
58
|
+
this.log = logger ?? NOOP_LOGGER;
|
|
59
|
+
this.heartbeatIntervalMs = heartbeatOptions?.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
60
|
+
this.heartbeatDeadAfterMs = heartbeatOptions?.deadAfterMs ?? DEFAULT_HEARTBEAT_DEAD_AFTER_MS;
|
|
51
61
|
}
|
|
52
62
|
/**
|
|
53
63
|
* Creates a proxied IframeWallet that implements the {@link Wallet} interface.
|
|
@@ -63,9 +73,11 @@ import { WalletMessageType } from '../../types.js';
|
|
|
63
73
|
* @param sharedKey - AES-256-GCM key derived from ECDH key exchange
|
|
64
74
|
* @param chainInfo - Network information (chainId and version)
|
|
65
75
|
* @param appId - Application identifier for the requesting dApp
|
|
76
|
+
* @param logger - Optional logger; defaults to a no-op logger to keep extension/page bundles small
|
|
77
|
+
* @param heartbeatOptions - Optional override for heartbeat tuning (mostly useful for tests)
|
|
66
78
|
* @returns A proxied IframeWallet — call `.asWallet()` to get the typed `Wallet`
|
|
67
|
-
*/ static create(walletId, sessionId, iframeWindow, walletOrigin, sharedKey, chainInfo, appId) {
|
|
68
|
-
const wallet = new IframeWallet(chainInfo, appId, walletId, sessionId, iframeWindow, walletOrigin, sharedKey);
|
|
79
|
+
*/ static create(walletId, sessionId, iframeWindow, walletOrigin, sharedKey, chainInfo, appId, logger, heartbeatOptions) {
|
|
80
|
+
const wallet = new IframeWallet(chainInfo, appId, walletId, sessionId, iframeWindow, walletOrigin, sharedKey, logger, heartbeatOptions);
|
|
69
81
|
wallet.messageListener = (event)=>{
|
|
70
82
|
if (event.origin !== walletOrigin) {
|
|
71
83
|
return;
|
|
@@ -74,9 +86,14 @@ import { WalletMessageType } from '../../types.js';
|
|
|
74
86
|
if (!msg || typeof msg !== 'object') {
|
|
75
87
|
return;
|
|
76
88
|
}
|
|
77
|
-
if (msg.
|
|
89
|
+
if (msg.sessionId !== sessionId) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Any inbound traffic on our session counts as proof of liveness.
|
|
93
|
+
wallet.lastInboundAt = Date.now();
|
|
94
|
+
if (msg.type === WalletMessageType.SECURE_RESPONSE) {
|
|
78
95
|
void wallet.handleEncryptedResponse(msg.encrypted);
|
|
79
|
-
} else if (msg.type === WalletMessageType.SESSION_DISCONNECTED
|
|
96
|
+
} else if (msg.type === WalletMessageType.SESSION_DISCONNECTED) {
|
|
80
97
|
wallet.handleDisconnect();
|
|
81
98
|
}
|
|
82
99
|
};
|
|
@@ -91,7 +108,7 @@ import { WalletMessageType } from '../../types.js';
|
|
|
91
108
|
type: prop.toString(),
|
|
92
109
|
args
|
|
93
110
|
});
|
|
94
|
-
return WalletSchema[prop.toString()]
|
|
111
|
+
return getSchemaReturnType(WalletSchema[prop.toString()]).parseAsync(result);
|
|
95
112
|
};
|
|
96
113
|
} else {
|
|
97
114
|
return target[prop];
|
|
@@ -122,8 +139,11 @@ import { WalletMessageType } from '../../types.js';
|
|
|
122
139
|
pending.resolve(result);
|
|
123
140
|
}
|
|
124
141
|
this.inFlight.delete(messageId);
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
this.maybeStopHeartbeat();
|
|
143
|
+
} catch (err) {
|
|
144
|
+
this.log.warn('Failed to decrypt wallet response', {
|
|
145
|
+
err
|
|
146
|
+
});
|
|
127
147
|
}
|
|
128
148
|
}
|
|
129
149
|
async postMessage(call) {
|
|
@@ -151,13 +171,62 @@ import { WalletMessageType } from '../../types.js';
|
|
|
151
171
|
resolve,
|
|
152
172
|
reject
|
|
153
173
|
});
|
|
174
|
+
this.startHeartbeat();
|
|
154
175
|
return promise;
|
|
155
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Start liveness probing while at least one request is in flight. PINGs are
|
|
179
|
+
* unencrypted control messages — older wallet handlers that don't understand
|
|
180
|
+
* them simply drop them, but any inbound traffic (PONG, encrypted response,
|
|
181
|
+
* disconnect notice) resets the idle timer, so a slow-but-alive legacy wallet
|
|
182
|
+
* never trips a false disconnect.
|
|
183
|
+
*/ startHeartbeat() {
|
|
184
|
+
if (this.heartbeatTimer !== null || this.disconnected) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
this.lastInboundAt = Date.now();
|
|
188
|
+
this.heartbeatTimer = setInterval(()=>this.heartbeatTick(), this.heartbeatIntervalMs);
|
|
189
|
+
}
|
|
190
|
+
maybeStopHeartbeat() {
|
|
191
|
+
if (this.inFlight.size === 0 && this.heartbeatTimer !== null) {
|
|
192
|
+
clearInterval(this.heartbeatTimer);
|
|
193
|
+
this.heartbeatTimer = null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
heartbeatTick() {
|
|
197
|
+
if (this.disconnected || this.inFlight.size === 0) {
|
|
198
|
+
this.maybeStopHeartbeat();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const idleMs = Date.now() - this.lastInboundAt;
|
|
202
|
+
if (idleMs >= this.heartbeatDeadAfterMs) {
|
|
203
|
+
this.log.warn('Iframe wallet channel unresponsive — declaring disconnect', {
|
|
204
|
+
idleMs,
|
|
205
|
+
inFlight: this.inFlight.size
|
|
206
|
+
});
|
|
207
|
+
this.handleDisconnect();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
this.iframeWindow.postMessage({
|
|
212
|
+
type: WalletMessageType.PING,
|
|
213
|
+
sessionId: this.sessionId
|
|
214
|
+
}, this.walletOrigin);
|
|
215
|
+
} catch (err) {
|
|
216
|
+
this.log.warn('Failed to send heartbeat PING', {
|
|
217
|
+
err
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
156
221
|
handleDisconnect() {
|
|
157
222
|
if (this.disconnected) {
|
|
158
223
|
return;
|
|
159
224
|
}
|
|
160
225
|
this.disconnected = true;
|
|
226
|
+
if (this.heartbeatTimer !== null) {
|
|
227
|
+
clearInterval(this.heartbeatTimer);
|
|
228
|
+
this.heartbeatTimer = null;
|
|
229
|
+
}
|
|
161
230
|
if (this.messageListener) {
|
|
162
231
|
window.removeEventListener('message', this.messageListener);
|
|
163
232
|
this.messageListener = null;
|