import type { ChainInfo } from '@aztec/aztec.js/account'; import { type Wallet } from '@aztec/aztec.js/wallet'; import { type DisconnectCallback, type HeartbeatOptions, type WalletSdkLogger } from '../../types.js'; /** * A wallet implementation that communicates with browser extension wallets * using an encrypted MessageChannel. * * This class uses a secure channel established after discovery: * * 1. **MessageChannel**: Created during discovery and transferred via window.postMessage. * Note: The port transfer is visible to page scripts, but security comes from encryption. * * 2. **ECDH Key Exchange**: The shared secret was derived after discovery using * Elliptic Curve Diffie-Hellman key exchange over the MessagePort. * * 3. **AES-GCM Encryption**: All messages are encrypted using AES-256-GCM, * providing both confidentiality and authenticity. This is what secures the channel. * * @example * ```typescript * // Discover and establish secure channel to a wallet * const discoveredWallets = await ExtensionProvider.discoverWallets(chainInfo, { appId: 'my-dapp' }); * const connection = await discoveredWallets[0].establishSecureChannel(); * * // User can verify emoji if desired * console.log('Verify:', hashToEmoji(connection.info.verificationHash!)); * * // Create wallet using the connection * const wallet = ExtensionWallet.create(connection.info.id, connection.port, connection.sharedKey, chainInfo, 'my-dapp'); * * // All subsequent calls are encrypted * const accounts = await wallet.getAccounts(); * ``` */ export declare class ExtensionWallet { private chainInfo; private appId; private extensionId; private port; private sharedKey; /** Map of pending requests awaiting responses, keyed by message ID */ private inFlight; private disconnected; private disconnectCallbacks; private heartbeatTimer; private lastInboundAt; private log; private heartbeatIntervalMs; private heartbeatDeadAfterMs; /** * Private constructor - use {@link ExtensionWallet.create} to instantiate. * @param chainInfo - The chain information (chainId and version) * @param appId - Application identifier for the requesting dApp * @param extensionId - The unique identifier of the target wallet extension * @param port - The MessagePort for private communication with the wallet * @param sharedKey - The derived AES-256-GCM shared key for encryption * @param logger - Optional logger; defaults to a no-op logger * @param heartbeatOptions - Optional heartbeat tuning (mostly useful for tests) */ private constructor(); /** * Creates a Wallet that communicates with a browser extension * over a secure encrypted MessageChannel. * * @param extensionId - The unique identifier of the wallet extension * @param port - The MessagePort for encrypted communication with the wallet * @param sharedKey - The derived AES-256-GCM shared key for encryption * @param chainInfo - The chain information (chainId and version) for request context * @param appId - Application identifier used to identify the requesting dApp to the wallet * @param logger - Optional logger; defaults to a no-op logger to keep extension/page bundles small * @param heartbeatOptions - Optional override for heartbeat tuning (mostly useful for tests) * @returns A Wallet interface where all method calls are encrypted * * @example * ```typescript * const discoveredWallets = await ExtensionProvider.discoverWallets(chainInfo, { appId: 'my-defi-app' }); * const connection = await discoveredWallets[0].establishSecureChannel(); * const wallet = ExtensionWallet.create( * connection.info.id, * connection.port, * connection.sharedKey, * chainInfo, * 'my-defi-app' * ); * * const accounts = await wallet.getAccounts(); * ``` */ static create(extensionId: string, port: MessagePort, sharedKey: CryptoKey, chainInfo: ChainInfo, appId: string, logger?: WalletSdkLogger, heartbeatOptions?: HeartbeatOptions): ExtensionWallet; asWallet(): Wallet; private handleEncryptedResponse; private postMessage; /** * Start the heartbeat probe loop while at least one request is in flight. * Idempotent — calling while already running is a no-op. * * Heartbeat is opt-in via wire protocol: PINGs are unencrypted control messages * (like DISCONNECT). Older wallets that do not understand PING simply drop it, * which is safe — we only declare disconnect when **no** inbound traffic of any * kind (PONG, encrypted response, DISCONNECT) arrives within the dead window. * A wallet that is processing a slow request will reset the timer when it * eventually responds, so this never causes false disconnects on legacy peers. */ private startHeartbeat; private maybeStopHeartbeat; private heartbeatTick; /** * Handles wallet disconnection. * Rejects all pending requests and notifies registered callbacks. * @internal */ private handleDisconnect; /** * Registers a callback to be invoked when the wallet disconnects. * * @param callback - Function to call when wallet disconnects * @returns A function to unregister the callback * * @example * ```typescript * const wallet = await ExtensionWallet.create(...); * const unsubscribe = wallet.onDisconnect(() => { * console.log('Wallet disconnected! Please reconnect.'); * // Clean up UI, prompt user to reconnect, etc. * }); * // Later: unsubscribe(); to stop receiving notifications * ``` */ onDisconnect(callback: DisconnectCallback): () => void; /** * Returns whether the wallet has been disconnected. * * @returns true if the wallet is no longer connected */ isDisconnected(): boolean; /** * Disconnects from the wallet and cleans up resources. * * This method notifies the wallet extension that the session is ending, * allowing it to clean up its state. After calling this method, the wallet * instance can no longer be used and any pending requests will be rejected. * * @example * ```typescript * const extensionWallet = ExtensionWallet.create(extensionId, port, sharedKey, chainInfo, 'my-app'); * // ... use wallet ... * await extensionWallet.disconnect(); // Clean disconnect when done * ``` */ disconnect(): Promise; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXh0ZW5zaW9uX3dhbGxldC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2V4dGVuc2lvbi9wcm92aWRlci9leHRlbnNpb25fd2FsbGV0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3pELE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx3QkFBd0IsQ0FBQztBQU9uRSxPQUFPLEVBR0wsS0FBSyxrQkFBa0IsRUFDdkIsS0FBSyxnQkFBZ0IsRUFLckIsS0FBSyxlQUFlLEVBQ3JCLE1BQU0sZ0JBQWdCLENBQUM7QUFheEI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQThCRztBQUNILHFCQUFhLGVBQWU7SUFzQnhCLE9BQU8sQ0FBQyxTQUFTO0lBQ2pCLE9BQU8sQ0FBQyxLQUFLO0lBQ2IsT0FBTyxDQUFDLFdBQVc7SUFDbkIsT0FBTyxDQUFDLElBQUk7SUFDWixPQUFPLENBQUMsU0FBUztJQXpCbkIsc0VBQXNFO0lBQ3RFLE9BQU8sQ0FBQyxRQUFRLENBQW9EO0lBQ3BFLE9BQU8sQ0FBQyxZQUFZLENBQVM7SUFDN0IsT0FBTyxDQUFDLG1CQUFtQixDQUE0QjtJQUN2RCxPQUFPLENBQUMsY0FBYyxDQUErQztJQUNyRSxPQUFPLENBQUMsYUFBYSxDQUFLO0lBQzFCLE9BQU8sQ0FBQyxHQUFHLENBQWtCO0lBQzdCLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBUztJQUNwQyxPQUFPLENBQUMsb0JBQW9CLENBQVM7SUFFckM7Ozs7Ozs7OztPQVNHO0lBQ0gsT0FBTyxlQVlOO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQTJCRztJQUNILE1BQU0sQ0FBQyxNQUFNLENBQ1gsV0FBVyxFQUFFLE1BQU0sRUFDbkIsSUFBSSxFQUFFLFdBQVcsRUFDakIsU0FBUyxFQUFFLFNBQVMsRUFDcEIsU0FBUyxFQUFFLFNBQVMsRUFDcEIsS0FBSyxFQUFFLE1BQU0sRUFDYixNQUFNLENBQUMsRUFBRSxlQUFlLEVBQ3hCLGdCQUFnQixDQUFDLEVBQUUsZ0JBQWdCLEdBQ2xDLGVBQWUsQ0FvQ2pCO0lBRUQsUUFBUSxJQUFJLE1BQU0sQ0FJakI7WUFVYSx1QkFBdUI7WUFnRHZCLFdBQVc7SUEyQnpCOzs7Ozs7Ozs7O09BVUc7SUFDSCxPQUFPLENBQUMsY0FBYztJQVF0QixPQUFPLENBQUMsa0JBQWtCO0lBTzFCLE9BQU8sQ0FBQyxhQUFhO0lBdUJyQjs7OztPQUlHO0lBQ0gsT0FBTyxDQUFDLGdCQUFnQjtJQStCeEI7Ozs7Ozs7Ozs7Ozs7OztPQWVHO0lBQ0gsWUFBWSxDQUFDLFFBQVEsRUFBRSxrQkFBa0IsR0FBRyxNQUFNLElBQUksQ0FRckQ7SUFFRDs7OztPQUlHO0lBQ0gsY0FBYyxJQUFJLE9BQU8sQ0FFeEI7SUFFRDs7Ozs7Ozs7Ozs7OztPQWFHO0lBRUcsVUFBVSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FhaEM7Q0FDRiJ9