@metamask/eth-ledger-bridge-keyring 12.1.0 → 12.2.0

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 (94) hide show
  1. package/CHANGELOG.md +28 -8
  2. package/dist/dmk/__testhelpers__/mock-error.cjs +24 -0
  3. package/dist/dmk/__testhelpers__/mock-error.cjs.map +1 -0
  4. package/dist/dmk/__testhelpers__/mock-error.d.cts +13 -0
  5. package/dist/dmk/__testhelpers__/mock-error.d.cts.map +1 -0
  6. package/dist/dmk/__testhelpers__/mock-error.d.mts +13 -0
  7. package/dist/dmk/__testhelpers__/mock-error.d.mts.map +1 -0
  8. package/dist/dmk/__testhelpers__/mock-error.mjs +20 -0
  9. package/dist/dmk/__testhelpers__/mock-error.mjs.map +1 -0
  10. package/dist/dmk/dmk-error-translator.cjs +35 -0
  11. package/dist/dmk/dmk-error-translator.cjs.map +1 -0
  12. package/dist/dmk/dmk-error-translator.d.cts +12 -0
  13. package/dist/dmk/dmk-error-translator.d.cts.map +1 -0
  14. package/dist/dmk/dmk-error-translator.d.mts +12 -0
  15. package/dist/dmk/dmk-error-translator.d.mts.map +1 -0
  16. package/dist/dmk/dmk-error-translator.mjs +30 -0
  17. package/dist/dmk/dmk-error-translator.mjs.map +1 -0
  18. package/dist/dmk/eth-get-app-configuration-command.cjs +155 -0
  19. package/dist/dmk/eth-get-app-configuration-command.cjs.map +1 -0
  20. package/dist/dmk/eth-get-app-configuration-command.d.cts +71 -0
  21. package/dist/dmk/eth-get-app-configuration-command.d.cts.map +1 -0
  22. package/dist/dmk/eth-get-app-configuration-command.d.mts +71 -0
  23. package/dist/dmk/eth-get-app-configuration-command.d.mts.map +1 -0
  24. package/dist/dmk/eth-get-app-configuration-command.mjs +151 -0
  25. package/dist/dmk/eth-get-app-configuration-command.mjs.map +1 -0
  26. package/dist/dmk/internal-utils.cjs +81 -0
  27. package/dist/dmk/internal-utils.cjs.map +1 -0
  28. package/dist/dmk/internal-utils.d.cts +44 -0
  29. package/dist/dmk/internal-utils.d.cts.map +1 -0
  30. package/dist/dmk/internal-utils.d.mts +44 -0
  31. package/dist/dmk/internal-utils.d.mts.map +1 -0
  32. package/dist/dmk/internal-utils.mjs +74 -0
  33. package/dist/dmk/internal-utils.mjs.map +1 -0
  34. package/dist/dmk/ledger-dmk-bridge.cjs +379 -0
  35. package/dist/dmk/ledger-dmk-bridge.cjs.map +1 -0
  36. package/dist/dmk/ledger-dmk-bridge.d.cts +157 -0
  37. package/dist/dmk/ledger-dmk-bridge.d.cts.map +1 -0
  38. package/dist/dmk/ledger-dmk-bridge.d.mts +157 -0
  39. package/dist/dmk/ledger-dmk-bridge.d.mts.map +1 -0
  40. package/dist/dmk/ledger-dmk-bridge.mjs +375 -0
  41. package/dist/dmk/ledger-dmk-bridge.mjs.map +1 -0
  42. package/dist/dmk/ledger-dmk-transport-middleware.cjs +170 -0
  43. package/dist/dmk/ledger-dmk-transport-middleware.cjs.map +1 -0
  44. package/dist/dmk/ledger-dmk-transport-middleware.d.cts +99 -0
  45. package/dist/dmk/ledger-dmk-transport-middleware.d.cts.map +1 -0
  46. package/dist/dmk/ledger-dmk-transport-middleware.d.mts +99 -0
  47. package/dist/dmk/ledger-dmk-transport-middleware.d.mts.map +1 -0
  48. package/dist/dmk/ledger-dmk-transport-middleware.mjs +166 -0
  49. package/dist/dmk/ledger-dmk-transport-middleware.mjs.map +1 -0
  50. package/dist/index.cjs +3 -0
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.d.cts +3 -0
  53. package/dist/index.d.cts.map +1 -1
  54. package/dist/index.d.mts +3 -0
  55. package/dist/index.d.mts.map +1 -1
  56. package/dist/index.mjs +3 -0
  57. package/dist/index.mjs.map +1 -1
  58. package/dist/ledger-bridge.cjs.map +1 -1
  59. package/dist/ledger-bridge.d.cts +12 -0
  60. package/dist/ledger-bridge.d.cts.map +1 -1
  61. package/dist/ledger-bridge.d.mts +12 -0
  62. package/dist/ledger-bridge.d.mts.map +1 -1
  63. package/dist/ledger-bridge.mjs.map +1 -1
  64. package/dist/ledger-iframe-bridge.cjs +3 -0
  65. package/dist/ledger-iframe-bridge.cjs.map +1 -1
  66. package/dist/ledger-iframe-bridge.d.cts +2 -1
  67. package/dist/ledger-iframe-bridge.d.cts.map +1 -1
  68. package/dist/ledger-iframe-bridge.d.mts +2 -1
  69. package/dist/ledger-iframe-bridge.d.mts.map +1 -1
  70. package/dist/ledger-iframe-bridge.mjs +3 -0
  71. package/dist/ledger-iframe-bridge.mjs.map +1 -1
  72. package/dist/ledger-keyring.cjs +59 -3
  73. package/dist/ledger-keyring.cjs.map +1 -1
  74. package/dist/ledger-keyring.d.cts +1 -0
  75. package/dist/ledger-keyring.d.cts.map +1 -1
  76. package/dist/ledger-keyring.d.mts +1 -0
  77. package/dist/ledger-keyring.d.mts.map +1 -1
  78. package/dist/ledger-keyring.mjs +61 -5
  79. package/dist/ledger-keyring.mjs.map +1 -1
  80. package/dist/ledger-mobile-bridge.cjs +3 -0
  81. package/dist/ledger-mobile-bridge.cjs.map +1 -1
  82. package/dist/ledger-mobile-bridge.d.cts +2 -1
  83. package/dist/ledger-mobile-bridge.d.cts.map +1 -1
  84. package/dist/ledger-mobile-bridge.d.mts +2 -1
  85. package/dist/ledger-mobile-bridge.d.mts.map +1 -1
  86. package/dist/ledger-mobile-bridge.mjs +3 -0
  87. package/dist/ledger-mobile-bridge.mjs.map +1 -1
  88. package/dist/v2/ledger-keyring.cjs +2 -1
  89. package/dist/v2/ledger-keyring.cjs.map +1 -1
  90. package/dist/v2/ledger-keyring.d.cts.map +1 -1
  91. package/dist/v2/ledger-keyring.d.mts.map +1 -1
  92. package/dist/v2/ledger-keyring.mjs +3 -2
  93. package/dist/v2/ledger-keyring.mjs.map +1 -1
  94. package/package.json +9 -5
@@ -0,0 +1,157 @@
1
+ import { DeviceManagementKitBuilder } from "@ledgerhq/device-management-kit";
2
+ import type Transport from "@ledgerhq/hw-transport";
3
+ import type { Observable } from "rxjs";
4
+ import { AppConfigurationResponse, GetAppNameAndVersionResponse, GetPublicKeyParams, GetPublicKeyResponse, LedgerBridge, LedgerSignDelegationAuthorizationParams, LedgerSignDelegationAuthorizationResponse, LedgerSignMessageParams, LedgerSignMessageResponse, LedgerSignTransactionParams, LedgerSignTransactionResponse, LedgerSignTypedDataParams, LedgerSignTypedDataResponse } from "../ledger-bridge.mjs";
5
+ import { LedgerDmkTransportMiddleware } from "./ledger-dmk-transport-middleware.mjs";
6
+ export type LedgerDmkBridgeOptions = {
7
+ transportFactory: Parameters<DeviceManagementKitBuilder['addTransport']>[0];
8
+ };
9
+ /**
10
+ * LedgerDmkBridge is a bridge between the LedgerKeyring and the
11
+ * LedgerDmkTransportMiddleware.
12
+ * It initializes and manages the DeviceManagementKit internally.
13
+ * The transport factory is injected via constructor, making it platform-agnostic.
14
+ */
15
+ export declare class LedgerDmkBridge implements LedgerBridge<LedgerDmkBridgeOptions> {
16
+ #private;
17
+ get onSessionStateChange(): Observable<{
18
+ connected: boolean;
19
+ }>;
20
+ get isDeviceConnected(): boolean;
21
+ constructor(opts: LedgerDmkBridgeOptions);
22
+ /**
23
+ * Compatibility shim for the shared {@link LedgerBridge} interface.
24
+ *
25
+ * DMK session setup happens externally via `updateSessionId` or `connect`,
26
+ * so this method is a no-op.
27
+ *
28
+ * @returns A promise that resolves immediately.
29
+ */
30
+ init(): Promise<void>;
31
+ /**
32
+ * Destroys the bridge and cleans up resources.
33
+ *
34
+ * Unsubscribes session monitoring, disposes the transport middleware, and
35
+ * completes the session-state subject so all subscribers are released. A
36
+ * fresh subject is installed afterward so the bridge can be reconnected
37
+ * after destroy. State cleanup happens in a `finally` block so the bridge
38
+ * is marked disconnected even when middleware `dispose()` rejects.
39
+ *
40
+ * @returns A promise that resolves when cleanup is complete.
41
+ */
42
+ destroy(): Promise<void>;
43
+ /**
44
+ * Returns the bridge options captured at construction.
45
+ *
46
+ * Compatibility shim for the shared {@link LedgerBridge} interface. The DMK
47
+ * bridge does not use runtime-reconfigurable options; this method exists
48
+ * only to satisfy the interface contract.
49
+ *
50
+ * @returns A promise that resolves with the current bridge options.
51
+ */
52
+ getOptions(): Promise<LedgerDmkBridgeOptions>;
53
+ /**
54
+ * Replaces the stored bridge options.
55
+ *
56
+ * Compatibility shim for the shared {@link LedgerBridge} interface. Stored
57
+ * options are not re-applied to the underlying DMK instance or transport
58
+ * middleware; the values used at construction time remain in effect.
59
+ *
60
+ * @param opts - The options to set for the bridge.
61
+ * @returns A promise that resolves when options are set.
62
+ */
63
+ setOptions(opts: LedgerDmkBridgeOptions): Promise<void>;
64
+ /**
65
+ * Updates the session ID used for communication.
66
+ *
67
+ * @param sessionId - The session ID from DMK.
68
+ * @returns A promise that resolves with true if the session was updated successfully.
69
+ */
70
+ updateSessionId(sessionId: string): Promise<boolean>;
71
+ /**
72
+ * Starts device discovery for the configured DMK transport.
73
+ *
74
+ * @param args - Optional DMK discovery options.
75
+ * @returns An observable that emits discovered devices.
76
+ */
77
+ startDiscovering(...args: Parameters<LedgerDmkTransportMiddleware['startDiscovering']>): ReturnType<LedgerDmkTransportMiddleware['startDiscovering']>;
78
+ /**
79
+ * Connects to a discovered device using the configured DMK transport.
80
+ *
81
+ * @param args - The DMK connection arguments.
82
+ * @returns The created session ID.
83
+ */
84
+ connect(...args: Parameters<LedgerDmkTransportMiddleware['connect']>): ReturnType<LedgerDmkTransportMiddleware['connect']>;
85
+ /**
86
+ * Compatibility shim for the shared {@link LedgerBridge} interface.
87
+ *
88
+ * DMK transport selection happens through the injected transport factory
89
+ * at construction time, so this method is a no-op that always succeeds.
90
+ *
91
+ * @param _transportType - The requested transport type (ignored).
92
+ * @returns A promise that resolves with `true`.
93
+ */
94
+ updateTransportMethod(_transportType: string | Transport): Promise<boolean>;
95
+ openEthApp(): Promise<void>;
96
+ closeApps(): Promise<void>;
97
+ /**
98
+ * Compatibility shim for the shared {@link LedgerBridge} interface.
99
+ *
100
+ * The Ethereum signer kit automatically opens the Ethereum app before each
101
+ * signing operation via DMK's `CallTaskInAppDeviceAction`, so this method
102
+ * is a no-op that always succeeds.
103
+ *
104
+ * @returns A promise that resolves with `true`.
105
+ */
106
+ attemptMakeApp(): Promise<boolean>;
107
+ /**
108
+ * Retrieves public key/address for a given HD path.
109
+ *
110
+ * @param options0 - The parameters object.
111
+ * @param options0.hdPath - The HD path to derive the public key from.
112
+ * @returns A promise that resolves with the public key response.
113
+ */
114
+ getPublicKey({ hdPath, }: GetPublicKeyParams): Promise<GetPublicKeyResponse>;
115
+ /**
116
+ * Signs an Ethereum transaction.
117
+ *
118
+ * @param options0 - The parameters object.
119
+ * @param options0.tx - The transaction hex string to sign.
120
+ * @param options0.hdPath - The HD path for signing.
121
+ * @returns A promise that resolves with the transaction signature.
122
+ */
123
+ deviceSignTransaction({ tx, hdPath, }: LedgerSignTransactionParams): Promise<LedgerSignTransactionResponse>;
124
+ /**
125
+ * Signs a personal message.
126
+ *
127
+ * @param options0 - The parameters object.
128
+ * @param options0.hdPath - The HD path for signing.
129
+ * @param options0.message - The message to sign.
130
+ * @returns A promise that resolves with the message signature.
131
+ */
132
+ deviceSignMessage({ hdPath, message, }: LedgerSignMessageParams): Promise<LedgerSignMessageResponse>;
133
+ /**
134
+ * Signs EIP-712 typed data.
135
+ *
136
+ * @param options0 - The parameters object.
137
+ * @param options0.hdPath - The HD path for signing.
138
+ * @param options0.message - The typed data message to sign.
139
+ * @returns A promise that resolves with the typed data signature.
140
+ */
141
+ deviceSignTypedData({ hdPath, message, }: LedgerSignTypedDataParams): Promise<LedgerSignTypedDataResponse>;
142
+ deviceSignDelegationAuthorization({ hdPath, chainId, contractAddress, nonce, }: LedgerSignDelegationAuthorizationParams): Promise<LedgerSignDelegationAuthorizationResponse>;
143
+ /**
144
+ * Retrieves the current app name and version.
145
+ *
146
+ * @returns A promise that resolves with the app name and version.
147
+ */
148
+ getAppNameAndVersion(): Promise<GetAppNameAndVersionResponse>;
149
+ /**
150
+ * Retrieves the Ethereum app configuration using the eth-specific
151
+ * GetAppConfiguration APDU command (CLA 0xE0, INS 0x06).
152
+ *
153
+ * @returns A promise that resolves with the app configuration.
154
+ */
155
+ getAppConfiguration(): Promise<AppConfigurationResponse>;
156
+ }
157
+ //# sourceMappingURL=ledger-dmk-bridge.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-dmk-bridge.d.mts","sourceRoot":"","sources":["../../src/dmk/ledger-dmk-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,0BAA0B,EAK3B,wCAAwC;AAMzC,OAAO,KAAK,SAAS,+BAA+B;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa;AAmBvC,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,uCAAuC,EACvC,yCAAyC,EACzC,uBAAuB,EACvB,yBAAyB,EACzB,2BAA2B,EAC3B,6BAA6B,EAC7B,yBAAyB,EACzB,2BAA2B,EAC5B,6BAAyB;AAY1B,OAAO,EAAE,4BAA4B,EAAE,8CAA0C;AAEjF,MAAM,MAAM,sBAAsB,GAAG;IACnC,gBAAgB,EAAE,UAAU,CAAC,0BAA0B,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7E,CAAC;AAOF;;;;;GAKG;AACH,qBAAa,eAAgB,YAAW,YAAY,CAAC,sBAAsB,CAAC;;IAW1E,IAAI,oBAAoB,IAAI,UAAU,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAE7D;IAID,IAAI,iBAAiB,IAAI,OAAO,CAE/B;gBAEW,IAAI,EAAE,sBAAsB;IAQxC;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;;;;;;;OAUG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAc9B;;;;;;;;OAQG;IACG,UAAU,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAInD;;;;;;;;;OASG;IACG,UAAU,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7D;;;;;OAKG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAO1D;;;;;OAKG;IACH,gBAAgB,CACd,GAAG,IAAI,EAAE,UAAU,CAAC,4BAA4B,CAAC,kBAAkB,CAAC,CAAC,GACpE,UAAU,CAAC,4BAA4B,CAAC,kBAAkB,CAAC,CAAC;IAI/D;;;;;OAKG;IACG,OAAO,CACX,GAAG,IAAI,EAAE,UAAU,CAAC,4BAA4B,CAAC,SAAS,CAAC,CAAC,GAC3D,UAAU,CAAC,4BAA4B,CAAC,SAAS,CAAC,CAAC;IAQtD;;;;;;;;OAQG;IACG,qBAAqB,CACzB,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,OAAO,CAAC,OAAO,CAAC;IAIb,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAYhC;;;;;;;;OAQG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxC;;;;;;OAMG;IACG,YAAY,CAAC,EACjB,MAAM,GACP,EAAE,kBAAkB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAiBrD;;;;;;;OAOG;IACG,qBAAqB,CAAC,EAC1B,EAAE,EACF,MAAM,GACP,EAAE,2BAA2B,GAAG,OAAO,CAAC,6BAA6B,CAAC;IAavE;;;;;;;OAOG;IACG,iBAAiB,CAAC,EACtB,MAAM,EACN,OAAO,GACR,EAAE,uBAAuB,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAa/D;;;;;;;OAOG;IACG,mBAAmB,CAAC,EACxB,MAAM,EACN,OAAO,GACR,EAAE,yBAAyB,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAa7D,iCAAiC,CAAC,EACtC,MAAM,EACN,OAAO,EACP,eAAe,EACf,KAAK,GACN,EAAE,uCAAuC,GAAG,OAAO,CAAC,yCAAyC,CAAC;IAkB/F;;;;OAIG;IACG,oBAAoB,IAAI,OAAO,CAAC,4BAA4B,CAAC;IAkBnE;;;;;OAKG;IACG,mBAAmB,IAAI,OAAO,CAAC,wBAAwB,CAAC;CAgJ/D"}
@@ -0,0 +1,375 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ 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");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
+ if (kind === "m") throw new TypeError("Private method is not writable");
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
+ 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");
10
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
+ };
12
+ var _LedgerDmkBridge_instances, _LedgerDmkBridge_transportMiddleware, _LedgerDmkBridge_sdk, _LedgerDmkBridge_opts, _LedgerDmkBridge_isConnected, _LedgerDmkBridge_sessionState$, _LedgerDmkBridge_sessionSubscription, _LedgerDmkBridge_waitForDeviceAction, _LedgerDmkBridge_toError, _LedgerDmkBridge_startSessionMonitoring;
13
+ import { CloseAppCommand, DeviceActionStatus, DeviceManagementKitBuilder, DeviceStatus, GetAppAndVersionCommand, isSuccessCommandResult, OpenAppCommand } from "@ledgerhq/device-management-kit";
14
+ import { concat, defer, firstValueFrom, of, Subject, Subscription, timer } from "rxjs";
15
+ import { catchError, distinctUntilChanged, endWith, filter, map, switchMap } from "rxjs/operators";
16
+ import { isDeviceExchangeError, translateDmkError } from "./dmk-error-translator.mjs";
17
+ import { EthGetAppConfigurationCommand } from "./eth-get-app-configuration-command.mjs";
18
+ import { hexToBytes, stripHexPrefix, stripPathPrefix, toHexString } from "./internal-utils.mjs";
19
+ import { LedgerDmkTransportMiddleware } from "./ledger-dmk-transport-middleware.mjs";
20
+ /**
21
+ * LedgerDmkBridge is a bridge between the LedgerKeyring and the
22
+ * LedgerDmkTransportMiddleware.
23
+ * It initializes and manages the DeviceManagementKit internally.
24
+ * The transport factory is injected via constructor, making it platform-agnostic.
25
+ */
26
+ export class LedgerDmkBridge {
27
+ get onSessionStateChange() {
28
+ return __classPrivateFieldGet(this, _LedgerDmkBridge_sessionState$, "f").asObservable();
29
+ }
30
+ get isDeviceConnected() {
31
+ return __classPrivateFieldGet(this, _LedgerDmkBridge_isConnected, "f");
32
+ }
33
+ constructor(opts) {
34
+ _LedgerDmkBridge_instances.add(this);
35
+ _LedgerDmkBridge_transportMiddleware.set(this, void 0);
36
+ _LedgerDmkBridge_sdk.set(this, void 0);
37
+ _LedgerDmkBridge_opts.set(this, void 0);
38
+ _LedgerDmkBridge_isConnected.set(this, false);
39
+ _LedgerDmkBridge_sessionState$.set(this, new Subject());
40
+ _LedgerDmkBridge_sessionSubscription.set(this, null);
41
+ __classPrivateFieldSet(this, _LedgerDmkBridge_sdk, new DeviceManagementKitBuilder()
42
+ .addTransport(opts.transportFactory)
43
+ .build(), "f");
44
+ __classPrivateFieldSet(this, _LedgerDmkBridge_opts, opts, "f");
45
+ __classPrivateFieldSet(this, _LedgerDmkBridge_transportMiddleware, new LedgerDmkTransportMiddleware(__classPrivateFieldGet(this, _LedgerDmkBridge_sdk, "f")), "f");
46
+ }
47
+ /**
48
+ * Compatibility shim for the shared {@link LedgerBridge} interface.
49
+ *
50
+ * DMK session setup happens externally via `updateSessionId` or `connect`,
51
+ * so this method is a no-op.
52
+ *
53
+ * @returns A promise that resolves immediately.
54
+ */
55
+ async init() {
56
+ return undefined;
57
+ }
58
+ /**
59
+ * Destroys the bridge and cleans up resources.
60
+ *
61
+ * Unsubscribes session monitoring, disposes the transport middleware, and
62
+ * completes the session-state subject so all subscribers are released. A
63
+ * fresh subject is installed afterward so the bridge can be reconnected
64
+ * after destroy. State cleanup happens in a `finally` block so the bridge
65
+ * is marked disconnected even when middleware `dispose()` rejects.
66
+ *
67
+ * @returns A promise that resolves when cleanup is complete.
68
+ */
69
+ async destroy() {
70
+ __classPrivateFieldGet(this, _LedgerDmkBridge_sessionSubscription, "f")?.unsubscribe();
71
+ __classPrivateFieldSet(this, _LedgerDmkBridge_sessionSubscription, null, "f");
72
+ try {
73
+ await __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f").dispose();
74
+ }
75
+ finally {
76
+ __classPrivateFieldSet(this, _LedgerDmkBridge_isConnected, false, "f");
77
+ __classPrivateFieldGet(this, _LedgerDmkBridge_sessionState$, "f").next({ connected: false });
78
+ __classPrivateFieldGet(this, _LedgerDmkBridge_sessionState$, "f").complete();
79
+ __classPrivateFieldSet(this, _LedgerDmkBridge_sessionState$, new Subject(), "f");
80
+ }
81
+ }
82
+ /**
83
+ * Returns the bridge options captured at construction.
84
+ *
85
+ * Compatibility shim for the shared {@link LedgerBridge} interface. The DMK
86
+ * bridge does not use runtime-reconfigurable options; this method exists
87
+ * only to satisfy the interface contract.
88
+ *
89
+ * @returns A promise that resolves with the current bridge options.
90
+ */
91
+ async getOptions() {
92
+ return __classPrivateFieldGet(this, _LedgerDmkBridge_opts, "f");
93
+ }
94
+ /**
95
+ * Replaces the stored bridge options.
96
+ *
97
+ * Compatibility shim for the shared {@link LedgerBridge} interface. Stored
98
+ * options are not re-applied to the underlying DMK instance or transport
99
+ * middleware; the values used at construction time remain in effect.
100
+ *
101
+ * @param opts - The options to set for the bridge.
102
+ * @returns A promise that resolves when options are set.
103
+ */
104
+ async setOptions(opts) {
105
+ __classPrivateFieldSet(this, _LedgerDmkBridge_opts, opts, "f");
106
+ }
107
+ /**
108
+ * Updates the session ID used for communication.
109
+ *
110
+ * @param sessionId - The session ID from DMK.
111
+ * @returns A promise that resolves with true if the session was updated successfully.
112
+ */
113
+ async updateSessionId(sessionId) {
114
+ await __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f").setSessionId(sessionId);
115
+ __classPrivateFieldSet(this, _LedgerDmkBridge_isConnected, true, "f");
116
+ __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_startSessionMonitoring).call(this, sessionId);
117
+ return true;
118
+ }
119
+ /**
120
+ * Starts device discovery for the configured DMK transport.
121
+ *
122
+ * @param args - Optional DMK discovery options.
123
+ * @returns An observable that emits discovered devices.
124
+ */
125
+ startDiscovering(...args) {
126
+ return __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f").startDiscovering(...args);
127
+ }
128
+ /**
129
+ * Connects to a discovered device using the configured DMK transport.
130
+ *
131
+ * @param args - The DMK connection arguments.
132
+ * @returns The created session ID.
133
+ */
134
+ async connect(...args) {
135
+ const sessionId = await __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f").connect(...args);
136
+ __classPrivateFieldSet(this, _LedgerDmkBridge_isConnected, true, "f");
137
+ __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_startSessionMonitoring).call(this, sessionId);
138
+ return sessionId;
139
+ }
140
+ /**
141
+ * Compatibility shim for the shared {@link LedgerBridge} interface.
142
+ *
143
+ * DMK transport selection happens through the injected transport factory
144
+ * at construction time, so this method is a no-op that always succeeds.
145
+ *
146
+ * @param _transportType - The requested transport type (ignored).
147
+ * @returns A promise that resolves with `true`.
148
+ */
149
+ async updateTransportMethod(_transportType) {
150
+ return true;
151
+ }
152
+ async openEthApp() {
153
+ const sessionId = __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f").getSessionId();
154
+ const result = await __classPrivateFieldGet(this, _LedgerDmkBridge_sdk, "f").sendCommand({
155
+ sessionId,
156
+ command: new OpenAppCommand({ appName: 'Ethereum' }),
157
+ });
158
+ if (!isSuccessCommandResult(result)) {
159
+ throw __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_toError).call(this, result.error);
160
+ }
161
+ }
162
+ async closeApps() {
163
+ const sessionId = __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f").getSessionId();
164
+ const result = await __classPrivateFieldGet(this, _LedgerDmkBridge_sdk, "f").sendCommand({
165
+ sessionId,
166
+ command: new CloseAppCommand(),
167
+ });
168
+ if (!isSuccessCommandResult(result)) {
169
+ throw __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_toError).call(this, result.error);
170
+ }
171
+ }
172
+ /**
173
+ * Compatibility shim for the shared {@link LedgerBridge} interface.
174
+ *
175
+ * The Ethereum signer kit automatically opens the Ethereum app before each
176
+ * signing operation via DMK's `CallTaskInAppDeviceAction`, so this method
177
+ * is a no-op that always succeeds.
178
+ *
179
+ * @returns A promise that resolves with `true`.
180
+ */
181
+ async attemptMakeApp() {
182
+ return true;
183
+ }
184
+ /**
185
+ * Retrieves public key/address for a given HD path.
186
+ *
187
+ * @param options0 - The parameters object.
188
+ * @param options0.hdPath - The HD path to derive the public key from.
189
+ * @returns A promise that resolves with the public key response.
190
+ */
191
+ async getPublicKey({ hdPath, }) {
192
+ const { observable } = __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f")
193
+ .getEthSigner()
194
+ .getAddress(stripPathPrefix(hdPath), {
195
+ checkOnDevice: false,
196
+ returnChainCode: true,
197
+ });
198
+ const response = await __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_waitForDeviceAction).call(this, observable);
199
+ return {
200
+ publicKey: response.publicKey,
201
+ address: response.address,
202
+ chainCode: response.chainCode,
203
+ };
204
+ }
205
+ /**
206
+ * Signs an Ethereum transaction.
207
+ *
208
+ * @param options0 - The parameters object.
209
+ * @param options0.tx - The transaction hex string to sign.
210
+ * @param options0.hdPath - The HD path for signing.
211
+ * @returns A promise that resolves with the transaction signature.
212
+ */
213
+ async deviceSignTransaction({ tx, hdPath, }) {
214
+ const { observable } = __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f")
215
+ .getEthSigner()
216
+ .signTransaction(stripPathPrefix(hdPath), hexToBytes(tx));
217
+ const signature = await __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_waitForDeviceAction).call(this, observable);
218
+ return {
219
+ v: toHexString(signature.v),
220
+ r: stripHexPrefix(signature.r),
221
+ s: stripHexPrefix(signature.s),
222
+ };
223
+ }
224
+ /**
225
+ * Signs a personal message.
226
+ *
227
+ * @param options0 - The parameters object.
228
+ * @param options0.hdPath - The HD path for signing.
229
+ * @param options0.message - The message to sign.
230
+ * @returns A promise that resolves with the message signature.
231
+ */
232
+ async deviceSignMessage({ hdPath, message, }) {
233
+ const { observable } = __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f")
234
+ .getEthSigner()
235
+ .signMessage(stripPathPrefix(hdPath), hexToBytes(message));
236
+ const signature = await __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_waitForDeviceAction).call(this, observable);
237
+ return {
238
+ v: signature.v,
239
+ r: stripHexPrefix(signature.r),
240
+ s: stripHexPrefix(signature.s),
241
+ };
242
+ }
243
+ /**
244
+ * Signs EIP-712 typed data.
245
+ *
246
+ * @param options0 - The parameters object.
247
+ * @param options0.hdPath - The HD path for signing.
248
+ * @param options0.message - The typed data message to sign.
249
+ * @returns A promise that resolves with the typed data signature.
250
+ */
251
+ async deviceSignTypedData({ hdPath, message, }) {
252
+ const { observable } = __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f")
253
+ .getEthSigner()
254
+ .signTypedData(stripPathPrefix(hdPath), message);
255
+ const signature = await __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_waitForDeviceAction).call(this, observable);
256
+ return {
257
+ v: signature.v,
258
+ r: stripHexPrefix(signature.r),
259
+ s: stripHexPrefix(signature.s),
260
+ };
261
+ }
262
+ async deviceSignDelegationAuthorization({ hdPath, chainId, contractAddress, nonce, }) {
263
+ const { observable } = __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f")
264
+ .getEthSigner()
265
+ .signDelegationAuthorization(stripPathPrefix(hdPath), chainId, contractAddress, nonce);
266
+ const signature = await __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_waitForDeviceAction).call(this, observable);
267
+ return {
268
+ v: toHexString(signature.v),
269
+ r: stripHexPrefix(signature.r),
270
+ s: stripHexPrefix(signature.s),
271
+ };
272
+ }
273
+ /**
274
+ * Retrieves the current app name and version.
275
+ *
276
+ * @returns A promise that resolves with the app name and version.
277
+ */
278
+ async getAppNameAndVersion() {
279
+ const sessionId = __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f").getSessionId();
280
+ const command = new GetAppAndVersionCommand();
281
+ const result = await __classPrivateFieldGet(this, _LedgerDmkBridge_sdk, "f").sendCommand({
282
+ sessionId,
283
+ command,
284
+ });
285
+ if (!isSuccessCommandResult(result)) {
286
+ throw __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_toError).call(this, result.error);
287
+ }
288
+ return {
289
+ appName: result.data.name,
290
+ version: result.data.version,
291
+ };
292
+ }
293
+ /**
294
+ * Retrieves the Ethereum app configuration using the eth-specific
295
+ * GetAppConfiguration APDU command (CLA 0xE0, INS 0x06).
296
+ *
297
+ * @returns A promise that resolves with the app configuration.
298
+ */
299
+ async getAppConfiguration() {
300
+ const sessionId = __classPrivateFieldGet(this, _LedgerDmkBridge_transportMiddleware, "f").getSessionId();
301
+ const command = new EthGetAppConfigurationCommand();
302
+ const result = await __classPrivateFieldGet(this, _LedgerDmkBridge_sdk, "f").sendCommand({
303
+ sessionId,
304
+ command,
305
+ });
306
+ if (!isSuccessCommandResult(result)) {
307
+ throw __classPrivateFieldGet(this, _LedgerDmkBridge_instances, "m", _LedgerDmkBridge_toError).call(this, result.error);
308
+ }
309
+ const { data } = result;
310
+ // The `ethapp.adoc` spec for `getAppConfiguration`
311
+ // (https://github.com/LedgerHQ/app-ethereum/blob/develop/doc/ethapp.adoc#get-app-configuration)
312
+ // defines four flag bits. This bridge only consumes three (blind signing,
313
+ // web3 checks enabled, web3 checks opt-in). The fourth — `0x02` *ERC 20
314
+ // Token information needs to be provided externally* — is intentionally
315
+ // mapped to `erc20ProvisioningNecessary: 0` because the rest of the
316
+ // keyring does not implement the `PROVIDE ERC 20 TOKEN INFORMATION`
317
+ // provisioning flow.
318
+ //
319
+ // TODO: The shared `AppConfigurationResponse` interface also requires
320
+ // `starkEnabled` and `starkv2Supported`, which are not exposed by the
321
+ // `getAppConfiguration` APDU at all. They are stubbed to 0 to satisfy
322
+ // the interface contract.
323
+ return {
324
+ arbitraryDataEnabled: data.blindSigningEnabled ? 1 : 0,
325
+ erc20ProvisioningNecessary: 0,
326
+ starkEnabled: 0,
327
+ starkv2Supported: 0,
328
+ version: data.version,
329
+ };
330
+ }
331
+ }
332
+ _LedgerDmkBridge_transportMiddleware = new WeakMap(), _LedgerDmkBridge_sdk = new WeakMap(), _LedgerDmkBridge_opts = new WeakMap(), _LedgerDmkBridge_isConnected = new WeakMap(), _LedgerDmkBridge_sessionState$ = new WeakMap(), _LedgerDmkBridge_sessionSubscription = new WeakMap(), _LedgerDmkBridge_instances = new WeakSet(), _LedgerDmkBridge_waitForDeviceAction = async function _LedgerDmkBridge_waitForDeviceAction(observable) {
333
+ const state = await firstValueFrom(observable.pipe(catchError((error) => {
334
+ throw translateDmkError(error);
335
+ }), filter(({ status }) => status === DeviceActionStatus.Completed ||
336
+ status === DeviceActionStatus.Error)));
337
+ if (state.status === DeviceActionStatus.Completed) {
338
+ return state.output;
339
+ }
340
+ if (state.status === DeviceActionStatus.Error) {
341
+ throw translateDmkError(state.error);
342
+ }
343
+ // Unreachable: the filter above only lets Completed or Error states
344
+ // through. Defensive guard retained for type safety.
345
+ throw new Error('Ledger device action ended without completion.');
346
+ }, _LedgerDmkBridge_toError = function _LedgerDmkBridge_toError(error) {
347
+ if (isDeviceExchangeError(error)) {
348
+ return translateDmkError(error);
349
+ }
350
+ if (error instanceof Error) {
351
+ return error;
352
+ }
353
+ return new Error('Ledger command failed.');
354
+ }, _LedgerDmkBridge_startSessionMonitoring = function _LedgerDmkBridge_startSessionMonitoring(sessionId) {
355
+ __classPrivateFieldGet(this, _LedgerDmkBridge_sessionSubscription, "f")?.unsubscribe();
356
+ const MAX_BACKOFF_MS = 30000;
357
+ const BASE_DELAY_MS = 1000;
358
+ const MAX_RETRIES = 10;
359
+ const createMonitoringStream = (retryCount = 0) => __classPrivateFieldGet(this, _LedgerDmkBridge_sdk, "f").getDeviceSessionState({ sessionId }).pipe(map((state) => ({
360
+ connected: state.deviceStatus !== DeviceStatus.NOT_CONNECTED,
361
+ })), catchError(() => {
362
+ if (retryCount >= MAX_RETRIES) {
363
+ return of({ connected: false });
364
+ }
365
+ const delay = Math.min(BASE_DELAY_MS * 2 ** retryCount, MAX_BACKOFF_MS);
366
+ return concat(of({ connected: false }), defer(() => timer(delay).pipe(switchMap(() => createMonitoringStream(retryCount + 1)))));
367
+ }), endWith({ connected: false }));
368
+ __classPrivateFieldSet(this, _LedgerDmkBridge_sessionSubscription, createMonitoringStream()
369
+ .pipe(distinctUntilChanged((a, b) => a.connected === b.connected))
370
+ .subscribe((state) => {
371
+ __classPrivateFieldSet(this, _LedgerDmkBridge_isConnected, state.connected, "f");
372
+ __classPrivateFieldGet(this, _LedgerDmkBridge_sessionState$, "f").next(state);
373
+ }), "f");
374
+ };
375
+ //# sourceMappingURL=ledger-dmk-bridge.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-dmk-bridge.mjs","sourceRoot":"","sources":["../../src/dmk/ledger-dmk-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CloseAppCommand,\n DeviceActionStatus,\n DeviceManagementKitBuilder,\n DeviceStatus,\n GetAppAndVersionCommand,\n isSuccessCommandResult,\n OpenAppCommand,\n} from '@ledgerhq/device-management-kit';\nimport type {\n DeviceActionState,\n DeviceManagementKit,\n} from '@ledgerhq/device-management-kit';\nimport type { Signature } from '@ledgerhq/device-signer-kit-ethereum';\nimport type Transport from '@ledgerhq/hw-transport';\nimport type { Observable } from 'rxjs';\nimport {\n concat,\n defer,\n firstValueFrom,\n of,\n Subject,\n Subscription,\n timer,\n} from 'rxjs';\nimport {\n catchError,\n distinctUntilChanged,\n endWith,\n filter,\n map,\n switchMap,\n} from 'rxjs/operators';\n\nimport {\n AppConfigurationResponse,\n GetAppNameAndVersionResponse,\n GetPublicKeyParams,\n GetPublicKeyResponse,\n LedgerBridge,\n LedgerSignDelegationAuthorizationParams,\n LedgerSignDelegationAuthorizationResponse,\n LedgerSignMessageParams,\n LedgerSignMessageResponse,\n LedgerSignTransactionParams,\n LedgerSignTransactionResponse,\n LedgerSignTypedDataParams,\n LedgerSignTypedDataResponse,\n} from '../ledger-bridge';\nimport {\n isDeviceExchangeError,\n translateDmkError,\n} from './dmk-error-translator';\nimport { EthGetAppConfigurationCommand } from './eth-get-app-configuration-command';\nimport {\n hexToBytes,\n stripHexPrefix,\n stripPathPrefix,\n toHexString,\n} from './internal-utils';\nimport { LedgerDmkTransportMiddleware } from './ledger-dmk-transport-middleware';\n\nexport type LedgerDmkBridgeOptions = {\n transportFactory: Parameters<DeviceManagementKitBuilder['addTransport']>[0];\n};\n\ntype PublicKeyOutput = Pick<\n GetPublicKeyResponse,\n 'address' | 'chainCode' | 'publicKey'\n>;\n\n/**\n * LedgerDmkBridge is a bridge between the LedgerKeyring and the\n * LedgerDmkTransportMiddleware.\n * It initializes and manages the DeviceManagementKit internally.\n * The transport factory is injected via constructor, making it platform-agnostic.\n */\nexport class LedgerDmkBridge implements LedgerBridge<LedgerDmkBridgeOptions> {\n readonly #transportMiddleware: LedgerDmkTransportMiddleware;\n\n readonly #sdk: DeviceManagementKit;\n\n #opts: LedgerDmkBridgeOptions;\n\n #isConnected = false;\n\n #sessionState$ = new Subject<{ connected: boolean }>();\n\n get onSessionStateChange(): Observable<{ connected: boolean }> {\n return this.#sessionState$.asObservable();\n }\n\n #sessionSubscription: Subscription | null = null;\n\n get isDeviceConnected(): boolean {\n return this.#isConnected;\n }\n\n constructor(opts: LedgerDmkBridgeOptions) {\n this.#sdk = new DeviceManagementKitBuilder()\n .addTransport(opts.transportFactory)\n .build();\n this.#opts = opts;\n this.#transportMiddleware = new LedgerDmkTransportMiddleware(this.#sdk);\n }\n\n /**\n * Compatibility shim for the shared {@link LedgerBridge} interface.\n *\n * DMK session setup happens externally via `updateSessionId` or `connect`,\n * so this method is a no-op.\n *\n * @returns A promise that resolves immediately.\n */\n async init(): Promise<void> {\n return undefined;\n }\n\n /**\n * Destroys the bridge and cleans up resources.\n *\n * Unsubscribes session monitoring, disposes the transport middleware, and\n * completes the session-state subject so all subscribers are released. A\n * fresh subject is installed afterward so the bridge can be reconnected\n * after destroy. State cleanup happens in a `finally` block so the bridge\n * is marked disconnected even when middleware `dispose()` rejects.\n *\n * @returns A promise that resolves when cleanup is complete.\n */\n async destroy(): Promise<void> {\n this.#sessionSubscription?.unsubscribe();\n this.#sessionSubscription = null;\n\n try {\n await this.#transportMiddleware.dispose();\n } finally {\n this.#isConnected = false;\n this.#sessionState$.next({ connected: false });\n this.#sessionState$.complete();\n this.#sessionState$ = new Subject<{ connected: boolean }>();\n }\n }\n\n /**\n * Returns the bridge options captured at construction.\n *\n * Compatibility shim for the shared {@link LedgerBridge} interface. The DMK\n * bridge does not use runtime-reconfigurable options; this method exists\n * only to satisfy the interface contract.\n *\n * @returns A promise that resolves with the current bridge options.\n */\n async getOptions(): Promise<LedgerDmkBridgeOptions> {\n return this.#opts;\n }\n\n /**\n * Replaces the stored bridge options.\n *\n * Compatibility shim for the shared {@link LedgerBridge} interface. Stored\n * options are not re-applied to the underlying DMK instance or transport\n * middleware; the values used at construction time remain in effect.\n *\n * @param opts - The options to set for the bridge.\n * @returns A promise that resolves when options are set.\n */\n async setOptions(opts: LedgerDmkBridgeOptions): Promise<void> {\n this.#opts = opts;\n }\n\n /**\n * Updates the session ID used for communication.\n *\n * @param sessionId - The session ID from DMK.\n * @returns A promise that resolves with true if the session was updated successfully.\n */\n async updateSessionId(sessionId: string): Promise<boolean> {\n await this.#transportMiddleware.setSessionId(sessionId);\n this.#isConnected = true;\n this.#startSessionMonitoring(sessionId);\n return true;\n }\n\n /**\n * Starts device discovery for the configured DMK transport.\n *\n * @param args - Optional DMK discovery options.\n * @returns An observable that emits discovered devices.\n */\n startDiscovering(\n ...args: Parameters<LedgerDmkTransportMiddleware['startDiscovering']>\n ): ReturnType<LedgerDmkTransportMiddleware['startDiscovering']> {\n return this.#transportMiddleware.startDiscovering(...args);\n }\n\n /**\n * Connects to a discovered device using the configured DMK transport.\n *\n * @param args - The DMK connection arguments.\n * @returns The created session ID.\n */\n async connect(\n ...args: Parameters<LedgerDmkTransportMiddleware['connect']>\n ): ReturnType<LedgerDmkTransportMiddleware['connect']> {\n const sessionId = await this.#transportMiddleware.connect(...args);\n this.#isConnected = true;\n this.#startSessionMonitoring(sessionId);\n\n return sessionId;\n }\n\n /**\n * Compatibility shim for the shared {@link LedgerBridge} interface.\n *\n * DMK transport selection happens through the injected transport factory\n * at construction time, so this method is a no-op that always succeeds.\n *\n * @param _transportType - The requested transport type (ignored).\n * @returns A promise that resolves with `true`.\n */\n async updateTransportMethod(\n _transportType: string | Transport,\n ): Promise<boolean> {\n return true;\n }\n\n async openEthApp(): Promise<void> {\n const sessionId = this.#transportMiddleware.getSessionId();\n const result = await this.#sdk.sendCommand({\n sessionId,\n command: new OpenAppCommand({ appName: 'Ethereum' }),\n });\n\n if (!isSuccessCommandResult(result)) {\n throw this.#toError(result.error);\n }\n }\n\n async closeApps(): Promise<void> {\n const sessionId = this.#transportMiddleware.getSessionId();\n const result = await this.#sdk.sendCommand({\n sessionId,\n command: new CloseAppCommand(),\n });\n\n if (!isSuccessCommandResult(result)) {\n throw this.#toError(result.error);\n }\n }\n\n /**\n * Compatibility shim for the shared {@link LedgerBridge} interface.\n *\n * The Ethereum signer kit automatically opens the Ethereum app before each\n * signing operation via DMK's `CallTaskInAppDeviceAction`, so this method\n * is a no-op that always succeeds.\n *\n * @returns A promise that resolves with `true`.\n */\n async attemptMakeApp(): Promise<boolean> {\n return true;\n }\n\n /**\n * Retrieves public key/address for a given HD path.\n *\n * @param options0 - The parameters object.\n * @param options0.hdPath - The HD path to derive the public key from.\n * @returns A promise that resolves with the public key response.\n */\n async getPublicKey({\n hdPath,\n }: GetPublicKeyParams): Promise<GetPublicKeyResponse> {\n const { observable } = this.#transportMiddleware\n .getEthSigner()\n .getAddress(stripPathPrefix(hdPath), {\n checkOnDevice: false,\n returnChainCode: true,\n });\n const response =\n await this.#waitForDeviceAction<PublicKeyOutput>(observable);\n\n return {\n publicKey: response.publicKey,\n address: response.address,\n chainCode: response.chainCode,\n };\n }\n\n /**\n * Signs an Ethereum transaction.\n *\n * @param options0 - The parameters object.\n * @param options0.tx - The transaction hex string to sign.\n * @param options0.hdPath - The HD path for signing.\n * @returns A promise that resolves with the transaction signature.\n */\n async deviceSignTransaction({\n tx,\n hdPath,\n }: LedgerSignTransactionParams): Promise<LedgerSignTransactionResponse> {\n const { observable } = this.#transportMiddleware\n .getEthSigner()\n .signTransaction(stripPathPrefix(hdPath), hexToBytes(tx));\n const signature = await this.#waitForDeviceAction<Signature>(observable);\n\n return {\n v: toHexString(signature.v),\n r: stripHexPrefix(signature.r),\n s: stripHexPrefix(signature.s),\n };\n }\n\n /**\n * Signs a personal message.\n *\n * @param options0 - The parameters object.\n * @param options0.hdPath - The HD path for signing.\n * @param options0.message - The message to sign.\n * @returns A promise that resolves with the message signature.\n */\n async deviceSignMessage({\n hdPath,\n message,\n }: LedgerSignMessageParams): Promise<LedgerSignMessageResponse> {\n const { observable } = this.#transportMiddleware\n .getEthSigner()\n .signMessage(stripPathPrefix(hdPath), hexToBytes(message));\n const signature = await this.#waitForDeviceAction<Signature>(observable);\n\n return {\n v: signature.v,\n r: stripHexPrefix(signature.r),\n s: stripHexPrefix(signature.s),\n };\n }\n\n /**\n * Signs EIP-712 typed data.\n *\n * @param options0 - The parameters object.\n * @param options0.hdPath - The HD path for signing.\n * @param options0.message - The typed data message to sign.\n * @returns A promise that resolves with the typed data signature.\n */\n async deviceSignTypedData({\n hdPath,\n message,\n }: LedgerSignTypedDataParams): Promise<LedgerSignTypedDataResponse> {\n const { observable } = this.#transportMiddleware\n .getEthSigner()\n .signTypedData(stripPathPrefix(hdPath), message);\n const signature = await this.#waitForDeviceAction<Signature>(observable);\n\n return {\n v: signature.v,\n r: stripHexPrefix(signature.r),\n s: stripHexPrefix(signature.s),\n };\n }\n\n async deviceSignDelegationAuthorization({\n hdPath,\n chainId,\n contractAddress,\n nonce,\n }: LedgerSignDelegationAuthorizationParams): Promise<LedgerSignDelegationAuthorizationResponse> {\n const { observable } = this.#transportMiddleware\n .getEthSigner()\n .signDelegationAuthorization(\n stripPathPrefix(hdPath),\n chainId,\n contractAddress,\n nonce,\n );\n const signature = await this.#waitForDeviceAction<Signature>(observable);\n\n return {\n v: toHexString(signature.v),\n r: stripHexPrefix(signature.r),\n s: stripHexPrefix(signature.s),\n };\n }\n\n /**\n * Retrieves the current app name and version.\n *\n * @returns A promise that resolves with the app name and version.\n */\n async getAppNameAndVersion(): Promise<GetAppNameAndVersionResponse> {\n const sessionId = this.#transportMiddleware.getSessionId();\n const command = new GetAppAndVersionCommand();\n const result = await this.#sdk.sendCommand({\n sessionId,\n command,\n });\n\n if (!isSuccessCommandResult(result)) {\n throw this.#toError(result.error);\n }\n\n return {\n appName: result.data.name,\n version: result.data.version,\n };\n }\n\n /**\n * Retrieves the Ethereum app configuration using the eth-specific\n * GetAppConfiguration APDU command (CLA 0xE0, INS 0x06).\n *\n * @returns A promise that resolves with the app configuration.\n */\n async getAppConfiguration(): Promise<AppConfigurationResponse> {\n const sessionId = this.#transportMiddleware.getSessionId();\n const command = new EthGetAppConfigurationCommand();\n const result = await this.#sdk.sendCommand({\n sessionId,\n command,\n });\n\n if (!isSuccessCommandResult(result)) {\n throw this.#toError(result.error);\n }\n\n const { data } = result;\n\n // The `ethapp.adoc` spec for `getAppConfiguration`\n // (https://github.com/LedgerHQ/app-ethereum/blob/develop/doc/ethapp.adoc#get-app-configuration)\n // defines four flag bits. This bridge only consumes three (blind signing,\n // web3 checks enabled, web3 checks opt-in). The fourth — `0x02` *ERC 20\n // Token information needs to be provided externally* — is intentionally\n // mapped to `erc20ProvisioningNecessary: 0` because the rest of the\n // keyring does not implement the `PROVIDE ERC 20 TOKEN INFORMATION`\n // provisioning flow.\n //\n // TODO: The shared `AppConfigurationResponse` interface also requires\n // `starkEnabled` and `starkv2Supported`, which are not exposed by the\n // `getAppConfiguration` APDU at all. They are stubbed to 0 to satisfy\n // the interface contract.\n return {\n arbitraryDataEnabled: data.blindSigningEnabled ? 1 : 0,\n erc20ProvisioningNecessary: 0,\n starkEnabled: 0,\n starkv2Supported: 0,\n version: data.version,\n };\n }\n\n async #waitForDeviceAction<TOutput>(\n observable: Observable<DeviceActionState<TOutput, unknown, unknown>>,\n ): Promise<TOutput> {\n const state = await firstValueFrom(\n observable.pipe(\n catchError((error: unknown) => {\n throw translateDmkError(error);\n }),\n filter(\n ({ status }) =>\n status === DeviceActionStatus.Completed ||\n status === DeviceActionStatus.Error,\n ),\n ),\n );\n\n if (state.status === DeviceActionStatus.Completed) {\n return state.output;\n }\n\n if (state.status === DeviceActionStatus.Error) {\n throw translateDmkError(state.error);\n }\n\n // Unreachable: the filter above only lets Completed or Error states\n // through. Defensive guard retained for type safety.\n throw new Error('Ledger device action ended without completion.');\n }\n\n #toError(error: unknown): Error {\n if (isDeviceExchangeError(error)) {\n return translateDmkError(error);\n }\n\n if (error instanceof Error) {\n return error;\n }\n\n return new Error('Ledger command failed.');\n }\n\n /**\n * Monitors the DMK session connection state with exponential backoff retries.\n *\n * This is necessary because the DMK does not emit disconnection events on its\n * own — the bridge must actively poll the session state to detect when a\n * Ledger device is unplugged, the Bluetooth/WUSB link drops, or the session\n * is invalidated. Without this monitoring the keyring would retain a stale\n * `isDeviceConnected = true` and subsequent signing requests would hang or\n * fail with cryptic transport errors.\n *\n * Subscribes to the device session state and emits connection changes through\n * {@link #sessionState$}. On error, retries up to 10 times with exponential\n * backoff (capped at 30 s) and emits a `connected: false` state in between\n * attempts. Once the retry limit is exhausted the stream ends with a final\n * `connected: false`. Duplicate consecutive states are suppressed via\n * `distinctUntilChanged`.\n *\n * Any previous monitoring subscription is cancelled before a new one is created.\n *\n * @param sessionId - The DMK session ID to monitor.\n */\n #startSessionMonitoring(sessionId: string): void {\n this.#sessionSubscription?.unsubscribe();\n\n const MAX_BACKOFF_MS = 30_000;\n const BASE_DELAY_MS = 1_000;\n const MAX_RETRIES = 10;\n\n const createMonitoringStream = (\n retryCount = 0,\n ): Observable<{ connected: boolean }> =>\n this.#sdk.getDeviceSessionState({ sessionId }).pipe(\n map((state) => ({\n connected: state.deviceStatus !== DeviceStatus.NOT_CONNECTED,\n })),\n catchError(() => {\n if (retryCount >= MAX_RETRIES) {\n return of({ connected: false });\n }\n const delay = Math.min(\n BASE_DELAY_MS * 2 ** retryCount,\n MAX_BACKOFF_MS,\n );\n return concat(\n of({ connected: false }),\n defer(() =>\n timer(delay).pipe(\n switchMap(() => createMonitoringStream(retryCount + 1)),\n ),\n ),\n );\n }),\n endWith({ connected: false } as { connected: boolean }),\n );\n\n this.#sessionSubscription = createMonitoringStream()\n .pipe(\n distinctUntilChanged(\n (a: { connected: boolean }, b: { connected: boolean }) =>\n a.connected === b.connected,\n ),\n )\n .subscribe((state) => {\n this.#isConnected = state.connected;\n this.#sessionState$.next(state);\n });\n }\n}\n"]}