UNPKG

@metamask/eth-ledger-bridge-keyring

Version:
170 lines 9.84 kB
"use strict"; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _LedgerDmkTransportMiddleware_instances, _LedgerDmkTransportMiddleware_sdk, _LedgerDmkTransportMiddleware_sessionId, _LedgerDmkTransportMiddleware_managedSessionId, _LedgerDmkTransportMiddleware_cachedSigner, _LedgerDmkTransportMiddleware_assignSession; Object.defineProperty(exports, "__esModule", { value: true }); exports.LedgerDmkTransportMiddleware = void 0; const device_signer_kit_ethereum_1 = require("@ledgerhq/device-signer-kit-ethereum"); /** * LedgerDmkTransportMiddleware is a middleware to communicate with the * Ledger device via DMK. * It adapts the new DMK Signer ETH to the existing bridging architectural patterns. */ class LedgerDmkTransportMiddleware { constructor(sdk) { _LedgerDmkTransportMiddleware_instances.add(this); _LedgerDmkTransportMiddleware_sdk.set(this, void 0); _LedgerDmkTransportMiddleware_sessionId.set(this, void 0); /** * Session created by this middleware's `connect()` call. Only managed * sessions are disconnected automatically; IDs assigned via `setSessionId` * are owned by the host. */ _LedgerDmkTransportMiddleware_managedSessionId.set(this, void 0); _LedgerDmkTransportMiddleware_cachedSigner.set(this, null); __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_sdk, sdk, "f"); } /** * Starts device discovery using the configured DMK transport. * * @param args - Optional DMK discovery options. * @returns An observable that emits discovered devices. */ startDiscovering(...args) { return __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").startDiscovering(...args); } /** * Set the session ID used for subsequent DMK commands. * * Binds the middleware to an existing DMK session without disconnecting * any prior session. If the current session was created by this middleware's * `connect()`, that managed session is released when replaced. * * Invalidates any cached signer so the next `getEthSigner()` call builds a * fresh signer bound to the new session. * * @param sessionId - The session ID for the connected Ledger device. */ async setSessionId(sessionId) { if (__classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f") === sessionId) { return; } const managedToRelease = __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_managedSessionId, "f"); __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_instances, "m", _LedgerDmkTransportMiddleware_assignSession).call(this, sessionId); if (managedToRelease && managedToRelease !== sessionId) { await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").disconnect({ sessionId: managedToRelease }); __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_managedSessionId, undefined, "f"); } } /** * Method to retrieve the session ID. * * @returns The session ID. * @throws {Error} If `setSessionId` or `connect` has not been called yet. */ getSessionId() { if (!__classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f")) { throw new Error('Session ID not set. Call connect() or setSessionId() first.'); } return __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f"); } /** * Connects to a discovered device and stores the resulting session ID. * * @param args - The DMK connection arguments. * @returns The created session ID. */ async connect(...args) { const previousManagedSessionId = __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_managedSessionId, "f"); const sessionId = await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").connect(...args); __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_instances, "m", _LedgerDmkTransportMiddleware_assignSession).call(this, sessionId); __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_managedSessionId, sessionId, "f"); if (previousManagedSessionId && previousManagedSessionId !== sessionId) { await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").disconnect({ sessionId: previousManagedSessionId }); } return sessionId; } /** * Build (or return the cached) Ethereum signer for the current session. * * The signer is memoized per session ID. `getEthSigner()` is called from * five different operations on the bridge (`getPublicKey`, * `deviceSignTransaction`, `deviceSignMessage`, `deviceSignTypedData`, * `deviceSignDelegationAuthorization`); a single bridge session therefore * needs at most one signer instance. * * Rebuilding is not free: `SignerEthBuilder.build()` allocates a fresh * `DefaultContextModule` from `@ledgerhq/context-module`, which in its * constructor builds a DI container and instantiates the default loaders * (calldata, dynamic-network, external-plugin, gated-signing, nft, proxy, * safe, token, trusted-name, typed-data, reporter, transaction-check, plus * the field loaders, typed-data loader and blind-signing reporter). See * `node_modules/@ledgerhq/context-module/lib/esm/src/DefaultContextModule.js`. * * Neither `DefaultSignerEth` nor `DefaultContextModule` expose a * `dispose()` / `disconnect()` / `destroy()` method (verified against * `node_modules/@ledgerhq/device-signer-kit-ethereum/lib/esm/internal/DefaultSignerEth.js` * and the `DefaultContextModule` source above). If any of those loaders * hold subscriptions to device-session state, re-creating the signer on * every call would leak them. Caching is therefore defensive against * subscription leaks, not just a micro-optimization. * * The cache is invalidated by `setSessionId()` when the session changes * and by `dispose()` on shutdown. * * @returns An Ethereum signer instance bound to the current session. */ getEthSigner() { const sessionId = this.getSessionId(); if (__classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_cachedSigner, "f")?.sessionId === sessionId) { return __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_cachedSigner, "f").signer; } const signer = new device_signer_kit_ethereum_1.SignerEthBuilder({ dmk: __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f"), sessionId, }).build(); __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_cachedSigner, { sessionId, signer }, "f"); return signer; } /** * Clears the stored session ID and signer cache without disconnecting from * DMK. * * Use when the bridge does not own the DMK instance and the host manages * session lifecycle externally. */ clearSession() { __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_sessionId, undefined, "f"); __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_managedSessionId, undefined, "f"); __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_cachedSigner, null, "f"); } /** * Disconnect the current session and clear cached state. * * Silently no-ops when no session has been established. * * @returns A promise that resolves when the session is closed. */ async dispose() { const sessionToDisconnect = __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_managedSessionId, "f") ?? __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sessionId, "f"); if (sessionToDisconnect) { await __classPrivateFieldGet(this, _LedgerDmkTransportMiddleware_sdk, "f").disconnect({ sessionId: sessionToDisconnect }); this.clearSession(); } } } exports.LedgerDmkTransportMiddleware = LedgerDmkTransportMiddleware; _LedgerDmkTransportMiddleware_sdk = new WeakMap(), _LedgerDmkTransportMiddleware_sessionId = new WeakMap(), _LedgerDmkTransportMiddleware_managedSessionId = new WeakMap(), _LedgerDmkTransportMiddleware_cachedSigner = new WeakMap(), _LedgerDmkTransportMiddleware_instances = new WeakSet(), _LedgerDmkTransportMiddleware_assignSession = function _LedgerDmkTransportMiddleware_assignSession(sessionId) { __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_cachedSigner, null, "f"); __classPrivateFieldSet(this, _LedgerDmkTransportMiddleware_sessionId, sessionId, "f"); }; //# sourceMappingURL=ledger-dmk-transport-middleware.cjs.map