@onekeyfe/hwk-ledger-adapter 1.1.26-alpha.100

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/dist/index.js ADDED
@@ -0,0 +1,2338 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AppManager: () => AppManager,
34
+ DmkTransport: () => DmkTransport,
35
+ LedgerAdapter: () => LedgerAdapter,
36
+ LedgerConnectorBase: () => LedgerConnectorBase,
37
+ LedgerDeviceManager: () => LedgerDeviceManager,
38
+ SignerBtc: () => SignerBtc,
39
+ SignerEth: () => SignerEth,
40
+ SignerManager: () => SignerManager,
41
+ SignerSol: () => SignerSol,
42
+ deviceActionToPromise: () => deviceActionToPromise,
43
+ isDeviceLockedError: () => isDeviceLockedError,
44
+ mapLedgerError: () => mapLedgerError
45
+ });
46
+ module.exports = __toCommonJS(index_exports);
47
+
48
+ // src/adapter/LedgerAdapter.ts
49
+ var import_hwk_adapter_core2 = require("@onekeyfe/hwk-adapter-core");
50
+
51
+ // src/errors.ts
52
+ var import_hwk_adapter_core = require("@onekeyfe/hwk-adapter-core");
53
+ var LOCKED_ERROR_CODES = /* @__PURE__ */ new Set(["5515", "21781", "6982", "27010", "5303", "21251"]);
54
+ var USER_REJECTED_CODES = /* @__PURE__ */ new Set(["6985", "27013"]);
55
+ var WRONG_APP_CODES = /* @__PURE__ */ new Set(["6e00", "28160", "6d00", "27904", "6a83", "27267"]);
56
+ var APP_NOT_INSTALLED_CODES = /* @__PURE__ */ new Set(["6807", "26631"]);
57
+ function isDeviceLockedError(err) {
58
+ if (!err || typeof err !== "object") return false;
59
+ const e = err;
60
+ if (e.errorCode != null && LOCKED_ERROR_CODES.has(String(e.errorCode))) return true;
61
+ if (e.statusCode != null && LOCKED_ERROR_CODES.has(String(e.statusCode))) return true;
62
+ if (e._tag === "DeviceLockedError") return true;
63
+ if (typeof e.message === "string" && /locked|device exchange error/i.test(e.message)) return true;
64
+ if (e.originalError != null && isDeviceLockedError(e.originalError)) return true;
65
+ if (e.error != null && e._tag && isDeviceLockedError(e.error)) return true;
66
+ return false;
67
+ }
68
+ function hasStatusCode(err, codeSet) {
69
+ if (!err || typeof err !== "object") return false;
70
+ const e = err;
71
+ if (e.errorCode != null && codeSet.has(String(e.errorCode))) return true;
72
+ if (e.statusCode != null && codeSet.has(String(e.statusCode))) return true;
73
+ if (e.originalError != null && hasStatusCode(e.originalError, codeSet)) return true;
74
+ if (e.error != null && e._tag && hasStatusCode(e.error, codeSet)) return true;
75
+ return false;
76
+ }
77
+ function isUserRejectedError(err) {
78
+ if (!err || typeof err !== "object") return false;
79
+ const e = err;
80
+ if (e._tag === "UserRefusedOnDevice") return true;
81
+ if (typeof e.message === "string" && /denied|rejected|refused/i.test(e.message)) return true;
82
+ if (hasStatusCode(err, USER_REJECTED_CODES)) return true;
83
+ return false;
84
+ }
85
+ function isWrongAppError(err) {
86
+ if (!err || typeof err !== "object") return false;
87
+ const e = err;
88
+ if (e._tag === "WrongAppOpenedError" || e._tag === "InvalidStatusWordError") {
89
+ if (hasStatusCode(err, WRONG_APP_CODES)) return true;
90
+ }
91
+ if (typeof e.message === "string" && /wrong app|open the .* app|CLA not supported/i.test(e.message))
92
+ return true;
93
+ if (hasStatusCode(err, WRONG_APP_CODES)) return true;
94
+ return false;
95
+ }
96
+ function isAppNotInstalledError(err) {
97
+ if (!err || typeof err !== "object") return false;
98
+ const e = err;
99
+ if (e._tag === "OpenAppCommandError") return true;
100
+ if (typeof e.message === "string" && /unknown application/i.test(e.message)) return true;
101
+ if (hasStatusCode(err, APP_NOT_INSTALLED_CODES)) return true;
102
+ return false;
103
+ }
104
+ function isDeviceDisconnectedError(err) {
105
+ if (!err || typeof err !== "object") return false;
106
+ const e = err;
107
+ if (e._tag === "DeviceNotRecognizedError" || e._tag === "DeviceSessionNotFound") return true;
108
+ if (typeof e.message === "string" && /disconnected|not found|no device|unplugged|session.*not.*found|timed out.*locked/i.test(
109
+ e.message
110
+ ))
111
+ return true;
112
+ return false;
113
+ }
114
+ var TIMEOUT_TAGS = /* @__PURE__ */ new Set([
115
+ "DeviceExchangeTimeoutError",
116
+ "SendApduTimeoutError",
117
+ "SendCommandTimeoutError"
118
+ ]);
119
+ function isTimeoutError(err) {
120
+ if (!err || typeof err !== "object") return false;
121
+ const e = err;
122
+ if (typeof e._tag === "string" && TIMEOUT_TAGS.has(e._tag)) return true;
123
+ if (e.code === import_hwk_adapter_core.HardwareErrorCode.OperationTimeout) return true;
124
+ return false;
125
+ }
126
+ function mapLedgerError(err) {
127
+ let originalMessage = "Unknown Ledger error";
128
+ if (err instanceof Error) {
129
+ originalMessage = err.message;
130
+ } else if (err && typeof err === "object") {
131
+ const e = err;
132
+ originalMessage = String(e.message ?? e._tag ?? e.type ?? JSON.stringify(err));
133
+ }
134
+ let code;
135
+ if (isDeviceLockedError(err)) {
136
+ code = import_hwk_adapter_core.HardwareErrorCode.DeviceLocked;
137
+ } else if (isUserRejectedError(err)) {
138
+ code = import_hwk_adapter_core.HardwareErrorCode.UserRejected;
139
+ } else if (isWrongAppError(err)) {
140
+ code = import_hwk_adapter_core.HardwareErrorCode.WrongApp;
141
+ } else if (isAppNotInstalledError(err)) {
142
+ code = import_hwk_adapter_core.HardwareErrorCode.AppNotOpen;
143
+ } else if (isDeviceDisconnectedError(err)) {
144
+ code = import_hwk_adapter_core.HardwareErrorCode.DeviceDisconnected;
145
+ } else if (isTimeoutError(err)) {
146
+ code = import_hwk_adapter_core.HardwareErrorCode.OperationTimeout;
147
+ } else {
148
+ code = import_hwk_adapter_core.HardwareErrorCode.UnknownError;
149
+ }
150
+ return { code, message: (0, import_hwk_adapter_core.enrichErrorMessage)(code, originalMessage) };
151
+ }
152
+
153
+ // src/adapter/LedgerAdapter.ts
154
+ var _LedgerAdapter = class _LedgerAdapter {
155
+ constructor(connector) {
156
+ this.vendor = "ledger";
157
+ this.emitter = new import_hwk_adapter_core2.TypedEventEmitter();
158
+ this._uiHandler = null;
159
+ // Device cache: tracks discovered devices from connector events
160
+ this._discoveredDevices = /* @__PURE__ */ new Map();
161
+ // Session tracking: maps connectId -> sessionId
162
+ this._sessions = /* @__PURE__ */ new Map();
163
+ // Pending device-connect resolve — set by _waitForDeviceConnect, resolved by uiResponse
164
+ this._deviceConnectResolve = null;
165
+ // Mutex for ensureConnected — prevents concurrent calls from establishing duplicate connections
166
+ this._connectingPromise = null;
167
+ // ---------------------------------------------------------------------------
168
+ // Event translation
169
+ // ---------------------------------------------------------------------------
170
+ this.deviceConnectHandler = (data) => {
171
+ const deviceInfo = this.connectorDeviceToDeviceInfo(data.device);
172
+ this._discoveredDevices.set(deviceInfo.connectId, deviceInfo);
173
+ this._sessions.delete(deviceInfo.connectId);
174
+ this.emitter.emit(import_hwk_adapter_core2.DEVICE.CONNECT, {
175
+ type: import_hwk_adapter_core2.DEVICE.CONNECT,
176
+ payload: deviceInfo
177
+ });
178
+ };
179
+ this.deviceDisconnectHandler = (data) => {
180
+ this._discoveredDevices.delete(data.connectId);
181
+ this._sessions.delete(data.connectId);
182
+ this.emitter.emit(import_hwk_adapter_core2.DEVICE.DISCONNECT, {
183
+ type: import_hwk_adapter_core2.DEVICE.DISCONNECT,
184
+ payload: { connectId: data.connectId }
185
+ });
186
+ };
187
+ this.uiRequestHandler = (data) => {
188
+ this.handleUiEvent(data);
189
+ };
190
+ this.uiEventHandler = (data) => {
191
+ this.handleUiEvent(data);
192
+ };
193
+ this.connector = connector;
194
+ this.registerEventListeners();
195
+ }
196
+ // ---------------------------------------------------------------------------
197
+ // Transport
198
+ // ---------------------------------------------------------------------------
199
+ // Transport is decided at connector creation time. These methods
200
+ // satisfy the IHardwareWallet interface with sensible defaults.
201
+ get activeTransport() {
202
+ return "hid";
203
+ }
204
+ getAvailableTransports() {
205
+ return ["hid"];
206
+ }
207
+ async switchTransport(_type) {
208
+ }
209
+ // ---------------------------------------------------------------------------
210
+ // UI handler
211
+ // ---------------------------------------------------------------------------
212
+ setUiHandler(handler) {
213
+ this._uiHandler = handler;
214
+ }
215
+ // ---------------------------------------------------------------------------
216
+ // Lifecycle
217
+ // ---------------------------------------------------------------------------
218
+ async init(_config) {
219
+ }
220
+ /**
221
+ * Clear cached device/session state without tearing down the adapter.
222
+ * Call before retrying after errors or when the device state may be stale.
223
+ * The next operation will re-discover and re-connect automatically.
224
+ */
225
+ resetState() {
226
+ this._discoveredDevices.clear();
227
+ this._sessions.clear();
228
+ this._connectingPromise = null;
229
+ }
230
+ async dispose() {
231
+ this._deviceConnectResolve?.(true);
232
+ this._deviceConnectResolve = null;
233
+ this.unregisterEventListeners();
234
+ this.connector.reset();
235
+ this._uiHandler = null;
236
+ this._discoveredDevices.clear();
237
+ this._sessions.clear();
238
+ this.emitter.removeAllListeners();
239
+ }
240
+ // ---------------------------------------------------------------------------
241
+ // Device management
242
+ // ---------------------------------------------------------------------------
243
+ async searchDevices() {
244
+ await this._ensureDevicePermission();
245
+ const devices = await this.connector.searchDevices();
246
+ console.log("[DMK] adapter.searchDevices raw:", JSON.stringify(devices));
247
+ for (const d of devices) {
248
+ if (d.connectId) {
249
+ this._discoveredDevices.set(d.connectId, this.connectorDeviceToDeviceInfo(d));
250
+ }
251
+ }
252
+ if (this._discoveredDevices.size === 0) {
253
+ await this._ensureDevicePermission();
254
+ }
255
+ return Array.from(this._discoveredDevices.values());
256
+ }
257
+ async connectDevice(connectId) {
258
+ await this._ensureDevicePermission(connectId);
259
+ try {
260
+ const session = await this.connector.connect(connectId);
261
+ this._sessions.set(connectId, session.sessionId);
262
+ if (session.deviceInfo) {
263
+ this._discoveredDevices.set(connectId, session.deviceInfo);
264
+ }
265
+ return (0, import_hwk_adapter_core2.success)(connectId);
266
+ } catch (err) {
267
+ return this.errorToFailure(err);
268
+ }
269
+ }
270
+ async disconnectDevice(connectId) {
271
+ const sessionId = this._sessions.get(connectId);
272
+ if (sessionId) {
273
+ await this.connector.disconnect(sessionId);
274
+ this._sessions.delete(connectId);
275
+ }
276
+ }
277
+ async getDeviceInfo(connectId, deviceId) {
278
+ await this._ensureDevicePermission(connectId, deviceId);
279
+ const cached = this._discoveredDevices.get(connectId) ?? Array.from(this._discoveredDevices.values()).find((d) => d.deviceId === deviceId);
280
+ if (cached) {
281
+ return (0, import_hwk_adapter_core2.success)(cached);
282
+ }
283
+ return (0, import_hwk_adapter_core2.failure)(
284
+ import_hwk_adapter_core2.HardwareErrorCode.DeviceNotFound,
285
+ "Device not found in cache. Call searchDevices() or wait for a device-connected event first."
286
+ );
287
+ }
288
+ getSupportedChains() {
289
+ return ["evm", "btc", "sol", "tron"];
290
+ }
291
+ // ---------------------------------------------------------------------------
292
+ // Chain call helper
293
+ // ---------------------------------------------------------------------------
294
+ async callChain(connectId, deviceId, chain, method, params, skipFingerprint = false) {
295
+ await this._ensureDevicePermission(connectId, deviceId);
296
+ if (!skipFingerprint && !await this._verifyDeviceFingerprint(connectId, deviceId, chain)) {
297
+ return (0, import_hwk_adapter_core2.failure)(import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch, "Wrong device connected");
298
+ }
299
+ try {
300
+ const result = await this.connectorCall(connectId, method, params);
301
+ return (0, import_hwk_adapter_core2.success)(result);
302
+ } catch (err) {
303
+ return this.errorToFailure(err);
304
+ }
305
+ }
306
+ /**
307
+ * Batch version of callChain — checks permission and fingerprint once,
308
+ * then calls the connector for each param sequentially.
309
+ */
310
+ async callChainBatch(connectId, deviceId, chain, method, params, onProgress, skipFingerprint = false) {
311
+ await this._ensureDevicePermission(connectId, deviceId);
312
+ if (!skipFingerprint && !await this._verifyDeviceFingerprint(connectId, deviceId, chain)) {
313
+ return (0, import_hwk_adapter_core2.failure)(import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch, "Wrong device connected");
314
+ }
315
+ const results = [];
316
+ for (let i = 0; i < params.length; i++) {
317
+ try {
318
+ const result = await this.connectorCall(connectId, method, params[i]);
319
+ results.push(result);
320
+ onProgress?.({ index: i, total: params.length });
321
+ } catch (err) {
322
+ return this.errorToFailure(err);
323
+ }
324
+ }
325
+ return (0, import_hwk_adapter_core2.success)(results);
326
+ }
327
+ // ---------------------------------------------------------------------------
328
+ // EVM chain methods
329
+ // ---------------------------------------------------------------------------
330
+ evmGetAddress(connectId, deviceId, params) {
331
+ return this.callChain(connectId, deviceId, "evm", "evmGetAddress", params);
332
+ }
333
+ evmGetAddresses(connectId, deviceId, params, onProgress) {
334
+ return this.callChainBatch(
335
+ connectId,
336
+ deviceId,
337
+ "evm",
338
+ "evmGetAddress",
339
+ params,
340
+ onProgress
341
+ );
342
+ }
343
+ evmGetPublicKey(connectId, deviceId, params) {
344
+ return this.callChain(connectId, deviceId, "evm", "evmGetAddress", params);
345
+ }
346
+ evmSignTransaction(connectId, deviceId, params) {
347
+ return this.callChain(connectId, deviceId, "evm", "evmSignTransaction", params);
348
+ }
349
+ evmSignMessage(connectId, deviceId, params) {
350
+ return this.callChain(connectId, deviceId, "evm", "evmSignMessage", params);
351
+ }
352
+ evmSignTypedData(connectId, deviceId, params) {
353
+ return this.callChain(connectId, deviceId, "evm", "evmSignTypedData", params);
354
+ }
355
+ // ---------------------------------------------------------------------------
356
+ // BTC chain methods
357
+ // ---------------------------------------------------------------------------
358
+ btcGetAddress(connectId, deviceId, params) {
359
+ return this.callChain(connectId, deviceId, "btc", "btcGetAddress", params);
360
+ }
361
+ btcGetAddresses(connectId, deviceId, params, onProgress) {
362
+ return this.callChainBatch(
363
+ connectId,
364
+ deviceId,
365
+ "btc",
366
+ "btcGetAddress",
367
+ params,
368
+ onProgress
369
+ );
370
+ }
371
+ btcGetPublicKey(connectId, deviceId, params) {
372
+ return this.callChain(connectId, deviceId, "btc", "btcGetPublicKey", params);
373
+ }
374
+ btcSignTransaction(connectId, deviceId, params) {
375
+ return this.callChain(connectId, deviceId, "btc", "btcSignTransaction", params);
376
+ }
377
+ btcSignMessage(connectId, deviceId, params) {
378
+ return this.callChain(connectId, deviceId, "btc", "btcSignMessage", params);
379
+ }
380
+ btcGetMasterFingerprint(connectId, deviceId) {
381
+ return this.callChain(
382
+ connectId,
383
+ deviceId,
384
+ "btc",
385
+ "btcGetMasterFingerprint",
386
+ {}
387
+ );
388
+ }
389
+ // ---------------------------------------------------------------------------
390
+ // SOL chain methods
391
+ // ---------------------------------------------------------------------------
392
+ solGetAddress(connectId, deviceId, params) {
393
+ return this.callChain(connectId, deviceId, "sol", "solGetAddress", params);
394
+ }
395
+ solGetAddresses(connectId, deviceId, params, onProgress) {
396
+ return this.callChainBatch(
397
+ connectId,
398
+ deviceId,
399
+ "sol",
400
+ "solGetAddress",
401
+ params,
402
+ onProgress
403
+ );
404
+ }
405
+ solGetPublicKey(connectId, deviceId, params) {
406
+ return this.callChain(connectId, deviceId, "sol", "solGetAddress", params);
407
+ }
408
+ solSignTransaction(connectId, deviceId, params) {
409
+ return this.callChain(connectId, deviceId, "sol", "solSignTransaction", params);
410
+ }
411
+ solSignMessage(connectId, deviceId, params) {
412
+ return this.callChain(connectId, deviceId, "sol", "solSignMessage", params);
413
+ }
414
+ // ---------------------------------------------------------------------------
415
+ // TRON chain methods
416
+ // ---------------------------------------------------------------------------
417
+ tronGetAddress(connectId, deviceId, params) {
418
+ return this.callChain(connectId, deviceId, "tron", "tronGetAddress", params, true);
419
+ }
420
+ tronGetAddresses(connectId, deviceId, params, onProgress) {
421
+ return this.callChainBatch(
422
+ connectId,
423
+ deviceId,
424
+ "tron",
425
+ "tronGetAddress",
426
+ params,
427
+ onProgress,
428
+ true
429
+ );
430
+ }
431
+ tronSignTransaction(connectId, deviceId, params) {
432
+ return this.callChain(
433
+ connectId,
434
+ deviceId,
435
+ "tron",
436
+ "tronSignTransaction",
437
+ params,
438
+ true
439
+ );
440
+ }
441
+ tronSignMessage(connectId, deviceId, params) {
442
+ return this.callChain(
443
+ connectId,
444
+ deviceId,
445
+ "tron",
446
+ "tronSignMessage",
447
+ params,
448
+ true
449
+ );
450
+ }
451
+ on(event, listener) {
452
+ this.emitter.on(event, listener);
453
+ }
454
+ off(event, listener) {
455
+ this.emitter.off(event, listener);
456
+ }
457
+ cancel(connectId) {
458
+ const sessionId = this._sessions.get(connectId) ?? connectId;
459
+ void this.connector.cancel(sessionId);
460
+ }
461
+ // ---------------------------------------------------------------------------
462
+ // Chain fingerprint
463
+ // ---------------------------------------------------------------------------
464
+ async getChainFingerprint(connectId, deviceId, chain) {
465
+ console.log(
466
+ "[LedgerAdapter] getChainFingerprint called, chain:",
467
+ chain,
468
+ "connectId:",
469
+ connectId || "(empty)",
470
+ "sessions:",
471
+ this._sessions.size
472
+ );
473
+ await this._ensureDevicePermission(connectId, deviceId);
474
+ console.log(
475
+ "[LedgerAdapter] getChainFingerprint permission ok, calling _deriveAddressForFingerprint"
476
+ );
477
+ try {
478
+ const address = await this._deriveAddressForFingerprint(connectId, chain);
479
+ console.log("[LedgerAdapter] getChainFingerprint address:", address?.substring(0, 20));
480
+ return (0, import_hwk_adapter_core2.success)((0, import_hwk_adapter_core2.deriveDeviceFingerprint)(address));
481
+ } catch (err) {
482
+ console.error(
483
+ "[LedgerAdapter] getChainFingerprint error in _deriveAddressForFingerprint:",
484
+ chain,
485
+ err
486
+ );
487
+ return this.errorToFailure(err);
488
+ }
489
+ }
490
+ /**
491
+ * Verify that the connected device matches the expected fingerprint.
492
+ *
493
+ * - If deviceId is empty, verification is skipped (returns true).
494
+ * - deviceId is used here as the stored fingerprint to compare against.
495
+ */
496
+ async _verifyDeviceFingerprint(connectId, deviceId, chain) {
497
+ if (!deviceId) return true;
498
+ try {
499
+ const address = await this._deriveAddressForFingerprint(connectId, chain);
500
+ const fingerprint = (0, import_hwk_adapter_core2.deriveDeviceFingerprint)(address);
501
+ return fingerprint === deviceId;
502
+ } catch (err) {
503
+ const mapped = mapLedgerError(err);
504
+ if (mapped.code === import_hwk_adapter_core2.HardwareErrorCode.WrongApp || mapped.code === import_hwk_adapter_core2.HardwareErrorCode.DeviceLocked) {
505
+ return true;
506
+ }
507
+ throw err;
508
+ }
509
+ }
510
+ /**
511
+ * Derive an address at the fixed testnet path for fingerprint generation.
512
+ */
513
+ async _deriveAddressForFingerprint(connectId, chain) {
514
+ const path = import_hwk_adapter_core2.CHAIN_FINGERPRINT_PATHS[chain];
515
+ if (chain === "evm") {
516
+ const result = await this.connectorCall(connectId, "evmGetAddress", {
517
+ path,
518
+ showOnDevice: false
519
+ });
520
+ return result.address;
521
+ }
522
+ if (chain === "btc") {
523
+ const result = await this.connectorCall(connectId, "btcGetPublicKey", {
524
+ path,
525
+ showOnDevice: false
526
+ });
527
+ return result.xpub;
528
+ }
529
+ if (chain === "sol") {
530
+ const result = await this.connectorCall(connectId, "solGetAddress", {
531
+ path,
532
+ showOnDevice: false
533
+ });
534
+ return result.address;
535
+ }
536
+ if (chain === "tron") {
537
+ const result = await this.connectorCall(connectId, "tronGetAddress", {
538
+ path,
539
+ showOnDevice: false
540
+ });
541
+ return result.address;
542
+ }
543
+ throw new Error(`Unsupported chain for fingerprint: ${chain}`);
544
+ }
545
+ /**
546
+ * Wait for user to connect and unlock device.
547
+ * Emits 'ui-request' event via the adapter's own emitter.
548
+ * The consumer (monorepo adapter wrapper) listens for this and shows UI.
549
+ * When user confirms, they call adapter.deviceConnectResponse() which resolves this promise.
550
+ * Times out after 60 seconds if no response is received.
551
+ */
552
+ _waitForDeviceConnect(attempt) {
553
+ return new Promise((resolve, reject) => {
554
+ let settled = false;
555
+ const timer = setTimeout(() => {
556
+ if (!settled) {
557
+ settled = true;
558
+ this._deviceConnectResolve = null;
559
+ reject(new Error("Ledger device connect timed out after 60 seconds"));
560
+ }
561
+ }, _LedgerAdapter.DEVICE_CONNECT_TIMEOUT_MS);
562
+ this._deviceConnectResolve = (cancelled) => {
563
+ if (settled) return;
564
+ settled = true;
565
+ clearTimeout(timer);
566
+ this._deviceConnectResolve = null;
567
+ if (cancelled) {
568
+ reject(
569
+ Object.assign(new Error("User cancelled Ledger connection"), {
570
+ _tag: "DeviceNotRecognizedError"
571
+ })
572
+ );
573
+ } else {
574
+ resolve();
575
+ }
576
+ };
577
+ this.emitter.emit(import_hwk_adapter_core2.UI_REQUEST.REQUEST_DEVICE_CONNECT, {
578
+ type: import_hwk_adapter_core2.UI_REQUEST.REQUEST_DEVICE_CONNECT,
579
+ payload: {
580
+ message: "Please connect and unlock your Ledger device",
581
+ retryCount: attempt,
582
+ maxRetries: _LedgerAdapter.MAX_DEVICE_RETRY
583
+ }
584
+ });
585
+ });
586
+ }
587
+ /**
588
+ * Called by consumer to respond to ui-request-device-connect.
589
+ * type='confirm' → retry search, type='cancel' → abort.
590
+ */
591
+ deviceConnectResponse(type) {
592
+ if (this._deviceConnectResolve) {
593
+ this._deviceConnectResolve(type === "cancel");
594
+ }
595
+ }
596
+ async ensureConnected(connectId) {
597
+ if (connectId && this._sessions.has(connectId)) {
598
+ return connectId;
599
+ }
600
+ if (this._sessions.size > 0) {
601
+ return this._sessions.keys().next().value;
602
+ }
603
+ if (this._connectingPromise) {
604
+ return this._connectingPromise;
605
+ }
606
+ this._connectingPromise = this._doConnect();
607
+ try {
608
+ return await this._connectingPromise;
609
+ } finally {
610
+ this._connectingPromise = null;
611
+ }
612
+ }
613
+ async _doConnect() {
614
+ for (let attempt = 0; attempt < _LedgerAdapter.MAX_DEVICE_RETRY; attempt++) {
615
+ const devices = await this.searchDevices();
616
+ if (devices.length > 0) {
617
+ return this._connectFirstOrSelect(devices);
618
+ }
619
+ if (attempt < _LedgerAdapter.MAX_DEVICE_RETRY - 1) {
620
+ await this._waitForDeviceConnect(attempt + 1);
621
+ }
622
+ }
623
+ throw Object.assign(
624
+ new Error(
625
+ "No Ledger device found after multiple attempts. Please connect and unlock your device."
626
+ ),
627
+ { _tag: "DeviceNotRecognizedError" }
628
+ );
629
+ }
630
+ async _connectFirstOrSelect(devices) {
631
+ if (devices.length === 1) {
632
+ const result2 = await this.connectDevice(devices[0].connectId);
633
+ if (!result2.success) {
634
+ throw Object.assign(new Error(result2.payload.error), { _tag: "DeviceNotRecognizedError" });
635
+ }
636
+ return devices[0].connectId;
637
+ }
638
+ if (this._uiHandler?.onSelectDevice) {
639
+ const selectedConnectId = await this._uiHandler.onSelectDevice(devices);
640
+ const result2 = await this.connectDevice(selectedConnectId);
641
+ if (!result2.success) {
642
+ throw Object.assign(new Error(result2.payload.error), { _tag: "DeviceNotRecognizedError" });
643
+ }
644
+ return selectedConnectId;
645
+ }
646
+ const result = await this.connectDevice(devices[0].connectId);
647
+ if (!result.success) {
648
+ throw Object.assign(new Error(result.payload.error), { _tag: "DeviceNotRecognizedError" });
649
+ }
650
+ return devices[0].connectId;
651
+ }
652
+ /**
653
+ * Call the connector with automatic session resolution and disconnect retry.
654
+ *
655
+ * 1. Resolves a valid connectId via ensureConnected()
656
+ * 2. Looks up sessionId from _sessions
657
+ * 3. Calls connector.call()
658
+ * 4. On disconnect error: clears stale session, re-connects, retries once
659
+ */
660
+ async connectorCall(connectId, method, params) {
661
+ console.log("[LedgerAdapter] connectorCall:", method, "connectId:", connectId || "(empty)");
662
+ const resolvedConnectId = await this.ensureConnected(connectId);
663
+ const sessionId = this._sessions.get(resolvedConnectId);
664
+ console.log(
665
+ "[LedgerAdapter] connectorCall resolved:",
666
+ method,
667
+ "resolvedConnectId:",
668
+ resolvedConnectId,
669
+ "sessionId:",
670
+ sessionId
671
+ );
672
+ if (!sessionId) {
673
+ throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
674
+ _tag: "DeviceSessionNotFound"
675
+ });
676
+ }
677
+ try {
678
+ return await this.connector.call(sessionId, method, params);
679
+ } catch (err) {
680
+ const errObj = err;
681
+ console.log("[LedgerAdapter] connectorCall error:", method, {
682
+ message: errObj?.message,
683
+ _tag: errObj?._tag,
684
+ errorCode: errObj?.errorCode,
685
+ statusCode: errObj?.statusCode,
686
+ isDisconnected: isDeviceDisconnectedError(err),
687
+ isLocked: isDeviceLockedError(err)
688
+ });
689
+ if (isDeviceDisconnectedError(err)) {
690
+ console.log("[LedgerAdapter] disconnected, retrying with fresh connection...");
691
+ this._discoveredDevices.clear();
692
+ return this._retryWithFreshConnection(resolvedConnectId, method, params, err);
693
+ }
694
+ if (isDeviceLockedError(err)) {
695
+ await this._waitForDeviceConnect(0);
696
+ return this.connector.call(sessionId, method, params);
697
+ }
698
+ if (isTimeoutError(err)) {
699
+ console.log("[LedgerAdapter] timeout, retrying with fresh connection...");
700
+ this._discoveredDevices.delete(resolvedConnectId);
701
+ return this._retryWithFreshConnection(resolvedConnectId, method, params, err);
702
+ }
703
+ throw err;
704
+ }
705
+ }
706
+ /** Clear stale session, reconnect, and retry the call. */
707
+ async _retryWithFreshConnection(resolvedConnectId, method, params, originalErr) {
708
+ this._sessions.delete(resolvedConnectId);
709
+ const retryConnectId = await this.ensureConnected();
710
+ const retrySessionId = this._sessions.get(retryConnectId);
711
+ if (!retrySessionId) {
712
+ throw originalErr;
713
+ }
714
+ return this.connector.call(retrySessionId, method, params);
715
+ }
716
+ /**
717
+ * Ensure device permission before proceeding.
718
+ * - No connectId (searchDevices): check environment-level permission
719
+ * - With connectId (business methods): check device-level permission
720
+ * If not granted, calls onDevicePermission so the consumer can request access.
721
+ */
722
+ async _ensureDevicePermission(connectId, deviceId) {
723
+ const transportType = "hid";
724
+ let granted = false;
725
+ let context;
726
+ if (this._uiHandler?.checkDevicePermission) {
727
+ try {
728
+ const result = await this._uiHandler.checkDevicePermission({
729
+ transportType,
730
+ connectId,
731
+ deviceId
732
+ });
733
+ granted = result.granted;
734
+ context = result.context;
735
+ } catch {
736
+ granted = false;
737
+ }
738
+ }
739
+ if (!granted) {
740
+ try {
741
+ await this._uiHandler?.onDevicePermission?.({ transportType, context });
742
+ } catch {
743
+ }
744
+ }
745
+ }
746
+ /**
747
+ * Convert a thrown error to a Response failure.
748
+ * Uses mapLedgerError to parse Ledger DMK error codes into HardwareErrorCode values.
749
+ */
750
+ errorToFailure(err) {
751
+ console.error("[LedgerAdapter] error:", err);
752
+ if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
753
+ const e = err;
754
+ return (0, import_hwk_adapter_core2.failure)(e.code, e.message ?? "Unknown error");
755
+ }
756
+ const mapped = mapLedgerError(err);
757
+ return (0, import_hwk_adapter_core2.failure)(mapped.code, mapped.message);
758
+ }
759
+ registerEventListeners() {
760
+ this.connector.on("device-connect", this.deviceConnectHandler);
761
+ this.connector.on("device-disconnect", this.deviceDisconnectHandler);
762
+ this.connector.on("ui-request", this.uiRequestHandler);
763
+ this.connector.on("ui-event", this.uiEventHandler);
764
+ }
765
+ unregisterEventListeners() {
766
+ this.connector.off("device-connect", this.deviceConnectHandler);
767
+ this.connector.off("device-disconnect", this.deviceDisconnectHandler);
768
+ this.connector.off("ui-request", this.uiRequestHandler);
769
+ this.connector.off("ui-event", this.uiEventHandler);
770
+ }
771
+ handleUiEvent(event) {
772
+ if (!event.type) return;
773
+ const payload = event.payload;
774
+ const deviceInfo = payload ? this.extractDeviceInfoFromPayload(payload) : this.unknownDevice();
775
+ switch (event.type) {
776
+ case "ui-request_confirmation":
777
+ this.emitter.emit(import_hwk_adapter_core2.UI_REQUEST.REQUEST_BUTTON, {
778
+ type: import_hwk_adapter_core2.UI_REQUEST.REQUEST_BUTTON,
779
+ payload: { device: deviceInfo }
780
+ });
781
+ break;
782
+ }
783
+ }
784
+ // ---------------------------------------------------------------------------
785
+ // Device info mapping
786
+ // ---------------------------------------------------------------------------
787
+ connectorDeviceToDeviceInfo(device) {
788
+ const isBle = device.connectId && /^[0-9A-Fa-f]{4}$/.test(device.connectId);
789
+ return {
790
+ vendor: "ledger",
791
+ model: device.model ?? "unknown",
792
+ firmwareVersion: "",
793
+ deviceId: device.deviceId,
794
+ connectId: device.connectId,
795
+ label: device.name,
796
+ connectionType: isBle ? "ble" : "usb",
797
+ capabilities: device.capabilities
798
+ };
799
+ }
800
+ extractDeviceInfoFromPayload(payload) {
801
+ return {
802
+ vendor: "ledger",
803
+ model: payload["model"] ?? "unknown",
804
+ firmwareVersion: "",
805
+ deviceId: payload["deviceId"] ?? payload["id"] ?? "",
806
+ connectId: payload["connectId"] ?? payload["path"] ?? "",
807
+ label: payload["label"],
808
+ connectionType: "usb"
809
+ };
810
+ }
811
+ unknownDevice() {
812
+ return {
813
+ vendor: "ledger",
814
+ model: "unknown",
815
+ firmwareVersion: "",
816
+ deviceId: "",
817
+ connectId: "",
818
+ connectionType: "usb"
819
+ };
820
+ }
821
+ };
822
+ // ---------------------------------------------------------------------------
823
+ // Private helpers
824
+ // ---------------------------------------------------------------------------
825
+ /**
826
+ * Ensure at least one device is connected and return a valid connectId.
827
+ *
828
+ * - If a session already exists for the given connectId, reuse it.
829
+ * - If ANY session exists (Ledger IDs are ephemeral), reuse it.
830
+ * - Otherwise: search → 1 device: auto-connect, multiple: ask user, 0: throw.
831
+ */
832
+ _LedgerAdapter.MAX_DEVICE_RETRY = 3;
833
+ _LedgerAdapter.DEVICE_CONNECT_TIMEOUT_MS = 6e4;
834
+ var LedgerAdapter = _LedgerAdapter;
835
+
836
+ // src/device/LedgerDeviceManager.ts
837
+ var LedgerDeviceManager = class {
838
+ constructor(dmk) {
839
+ this._discovered = /* @__PURE__ */ new Map();
840
+ this._sessions = /* @__PURE__ */ new Map();
841
+ // deviceId → sessionId
842
+ this._sessionToDevice = /* @__PURE__ */ new Map();
843
+ // sessionId → deviceId
844
+ this._listenSub = null;
845
+ /**
846
+ * Trigger browser device selection (WebHID requestDevice).
847
+ * Starts discovery for a short period, then stops.
848
+ */
849
+ /**
850
+ * Start BLE discovery if not already running.
851
+ * Does NOT stop — DMK keeps scanning in the background so that
852
+ * listenToAvailableDevices() always has fresh data.
853
+ */
854
+ this._discoverySub = null;
855
+ this._dmk = dmk;
856
+ }
857
+ /**
858
+ * One-shot enumeration: subscribe to listenToAvailableDevices,
859
+ * take the first emission, unsubscribe, return DeviceDescriptors.
860
+ */
861
+ enumerate() {
862
+ console.log(
863
+ "[DMK] enumerate() called, dmk exists:",
864
+ !!this._dmk,
865
+ "listenToAvailableDevices exists:",
866
+ typeof this._dmk?.listenToAvailableDevices
867
+ );
868
+ return new Promise((resolve) => {
869
+ let resolved = false;
870
+ let syncResult = null;
871
+ let sub = null;
872
+ sub = this._dmk.listenToAvailableDevices({}).subscribe({
873
+ next: (devices) => {
874
+ if (resolved) return;
875
+ resolved = true;
876
+ this._discovered.clear();
877
+ console.log(
878
+ "[DMK] enumerate raw devices:",
879
+ JSON.stringify(
880
+ devices.map((d) => ({
881
+ id: d.id,
882
+ deviceModel: d.deviceModel,
883
+ transport: d.transport,
884
+ name: d.name,
885
+ rssi: d.rssi,
886
+ // Dump all keys to see what else is available
887
+ _keys: Object.keys(d)
888
+ }))
889
+ )
890
+ );
891
+ for (const d of devices) {
892
+ this._discovered.set(d.id, d);
893
+ }
894
+ if (sub) {
895
+ sub.unsubscribe();
896
+ resolve(
897
+ devices.map((d) => ({
898
+ path: d.id,
899
+ type: d.deviceModel.model,
900
+ name: d.name,
901
+ transport: d.transport
902
+ }))
903
+ );
904
+ } else {
905
+ syncResult = devices;
906
+ }
907
+ },
908
+ error: () => {
909
+ if (!resolved) {
910
+ resolved = true;
911
+ resolve([]);
912
+ }
913
+ }
914
+ });
915
+ if (syncResult !== null) {
916
+ sub.unsubscribe();
917
+ const devices = syncResult;
918
+ resolve(
919
+ devices.map((d) => ({
920
+ path: d.id,
921
+ type: d.deviceModel.model,
922
+ name: d.name,
923
+ transport: d.transport
924
+ }))
925
+ );
926
+ }
927
+ });
928
+ }
929
+ /**
930
+ * Continuous listening: tracks device connect/disconnect via diffing.
931
+ */
932
+ listen(onChange) {
933
+ this.stopListening();
934
+ let previousIds = /* @__PURE__ */ new Set();
935
+ this._listenSub = this._dmk.listenToAvailableDevices({}).subscribe({
936
+ next: (devices) => {
937
+ const currentIds = new Set(devices.map((d) => d.id));
938
+ for (const d of devices) {
939
+ this._discovered.set(d.id, d);
940
+ console.log(
941
+ "[DMK] listen device:",
942
+ JSON.stringify({
943
+ id: d.id,
944
+ deviceModel: d.deviceModel,
945
+ name: d.name
946
+ })
947
+ );
948
+ if (!previousIds.has(d.id)) {
949
+ onChange({
950
+ type: "device-connected",
951
+ descriptor: {
952
+ path: d.id,
953
+ type: d.deviceModel.model,
954
+ name: d.name,
955
+ transport: d.transport
956
+ }
957
+ });
958
+ }
959
+ }
960
+ for (const id of previousIds) {
961
+ if (!currentIds.has(id)) {
962
+ this._discovered.delete(id);
963
+ onChange({ type: "device-disconnected", descriptor: { path: id } });
964
+ }
965
+ }
966
+ previousIds = currentIds;
967
+ }
968
+ });
969
+ }
970
+ stopListening() {
971
+ this._listenSub?.unsubscribe();
972
+ this._listenSub = null;
973
+ }
974
+ requestDevice() {
975
+ if (this._discoverySub) {
976
+ return Promise.resolve();
977
+ }
978
+ console.log("[DMK] requestDevice() starting persistent BLE scan");
979
+ this._discoverySub = this._dmk.startDiscovering({}).subscribe({
980
+ next: (d) => {
981
+ console.log("[DMK] BLE discovered:", d.name || d.id);
982
+ this._discovered.set(d.id, d);
983
+ },
984
+ error: (err) => {
985
+ console.error("[DMK] BLE scan error:", err);
986
+ this._discoverySub = null;
987
+ }
988
+ });
989
+ return Promise.resolve();
990
+ }
991
+ /** Connect to a previously discovered device. Returns sessionId. */
992
+ async connect(deviceId) {
993
+ const device = this._discovered.get(deviceId);
994
+ if (!device) {
995
+ throw new Error(`Device "${deviceId}" not found. Call enumerate() or listen() first.`);
996
+ }
997
+ const sessionId = await this._dmk.connect({ device });
998
+ this._sessions.set(deviceId, sessionId);
999
+ this._sessionToDevice.set(sessionId, deviceId);
1000
+ return sessionId;
1001
+ }
1002
+ /** Disconnect a session. */
1003
+ async disconnect(sessionId) {
1004
+ await this._dmk.disconnect({ sessionId });
1005
+ const deviceId = this._sessionToDevice.get(sessionId);
1006
+ if (deviceId) this._sessions.delete(deviceId);
1007
+ this._sessionToDevice.delete(sessionId);
1008
+ }
1009
+ getSessionId(deviceId) {
1010
+ return this._sessions.get(deviceId);
1011
+ }
1012
+ getDeviceId(sessionId) {
1013
+ return this._sessionToDevice.get(sessionId);
1014
+ }
1015
+ /** Get the underlying DMK instance (needed by SignerManager). */
1016
+ getDmk() {
1017
+ return this._dmk;
1018
+ }
1019
+ stopDiscovery() {
1020
+ if (this._discoverySub) {
1021
+ this._discoverySub.unsubscribe();
1022
+ this._discoverySub = null;
1023
+ this._dmk.stopDiscovering();
1024
+ }
1025
+ }
1026
+ dispose() {
1027
+ this.stopListening();
1028
+ this.stopDiscovery();
1029
+ this._discovered.clear();
1030
+ this._sessions.clear();
1031
+ this._sessionToDevice.clear();
1032
+ this._dmk.close();
1033
+ }
1034
+ };
1035
+
1036
+ // src/signer/deviceActionToPromise.ts
1037
+ var import_device_management_kit = require("@ledgerhq/device-management-kit");
1038
+ var DEFAULT_TIMEOUT_MS = 3e4;
1039
+ function deviceActionToPromise(action, onInteraction, timeoutMs = DEFAULT_TIMEOUT_MS) {
1040
+ return new Promise((resolve, reject) => {
1041
+ let settled = false;
1042
+ let sub;
1043
+ let timer = null;
1044
+ const resetTimer = () => {
1045
+ if (timer) clearTimeout(timer);
1046
+ if (timeoutMs > 0) {
1047
+ timer = setTimeout(() => {
1048
+ if (!settled) {
1049
+ settled = true;
1050
+ sub?.unsubscribe();
1051
+ reject(new Error("Device action timed out \u2014 device may be locked or disconnected"));
1052
+ }
1053
+ }, timeoutMs);
1054
+ }
1055
+ };
1056
+ resetTimer();
1057
+ console.log("[DMK-Observable] subscribing to action.observable...");
1058
+ sub = action.observable.subscribe({
1059
+ next: (state) => {
1060
+ console.log("[DMK-Observable] next received");
1061
+ resetTimer();
1062
+ console.log(
1063
+ "[DMK-Observable] state:",
1064
+ JSON.stringify({
1065
+ status: state.status,
1066
+ intermediateValue: state.status === import_device_management_kit.DeviceActionStatus.Pending ? state.intermediateValue : void 0,
1067
+ hasOutput: state.status === import_device_management_kit.DeviceActionStatus.Completed,
1068
+ hasError: state.status === import_device_management_kit.DeviceActionStatus.Error
1069
+ })
1070
+ );
1071
+ if (settled) return;
1072
+ if (state.status === import_device_management_kit.DeviceActionStatus.Completed) {
1073
+ settled = true;
1074
+ if (timer) clearTimeout(timer);
1075
+ onInteraction?.("interaction-complete");
1076
+ sub?.unsubscribe();
1077
+ resolve(state.output);
1078
+ } else if (state.status === import_device_management_kit.DeviceActionStatus.Error) {
1079
+ settled = true;
1080
+ if (timer) clearTimeout(timer);
1081
+ onInteraction?.("interaction-complete");
1082
+ sub?.unsubscribe();
1083
+ reject(state.error);
1084
+ } else if (state.status === import_device_management_kit.DeviceActionStatus.Pending && onInteraction) {
1085
+ const interaction = state.intermediateValue?.requiredUserInteraction;
1086
+ if (interaction && interaction !== "none") {
1087
+ onInteraction(String(interaction));
1088
+ }
1089
+ }
1090
+ },
1091
+ error: (err) => {
1092
+ if (!settled) {
1093
+ settled = true;
1094
+ if (timer) clearTimeout(timer);
1095
+ sub?.unsubscribe();
1096
+ reject(err);
1097
+ }
1098
+ },
1099
+ complete: () => {
1100
+ if (!settled) {
1101
+ settled = true;
1102
+ if (timer) clearTimeout(timer);
1103
+ reject(new Error("Device action completed without result"));
1104
+ }
1105
+ }
1106
+ });
1107
+ });
1108
+ }
1109
+
1110
+ // src/signer/SignerEth.ts
1111
+ function hexToBytes(hex) {
1112
+ const h = hex.startsWith("0x") ? hex.slice(2) : hex;
1113
+ const bytes = new Uint8Array(h.length / 2);
1114
+ for (let i = 0; i < bytes.length; i++) {
1115
+ bytes[i] = parseInt(h.substring(i * 2, i * 2 + 2), 16);
1116
+ }
1117
+ return bytes;
1118
+ }
1119
+ var INTERACTIVE_TIMEOUT_MS = 5 * 6e4;
1120
+ var SignerEth = class {
1121
+ constructor(_sdk) {
1122
+ this._sdk = _sdk;
1123
+ }
1124
+ async getAddress(derivationPath, options) {
1125
+ const checkOnDevice = options?.checkOnDevice ?? false;
1126
+ console.log("[DMK] getAddress \u2192 DMK:", { derivationPath, checkOnDevice });
1127
+ const action = this._sdk.getAddress(derivationPath, {
1128
+ checkOnDevice
1129
+ });
1130
+ const timeout = checkOnDevice ? INTERACTIVE_TIMEOUT_MS : void 0;
1131
+ return deviceActionToPromise(action, this.onInteraction, timeout);
1132
+ }
1133
+ async signTransaction(derivationPath, serializedTxHex) {
1134
+ const action = this._sdk.signTransaction(derivationPath, hexToBytes(serializedTxHex));
1135
+ return deviceActionToPromise(
1136
+ action,
1137
+ this.onInteraction,
1138
+ INTERACTIVE_TIMEOUT_MS
1139
+ );
1140
+ }
1141
+ async signMessage(derivationPath, message) {
1142
+ const action = this._sdk.signMessage(derivationPath, hexToBytes(message));
1143
+ return deviceActionToPromise(
1144
+ action,
1145
+ this.onInteraction,
1146
+ INTERACTIVE_TIMEOUT_MS
1147
+ );
1148
+ }
1149
+ async signTypedData(derivationPath, data) {
1150
+ const action = this._sdk.signTypedData(derivationPath, data);
1151
+ return deviceActionToPromise(
1152
+ action,
1153
+ this.onInteraction,
1154
+ INTERACTIVE_TIMEOUT_MS
1155
+ );
1156
+ }
1157
+ };
1158
+
1159
+ // src/signer/SignerManager.ts
1160
+ var SignerManager = class _SignerManager {
1161
+ constructor(dmk, builderFn) {
1162
+ this._cache = /* @__PURE__ */ new Map();
1163
+ this._dmk = dmk;
1164
+ this._builderFn = builderFn ?? _SignerManager._defaultBuilder();
1165
+ }
1166
+ async getOrCreate(sessionId) {
1167
+ const hadCached = this._cache.has(sessionId);
1168
+ this._cache.delete(sessionId);
1169
+ console.log("[DMK] SignerManager.getOrCreate:", { sessionId, hadCached, creating: true });
1170
+ const builder = await this._builderFn({ dmk: this._dmk, sessionId });
1171
+ const sdkSigner = builder.build();
1172
+ console.log("[DMK] SignerManager: new signer built");
1173
+ const signer = new SignerEth(sdkSigner);
1174
+ this._cache.set(sessionId, signer);
1175
+ return signer;
1176
+ }
1177
+ invalidate(sessionId) {
1178
+ this._cache.delete(sessionId);
1179
+ }
1180
+ clearAll() {
1181
+ this._cache.clear();
1182
+ }
1183
+ static _defaultBuilder() {
1184
+ let BuilderClass = null;
1185
+ return async (args) => {
1186
+ if (!BuilderClass) {
1187
+ const mod = await import("@ledgerhq/device-signer-kit-ethereum");
1188
+ BuilderClass = mod.SignerEthBuilder;
1189
+ }
1190
+ return new BuilderClass(args);
1191
+ };
1192
+ }
1193
+ };
1194
+
1195
+ // src/connector/chains/evm.ts
1196
+ var import_hwk_adapter_core3 = require("@onekeyfe/hwk-adapter-core");
1197
+
1198
+ // src/connector/chains/utils.ts
1199
+ function normalizePath(path) {
1200
+ return path.startsWith("m/") ? path.slice(2) : path;
1201
+ }
1202
+
1203
+ // src/connector/chains/evm.ts
1204
+ async function evmGetAddress(ctx, sessionId, params) {
1205
+ const signer = await _getEthSigner(ctx, sessionId);
1206
+ const path = normalizePath(params.path);
1207
+ const checkOnDevice = params.showOnDevice ?? false;
1208
+ console.log("[DMK] evmGetAddress -> signer.getAddress:", { path, checkOnDevice });
1209
+ try {
1210
+ const result = await signer.getAddress(path, { checkOnDevice });
1211
+ return { address: result.address, publicKey: result.publicKey };
1212
+ } catch (err) {
1213
+ ctx.invalidateSession(sessionId);
1214
+ throw ctx.wrapError(err);
1215
+ }
1216
+ }
1217
+ async function evmSignTransaction(ctx, sessionId, params) {
1218
+ if (!params.serializedTx) {
1219
+ throw Object.assign(
1220
+ new Error(
1221
+ "Ledger requires a pre-serialized transaction (serializedTx). Provide an RLP-encoded hex string."
1222
+ ),
1223
+ { code: import_hwk_adapter_core3.HardwareErrorCode.InvalidParams }
1224
+ );
1225
+ }
1226
+ const signer = await _getEthSigner(ctx, sessionId);
1227
+ const path = normalizePath(params.path);
1228
+ try {
1229
+ const result = await signer.signTransaction(path, params.serializedTx);
1230
+ return {
1231
+ v: `0x${result.v.toString(16)}`,
1232
+ r: (0, import_hwk_adapter_core3.padHex64)(result.r),
1233
+ s: (0, import_hwk_adapter_core3.padHex64)(result.s)
1234
+ };
1235
+ } catch (err) {
1236
+ ctx.invalidateSession(sessionId);
1237
+ throw ctx.wrapError(err);
1238
+ }
1239
+ }
1240
+ async function evmSignMessage(ctx, sessionId, params) {
1241
+ const signer = await _getEthSigner(ctx, sessionId);
1242
+ const path = normalizePath(params.path);
1243
+ try {
1244
+ const result = await signer.signMessage(path, params.message);
1245
+ const rHex = (0, import_hwk_adapter_core3.stripHex)(result.r).padStart(64, "0");
1246
+ const sHex = (0, import_hwk_adapter_core3.stripHex)(result.s).padStart(64, "0");
1247
+ const vHex = result.v.toString(16).padStart(2, "0");
1248
+ return { signature: `0x${rHex}${sHex}${vHex}` };
1249
+ } catch (err) {
1250
+ ctx.invalidateSession(sessionId);
1251
+ throw ctx.wrapError(err);
1252
+ }
1253
+ }
1254
+ async function evmSignTypedData(ctx, sessionId, params) {
1255
+ if (params.mode === "hash") {
1256
+ throw Object.assign(
1257
+ new Error(
1258
+ 'Ledger does not support hash-only EIP-712 signing. Use mode "full" with the complete typed data structure.'
1259
+ ),
1260
+ { code: import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported }
1261
+ );
1262
+ }
1263
+ const signer = await _getEthSigner(ctx, sessionId);
1264
+ const path = normalizePath(params.path);
1265
+ try {
1266
+ const result = await signer.signTypedData(path, params.data);
1267
+ const rHex = (0, import_hwk_adapter_core3.stripHex)(result.r).padStart(64, "0");
1268
+ const sHex = (0, import_hwk_adapter_core3.stripHex)(result.s).padStart(64, "0");
1269
+ const vHex = result.v.toString(16).padStart(2, "0");
1270
+ return { signature: `0x${rHex}${sHex}${vHex}` };
1271
+ } catch (err) {
1272
+ ctx.invalidateSession(sessionId);
1273
+ throw ctx.wrapError(err);
1274
+ }
1275
+ }
1276
+ async function _getEthSigner(ctx, sessionId) {
1277
+ const signerManager = await ctx.getSignerManager();
1278
+ const signer = await signerManager.getOrCreate(sessionId);
1279
+ signer.onInteraction = (interaction) => {
1280
+ ctx.emit("ui-event", {
1281
+ type: interaction,
1282
+ payload: { sessionId }
1283
+ });
1284
+ };
1285
+ return signer;
1286
+ }
1287
+
1288
+ // src/connector/chains/btc.ts
1289
+ var import_hwk_adapter_core4 = require("@onekeyfe/hwk-adapter-core");
1290
+
1291
+ // src/signer/SignerBtc.ts
1292
+ function hexToUtf8(hex) {
1293
+ const h = hex.startsWith("0x") ? hex.slice(2) : hex;
1294
+ const bytes = new Uint8Array(h.length / 2);
1295
+ for (let i = 0; i < bytes.length; i++) {
1296
+ bytes[i] = parseInt(h.substring(i * 2, i * 2 + 2), 16);
1297
+ }
1298
+ return new TextDecoder().decode(bytes);
1299
+ }
1300
+ var INTERACTIVE_TIMEOUT_MS2 = 5 * 6e4;
1301
+ var SignerBtc = class {
1302
+ constructor(_sdk) {
1303
+ this._sdk = _sdk;
1304
+ }
1305
+ async getWalletAddress(wallet, addressIndex, options) {
1306
+ const action = this._sdk.getWalletAddress(wallet, addressIndex, {
1307
+ checkOnDevice: options?.checkOnDevice ?? false,
1308
+ change: options?.change ?? false
1309
+ });
1310
+ return deviceActionToPromise(action, this.onInteraction);
1311
+ }
1312
+ async getExtendedPublicKey(derivationPath, options) {
1313
+ console.log(
1314
+ "[SignerBtc] getExtendedPublicKey called, path:",
1315
+ derivationPath,
1316
+ "options:",
1317
+ JSON.stringify(options)
1318
+ );
1319
+ const action = this._sdk.getExtendedPublicKey(derivationPath, {
1320
+ checkOnDevice: options?.checkOnDevice ?? false
1321
+ });
1322
+ try {
1323
+ const result = await deviceActionToPromise(
1324
+ action,
1325
+ this.onInteraction
1326
+ );
1327
+ console.log(
1328
+ "[SignerBtc] getExtendedPublicKey result type:",
1329
+ typeof result,
1330
+ "value:",
1331
+ typeof result === "string" ? result.substring(0, 20) + "..." : JSON.stringify(result).substring(0, 50)
1332
+ );
1333
+ if (typeof result === "string") return result;
1334
+ return result.extendedPublicKey;
1335
+ } catch (err) {
1336
+ console.error("[SignerBtc] getExtendedPublicKey error:", err);
1337
+ throw err;
1338
+ }
1339
+ }
1340
+ async getMasterFingerprint(options) {
1341
+ const action = this._sdk.getMasterFingerprint(options);
1342
+ const result = await deviceActionToPromise(
1343
+ action,
1344
+ this.onInteraction
1345
+ );
1346
+ return result.masterFingerprint;
1347
+ }
1348
+ /**
1349
+ * Sign a PSBT and return the array of partial signatures.
1350
+ * The `wallet` param is a DefaultWallet or WalletPolicy instance.
1351
+ * The `psbt` param can be a hex string, base64 string, or Uint8Array.
1352
+ */
1353
+ async signPsbt(wallet, psbt, options) {
1354
+ const action = this._sdk.signPsbt(wallet, psbt, options);
1355
+ return deviceActionToPromise(action, this.onInteraction, INTERACTIVE_TIMEOUT_MS2);
1356
+ }
1357
+ /**
1358
+ * Sign a PSBT and return the fully extracted raw transaction as a hex string.
1359
+ * Like signPsbt, but also finalises the PSBT and extracts the transaction.
1360
+ */
1361
+ async signTransaction(wallet, psbt, options) {
1362
+ const action = this._sdk.signTransaction(wallet, psbt, options);
1363
+ return deviceActionToPromise(action, this.onInteraction, INTERACTIVE_TIMEOUT_MS2);
1364
+ }
1365
+ /**
1366
+ * Sign a message with the BTC app (BIP-137 / "Bitcoin Signed Message").
1367
+ * Returns `{ r, s, v }` signature object.
1368
+ */
1369
+ async signMessage(derivationPath, message, options) {
1370
+ const action = this._sdk.signMessage(derivationPath, hexToUtf8(message), options);
1371
+ return deviceActionToPromise(
1372
+ action,
1373
+ this.onInteraction,
1374
+ INTERACTIVE_TIMEOUT_MS2
1375
+ );
1376
+ }
1377
+ };
1378
+
1379
+ // src/connector/chains/btc.ts
1380
+ function readCompactSize(data, pos) {
1381
+ const first = data[pos];
1382
+ if (first < 253) return [first, 1];
1383
+ if (first === 253) return [data[pos + 1] | data[pos + 2] << 8, 3];
1384
+ return [0, 1];
1385
+ }
1386
+ function writeCompactSize(out, val) {
1387
+ if (val < 253) {
1388
+ out.push(val);
1389
+ } else {
1390
+ out.push(253, val & 255, val >>> 8 & 255);
1391
+ }
1392
+ }
1393
+ function readKeyValue(data, pos) {
1394
+ const start = pos;
1395
+ const [keyLen, keyLenSize] = readCompactSize(data, pos);
1396
+ pos += keyLenSize;
1397
+ const keyType = data[pos];
1398
+ pos += keyLen;
1399
+ const [valLen, valLenSize] = readCompactSize(data, pos);
1400
+ pos += valLenSize;
1401
+ const value = data.slice(pos, pos + valLen);
1402
+ pos += valLen;
1403
+ return { bytes: data.slice(start, pos), end: pos, keyType, value };
1404
+ }
1405
+ function writeKv(out, key, value) {
1406
+ writeCompactSize(out, key.length);
1407
+ key.forEach((b) => out.push(b));
1408
+ writeCompactSize(out, value.length);
1409
+ value.forEach((b) => out.push(b));
1410
+ }
1411
+ async function btcGetAddress(ctx, sessionId, params) {
1412
+ const btcSigner = await _createBtcSigner(ctx, sessionId);
1413
+ const path = normalizePath(params.path);
1414
+ try {
1415
+ const { DefaultWallet, DefaultDescriptorTemplate } = await ctx.importLedgerKit(
1416
+ "@ledgerhq/device-signer-kit-bitcoin"
1417
+ );
1418
+ const purpose = path.split("/")[0]?.replace("'", "");
1419
+ let template = DefaultDescriptorTemplate.NATIVE_SEGWIT;
1420
+ if (purpose === "44") template = DefaultDescriptorTemplate.LEGACY;
1421
+ else if (purpose === "49") template = DefaultDescriptorTemplate.NESTED_SEGWIT;
1422
+ else if (purpose === "86") template = DefaultDescriptorTemplate.TAPROOT;
1423
+ const wallet = new DefaultWallet(path, template);
1424
+ console.log("[LedgerConnector] btcGetAddress params:", {
1425
+ path,
1426
+ purpose,
1427
+ template,
1428
+ addressIndex: params.addressIndex,
1429
+ change: params.change,
1430
+ showOnDevice: params.showOnDevice,
1431
+ rawParams: JSON.stringify(params)
1432
+ });
1433
+ const result = await btcSigner.getWalletAddress(wallet, params.addressIndex ?? 0, {
1434
+ checkOnDevice: params.showOnDevice ?? false,
1435
+ change: params.change ?? false
1436
+ });
1437
+ return { address: result.address, path: params.path };
1438
+ } catch (err) {
1439
+ ctx.invalidateSession(sessionId);
1440
+ throw ctx.wrapError(err);
1441
+ }
1442
+ }
1443
+ async function btcGetPublicKey(ctx, sessionId, params) {
1444
+ const btcSigner = await _createBtcSigner(ctx, sessionId);
1445
+ const path = normalizePath(params.path);
1446
+ console.log("[LedgerConnector] btcGetPublicKey called, path:", path, "sessionId:", sessionId);
1447
+ try {
1448
+ const xpub = await btcSigner.getExtendedPublicKey(path, {
1449
+ checkOnDevice: params.showOnDevice ?? false
1450
+ });
1451
+ console.log("[LedgerConnector] btcGetPublicKey success, xpub:", xpub?.substring(0, 20) + "...");
1452
+ return { xpub, path: params.path };
1453
+ } catch (err) {
1454
+ console.error("[LedgerConnector] btcGetPublicKey error, path:", path, "err:", err);
1455
+ ctx.invalidateSession(sessionId);
1456
+ throw ctx.wrapError(err);
1457
+ }
1458
+ }
1459
+ async function btcSignTransaction(ctx, sessionId, params) {
1460
+ if (!params.psbt) {
1461
+ throw Object.assign(
1462
+ new Error("Ledger requires PSBT format for BTC transaction signing. Provide params.psbt."),
1463
+ { code: import_hwk_adapter_core4.HardwareErrorCode.InvalidParams }
1464
+ );
1465
+ }
1466
+ const btcSigner = await _createBtcSigner(ctx, sessionId);
1467
+ try {
1468
+ const { DefaultWallet, DefaultDescriptorTemplate } = await ctx.importLedgerKit(
1469
+ "@ledgerhq/device-signer-kit-bitcoin"
1470
+ );
1471
+ const path = normalizePath(params.path || "84'/0'/0'");
1472
+ const purpose = path.split("/")[0]?.replace("'", "");
1473
+ let template = DefaultDescriptorTemplate.NATIVE_SEGWIT;
1474
+ if (purpose === "44") template = DefaultDescriptorTemplate.LEGACY;
1475
+ else if (purpose === "49") template = DefaultDescriptorTemplate.NESTED_SEGWIT;
1476
+ else if (purpose === "86") template = DefaultDescriptorTemplate.TAPROOT;
1477
+ const wallet = new DefaultWallet(path, template);
1478
+ let psbtToSign = params.psbt;
1479
+ if (purpose === "86" && params.inputDerivations?.length) {
1480
+ psbtToSign = await _enrichTaprootPsbt(btcSigner, psbtToSign, params.inputDerivations);
1481
+ }
1482
+ const signedTxHex = await btcSigner.signTransaction(wallet, psbtToSign);
1483
+ return { signedPsbt: (0, import_hwk_adapter_core4.stripHex)(signedTxHex) };
1484
+ } catch (err) {
1485
+ ctx.invalidateSession(sessionId);
1486
+ throw ctx.wrapError(err);
1487
+ }
1488
+ }
1489
+ async function btcSignMessage(ctx, sessionId, params) {
1490
+ const btcSigner = await _createBtcSigner(ctx, sessionId);
1491
+ const path = normalizePath(params.path);
1492
+ try {
1493
+ const result = await btcSigner.signMessage(path, params.message);
1494
+ const vHex = result.v.toString(16).padStart(2, "0");
1495
+ const rHex = (0, import_hwk_adapter_core4.stripHex)(result.r).padStart(64, "0");
1496
+ const sHex = (0, import_hwk_adapter_core4.stripHex)(result.s).padStart(64, "0");
1497
+ return { signature: `${vHex}${rHex}${sHex}`, address: "" };
1498
+ } catch (err) {
1499
+ ctx.invalidateSession(sessionId);
1500
+ throw ctx.wrapError(err);
1501
+ }
1502
+ }
1503
+ async function btcGetMasterFingerprint(ctx, sessionId, params) {
1504
+ const btcSigner = await _createBtcSigner(ctx, sessionId);
1505
+ try {
1506
+ const fingerprint = await btcSigner.getMasterFingerprint({
1507
+ skipOpenApp: params?.skipOpenApp
1508
+ });
1509
+ const hex = Array.from(fingerprint).map((b) => b.toString(16).padStart(2, "0")).join("");
1510
+ return { masterFingerprint: hex };
1511
+ } catch (err) {
1512
+ ctx.invalidateSession(sessionId);
1513
+ throw ctx.wrapError(err);
1514
+ }
1515
+ }
1516
+ async function _createBtcSigner(ctx, sessionId) {
1517
+ const dmk = await ctx.getOrCreateDmk();
1518
+ const { SignerBtcBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-bitcoin");
1519
+ const sdkSigner = new SignerBtcBuilder({ dmk, sessionId }).build();
1520
+ const signer = new SignerBtc(sdkSigner);
1521
+ signer.onInteraction = (interaction) => {
1522
+ ctx.emit("ui-event", {
1523
+ type: interaction,
1524
+ payload: { sessionId }
1525
+ });
1526
+ };
1527
+ return signer;
1528
+ }
1529
+ async function _enrichTaprootPsbt(btcSigner, psbtHex, inputDerivations) {
1530
+ const masterFp = await btcSigner.getMasterFingerprint({ skipOpenApp: true });
1531
+ const fpBytes = masterFp.length === 4 ? masterFp : new Uint8Array([0, 0, 0, 0]);
1532
+ const raw = (0, import_hwk_adapter_core4.hexToBytes)(psbtHex);
1533
+ const result = [];
1534
+ let pos = 0;
1535
+ for (let i = 0; i < 5; i++) result.push(raw[pos++]);
1536
+ while (pos < raw.length && raw[pos] !== 0) {
1537
+ const { bytes: kv, end } = readKeyValue(raw, pos);
1538
+ kv.forEach((b) => result.push(b));
1539
+ pos = end;
1540
+ }
1541
+ result.push(raw[pos++]);
1542
+ for (let inputIdx = 0; pos < raw.length; inputIdx++) {
1543
+ const inputKvs = [];
1544
+ let witnessUtxoScript = null;
1545
+ while (pos < raw.length && raw[pos] !== 0) {
1546
+ const { bytes: kv, end, keyType, value } = readKeyValue(raw, pos);
1547
+ inputKvs.push(kv);
1548
+ if (keyType === 1 && value) {
1549
+ const scriptStart = 8;
1550
+ const scriptLen = value[scriptStart];
1551
+ witnessUtxoScript = value.slice(scriptStart + 1, scriptStart + 1 + scriptLen);
1552
+ }
1553
+ pos = end;
1554
+ }
1555
+ inputKvs.forEach((kv) => kv.forEach((b) => result.push(b)));
1556
+ if (witnessUtxoScript && witnessUtxoScript.length === 34 && witnessUtxoScript[0] === 81 && // OP_1
1557
+ witnessUtxoScript[1] === 32 && // PUSH 32
1558
+ inputDerivations[inputIdx]) {
1559
+ const xOnlyKey = witnessUtxoScript.slice(2, 34);
1560
+ const fullPath = inputDerivations[inputIdx].path;
1561
+ writeKv(result, new Uint8Array([23]), xOnlyKey);
1562
+ const pathComponents = fullPath.replace(/^m\//, "").split("/");
1563
+ const pathBuf = new Uint8Array(1 + 4 + pathComponents.length * 4);
1564
+ pathBuf[0] = 0;
1565
+ pathBuf.set(fpBytes, 1);
1566
+ for (let i = 0; i < pathComponents.length; i++) {
1567
+ const comp = pathComponents[i];
1568
+ const hardened = comp.endsWith("'");
1569
+ let val = parseInt(hardened ? comp.slice(0, -1) : comp, 10);
1570
+ if (hardened) val += 2147483648;
1571
+ const off = 5 + i * 4;
1572
+ pathBuf[off] = val & 255;
1573
+ pathBuf[off + 1] = val >>> 8 & 255;
1574
+ pathBuf[off + 2] = val >>> 16 & 255;
1575
+ pathBuf[off + 3] = val >>> 24 & 255;
1576
+ }
1577
+ const tapBipKey = new Uint8Array(1 + 32);
1578
+ tapBipKey[0] = 22;
1579
+ tapBipKey.set(xOnlyKey, 1);
1580
+ writeKv(result, tapBipKey, pathBuf);
1581
+ }
1582
+ result.push(raw[pos++]);
1583
+ }
1584
+ while (pos < raw.length) {
1585
+ result.push(raw[pos++]);
1586
+ }
1587
+ return (0, import_hwk_adapter_core4.bytesToHex)(new Uint8Array(result));
1588
+ }
1589
+
1590
+ // src/connector/chains/sol.ts
1591
+ var import_hwk_adapter_core5 = require("@onekeyfe/hwk-adapter-core");
1592
+
1593
+ // src/signer/SignerSol.ts
1594
+ var SignerSol = class {
1595
+ constructor(_sdk) {
1596
+ this._sdk = _sdk;
1597
+ }
1598
+ /**
1599
+ * Get the Solana address (base58-encoded Ed25519 public key) at the given derivation path.
1600
+ */
1601
+ async getAddress(derivationPath, options) {
1602
+ const action = this._sdk.getAddress(derivationPath, {
1603
+ checkOnDevice: options?.checkOnDevice ?? false
1604
+ });
1605
+ return deviceActionToPromise(action, this.onInteraction);
1606
+ }
1607
+ /**
1608
+ * Sign a Solana transaction.
1609
+ */
1610
+ async signTransaction(derivationPath, transaction, options) {
1611
+ const action = this._sdk.signTransaction(derivationPath, transaction, options);
1612
+ return deviceActionToPromise(action, this.onInteraction);
1613
+ }
1614
+ /**
1615
+ * Sign a message with the Solana app.
1616
+ * DMK returns { signature: string } for signMessage (unlike signTransaction which returns Uint8Array).
1617
+ */
1618
+ async signMessage(derivationPath, message, options) {
1619
+ const action = this._sdk.signMessage(derivationPath, message, options);
1620
+ return deviceActionToPromise(action, this.onInteraction);
1621
+ }
1622
+ };
1623
+
1624
+ // src/connector/chains/sol.ts
1625
+ async function solGetAddress(ctx, sessionId, params) {
1626
+ const solSigner = await _createSolSigner(ctx, sessionId);
1627
+ const path = normalizePath(params.path);
1628
+ try {
1629
+ const publicKey = await solSigner.getAddress(path, {
1630
+ checkOnDevice: params.showOnDevice ?? false
1631
+ });
1632
+ return { address: publicKey, path: params.path };
1633
+ } catch (err) {
1634
+ ctx.invalidateSession(sessionId);
1635
+ throw ctx.wrapError(err);
1636
+ }
1637
+ }
1638
+ async function solSignTransaction(ctx, sessionId, params) {
1639
+ const solSigner = await _createSolSigner(ctx, sessionId);
1640
+ const path = normalizePath(params.path);
1641
+ const txBytes = (0, import_hwk_adapter_core5.hexToBytes)(params.serializedTx);
1642
+ try {
1643
+ const result = await solSigner.signTransaction(path, txBytes);
1644
+ return { signature: (0, import_hwk_adapter_core5.bytesToHex)(result) };
1645
+ } catch (err) {
1646
+ ctx.invalidateSession(sessionId);
1647
+ throw ctx.wrapError(err);
1648
+ }
1649
+ }
1650
+ async function solSignMessage(ctx, sessionId, params) {
1651
+ const solSigner = await _createSolSigner(ctx, sessionId);
1652
+ const path = normalizePath(params.path);
1653
+ const messageBytes = (0, import_hwk_adapter_core5.hexToBytes)(params.message);
1654
+ try {
1655
+ const result = await solSigner.signMessage(path, messageBytes);
1656
+ return { signature: result.signature };
1657
+ } catch (err) {
1658
+ ctx.invalidateSession(sessionId);
1659
+ throw ctx.wrapError(err);
1660
+ }
1661
+ }
1662
+ async function _createSolSigner(ctx, sessionId) {
1663
+ const dmk = await ctx.getOrCreateDmk();
1664
+ const { SignerSolanaBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-solana");
1665
+ const sdkSigner = new SignerSolanaBuilder({ dmk, sessionId }).build();
1666
+ const signer = new SignerSol(sdkSigner);
1667
+ signer.onInteraction = (interaction) => {
1668
+ ctx.emit("ui-event", {
1669
+ type: interaction,
1670
+ payload: { sessionId }
1671
+ });
1672
+ };
1673
+ return signer;
1674
+ }
1675
+
1676
+ // src/connector/chains/tron.ts
1677
+ var import_hwk_adapter_core7 = require("@onekeyfe/hwk-adapter-core");
1678
+ var import_hw_app_trx = __toESM(require("@ledgerhq/hw-app-trx"));
1679
+
1680
+ // src/connector/chains/legacyAppRetry.ts
1681
+ var import_hwk_adapter_core6 = require("@onekeyfe/hwk-adapter-core");
1682
+
1683
+ // src/app/AppManager.ts
1684
+ var import_device_management_kit2 = require("@ledgerhq/device-management-kit");
1685
+ var APP_NAME_MAP = {
1686
+ ETH: "Ethereum",
1687
+ BTC: "Bitcoin",
1688
+ SOL: "Solana",
1689
+ TRX: "Tron",
1690
+ XRP: "XRP",
1691
+ ADA: "Cardano",
1692
+ DOT: "Polkadot",
1693
+ ATOM: "Cosmos"
1694
+ };
1695
+ var DASHBOARD_APP_NAME = "BOLOS";
1696
+ var AppManager = class {
1697
+ constructor(dmk, options) {
1698
+ this._dmk = dmk;
1699
+ this._waitMs = options?.waitMs ?? 1e3;
1700
+ this._maxRetries = options?.maxRetries ?? 10;
1701
+ }
1702
+ /**
1703
+ * Return the Ledger app name for a given chain ticker,
1704
+ * or undefined if the chain is not supported.
1705
+ */
1706
+ static getAppName(chain) {
1707
+ return APP_NAME_MAP[chain];
1708
+ }
1709
+ /**
1710
+ * Ensure the target app is open on the device identified by `sessionId`.
1711
+ *
1712
+ * Flow:
1713
+ * 1. Check the currently running app.
1714
+ * 2. If it is already the target, return immediately.
1715
+ * 3. If a different app is running (not dashboard), close it first.
1716
+ * 4. Open the target app.
1717
+ * 5. Poll until the device confirms the target app is running.
1718
+ */
1719
+ /**
1720
+ * @param onConfirmOnDevice Called after OpenAppCommand succeeds —
1721
+ * the device is now showing a confirmation prompt to the user.
1722
+ * NOT called if the app is already open or if the app is not installed.
1723
+ */
1724
+ async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
1725
+ const currentApp = await this._getCurrentApp(sessionId);
1726
+ if (currentApp === targetAppName) {
1727
+ return;
1728
+ }
1729
+ if (!this._isDashboard(currentApp)) {
1730
+ await this._closeCurrentApp(sessionId);
1731
+ await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
1732
+ }
1733
+ await this._openApp(sessionId, targetAppName);
1734
+ onConfirmOnDevice?.();
1735
+ await this._waitForApp(sessionId, targetAppName);
1736
+ }
1737
+ // ---------------------------------------------------------------------------
1738
+ // Private helpers
1739
+ // ---------------------------------------------------------------------------
1740
+ async _getCurrentApp(sessionId) {
1741
+ const result = await this._dmk.sendCommand({
1742
+ sessionId,
1743
+ command: new import_device_management_kit2.GetAppAndVersionCommand()
1744
+ });
1745
+ if ((0, import_device_management_kit2.isSuccessCommandResult)(result)) {
1746
+ return result.data.name;
1747
+ }
1748
+ throw new Error("Failed to get current app from device");
1749
+ }
1750
+ async _openApp(sessionId, appName) {
1751
+ const result = await this._dmk.sendCommand({
1752
+ sessionId,
1753
+ command: new import_device_management_kit2.OpenAppCommand({ appName })
1754
+ });
1755
+ if (!(0, import_device_management_kit2.isSuccessCommandResult)(result)) {
1756
+ const statusCode = result.statusCode;
1757
+ throw Object.assign(
1758
+ new Error(`Failed to open "${appName}": app may not be installed`),
1759
+ { _tag: "OpenAppCommandError", errorCode: String(statusCode ?? ""), statusCode }
1760
+ );
1761
+ }
1762
+ }
1763
+ async _closeCurrentApp(sessionId) {
1764
+ await this._dmk.sendCommand({
1765
+ sessionId,
1766
+ command: new import_device_management_kit2.CloseAppCommand()
1767
+ });
1768
+ }
1769
+ /**
1770
+ * Poll the device until the expected app is reported as running,
1771
+ * or throw after `_maxRetries` attempts.
1772
+ */
1773
+ async _waitForApp(sessionId, expectedAppName) {
1774
+ for (let i = 0; i < this._maxRetries; i++) {
1775
+ await this._wait();
1776
+ const current = await this._getCurrentApp(sessionId);
1777
+ if (current === expectedAppName) {
1778
+ return;
1779
+ }
1780
+ }
1781
+ throw new Error(
1782
+ `Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries`
1783
+ );
1784
+ }
1785
+ _isDashboard(appName) {
1786
+ return appName === DASHBOARD_APP_NAME;
1787
+ }
1788
+ _wait() {
1789
+ return new Promise((resolve) => setTimeout(resolve, this._waitMs));
1790
+ }
1791
+ };
1792
+
1793
+ // src/connector/chains/legacyAppRetry.ts
1794
+ var KNOWN_APP_CODES = {
1795
+ Tron: /* @__PURE__ */ new Set([
1796
+ 27013,
1797
+ // user denied
1798
+ 21781,
1799
+ // device locked
1800
+ 27274,
1801
+ // invalid BIP32 path
1802
+ 27275,
1803
+ 27276,
1804
+ 27277,
1805
+ // app-specific data errors
1806
+ 27264,
1807
+ // invalid data
1808
+ 27392,
1809
+ // wrong parameter
1810
+ 26368
1811
+ // wrong length
1812
+ ])
1813
+ };
1814
+ function isLegacyWrongAppError(err, appName) {
1815
+ if (isWrongAppError(err)) return true;
1816
+ const msg = err instanceof Error ? err.message : "";
1817
+ const match = msg.match(/0x([0-9a-fA-F]{4})/);
1818
+ if (!match) return false;
1819
+ const sw = parseInt(match[1], 16);
1820
+ const knownCodes = KNOWN_APP_CODES[appName];
1821
+ if (knownCodes?.has(sw)) return false;
1822
+ return true;
1823
+ }
1824
+ async function withLegacyAppRetry(ctx, sessionId, appName, action) {
1825
+ try {
1826
+ return await action(sessionId);
1827
+ } catch (err) {
1828
+ ctx.emit("ui-event", {
1829
+ type: import_hwk_adapter_core6.EConnectorInteraction.InteractionComplete,
1830
+ payload: { sessionId }
1831
+ });
1832
+ if (isLegacyWrongAppError(err, appName)) {
1833
+ const dmk = await ctx.getOrCreateDmk();
1834
+ const appManager = new AppManager(dmk);
1835
+ try {
1836
+ await appManager.ensureAppOpen(sessionId, appName, () => {
1837
+ ctx.emit("ui-event", {
1838
+ type: import_hwk_adapter_core6.EConnectorInteraction.ConfirmOpenApp,
1839
+ payload: { sessionId }
1840
+ });
1841
+ });
1842
+ } catch (switchErr) {
1843
+ ctx.emit("ui-event", {
1844
+ type: import_hwk_adapter_core6.EConnectorInteraction.InteractionComplete,
1845
+ payload: { sessionId }
1846
+ });
1847
+ throw ctx.wrapError(switchErr);
1848
+ }
1849
+ ctx.clearAllSigners();
1850
+ ctx.emit("ui-event", {
1851
+ type: import_hwk_adapter_core6.EConnectorInteraction.InteractionComplete,
1852
+ payload: { sessionId }
1853
+ });
1854
+ return await action(sessionId);
1855
+ }
1856
+ ctx.invalidateSession(sessionId);
1857
+ throw ctx.wrapError(err);
1858
+ }
1859
+ }
1860
+
1861
+ // src/transport/DmkTransport.ts
1862
+ var import_hw_transport = __toESM(require("@ledgerhq/hw-transport"));
1863
+ var DmkTransport = class extends import_hw_transport.default {
1864
+ constructor(dmk, sessionId) {
1865
+ super();
1866
+ this._dmk = dmk;
1867
+ this._sessionId = sessionId;
1868
+ }
1869
+ async exchange(apdu) {
1870
+ const response = await this._dmk.sendApdu({
1871
+ sessionId: this._sessionId,
1872
+ apdu: new Uint8Array(apdu)
1873
+ });
1874
+ const result = Buffer.alloc(response.data.length + 2);
1875
+ if (response.data.length > 0) {
1876
+ result.set(response.data, 0);
1877
+ }
1878
+ result.set(response.statusCode, response.data.length);
1879
+ return result;
1880
+ }
1881
+ async close() {
1882
+ }
1883
+ };
1884
+
1885
+ // src/connector/chains/tron.ts
1886
+ async function tronGetAddress(ctx, sessionId, params) {
1887
+ const path = normalizePath(params.path);
1888
+ return withLegacyAppRetry(ctx, sessionId, "Tron", async (sid) => {
1889
+ const trx = await _createTrx(ctx, sid);
1890
+ if (params.showOnDevice) {
1891
+ ctx.emit("ui-event", {
1892
+ type: import_hwk_adapter_core7.EConnectorInteraction.ConfirmOnDevice,
1893
+ payload: { sessionId: sid }
1894
+ });
1895
+ }
1896
+ const result = await trx.getAddress(path, params.showOnDevice ?? false);
1897
+ ctx.emit("ui-event", {
1898
+ type: import_hwk_adapter_core7.EConnectorInteraction.InteractionComplete,
1899
+ payload: { sessionId: sid }
1900
+ });
1901
+ return { address: result.address, publicKey: result.publicKey, path: params.path };
1902
+ });
1903
+ }
1904
+ async function tronSignTransaction(ctx, sessionId, params) {
1905
+ if (!params.rawTxHex) {
1906
+ throw Object.assign(
1907
+ new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
1908
+ { code: import_hwk_adapter_core7.HardwareErrorCode.InvalidParams }
1909
+ );
1910
+ }
1911
+ const path = normalizePath(params.path);
1912
+ return withLegacyAppRetry(ctx, sessionId, "Tron", async (sid) => {
1913
+ const trx = await _createTrx(ctx, sid);
1914
+ ctx.emit("ui-event", {
1915
+ type: import_hwk_adapter_core7.EConnectorInteraction.ConfirmOnDevice,
1916
+ payload: { sessionId: sid }
1917
+ });
1918
+ const signature = await trx.signTransaction(path, params.rawTxHex, []);
1919
+ ctx.emit("ui-event", {
1920
+ type: import_hwk_adapter_core7.EConnectorInteraction.InteractionComplete,
1921
+ payload: { sessionId: sid }
1922
+ });
1923
+ return { signature };
1924
+ });
1925
+ }
1926
+ async function tronSignMessage(ctx, sessionId, params) {
1927
+ const path = normalizePath(params.path);
1928
+ return withLegacyAppRetry(ctx, sessionId, "Tron", async (sid) => {
1929
+ const trx = await _createTrx(ctx, sid);
1930
+ ctx.emit("ui-event", {
1931
+ type: import_hwk_adapter_core7.EConnectorInteraction.ConfirmOnDevice,
1932
+ payload: { sessionId: sid }
1933
+ });
1934
+ const signature = await trx.signPersonalMessage(path, params.messageHex);
1935
+ ctx.emit("ui-event", {
1936
+ type: import_hwk_adapter_core7.EConnectorInteraction.InteractionComplete,
1937
+ payload: { sessionId: sid }
1938
+ });
1939
+ return { signature };
1940
+ });
1941
+ }
1942
+ async function _createTrx(ctx, sessionId) {
1943
+ const dmk = await ctx.getOrCreateDmk();
1944
+ return new import_hw_app_trx.default(new DmkTransport(dmk, sessionId));
1945
+ }
1946
+
1947
+ // src/connector/LedgerConnectorBase.ts
1948
+ async function defaultLedgerKitImporter(pkg) {
1949
+ switch (pkg) {
1950
+ case "@ledgerhq/device-management-kit":
1951
+ return import("@ledgerhq/device-management-kit");
1952
+ case "@ledgerhq/device-signer-kit-ethereum":
1953
+ return import("@ledgerhq/device-signer-kit-ethereum");
1954
+ case "@ledgerhq/device-signer-kit-bitcoin":
1955
+ return import("@ledgerhq/device-signer-kit-bitcoin");
1956
+ case "@ledgerhq/device-signer-kit-solana":
1957
+ return import("@ledgerhq/device-signer-kit-solana");
1958
+ default:
1959
+ throw new Error(`Unknown Ledger kit package: ${pkg}`);
1960
+ }
1961
+ }
1962
+ var LedgerConnectorBase = class {
1963
+ constructor(createTransport, options) {
1964
+ this._deviceManager = null;
1965
+ this._signerManager = null;
1966
+ this._dmk = null;
1967
+ this._eventHandlers = /* @__PURE__ */ new Map();
1968
+ // ---------------------------------------------------------------------------
1969
+ // ConnectId <-> DMK path mapping
1970
+ //
1971
+ // DMK uses internal paths (BLE MAC, USB UUID) that may change across sessions.
1972
+ // _resolveConnectId() maps these to stable external IDs (BLE: "A58F", USB: same).
1973
+ // This bidirectional map is the SINGLE SOURCE OF TRUTH for all connectId usage.
1974
+ // ---------------------------------------------------------------------------
1975
+ this._connectIdToPath = /* @__PURE__ */ new Map();
1976
+ // "A58F" -> "D5:75:7D:4B:51:E8"
1977
+ this._pathToConnectId = /* @__PURE__ */ new Map();
1978
+ this._createTransport = createTransport;
1979
+ this._connectionType = options?.connectionType ?? "usb";
1980
+ this._providedDmk = options?.dmk;
1981
+ this._importLedgerKit = options?.importLedgerKit ?? defaultLedgerKitImporter;
1982
+ if (this._providedDmk) {
1983
+ this._initManagers(this._providedDmk);
1984
+ }
1985
+ this._ctx = {
1986
+ emit: (event, data) => this._emit(event, data),
1987
+ invalidateSession: (sid) => this._invalidateSession(sid),
1988
+ wrapError: (err) => this._wrapError(err),
1989
+ getOrCreateDmk: () => this._getOrCreateDmk(),
1990
+ getDeviceManager: () => this._getDeviceManager(),
1991
+ getSignerManager: () => this._getSignerManager(),
1992
+ clearAllSigners: () => this._signerManager?.clearAll(),
1993
+ replaceSession: (oldSid, newSid) => this._replaceSession(oldSid, newSid),
1994
+ importLedgerKit: this._importLedgerKit
1995
+ };
1996
+ }
1997
+ // "D5:75:7D:4B:51:E8" -> "A58F"
1998
+ /** Register a connectId <-> path mapping from a device descriptor. */
1999
+ _registerDeviceId(descriptor) {
2000
+ const connectId = this._resolveConnectId(descriptor);
2001
+ this._connectIdToPath.set(connectId, descriptor.path);
2002
+ this._pathToConnectId.set(descriptor.path, connectId);
2003
+ return connectId;
2004
+ }
2005
+ /** Get DMK path from external connectId. Falls back to connectId itself. */
2006
+ _getPathForConnectId(connectId) {
2007
+ return this._connectIdToPath.get(connectId) ?? connectId;
2008
+ }
2009
+ /** Get external connectId from DMK path. Falls back to path itself. */
2010
+ _getConnectIdForPath(path) {
2011
+ return this._pathToConnectId.get(path) ?? path;
2012
+ }
2013
+ // ---------------------------------------------------------------------------
2014
+ // Protected — hooks for subclasses
2015
+ // ---------------------------------------------------------------------------
2016
+ /**
2017
+ * Resolve the connectId for a discovered device descriptor.
2018
+ * Default: use the DMK path (ephemeral UUID).
2019
+ * Override in subclasses to extract stable identifiers (e.g. BLE hex ID).
2020
+ */
2021
+ _resolveConnectId(descriptor) {
2022
+ return descriptor.path;
2023
+ }
2024
+ // ---------------------------------------------------------------------------
2025
+ // IConnector -- Device discovery
2026
+ // ---------------------------------------------------------------------------
2027
+ async searchDevices() {
2028
+ const dm = await this._getDeviceManager();
2029
+ let descriptors = await dm.enumerate();
2030
+ if (descriptors.length === 0) {
2031
+ try {
2032
+ await dm.requestDevice();
2033
+ } catch {
2034
+ }
2035
+ descriptors = await dm.enumerate();
2036
+ }
2037
+ const result = descriptors.map((d) => {
2038
+ const connectId = this._registerDeviceId(d);
2039
+ return {
2040
+ connectId,
2041
+ deviceId: d.path,
2042
+ name: d.name || d.type || "Ledger",
2043
+ model: d.type
2044
+ };
2045
+ });
2046
+ return result;
2047
+ }
2048
+ // ---------------------------------------------------------------------------
2049
+ // IConnector -- Connection
2050
+ // ---------------------------------------------------------------------------
2051
+ async connect(deviceId) {
2052
+ const dm = await this._getDeviceManager();
2053
+ await this.searchDevices();
2054
+ const dmkPath = deviceId ? this._getPathForConnectId(deviceId) : void 0;
2055
+ let targetPath = dmkPath;
2056
+ if (!targetPath) {
2057
+ const descriptors = await dm.enumerate();
2058
+ if (descriptors.length === 0) {
2059
+ throw new Error(
2060
+ `No Ledger device found. Make sure the device is connected${this._connectionType === "ble" ? " nearby with Bluetooth enabled" : " via USB"} and unlocked.`
2061
+ );
2062
+ }
2063
+ targetPath = descriptors[0].path;
2064
+ }
2065
+ const externalConnectId = this._getConnectIdForPath(targetPath);
2066
+ const doConnect = async (path) => {
2067
+ const sessionId = await dm.connect(path);
2068
+ const session = {
2069
+ sessionId,
2070
+ deviceInfo: {
2071
+ vendor: "ledger",
2072
+ model: "unknown",
2073
+ firmwareVersion: "unknown",
2074
+ deviceId: path,
2075
+ connectId: externalConnectId,
2076
+ connectionType: this._connectionType,
2077
+ capabilities: { persistentDeviceIdentity: false }
2078
+ }
2079
+ };
2080
+ this._emit("device-connect", {
2081
+ device: {
2082
+ connectId: externalConnectId,
2083
+ deviceId: path,
2084
+ name: "Ledger"
2085
+ }
2086
+ });
2087
+ return session;
2088
+ };
2089
+ try {
2090
+ return await doConnect(targetPath);
2091
+ } catch {
2092
+ this._resetSignersAndSessions();
2093
+ const dm2 = await this._getDeviceManager();
2094
+ await this.searchDevices();
2095
+ const retryPath = this._getPathForConnectId(externalConnectId);
2096
+ if (!retryPath || retryPath === externalConnectId) {
2097
+ const descriptors = await dm2.enumerate();
2098
+ if (descriptors.length === 0) {
2099
+ throw new Error(
2100
+ `No Ledger device found after retry. Make sure the device is connected${this._connectionType === "ble" ? " nearby with Bluetooth enabled" : " via USB"} and unlocked.`
2101
+ );
2102
+ }
2103
+ return doConnect(descriptors[0].path);
2104
+ }
2105
+ return doConnect(retryPath);
2106
+ }
2107
+ }
2108
+ async disconnect(sessionId) {
2109
+ if (!this._deviceManager) return;
2110
+ const deviceId = this._deviceManager.getDeviceId(sessionId);
2111
+ this._signerManager?.invalidate(sessionId);
2112
+ await this._deviceManager.disconnect(sessionId);
2113
+ if (deviceId) {
2114
+ this._emit("device-disconnect", { connectId: deviceId });
2115
+ }
2116
+ }
2117
+ // ---------------------------------------------------------------------------
2118
+ // IConnector -- Method dispatch
2119
+ // ---------------------------------------------------------------------------
2120
+ async call(sessionId, method, params) {
2121
+ console.log("[DMK] call:", method, JSON.stringify(params));
2122
+ switch (method) {
2123
+ // EVM
2124
+ case "evmGetAddress":
2125
+ return evmGetAddress(this._ctx, sessionId, params);
2126
+ case "evmSignTransaction":
2127
+ return evmSignTransaction(this._ctx, sessionId, params);
2128
+ case "evmSignMessage":
2129
+ return evmSignMessage(this._ctx, sessionId, params);
2130
+ case "evmSignTypedData":
2131
+ return evmSignTypedData(this._ctx, sessionId, params);
2132
+ // BTC
2133
+ case "btcGetAddress":
2134
+ return btcGetAddress(this._ctx, sessionId, params);
2135
+ case "btcGetPublicKey":
2136
+ return btcGetPublicKey(this._ctx, sessionId, params);
2137
+ case "btcSignTransaction":
2138
+ return btcSignTransaction(this._ctx, sessionId, params);
2139
+ case "btcSignMessage":
2140
+ return btcSignMessage(this._ctx, sessionId, params);
2141
+ case "btcGetMasterFingerprint":
2142
+ return btcGetMasterFingerprint(
2143
+ this._ctx,
2144
+ sessionId,
2145
+ params
2146
+ );
2147
+ // SOL
2148
+ case "solGetAddress":
2149
+ return solGetAddress(this._ctx, sessionId, params);
2150
+ case "solSignTransaction":
2151
+ return solSignTransaction(this._ctx, sessionId, params);
2152
+ case "solSignMessage":
2153
+ return solSignMessage(this._ctx, sessionId, params);
2154
+ // TRON
2155
+ case "tronGetAddress":
2156
+ return tronGetAddress(this._ctx, sessionId, params);
2157
+ case "tronSignTransaction":
2158
+ return tronSignTransaction(this._ctx, sessionId, params);
2159
+ case "tronSignMessage":
2160
+ return tronSignMessage(this._ctx, sessionId, params);
2161
+ default:
2162
+ throw new Error(`LedgerConnector: unknown method "${method}"`);
2163
+ }
2164
+ }
2165
+ async cancel(_sessionId) {
2166
+ }
2167
+ uiResponse(_response) {
2168
+ }
2169
+ // ---------------------------------------------------------------------------
2170
+ // IConnector -- Events
2171
+ // ---------------------------------------------------------------------------
2172
+ on(event, handler) {
2173
+ if (!this._eventHandlers.has(event)) {
2174
+ this._eventHandlers.set(event, /* @__PURE__ */ new Set());
2175
+ }
2176
+ this._eventHandlers.get(event).add(handler);
2177
+ }
2178
+ off(event, handler) {
2179
+ this._eventHandlers.get(event)?.delete(handler);
2180
+ }
2181
+ // ---------------------------------------------------------------------------
2182
+ // IConnector -- Reset
2183
+ // ---------------------------------------------------------------------------
2184
+ reset() {
2185
+ this._resetAll();
2186
+ }
2187
+ // ---------------------------------------------------------------------------
2188
+ // Private -- DMK / Manager lifecycle
2189
+ // ---------------------------------------------------------------------------
2190
+ /**
2191
+ * Lazily create or return the DMK instance.
2192
+ * If a DMK was provided via constructor, it is used directly.
2193
+ * Otherwise, one is created via the transport factory.
2194
+ */
2195
+ async _getOrCreateDmk() {
2196
+ console.log(
2197
+ "[DMK] _getOrCreateDmk called, _dmk exists:",
2198
+ !!this._dmk,
2199
+ "_providedDmk exists:",
2200
+ !!this._providedDmk,
2201
+ "stack:",
2202
+ new Error().stack?.split("\n").slice(1, 4).join(" | ")
2203
+ );
2204
+ if (this._dmk) return this._dmk;
2205
+ if (this._providedDmk) {
2206
+ this._dmk = this._providedDmk;
2207
+ return this._dmk;
2208
+ }
2209
+ const { DeviceManagementKitBuilder } = await this._importLedgerKit(
2210
+ "@ledgerhq/device-management-kit"
2211
+ );
2212
+ const transportFactory = await this._createTransport();
2213
+ console.log(
2214
+ "[DMK] _getOrCreateDmk: transportFactory type:",
2215
+ typeof transportFactory,
2216
+ "value:",
2217
+ String(transportFactory).substring(0, 80)
2218
+ );
2219
+ const dmk = new DeviceManagementKitBuilder().addTransport(transportFactory).build();
2220
+ this._dmk = dmk;
2221
+ console.log(
2222
+ "[DMK] _getOrCreateDmk: DMK created, methods:",
2223
+ Object.getOwnPropertyNames(Object.getPrototypeOf(dmk)).join(", ")
2224
+ );
2225
+ return dmk;
2226
+ }
2227
+ _initManagers(dmk) {
2228
+ this._dmk = dmk;
2229
+ this._deviceManager = new LedgerDeviceManager(dmk);
2230
+ const importKit = this._importLedgerKit;
2231
+ this._signerManager = new SignerManager(dmk, async (args) => {
2232
+ const mod = await importKit("@ledgerhq/device-signer-kit-ethereum");
2233
+ return new mod.SignerEthBuilder(args);
2234
+ });
2235
+ }
2236
+ async _getDeviceManager() {
2237
+ if (this._deviceManager) return this._deviceManager;
2238
+ const dmk = await this._getOrCreateDmk();
2239
+ this._initManagers(dmk);
2240
+ return this._deviceManager;
2241
+ }
2242
+ async _getSignerManager() {
2243
+ if (!this._signerManager) {
2244
+ const dmk = await this._getOrCreateDmk();
2245
+ this._initManagers(dmk);
2246
+ }
2247
+ return this._signerManager;
2248
+ }
2249
+ _invalidateSession(sessionId) {
2250
+ this._signerManager?.invalidate(sessionId);
2251
+ }
2252
+ /**
2253
+ * Replace an old session with a new one after app switch.
2254
+ * Updates the connectId→path mapping so subsequent calls() use the new session,
2255
+ * and emits device-connect so the adapter updates its _sessions Map.
2256
+ */
2257
+ _replaceSession(oldSessionId, _newSessionId) {
2258
+ const dm = this._deviceManager;
2259
+ if (!dm) return;
2260
+ const oldDeviceId = dm.getDeviceId(oldSessionId);
2261
+ const connectId = oldDeviceId ? this._pathToConnectId.get(oldDeviceId) : void 0;
2262
+ this._signerManager?.invalidate(oldSessionId);
2263
+ if (connectId) {
2264
+ this._emit("device-connect", {
2265
+ device: {
2266
+ connectId,
2267
+ deviceId: connectId,
2268
+ name: "Ledger"
2269
+ }
2270
+ });
2271
+ }
2272
+ }
2273
+ /**
2274
+ * Light reset: clear signer/session state but keep DMK, BLE scan, and ID mapping alive.
2275
+ * Used by connect() retry — we want to re-discover with the same transport.
2276
+ */
2277
+ _resetSignersAndSessions() {
2278
+ console.log(
2279
+ "[DMK] _resetSignersAndSessions called, stack:",
2280
+ new Error().stack?.split("\n").slice(1, 3).join(" | ")
2281
+ );
2282
+ this._signerManager?.clearAll();
2283
+ this._signerManager = null;
2284
+ this._deviceManager = null;
2285
+ }
2286
+ _resetAll() {
2287
+ console.log(
2288
+ "[DMK] _resetAll called, stack:",
2289
+ new Error().stack?.split("\n").slice(1, 3).join(" | ")
2290
+ );
2291
+ this._signerManager?.clearAll();
2292
+ this._deviceManager?.dispose();
2293
+ this._deviceManager = null;
2294
+ this._signerManager = null;
2295
+ this._dmk = null;
2296
+ this._connectIdToPath.clear();
2297
+ this._pathToConnectId.clear();
2298
+ }
2299
+ // ---------------------------------------------------------------------------
2300
+ // Private -- Events
2301
+ // ---------------------------------------------------------------------------
2302
+ _emit(event, data) {
2303
+ const handlers = this._eventHandlers.get(event);
2304
+ if (handlers) {
2305
+ for (const handler of handlers) {
2306
+ try {
2307
+ handler(data);
2308
+ } catch {
2309
+ }
2310
+ }
2311
+ }
2312
+ }
2313
+ // ---------------------------------------------------------------------------
2314
+ // Private -- Error handling
2315
+ // ---------------------------------------------------------------------------
2316
+ _wrapError(err) {
2317
+ const mapped = mapLedgerError(err);
2318
+ const error = new Error(mapped.message);
2319
+ Object.assign(error, { code: mapped.code });
2320
+ return error;
2321
+ }
2322
+ };
2323
+ // Annotate the CommonJS export names for ESM import in node:
2324
+ 0 && (module.exports = {
2325
+ AppManager,
2326
+ DmkTransport,
2327
+ LedgerAdapter,
2328
+ LedgerConnectorBase,
2329
+ LedgerDeviceManager,
2330
+ SignerBtc,
2331
+ SignerEth,
2332
+ SignerManager,
2333
+ SignerSol,
2334
+ deviceActionToPromise,
2335
+ isDeviceLockedError,
2336
+ mapLedgerError
2337
+ });
2338
+ //# sourceMappingURL=index.js.map