@onekeyfe/hwk-ledger-adapter 1.2.5-alpha.1 → 1.2.5-alpha.3

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 CHANGED
@@ -39,7 +39,6 @@ __export(index_exports, {
39
39
  SignerEth: () => SignerEth,
40
40
  SignerManager: () => SignerManager,
41
41
  SignerSol: () => SignerSol,
42
- SignerZcash: () => SignerZcash,
43
42
  debugLog: () => debugLog,
44
43
  deviceActionToPromise: () => deviceActionToPromise,
45
44
  isDeviceLockedError: () => isDeviceLockedError,
@@ -58,14 +57,17 @@ var import_hwk_adapter_core3 = require("@onekeyfe/hwk-adapter-core");
58
57
 
59
58
  // src/errors.ts
60
59
  var import_hwk_adapter_core = require("@onekeyfe/hwk-adapter-core");
61
- function ledgerFailure(code, error, appName, tag, params, origin, recovery) {
60
+ var MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE = "Multiple Ledger USB devices are connected. Please connect only one Ledger device and try again.";
61
+ function createMultipleUsbLedgerDevicesError() {
62
+ return Object.assign(new Error(MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE), {
63
+ code: import_hwk_adapter_core.HardwareErrorCode.DeviceOneDeviceOnly
64
+ });
65
+ }
66
+ function ledgerFailure(code, error, appName, tag, params) {
62
67
  const payload = { error, code };
63
68
  if (appName !== void 0) payload.appName = appName;
64
69
  if (tag !== void 0) payload._tag = tag;
65
70
  if (params !== void 0) payload.params = params;
66
- const resolvedOrigin = origin ?? (0, import_hwk_adapter_core.defaultOriginForCode)(code);
67
- if (resolvedOrigin !== void 0) payload.origin = resolvedOrigin;
68
- payload.recovery = recovery ?? (0, import_hwk_adapter_core.defaultRecoveryForCode)(code);
69
71
  return { success: false, payload };
70
72
  }
71
73
  var LOCKED_ERROR_CODES = /* @__PURE__ */ new Set(["5515", "21781", "6982", "27010", "5303", "21251"]);
@@ -206,10 +208,7 @@ var ERROR_TAG = {
206
208
  UnknownDevice: "UnknownDeviceError",
207
209
  DeviceSessionRefresher: "DeviceSessionRefresherError",
208
210
  DeviceNotInitialized: "DeviceNotInitializedError",
209
- // DMK class OpeningConnectionError carries `_tag` "ConnectionOpeningError"; both
210
- // spellings are kept so a DMK release that aligns them still classifies.
211
- OpeningConnection: "ConnectionOpeningError",
212
- OpeningConnectionLegacy: "OpeningConnectionError",
211
+ OpeningConnection: "OpeningConnectionError",
213
212
  DeviceDisconnectedBeforeSendingApdu: "DeviceDisconnectedBeforeSendingApdu",
214
213
  DeviceDisconnectedWhileSending: "DeviceDisconnectedWhileSendingError",
215
214
  Disconnect: "DisconnectError",
@@ -222,16 +221,7 @@ var ERROR_TAG = {
222
221
  // DMK remote-network failures (manager-api HTTP / secure-channel WS).
223
222
  WebSocketConnection: "WebSocketConnectionError",
224
223
  HttpFetch: "FetchError",
225
- NetworkDA: "NetworkDAError",
226
- InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError",
227
- ApplicationsMetadataTask: "GetApplicationsMetadataTaskError",
228
- // DMK OS device actions. SecureChannelError is what mapInstallDAErrors() leaves
229
- // after splitting out device answers, i.e. the relay itself broke.
230
- SecureChannel: "SecureChannelError",
231
- RefusedByUserDA: "RefusedByUserDAError",
232
- AppAlreadyInstalledDA: "AppAlreadyInstalledDAError",
233
- OutOfMemoryDA: "OutOfMemoryDAError",
234
- DeviceNotOnboarded: "DeviceNotOnboardedError"
224
+ InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError"
235
225
  };
236
226
  function isDeviceLockedError(err) {
237
227
  if (!err || typeof err !== "object") return false;
@@ -261,6 +251,7 @@ function isBlePairingFailureError(err) {
261
251
  return false;
262
252
  }
263
253
  var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
254
+ ERROR_TAG.DeviceLocked,
264
255
  ERROR_TAG.DeviceNotAdvertising,
265
256
  ERROR_TAG.BlePairingTimeout,
266
257
  ERROR_TAG.BleGattBondingFailed,
@@ -272,7 +263,6 @@ var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
272
263
  ERROR_TAG.DeviceSessionRefresher,
273
264
  ERROR_TAG.DeviceNotInitialized,
274
265
  ERROR_TAG.OpeningConnection,
275
- ERROR_TAG.OpeningConnectionLegacy,
276
266
  ERROR_TAG.DeviceDisconnectedBeforeSendingApdu,
277
267
  ERROR_TAG.DeviceDisconnectedWhileSending,
278
268
  ERROR_TAG.Disconnect,
@@ -287,10 +277,7 @@ var DEVICE_NOT_FOUND_TAGS = /* @__PURE__ */ new Set([
287
277
  // Map to DeviceNotFound so non-BLE-direct paths get a sensible error code.
288
278
  ERROR_TAG.DeviceNotInDiscoveryCache
289
279
  ]);
290
- var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([
291
- ERROR_TAG.OpeningConnection,
292
- ERROR_TAG.OpeningConnectionLegacy
293
- ]);
280
+ var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.OpeningConnection]);
294
281
  var DEVICE_DISCONNECTED_TAGS = /* @__PURE__ */ new Set([
295
282
  ERROR_TAG.DeviceNotRecognized,
296
283
  ERROR_TAG.DeviceSessionNotFound,
@@ -302,18 +289,17 @@ var DEVICE_DISCONNECTED_TAGS = /* @__PURE__ */ new Set([
302
289
  ERROR_TAG.WebHIDDisconnect
303
290
  ]);
304
291
  function isConnectionLevelError(err) {
305
- return hasErrorTag(err, CONNECTION_LEVEL_TAGS);
292
+ if (!err || typeof err !== "object") return false;
293
+ const tag = err._tag;
294
+ if (tag && CONNECTION_LEVEL_TAGS.has(tag)) return true;
295
+ const e = err;
296
+ if (e.originalError != null && isConnectionLevelError(e.originalError)) return true;
297
+ if (e.error != null && e._tag && isConnectionLevelError(e.error)) return true;
298
+ return false;
306
299
  }
307
300
  function isKnownConnectionTag(tag) {
308
301
  return typeof tag === "string" && CONNECTION_LEVEL_TAGS.has(tag);
309
302
  }
310
- var CONNECTION_OPENING_TAGS = /* @__PURE__ */ new Set([
311
- ERROR_TAG.OpeningConnection,
312
- ERROR_TAG.OpeningConnectionLegacy
313
- ]);
314
- function isConnectionOpeningTag(tag) {
315
- return typeof tag === "string" && CONNECTION_OPENING_TAGS.has(tag);
316
- }
317
303
  function hasStatusCode(err, codeSet) {
318
304
  if (!err || typeof err !== "object") return false;
319
305
  const e = err;
@@ -349,28 +335,28 @@ function hasInvalidArgumentCode(err) {
349
335
  if (e.error != null && e._tag && hasInvalidArgumentCode(e.error)) return true;
350
336
  return false;
351
337
  }
352
- function hasErrorTag(err, tags) {
338
+ function isDeviceNotFoundError(err) {
353
339
  if (!err || typeof err !== "object") return false;
340
+ const tag = err._tag;
341
+ if (tag && DEVICE_NOT_FOUND_TAGS.has(tag)) return true;
354
342
  const e = err;
355
- if (typeof e._tag === "string" && tags.has(e._tag)) return true;
356
- if (e.originalError != null && hasErrorTag(e.originalError, tags)) return true;
357
- if (e.error != null && e._tag && hasErrorTag(e.error, tags)) return true;
343
+ if (e.originalError != null && isDeviceNotFoundError(e.originalError)) return true;
344
+ if (e.error != null && e._tag && isDeviceNotFoundError(e.error)) return true;
358
345
  return false;
359
346
  }
360
- function isDeviceNotFoundError(err) {
361
- return hasErrorTag(err, DEVICE_NOT_FOUND_TAGS);
362
- }
363
347
  function isDeviceBusyError(err) {
364
- return hasErrorTag(err, DEVICE_BUSY_TAGS);
348
+ if (!err || typeof err !== "object") return false;
349
+ const tag = err._tag;
350
+ if (tag && DEVICE_BUSY_TAGS.has(tag)) return true;
351
+ const e = err;
352
+ if (e.originalError != null && isDeviceBusyError(e.originalError)) return true;
353
+ if (e.error != null && e._tag && isDeviceBusyError(e.error)) return true;
354
+ return false;
365
355
  }
366
- var USER_REJECTED_TAGS = /* @__PURE__ */ new Set([
367
- ERROR_TAG.UserRefusedOnDevice,
368
- ERROR_TAG.RefusedByUserDA
369
- ]);
370
356
  function isUserRejectedError(err) {
371
357
  if (!err || typeof err !== "object") return false;
372
358
  const e = err;
373
- if (hasErrorTag(err, USER_REJECTED_TAGS)) return true;
359
+ if (e._tag === ERROR_TAG.UserRefusedOnDevice) return true;
374
360
  if (typeof e.message === "string" && /denied|rejected|refused/i.test(e.message)) return true;
375
361
  if (hasStatusCode(err, USER_REJECTED_CODES)) return true;
376
362
  return false;
@@ -401,33 +387,15 @@ function isAppNotInstalledError(err) {
401
387
  if (hasStatusCode(err, APP_NOT_INSTALLED_CODES)) return true;
402
388
  return false;
403
389
  }
404
- var OUT_OF_MEMORY_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.OutOfMemoryDA]);
405
390
  function isOutOfMemoryError(err) {
406
- return hasErrorTag(err, OUT_OF_MEMORY_TAGS);
407
- }
408
- var APP_ALREADY_INSTALLED_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.AppAlreadyInstalledDA]);
409
- function isAppAlreadyInstalledError(err) {
410
- return hasErrorTag(err, APP_ALREADY_INSTALLED_TAGS);
411
- }
412
- var DEVICE_NOT_ONBOARDED_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.DeviceNotOnboarded]);
413
- function isDeviceNotOnboardedError(err) {
414
- return hasErrorTag(err, DEVICE_NOT_ONBOARDED_TAGS);
415
- }
416
- var FIRMWARE_METADATA_TAGS = /* @__PURE__ */ new Set([
417
- ERROR_TAG.InvalidFirmwareMetadataResponse,
418
- ERROR_TAG.ApplicationsMetadataTask
419
- ]);
420
- function isFirmwareMetadataError(err) {
421
- return hasErrorTag(err, FIRMWARE_METADATA_TAGS);
422
- }
423
- var SECURE_CHANNEL_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.SecureChannel]);
424
- function isSecureChannelError(err) {
425
- return hasErrorTag(err, SECURE_CHANNEL_TAGS);
391
+ if (!err || typeof err !== "object") return false;
392
+ const e = err;
393
+ return e._tag === "OutOfMemoryDAError";
426
394
  }
427
395
  var NETWORK_ERROR_TAGS = /* @__PURE__ */ new Set([
428
396
  ERROR_TAG.WebSocketConnection,
429
397
  ERROR_TAG.HttpFetch,
430
- ERROR_TAG.NetworkDA
398
+ ERROR_TAG.InvalidFirmwareMetadataResponse
431
399
  ]);
432
400
  function isNetworkError(err) {
433
401
  if (!err || typeof err !== "object") return false;
@@ -477,34 +445,23 @@ function isTransportStuck(err) {
477
445
  function isStuckAppStateError(err) {
478
446
  return isAppStuckByApdu(err) || isTransportStuck(err);
479
447
  }
480
- var DMK_PLACEHOLDER_MESSAGE = "Unknown error.";
481
- function nestedErrorMessage(nested) {
482
- if (!nested || typeof nested !== "object") return void 0;
483
- const { message } = nested;
484
- if (typeof message !== "string") return void 0;
485
- const trimmed = message.trim();
486
- if (!trimmed || trimmed === DMK_PLACEHOLDER_MESSAGE) return void 0;
487
- return message;
488
- }
489
448
  function mapLedgerError(err, opts) {
490
449
  let originalMessage = "Unknown Ledger error";
491
450
  if (err instanceof Error) {
492
451
  originalMessage = err.message;
493
452
  } else if (err && typeof err === "object") {
494
453
  const e = err;
495
- originalMessage = String(
496
- e.message ?? nestedErrorMessage(e.originalError) ?? e._tag ?? e.type ?? JSON.stringify(err)
497
- );
454
+ originalMessage = String(e.message ?? e._tag ?? e.type ?? JSON.stringify(err));
498
455
  }
499
456
  let code;
500
457
  if (isDeviceLockedError(err)) {
501
458
  code = import_hwk_adapter_core.HardwareErrorCode.DeviceLocked;
502
459
  } else if (isDeviceNotAdvertisingError(err) || isDeviceNotFoundError(err)) {
503
460
  code = import_hwk_adapter_core.HardwareErrorCode.DeviceNotFound;
504
- } else if (isBlePairingFailureError(err)) {
505
- code = import_hwk_adapter_core.HardwareErrorCode.BlePairingTimeout;
506
461
  } else if (isDeviceBusyError(err)) {
507
462
  code = import_hwk_adapter_core.HardwareErrorCode.DeviceBusy;
463
+ } else if (isBlePairingFailureError(err)) {
464
+ code = import_hwk_adapter_core.HardwareErrorCode.BlePairingTimeout;
508
465
  } else if (isUserAbortedError(err)) {
509
466
  code = import_hwk_adapter_core.HardwareErrorCode.UserAborted;
510
467
  } else if (isUserRejectedError(err)) {
@@ -513,18 +470,10 @@ function mapLedgerError(err, opts) {
513
470
  code = import_hwk_adapter_core.HardwareErrorCode.WrongApp;
514
471
  } else if (isAppNotInstalledError(err)) {
515
472
  code = import_hwk_adapter_core.HardwareErrorCode.AppNotInstalled;
516
- } else if (isAppAlreadyInstalledError(err)) {
517
- code = import_hwk_adapter_core.HardwareErrorCode.AppAlreadyInstalled;
518
473
  } else if (isOutOfMemoryError(err)) {
519
474
  code = import_hwk_adapter_core.HardwareErrorCode.DeviceOutOfMemory;
520
- } else if (isDeviceNotOnboardedError(err)) {
521
- code = import_hwk_adapter_core.HardwareErrorCode.DeviceNotInitialized;
522
- } else if (isFirmwareMetadataError(err)) {
523
- code = import_hwk_adapter_core.HardwareErrorCode.LedgerFirmwareMetadataError;
524
475
  } else if (isNetworkError(err)) {
525
476
  code = import_hwk_adapter_core.HardwareErrorCode.NetworkError;
526
- } else if (isSecureChannelError(err)) {
527
- code = import_hwk_adapter_core.HardwareErrorCode.LedgerSecureChannelError;
528
477
  } else if (isDeviceDisconnectedError(err)) {
529
478
  code = import_hwk_adapter_core.HardwareErrorCode.DeviceDisconnected;
530
479
  } else if (isTimeoutError(err)) {
@@ -537,21 +486,7 @@ function mapLedgerError(err, opts) {
537
486
  }
538
487
  const errAppName = err && typeof err === "object" ? err.appName : void 0;
539
488
  const appName = errAppName ?? opts?.defaultAppName;
540
- return {
541
- code,
542
- message: (0, import_hwk_adapter_core.enrichErrorMessage)(code, originalMessage),
543
- origin: (0, import_hwk_adapter_core.defaultOriginForCode)(code),
544
- appName
545
- };
546
- }
547
-
548
- // src/utils/queueKey.ts
549
- var LEDGER_DEFAULT_QUEUE_KEY = "__ledger_default__";
550
- function ledgerQueueKey({
551
- operationId,
552
- connectId
553
- }) {
554
- return (operationId ?? connectId) || LEDGER_DEFAULT_QUEUE_KEY;
489
+ return { code, message: (0, import_hwk_adapter_core.enrichErrorMessage)(code, originalMessage), appName };
555
490
  }
556
491
 
557
492
  // src/adapter/methods/allNetworkGetAddress.ts
@@ -612,103 +547,65 @@ var LEDGER_BTC_NETWORK_COIN_MAP = {
612
547
  var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "dogecoin"]);
613
548
  function createAllNetworkGetAddress({
614
549
  callChain,
615
- getChainFingerprint,
616
- retainOperation,
617
- errorToFailure,
618
- createCancelScope
550
+ getChainFingerprint
619
551
  }) {
620
552
  return async function allNetworkGetAddress(connectId, _deviceId, params) {
621
- debugLog("[LedgerAdapter][REQ]", {
622
- method: "allNetworkGetAddress",
623
- connectId,
624
- itemCount: params.bundle.length
625
- });
626
- const target = (0, import_hwk_adapter_core2.resolveHardwareOperationTarget)(connectId, params.operationId, "ledger");
627
- if (!target.success) return target;
628
- const effectiveTargetId = target.payload.targetId ?? "";
629
- let releaseOperationRetention;
630
- try {
631
- releaseOperationRetention = target.payload.operationId ? retainOperation(target.payload.operationId) : void 0;
632
- } catch (error) {
633
- return errorToFailure(error);
634
- }
635
- const cancelScope = createCancelScope(
636
- ledgerQueueKey({ operationId: target.payload.operationId, connectId: effectiveTargetId })
637
- );
553
+ debugLog("[LedgerAdapter][REQ]", { method: "allNetworkGetAddress", connectId, params });
638
554
  const installContext = {};
639
555
  const commonParams = {
640
- autoInstallApp: params.autoInstallApp,
641
- operationId: target.payload.operationId,
642
- knownConnections: params.knownConnections,
643
- extra: params.extra,
644
- allowDeviceSelection: params.allowDeviceSelection,
645
- supportedTransports: params.supportedTransports
556
+ autoInstallApp: params.autoInstallApp
646
557
  };
647
558
  const chainFingerprints = /* @__PURE__ */ new Map();
648
- try {
649
- const result = await (0, import_hwk_adapter_core2.runAllNetworkGetAddress)({
650
- connectId: effectiveTargetId,
651
- deviceId: _deviceId,
652
- params,
653
- normalizeItem: normalizeLedgerAllNetworkItem,
654
- buildUnsupportedNetworkResponse: (item) => isUnsupportedLedgerAllNetworkNetwork(item) ? buildUnsupportedNetworkResponse(item) : void 0,
655
- callItem: async ({ method, chain, item }) => {
656
- if (cancelScope.signal.aborted) return buildCancelledFailure(cancelScope.signal);
657
- const itemDeviceId = getItemDeviceId(item) ?? chainFingerprints.get(chain) ?? "";
658
- return callAllNetworkMethod(
659
- callChain,
660
- effectiveTargetId,
661
- itemDeviceId,
662
- method,
663
- item,
664
- commonParams,
665
- installContext
666
- );
667
- },
668
- attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
669
- getChainFingerprint,
670
- effectiveTargetId,
559
+ const result = await (0, import_hwk_adapter_core2.runAllNetworkGetAddress)({
560
+ connectId,
561
+ deviceId: _deviceId,
562
+ params,
563
+ normalizeItem: normalizeLedgerAllNetworkItem,
564
+ buildUnsupportedNetworkResponse: (item) => isUnsupportedLedgerAllNetworkNetwork(item) ? buildUnsupportedNetworkResponse(item) : void 0,
565
+ callItem: async ({ method, chain, item }) => {
566
+ const itemDeviceId = getItemDeviceId(item) ?? chainFingerprints.get(chain) ?? "";
567
+ return callAllNetworkMethod(
568
+ callChain,
569
+ connectId,
570
+ itemDeviceId,
571
+ method,
671
572
  item,
672
- chain,
673
- payload,
674
- chainFingerprints,
675
- installContext,
676
- cancelScope.signal
677
- ),
678
- shouldAbortBundle: isTopLevelAllNetworkFailure,
679
- buildTopLevelFailure: (response) => {
680
- const code = response.payload?.code ?? import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch;
681
- return (0, import_hwk_adapter_core2.failure)(
682
- code,
683
- response.payload?.error ?? "All-network get-address aborted",
684
- response.payload?.params
685
- );
686
- }
687
- });
688
- debugLog("[LedgerAdapter][RES]", {
689
- method: "allNetworkGetAddress",
690
- success: result.success,
691
- payload: result
692
- });
693
- return result;
694
- } finally {
695
- cancelScope.release();
696
- releaseOperationRetention?.();
697
- }
573
+ commonParams,
574
+ installContext
575
+ );
576
+ },
577
+ attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
578
+ getChainFingerprint,
579
+ connectId,
580
+ item,
581
+ chain,
582
+ payload,
583
+ chainFingerprints
584
+ ),
585
+ shouldAbortBundle: isTopLevelAllNetworkFailure,
586
+ buildTopLevelFailure: (response) => {
587
+ const code = response.payload?.code ?? import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch;
588
+ return (0, import_hwk_adapter_core2.failure)(
589
+ code,
590
+ response.payload?.error ?? "All-network get-address aborted",
591
+ response.payload?.params
592
+ );
593
+ }
594
+ });
595
+ debugLog("[LedgerAdapter][RES]", {
596
+ method: "allNetworkGetAddress",
597
+ success: result.success,
598
+ payload: result
599
+ });
600
+ return result;
698
601
  };
699
602
  }
700
- function buildCancelledFailure(signal) {
701
- const reason = signal.reason;
702
- const code = typeof reason?.code === "number" ? reason.code : import_hwk_adapter_core2.HardwareErrorCode.UserAborted;
703
- const message = typeof reason?.message === "string" ? reason.message : "";
704
- return (0, import_hwk_adapter_core2.failure)(code, message || "All-network get-address cancelled");
705
- }
706
603
  function isTopLevelAllNetworkFailure(response) {
707
604
  if (response.success) {
708
605
  return false;
709
606
  }
710
607
  const code = response.payload?.code;
711
- return code === import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch || (0, import_hwk_adapter_core2.isUserRefusal)(code) || (0, import_hwk_adapter_core2.isConnectionLost)(code);
608
+ return code === import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch || code === import_hwk_adapter_core2.HardwareErrorCode.UserAborted || code === import_hwk_adapter_core2.HardwareErrorCode.UserRejected;
712
609
  }
713
610
  function getItemDeviceId(item) {
714
611
  const { deviceId } = item;
@@ -738,18 +635,8 @@ function normalizeLedgerAllNetworkItem(method, item) {
738
635
  const coin = LEDGER_BTC_NETWORK_COIN_MAP[item.network];
739
636
  return coin ? { ...item, coin } : item;
740
637
  }
741
- async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints, context, cancelSignal) {
742
- const knownFingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || "";
743
- if (!knownFingerprint && cancelSignal.aborted) {
744
- const cancelled = buildCancelledFailure(cancelSignal);
745
- return { ...item, success: false, payload: cancelled.payload };
746
- }
747
- const fingerprint = knownFingerprint || await bootstrapChainFingerprint(
748
- getChainFingerprint,
749
- context.connection?.connectId ?? connectId,
750
- chain,
751
- context
752
- );
638
+ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints) {
639
+ const fingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || await bootstrapChainFingerprint(getChainFingerprint, connectId, chain);
753
640
  if (!fingerprint) {
754
641
  return buildFingerprintBootstrapFailure(item, chain);
755
642
  }
@@ -770,8 +657,8 @@ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain,
770
657
  }
771
658
  };
772
659
  }
773
- async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain, context) {
774
- const response = await getChainFingerprint(connectId, chain, context);
660
+ async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain) {
661
+ const response = await getChainFingerprint(connectId, chain);
775
662
  return response.success ? response.payload : "";
776
663
  }
777
664
  function buildFingerprintBootstrapFailure(item, chain) {
@@ -864,16 +751,6 @@ function isLedgerBleDescriptor(connectionType, descriptor) {
864
751
  function formatDeviceMismatchError(expected, actual) {
865
752
  return `Wrong device: expected ${expected}, got ${actual}`;
866
753
  }
867
- var LOG_REDACTED_RESULT_KEYS = ["ufvk", "orchardFvk"];
868
- function redactResultForLog(result) {
869
- if (!result || typeof result !== "object") return result;
870
- const keys = LOG_REDACTED_RESULT_KEYS.filter((key) => key in result);
871
- if (!keys.length) return result;
872
- return { ...result, ...Object.fromEntries(keys.map((key) => [key, "[redacted]"])) };
873
- }
874
- function isLostConnectionError(err) {
875
- return isDeviceDisconnectedError(err) || isDeviceNotAdvertisingError(err) || isTimeoutError(err) || isConnectionLevelError(err);
876
- }
877
754
  var BTC_HIGH_INDEX_THRESHOLD = 100;
878
755
  function btcAccountIndexFromPath(path) {
879
756
  const segments = path.replace(/^m\//, "").split("/");
@@ -885,31 +762,9 @@ function btcAccountIndexFromPath(path) {
885
762
  var _LedgerAdapter = class _LedgerAdapter {
886
763
  constructor(connector, options) {
887
764
  this.vendor = "ledger";
888
- // Cancel frees the DMK action and its intent-queue slot, but cannot retract a
889
- // confirmation screen the device is already showing.
890
- this.cancelCapability = "stops-waiting";
891
765
  this.emitter = new import_hwk_adapter_core3.TypedEventEmitter();
892
- this._operations = new import_hwk_adapter_core3.OperationRegistry({
893
- vendor: "ledger",
894
- onEnded: (operation, reason) => {
895
- const binding = this._pendingOperationBindings.get(operation.operationId);
896
- if (binding?.selectedConnection?.requestId === this._bindingSelectionRequestId) {
897
- this._finishBleBinding("cancelled");
898
- }
899
- this._pendingOperationBindings.delete(operation.operationId);
900
- this.emitter.emit(import_hwk_adapter_core3.SDK.OPERATION_ENDED, {
901
- type: import_hwk_adapter_core3.SDK.OPERATION_ENDED,
902
- payload: { operationId: operation.operationId, reason }
903
- });
904
- if (reason === "timeout") {
905
- void this._releaseOperationConnection(operation);
906
- }
907
- }
908
- });
909
766
  this._discoveredDevices = /* @__PURE__ */ new Map();
910
767
  this._sessions = /* @__PURE__ */ new Map();
911
- this._pendingOperationBindings = /* @__PURE__ */ new Map();
912
- this._verifiedBleReconnectTargets = /* @__PURE__ */ new Map();
913
768
  this._uiRegistry = new import_hwk_adapter_core3.UiRequestRegistry();
914
769
  // BTC App rejects account index >= 100 unless display=true. Cached per
915
770
  // adapter instance: first 100+ path asks the user once via UI request,
@@ -923,31 +778,10 @@ var _LedgerAdapter = class _LedgerAdapter {
923
778
  this._deviceAuthenticityQueueTail = Promise.resolve();
924
779
  // Shared across concurrent callers — only `cancel()` aborts.
925
780
  this._doConnectAbortController = null;
926
- this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
927
- this._connectorIdleWaiters = /* @__PURE__ */ new Set();
928
- this._resetPromise = null;
929
- this._stateGeneration = 0;
930
- this._connectorTeardownTail = Promise.resolve();
931
- this._pendingConnectorTeardowns = 0;
932
- this._activeOperationJobs = /* @__PURE__ */ new Set();
933
- this._pendingOperationDisconnects = /* @__PURE__ */ new Set();
934
781
  this._installProgressLastEmittedValue = -Infinity;
935
782
  this.allNetworkGetAddress = createAllNetworkGetAddress({
936
783
  callChain: this.callChain.bind(this),
937
- getChainFingerprint: async (connectId, chain, context) => {
938
- try {
939
- const fingerprint = await this._computeChainFingerprint(
940
- chain,
941
- (method, params) => this.connectorCall(connectId, method, params, void 0, void 0, void 0, context)
942
- );
943
- return (0, import_hwk_adapter_core3.success)(fingerprint);
944
- } catch (error) {
945
- return this.errorToFailure(error);
946
- }
947
- },
948
- retainOperation: (operationId) => this._operations.retain(operationId),
949
- errorToFailure: (error) => this.errorToFailure(error),
950
- createCancelScope: (queueKey) => this._jobQueue.createCancelScope(queueKey)
784
+ getChainFingerprint: (connectId, chain) => this.getChainFingerprint(connectId, "", chain)
951
785
  });
952
786
  // ---------------------------------------------------------------------------
953
787
  // Private helpers
@@ -957,7 +791,7 @@ var _LedgerAdapter = class _LedgerAdapter {
957
791
  *
958
792
  * - If a session already exists for the given connectId, reuse it.
959
793
  * - If ANY session exists (Ledger IDs are ephemeral), reuse it.
960
- * - Otherwise: search → one USB device auto-connects; multiple asks the host to choose.
794
+ * - Otherwise: search → exactly 1 USB device auto-connects; multiple or none throws.
961
795
  */
962
796
  // Mutex for ensureConnected — prevents concurrent calls from establishing duplicate connections
963
797
  this._connectingPromise = null;
@@ -974,12 +808,6 @@ var _LedgerAdapter = class _LedgerAdapter {
974
808
  });
975
809
  };
976
810
  this.deviceDisconnectHandler = (data) => {
977
- const activeOperation = this._operations.findActiveByConnectionKey(data.connectId);
978
- if (activeOperation && this._activeOperationJobs.has(activeOperation.operationId)) {
979
- this._pendingOperationDisconnects.add(activeOperation.operationId);
980
- } else {
981
- this._operations.endByConnectionKey(data.connectId, "disconnect");
982
- }
983
811
  this._discoveredDevices.delete(data.connectId);
984
812
  this._sessions.delete(data.connectId);
985
813
  this.emitter.emit(import_hwk_adapter_core3.DEVICE.DISCONNECT, {
@@ -988,8 +816,10 @@ var _LedgerAdapter = class _LedgerAdapter {
988
816
  });
989
817
  };
990
818
  // Forward connector `ui-event` to the public hw.emitter so consumers only
991
- // need to subscribe in one place. The AppInstallProgress variant re-keys
992
- // sessionId to connectId via `_sessions`, and drops if no mapping exists.
819
+ // need to subscribe in one place. For the AppInstallProgress variant we
820
+ // re-key sessionId → connectId via the live _sessions map; if no mapping
821
+ // exists (race during teardown) we drop. All other variants pass through
822
+ // unchanged.
993
823
  this.uiEventForwarder = (event) => {
994
824
  if (event.type === import_hwk_adapter_core3.EConnectorInteraction.AppInstallProgress) {
995
825
  let connectId;
@@ -1034,28 +864,11 @@ var _LedgerAdapter = class _LedgerAdapter {
1034
864
  this._jobQueue = new import_hwk_adapter_core3.DeviceJobQueue();
1035
865
  this.registerEventListeners();
1036
866
  }
1037
- _finishBleBinding(status) {
1038
- const selectionRequestId = this._bindingSelectionRequestId;
1039
- this._bindingSelectionRequestId = void 0;
1040
- if (selectionRequestId)
1041
- this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS, {
1042
- type: import_hwk_adapter_core3.UI_REQUEST.DEVICE_BINDING_STATUS,
1043
- payload: { selectionRequestId, status }
1044
- });
1045
- }
1046
- _isBleConnection() {
1047
- return isLedgerBleConnectionType(this._activeConnectionType ?? this.connector.connectionType);
1048
- }
1049
867
  // Transport
1050
868
  get activeTransport() {
1051
- return this._isBleConnection() ? "ble" : "hid";
869
+ return isLedgerBleConnectionType(this.connector.connectionType) ? "ble" : "hid";
1052
870
  }
1053
871
  getAvailableTransports() {
1054
- if (this.connector.availableTransports) {
1055
- return this.connector.availableTransports.map(
1056
- (transport) => transport === "ble" ? "ble" : "hid"
1057
- );
1058
- }
1059
872
  return this.activeTransport ? [this.activeTransport] : [];
1060
873
  }
1061
874
  // Connector is bound at construction; switching requires a new adapter.
@@ -1072,39 +885,21 @@ var _LedgerAdapter = class _LedgerAdapter {
1072
885
  * The next operation will re-discover and re-connect automatically.
1073
886
  */
1074
887
  resetState() {
1075
- void this._resetStateAndDisconnectSessions();
1076
- }
1077
- _resetStateAndDisconnectSessions() {
1078
- if (this._resetPromise) return this._resetPromise;
1079
- const sessionIds = new Set(this._sessions.values());
1080
- this._stateGeneration += 1;
1081
- this._finishBleBinding("cancelled");
1082
- this._operations.endAll("runtime-reset");
1083
- this._doConnectAbortController?.abort();
1084
888
  this._discoveredDevices.clear();
1085
889
  this._sessions.clear();
1086
- this._verifiedBleReconnectTargets.clear();
1087
890
  this._connectingPromise = null;
1088
- this._doConnectAbortController = null;
1089
891
  this._uiRegistry.reset();
1090
892
  this._jobQueue.clear();
1091
893
  this._btcHighIndexConfirmedThisSession = false;
1092
- const resetPromise = this._runConnectorTeardown(async () => {
1093
- for (const sessionId of sessionIds) {
1094
- await this.connector.disconnect(sessionId).catch(() => void 0);
1095
- }
1096
- });
1097
- this._resetPromise = resetPromise;
1098
- return resetPromise.finally(() => {
1099
- if (this._resetPromise === resetPromise) {
1100
- this._resetPromise = null;
1101
- }
1102
- });
1103
894
  }
1104
895
  async dispose() {
1105
- await this._resetStateAndDisconnectSessions();
896
+ this._uiRegistry.reset();
897
+ this._jobQueue.clear();
1106
898
  this.unregisterEventListeners();
1107
899
  this.connector.reset();
900
+ this._discoveredDevices.clear();
901
+ this._sessions.clear();
902
+ this._btcHighIndexConfirmedThisSession = false;
1108
903
  this.emitter.removeAllListeners();
1109
904
  }
1110
905
  uiResponse(response) {
@@ -1114,32 +909,17 @@ var _LedgerAdapter = class _LedgerAdapter {
1114
909
  // Device management
1115
910
  // ---------------------------------------------------------------------------
1116
911
  async searchDevices(options) {
1117
- return this._searchDevices(options);
1118
- }
1119
- async _searchDevices(options, signal) {
1120
912
  debugLog("[LedgerAdapter][REQ]", { method: "searchDevices", params: options });
1121
913
  try {
1122
914
  if (options?.resetSession) {
1123
- await this._resetStateAndDisconnectSessions();
1124
- } else {
1125
- await this._connectorTeardownTail;
1126
- }
1127
- await this._ensureDevicePermission(void 0, void 0, signal);
1128
- if (signal) _LedgerAdapter._throwIfAborted(signal);
1129
- const stateGeneration = this._stateGeneration;
1130
- const devices = await this.connector.searchDevices(
1131
- options?.transportType ? {
1132
- transportType: options.transportType,
1133
- waitForAll: options.waitForAllTransports
1134
- } : void 0
1135
- );
1136
- if (signal) _LedgerAdapter._throwIfAborted(signal);
1137
- if (stateGeneration !== this._stateGeneration) {
1138
- throw (0, import_hwk_adapter_core3.createHwkError)({
1139
- code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted,
1140
- message: "Ledger discovery was reset"
1141
- });
915
+ this._doConnectAbortController?.abort();
916
+ this._sessions.clear();
917
+ this._connectingPromise = null;
918
+ this._doConnectAbortController = null;
919
+ this._btcHighIndexConfirmedThisSession = false;
1142
920
  }
921
+ await this._ensureDevicePermission();
922
+ const devices = await this.connector.searchDevices();
1143
923
  this._discoveredDevices.clear();
1144
924
  for (const d of devices) {
1145
925
  if (d.connectId) {
@@ -1147,7 +927,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1147
927
  }
1148
928
  }
1149
929
  if (this._discoveredDevices.size === 0) {
1150
- await this._ensureDevicePermission(void 0, void 0, signal);
930
+ await this._ensureDevicePermission();
1151
931
  }
1152
932
  const result = Array.from(this._discoveredDevices.values());
1153
933
  debugLog("[LedgerAdapter][RES]", {
@@ -1166,288 +946,41 @@ var _LedgerAdapter = class _LedgerAdapter {
1166
946
  throw err;
1167
947
  }
1168
948
  }
1169
- async searchDeviceTargets(options) {
1170
- const devices = await this.searchDevices(options);
1171
- return devices.map((device) => ({
1172
- searchTargetId: device.connectId,
1173
- searchTargetReusePolicy: (0, import_hwk_adapter_core3.resolveSearchTargetReusePolicy)(device),
1174
- vendor: "ledger",
1175
- connectionType: device.connectionType,
1176
- kind: "physical",
1177
- label: device.label,
1178
- model: device.model,
1179
- modelName: device.modelName,
1180
- serialNumber: device.serialNumber
1181
- }));
1182
- }
1183
- async listConnectionTargets(options) {
1184
- const targets = await this.searchDeviceTargets(options);
1185
- return targets.map(({ searchTargetId, ...target }) => ({
1186
- ...target,
1187
- targetId: searchTargetId
1188
- }));
1189
- }
1190
949
  // USB single-session invariant: evict all sessions, best-effort (see connectDevice).
1191
- async _evictAllSessions(preserveOperationId) {
1192
- this._operations.endAll("explicit", preserveOperationId);
950
+ async _evictAllSessions() {
1193
951
  if (this._sessions.size === 0) return;
1194
952
  const stale = [...this._sessions.values()];
1195
953
  this._sessions.clear();
1196
- await this._runConnectorTeardown(async () => {
1197
- for (const sid of stale) {
1198
- try {
1199
- await this.connector.disconnect(sid);
1200
- } catch {
1201
- }
954
+ for (const sid of stale) {
955
+ try {
956
+ await this.connector.disconnect(sid);
957
+ } catch {
1202
958
  }
1203
- });
959
+ }
1204
960
  }
1205
961
  static _createDeviceBusyError(method) {
1206
962
  return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
1207
963
  code: import_hwk_adapter_core3.HardwareErrorCode.DeviceBusy
1208
964
  });
1209
965
  }
1210
- async connectDevice(searchTargetId) {
1211
- try {
1212
- return await this._jobQueue.enqueue(
1213
- searchTargetId || "__ledger_connect__",
1214
- async (signal) => {
1215
- const connected = await this._connectTarget(searchTargetId, void 0, signal);
1216
- if (!connected.success) return connected;
1217
- return this._createOperation(searchTargetId, connected.payload);
1218
- },
1219
- {
1220
- label: "connectDevice",
1221
- rejectIfBusy: true,
1222
- busyError: _LedgerAdapter._createDeviceBusyError("connectDevice")
1223
- }
1224
- );
1225
- } catch (error) {
1226
- return this.errorToFailure(error);
1227
- }
1228
- }
1229
- async bindBleDevice(params) {
1230
- if (params.identity.vendor !== "ledger" || !params.identity.value) {
1231
- return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "Ledger wallet identity is required");
1232
- }
1233
- const { chain, value: expectedFingerprint } = params.identity;
1234
- try {
1235
- return await this._jobQueue.enqueue(
1236
- expectedFingerprint,
1237
- async (signal) => {
1238
- if (!this.getAvailableTransports().includes("ble")) {
1239
- throw (0, import_hwk_adapter_core3.createHwkError)({
1240
- code: import_hwk_adapter_core3.HardwareErrorCode.TransportNotAvailable,
1241
- message: "Ledger Bluetooth transport is not available"
1242
- });
1243
- }
1244
- this._activeConnectionType = "ble";
1245
- await this._ensureDevicePermission(void 0, void 0, signal);
1246
- const attempt = {
1247
- extra: params.extra,
1248
- bindingReason: "manual-rebind"
1249
- };
1250
- try {
1251
- for (; ; ) {
1252
- const connectId = await this._connectFirstOrSelect(
1253
- [],
1254
- void 0,
1255
- true,
1256
- void 0,
1257
- attempt,
1258
- signal
1259
- );
1260
- const sessionId = this._sessions.get(connectId);
1261
- if (!sessionId) {
1262
- throw (0, import_hwk_adapter_core3.createHwkError)({
1263
- code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
1264
- message: "Selected Ledger connection ended"
1265
- });
1266
- }
1267
- let saved = false;
1268
- try {
1269
- const installContext = {
1270
- connection: { connectId, sessionId }
1271
- };
1272
- const fingerprint = await this._computeChainFingerprint(
1273
- chain,
1274
- (method, callParams) => this._runConnectorCall(
1275
- connectId,
1276
- method,
1277
- callParams,
1278
- signal,
1279
- void 0,
1280
- void 0,
1281
- { autoInstallApp: true },
1282
- installContext
1283
- )
1284
- );
1285
- if (fingerprint !== expectedFingerprint) {
1286
- attempt.rejectedConnectIds ?? (attempt.rejectedConnectIds = /* @__PURE__ */ new Set());
1287
- attempt.rejectedConnectIds.add(connectId);
1288
- attempt.rejectedConnectId = connectId;
1289
- } else {
1290
- const persisted = await this._publishVerifiedBleBinding(
1291
- connectId,
1292
- chain,
1293
- fingerprint,
1294
- attempt,
1295
- void 0,
1296
- signal
1297
- );
1298
- if (!persisted) {
1299
- throw (0, import_hwk_adapter_core3.createHwkError)({
1300
- code: import_hwk_adapter_core3.HardwareErrorCode.UnknownError,
1301
- message: "Bluetooth binding could not be saved",
1302
- origin: "host"
1303
- });
1304
- }
1305
- saved = true;
1306
- return (0, import_hwk_adapter_core3.success)(connectId);
1307
- }
1308
- } finally {
1309
- if (!saved && this._sessions.get(connectId) === sessionId) {
1310
- this._sessions.delete(connectId);
1311
- const teardown = this._runConnectorTeardown(
1312
- () => this.connector.disconnect(sessionId)
1313
- ).catch(() => void 0);
1314
- if (!signal.aborted) await teardown;
1315
- }
1316
- }
1317
- }
1318
- } catch (error) {
1319
- this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
1320
- throw error;
1321
- }
1322
- },
1323
- {
1324
- label: "bindBleDevice",
1325
- rejectIfBusy: true,
1326
- busyError: _LedgerAdapter._createDeviceBusyError("bindBleDevice")
1327
- }
1328
- );
1329
- } catch (error) {
1330
- return this.errorToFailure(error);
1331
- }
1332
- }
1333
- async acquireOperation(connectId, context) {
1334
- try {
1335
- return await this._jobQueue.enqueue(
1336
- connectId || "__ledger_acquire__",
1337
- async (signal) => {
1338
- const transport = this._isBleConnection() ? "ble" : "usb";
1339
- const hint = context.knownConnections?.find(
1340
- (connection) => connection.transport === transport
1341
- );
1342
- const target = hint && hint.transport !== "qr" ? hint.connectId : connectId;
1343
- await this._ensureDevicePermission(target, void 0, signal);
1344
- const attempt = {
1345
- ...context,
1346
- extra: context.extra ? { ...context.extra } : void 0
1347
- };
1348
- try {
1349
- const resolvedConnectId = await this.ensureConnected(
1350
- target,
1351
- signal,
1352
- true,
1353
- void 0,
1354
- attempt
1355
- );
1356
- _LedgerAdapter._throwIfAborted(signal);
1357
- const result = this._createOperation(connectId, resolvedConnectId);
1358
- if (result.success && attempt.selectedConnection) {
1359
- this._pendingOperationBindings.set(result.payload, attempt);
1360
- }
1361
- return result;
1362
- } catch (error) {
1363
- this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
1364
- throw error;
1365
- }
1366
- },
1367
- {
1368
- label: "acquireOperation",
1369
- rejectIfBusy: true,
1370
- busyError: _LedgerAdapter._createDeviceBusyError("acquireOperation")
1371
- }
1372
- );
1373
- } catch (error) {
1374
- return this.errorToFailure(error);
1375
- }
1376
- }
1377
- _createOperation(searchTargetId, resolvedConnectId) {
1378
- this._operations.endByConnectionKey(resolvedConnectId, "explicit");
1379
- const sessionId = this._sessions.get(resolvedConnectId);
1380
- if (sessionId) this._operations.endByConnectionKey(sessionId, "explicit");
1381
- const connectionType = this._isBleConnection() ? "ble" : "usb";
1382
- const device = this._discoveredDevices.get(resolvedConnectId) ?? {
1383
- vendor: "ledger",
1384
- model: "unknown",
1385
- firmwareVersion: "",
1386
- deviceId: "",
1387
- connectId: resolvedConnectId,
1388
- connectionType
1389
- };
1390
- const operation = this._operations.create({
1391
- searchTargetId,
1392
- connectId: resolvedConnectId,
1393
- device,
1394
- connectionType,
1395
- connectionKeys: [this._sessions.get(resolvedConnectId) ?? ""]
1396
- });
1397
- return (0, import_hwk_adapter_core3.success)(operation.operationId);
1398
- }
1399
- async _connectTarget(connectId, preserveOperationId, signal) {
966
+ async connectDevice(connectId) {
1400
967
  debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
1401
968
  try {
1402
- this._assertConnectorReady("connectDevice");
1403
- const discoveredType = this._discoveredDevices.get(connectId)?.connectionType;
1404
- if (discoveredType === "usb" || discoveredType === "ble") {
1405
- this._activeConnectionType = discoveredType;
1406
- }
1407
- if (this._isBleConnection() && !connectId) {
969
+ if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
1408
970
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
1409
971
  code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
1410
972
  });
1411
973
  }
1412
- await this._ensureDevicePermission(connectId, void 0, signal);
1413
- if (signal) _LedgerAdapter._throwIfAborted(signal);
1414
- const isMultiTransport = (this.connector.availableTransports?.length ?? 0) > 1;
1415
- if (!isMultiTransport && this._isBleConnection()) {
1416
- const previousSessionId = this._sessions.get(connectId);
1417
- this._operations.endByConnectionKey(connectId, "explicit", preserveOperationId);
1418
- if (previousSessionId) {
1419
- this._operations.endByConnectionKey(previousSessionId, "explicit", preserveOperationId);
1420
- this._sessions.delete(connectId);
1421
- await this.connector.disconnect(previousSessionId).catch(() => void 0);
1422
- }
1423
- } else {
1424
- await this._evictAllSessions(preserveOperationId);
1425
- }
1426
- if (signal) _LedgerAdapter._throwIfAborted(signal);
1427
- const stateGeneration = this._stateGeneration;
1428
- const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
1429
- let session;
1430
- this._connectingConnectId = connectId;
1431
- try {
1432
- session = this.connector.availableTransports?.length ? await this.connector.connect(connectId, {
1433
- transportType: this._activeConnectionType ?? "usb"
1434
- }) : await this.connector.connect(connectId);
1435
- if (signal?.aborted || stateGeneration !== this._stateGeneration) {
1436
- await this.connector.disconnect(session.sessionId).catch(() => void 0);
1437
- throw Object.assign(new Error("Ledger connection aborted"), {
1438
- code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted
1439
- });
1440
- }
1441
- } finally {
1442
- if (this._connectingConnectId === connectId) this._connectingConnectId = void 0;
1443
- releaseOperation();
974
+ if (!isLedgerBleConnectionType(this.connector.connectionType)) {
975
+ await this._evictAllSessions();
1444
976
  }
1445
- const resolvedConnectId = session.deviceInfo?.connectId || connectId;
1446
- this._sessions.set(resolvedConnectId, session.sessionId);
977
+ await this._ensureDevicePermission(connectId);
978
+ const session = await this.connector.connect(connectId);
979
+ this._sessions.set(connectId, session.sessionId);
1447
980
  if (session.deviceInfo) {
1448
- this._discoveredDevices.set(resolvedConnectId, session.deviceInfo);
981
+ this._discoveredDevices.set(connectId, session.deviceInfo);
1449
982
  }
1450
- const result = (0, import_hwk_adapter_core3.success)(resolvedConnectId);
983
+ const result = (0, import_hwk_adapter_core3.success)(connectId);
1451
984
  debugLog("[LedgerAdapter][RES]", { method: "connectDevice", success: true, payload: result });
1452
985
  return result;
1453
986
  } catch (err) {
@@ -1460,60 +993,30 @@ var _LedgerAdapter = class _LedgerAdapter {
1460
993
  return failureResult;
1461
994
  }
1462
995
  }
1463
- async releaseOperation(operationId) {
1464
- const operation = this._operations.find(operationId);
1465
- if (!operation) {
1466
- this._operations.resolve(operationId);
1467
- return;
1468
- }
1469
- const endedOperation = this._operations.end(operationId, "explicit");
1470
- if (!endedOperation) return;
1471
- const { connectId } = operation;
996
+ async disconnectDevice(connectId) {
1472
997
  debugLog("[LedgerAdapter][REQ]", {
1473
- method: "releaseOperation",
998
+ method: "disconnectDevice",
1474
999
  connectId,
1475
1000
  params: { connectId }
1476
1001
  });
1477
1002
  try {
1478
- await this._releaseOperationConnection(endedOperation);
1479
- debugLog("[LedgerAdapter][RES]", { method: "releaseOperation", success: true });
1003
+ const sessionId = this._sessions.get(connectId);
1004
+ if (sessionId) {
1005
+ await this.connector.disconnect(sessionId);
1006
+ this._sessions.delete(connectId);
1007
+ }
1008
+ debugLog("[LedgerAdapter][RES]", { method: "disconnectDevice", success: true });
1480
1009
  } catch (err) {
1481
1010
  const e = err;
1482
1011
  debugLog("[LedgerAdapter][RES]", {
1483
- method: "releaseOperation",
1012
+ method: "disconnectDevice",
1484
1013
  success: false,
1485
1014
  error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
1486
1015
  });
1487
1016
  throw err;
1488
1017
  }
1489
1018
  }
1490
- async _releaseOperationConnection(operation) {
1491
- const sessionIds = /* @__PURE__ */ new Set();
1492
- for (const [connectId, sessionId] of this._sessions) {
1493
- if (connectId === operation.connectId || operation.connectionKeys.includes(connectId) || operation.connectionKeys.includes(sessionId)) {
1494
- this._sessions.delete(connectId);
1495
- sessionIds.add(sessionId);
1496
- }
1497
- }
1498
- for (const sessionId of sessionIds) {
1499
- await this._runConnectorTeardown(
1500
- () => this.connector.disconnect(sessionId).catch(() => void 0)
1501
- );
1502
- }
1503
- }
1504
- async _releaseLostOperationConnection(operationId) {
1505
- const endedOperation = this._operations.end(operationId, "disconnect");
1506
- if (!endedOperation) return;
1507
- this._discoveredDevices.delete(endedOperation.connectId);
1508
- await this._releaseOperationConnection(endedOperation);
1509
- }
1510
- async getDeviceInfo(connectIdOrOperationId, deviceId) {
1511
- let connectId;
1512
- try {
1513
- connectId = (0, import_hwk_adapter_core3.isHardwareOperationId)(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
1514
- } catch (error) {
1515
- return this.errorToFailure(error);
1516
- }
1019
+ async getDeviceInfo(connectId, deviceId) {
1517
1020
  debugLog("[LedgerAdapter][REQ]", {
1518
1021
  method: "getDeviceInfo",
1519
1022
  connectId,
@@ -1548,9 +1051,12 @@ var _LedgerAdapter = class _LedgerAdapter {
1548
1051
  success: false,
1549
1052
  error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
1550
1053
  });
1551
- return this.errorToFailure(err);
1054
+ throw err;
1552
1055
  }
1553
1056
  }
1057
+ getSupportedChains() {
1058
+ return ["evm", "btc", "sol", "tron"];
1059
+ }
1554
1060
  // ---------------------------------------------------------------------------
1555
1061
  // Chain call helper
1556
1062
  // ---------------------------------------------------------------------------
@@ -1585,23 +1091,13 @@ var _LedgerAdapter = class _LedgerAdapter {
1585
1091
  if (params && typeof params === "object") {
1586
1092
  const {
1587
1093
  autoInstallApp,
1588
- operationId,
1589
1094
  passphraseState: _passphraseState,
1590
1095
  useEmptyPassphrase: _useEmptyPassphrase,
1591
- knownConnections,
1592
- extra,
1593
- allowDeviceSelection,
1594
- supportedTransports,
1595
1096
  ...rest
1596
1097
  } = params;
1597
1098
  return {
1598
1099
  commonParams: {
1599
- autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0,
1600
- operationId: typeof operationId === "string" ? operationId : void 0,
1601
- knownConnections,
1602
- extra,
1603
- allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0,
1604
- supportedTransports
1100
+ autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
1605
1101
  },
1606
1102
  rest
1607
1103
  };
@@ -1777,28 +1273,9 @@ var _LedgerAdapter = class _LedgerAdapter {
1777
1273
  );
1778
1274
  }
1779
1275
  // ---------------------------------------------------------------------------
1780
- // Zcash chain methods (viewing key + shielded address reads; Zcash app >= 3.8.0)
1781
- // ---------------------------------------------------------------------------
1782
- zcashGetFullViewingKey(connectId, deviceId, params) {
1783
- return this.callChainWithMergedParams(
1784
- connectId,
1785
- deviceId,
1786
- "zcash",
1787
- "zcashGetFullViewingKey",
1788
- params
1789
- );
1790
- }
1791
- zcashGetShieldedAddress(connectId, deviceId, params) {
1792
- return this.callChainWithMergedParams(
1793
- connectId,
1794
- deviceId,
1795
- "zcash",
1796
- "zcashGetShieldedAddress",
1797
- params
1798
- );
1799
- }
1800
- // ---------------------------------------------------------------------------
1801
- // App management: OS-level app install/list, no fingerprint or chain dispatch.
1276
+ // App management — OS-level Ledger app install / list. Bypasses fingerprint
1277
+ // and chain-handler dispatch; installApp progress is forwarded to the adapter
1278
+ // emitter via 'ui-event' AppInstallProgress events.
1802
1279
  // ---------------------------------------------------------------------------
1803
1280
  async installApp(connectId, appName) {
1804
1281
  try {
@@ -1884,7 +1361,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1884
1361
  );
1885
1362
  }
1886
1363
  await this.connector.configure({ ledgerGenuineCheckWebSocketUrl: relayUrl });
1887
- await this._resetStateAndDisconnectSessions();
1364
+ this.resetState();
1888
1365
  }
1889
1366
  const result = await this.connectorCall(connectId, "getDeviceGenuineCheck", {});
1890
1367
  if (!result.isGenuine) {
@@ -1910,10 +1387,10 @@ var _LedgerAdapter = class _LedgerAdapter {
1910
1387
  if (relayUrl) {
1911
1388
  try {
1912
1389
  await this.connector.configure?.({ ledgerGenuineCheckWebSocketUrl: void 0 });
1913
- await this._resetStateAndDisconnectSessions();
1390
+ this.resetState();
1914
1391
  } catch {
1915
1392
  this.connector.reset();
1916
- await this._resetStateAndDisconnectSessions();
1393
+ this.resetState();
1917
1394
  }
1918
1395
  }
1919
1396
  }
@@ -1925,20 +1402,6 @@ var _LedgerAdapter = class _LedgerAdapter {
1925
1402
  this.emitter.off(event, listener);
1926
1403
  }
1927
1404
  cancel(connectId) {
1928
- const namedOperationIsLive = (id) => {
1929
- try {
1930
- this._operations.resolve(id);
1931
- return true;
1932
- } catch {
1933
- return false;
1934
- }
1935
- };
1936
- if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId) && !namedOperationIsLive(connectId)) {
1937
- debugLog("[LedgerAdapter] cancel target already ended; nothing to cancel", {
1938
- connectId
1939
- });
1940
- return;
1941
- }
1942
1405
  const userAbortReason = Object.assign(new Error("User aborted operation"), {
1943
1406
  code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted,
1944
1407
  _tag: ERROR_TAG.UserAborted
@@ -1949,50 +1412,14 @@ var _LedgerAdapter = class _LedgerAdapter {
1949
1412
  this._lastCancelReason = void 0;
1950
1413
  }
1951
1414
  }, 2e3);
1952
- const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
1953
- let operationId;
1954
- if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId)) {
1955
- operationId = connectId;
1956
- } else if (!connectId && (0, import_hwk_adapter_core3.isHardwareOperationId)(activeJobId)) {
1957
- operationId = activeJobId;
1958
- }
1959
- let resolvedConnectId = connectId;
1960
- if (operationId) {
1961
- try {
1962
- resolvedConnectId = this._operations.resolve(operationId).connectId;
1963
- } catch {
1964
- resolvedConnectId = void 0;
1965
- }
1966
- }
1967
- const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
1968
- const pendingOperationId = operationId ?? interactionForPhysicalId?.operationId;
1969
- if (!connectId) {
1970
- this._uiRegistry.cancel();
1971
- } else if (pendingOperationId) {
1972
- this._uiRegistry.cancel(void 0, void 0, pendingOperationId);
1973
- }
1974
- this._finishBleBinding("cancelled");
1975
- if (!connectId) this._pendingOperationBindings.clear();
1976
- else if (pendingOperationId) {
1977
- this._pendingOperationBindings.delete(pendingOperationId);
1978
- }
1979
- const queueKeys = /* @__PURE__ */ new Set();
1980
- if (connectId) queueKeys.add(ledgerQueueKey({ connectId }));
1981
- if (pendingOperationId) queueKeys.add(ledgerQueueKey({ operationId: pendingOperationId }));
1982
- if (queueKeys.size) {
1983
- let cancelledAnyJob = false;
1984
- for (const key of queueKeys) {
1985
- cancelledAnyJob = this._jobQueue.cancelActiveAndPending(key, userAbortReason) || cancelledAnyJob;
1986
- }
1987
- debugLog("[LedgerAdapter] cancel routed to queue keys", {
1988
- queueKeys: [...queueKeys],
1989
- cancelledAnyJob
1990
- });
1415
+ this._uiRegistry.cancel();
1416
+ if (connectId) {
1417
+ this._jobQueue.cancelActiveAndPending(connectId, userAbortReason);
1991
1418
  } else {
1992
1419
  this._jobQueue.cancelActiveAndPending(void 0, userAbortReason);
1993
1420
  }
1994
- if (resolvedConnectId) {
1995
- const sessionId = this._sessions.get(resolvedConnectId) ?? resolvedConnectId;
1421
+ if (connectId) {
1422
+ const sessionId = this._sessions.get(connectId) ?? connectId;
1996
1423
  void this.connector.cancel(sessionId);
1997
1424
  } else {
1998
1425
  for (const sid of this._sessions.values()) void this.connector.cancel(sid);
@@ -2000,104 +1427,44 @@ var _LedgerAdapter = class _LedgerAdapter {
2000
1427
  if (this._connectingPromise) {
2001
1428
  this._doConnectAbortController?.abort(userAbortReason);
2002
1429
  }
2003
- const connectingConnectId = this._connectingConnectId;
2004
- if (connectingConnectId) void this.connector.cancel(connectingConnectId);
2005
1430
  }
2006
1431
  // ---------------------------------------------------------------------------
2007
1432
  // Chain fingerprint
2008
1433
  // ---------------------------------------------------------------------------
2009
- /** A non-empty deviceId is an expected chain fingerprint, not a transport identifier. */
2010
1434
  async getChainFingerprint(connectId, deviceId, chain) {
2011
1435
  try {
2012
1436
  const fingerprint = await this._computeChainFingerprint(
2013
1437
  chain,
2014
1438
  (method, params) => this.connectorCall(connectId, method, params, void 0, deviceId)
2015
1439
  );
2016
- if (deviceId) {
2017
- if (fingerprint !== deviceId) {
2018
- return (0, import_hwk_adapter_core3.failure)(
2019
- import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
2020
- formatDeviceMismatchError(deviceId, fingerprint)
2021
- );
2022
- }
2023
- if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId)) {
2024
- const operation = this._operations.resolve(connectId);
2025
- await this._publishVerifiedBleBinding(
2026
- operation.connectId,
2027
- chain,
2028
- fingerprint,
2029
- void 0,
2030
- connectId
2031
- );
2032
- }
2033
- }
2034
1440
  return (0, import_hwk_adapter_core3.success)(fingerprint);
2035
1441
  } catch (err) {
2036
1442
  debugError("[LedgerAdapter] getChainFingerprint error:", chain, err);
2037
1443
  return this.errorToFailure(err);
2038
1444
  }
2039
1445
  }
2040
- /** Discovery may select a BLE target before a later call verifies its wallet. */
2041
- async _publishVerifiedBleBinding(connectId, chain, fingerprint, attempt, operationId, signal) {
2042
- const binding = operationId ? this._pendingOperationBindings.get(operationId) : attempt;
2043
- if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== connectId) {
2044
- return false;
2045
- }
1446
+ /**
1447
+ * Verify fingerprint using an existing sessionId directly.
1448
+ * Safe to call inside connectorCall without causing queue deadlock.
1449
+ */
1450
+ async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
1451
+ if (!deviceId) return { success: true };
2046
1452
  try {
2047
- const outcome = await (0, import_hwk_adapter_core3.requestSaveDeviceBinding)(
2048
- this.emitter,
2049
- this._uiRegistry,
2050
- {
2051
- selectionRequestId: binding.selectedConnection.requestId,
2052
- connection: { transport: "ble", connectId },
2053
- identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
2054
- extra: binding.extra,
2055
- operationId
2056
- },
2057
- signal
1453
+ const fingerprint = await this._computeChainFingerprint(
1454
+ chain,
1455
+ (method, params) => this._callConnector(sessionId, method, params)
2058
1456
  );
2059
- if (operationId) this._operations.resolve(operationId);
2060
- if (!outcome.saved) {
2061
- debugLog("[LedgerAdapter] BLE binding not persisted by host", {
2062
- connectId,
2063
- reason: outcome.reason
2064
- });
1457
+ if (fingerprint === deviceId) {
1458
+ return { success: true };
2065
1459
  }
2066
- return outcome.saved;
2067
- } catch (error) {
2068
- if (operationId) {
2069
- this._pendingOperationBindings.delete(operationId);
2070
- this._operations.end(operationId, "explicit");
1460
+ return { success: false, expected: deviceId, actual: fingerprint };
1461
+ } catch (err) {
1462
+ const mapped = mapLedgerError(err);
1463
+ if (mapped.code === import_hwk_adapter_core3.HardwareErrorCode.WrongApp || mapped.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceLocked) {
1464
+ return { success: true };
2071
1465
  }
2072
- const sessionId = this._sessions.get(connectId);
2073
- this._sessions.delete(connectId);
2074
- if (sessionId) {
2075
- const teardown = this._runConnectorTeardown(
2076
- () => this.connector.disconnect(sessionId)
2077
- ).catch(() => void 0);
2078
- if (!signal?.aborted) await teardown;
2079
- }
2080
- throw error;
2081
- } finally {
2082
- if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
2083
- this._bindingSelectionRequestId = void 0;
2084
- }
2085
- if (operationId) {
2086
- this._pendingOperationBindings.delete(operationId);
2087
- }
2088
- }
2089
- }
2090
- /** Verify on the acquired session without re-entering the job queue. */
2091
- async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
2092
- if (!deviceId) return { success: true };
2093
- const fingerprint = await this._computeChainFingerprint(
2094
- chain,
2095
- (method, params) => this._callConnector(sessionId, method, params)
2096
- );
2097
- if (fingerprint === deviceId) {
2098
- return { success: true };
1466
+ throw err;
2099
1467
  }
2100
- return { success: false, expected: deviceId, actual: fingerprint };
2101
1468
  }
2102
1469
  /**
2103
1470
  * Compute the chain fingerprint via a caller-supplied call strategy.
@@ -2126,23 +1493,11 @@ var _LedgerAdapter = class _LedgerAdapter {
2126
1493
  address = (await callMethod("solGetAddress", { path, showOnDevice: false })).address;
2127
1494
  } else if (chain === "tron") {
2128
1495
  address = (await callMethod("tronGetAddress", { path, showOnDevice: false })).address;
2129
- } else if (chain === "zcash") {
2130
- address = (await callMethod("zcashGetShieldedAddress", { path, showOnDevice: false })).address;
2131
1496
  } else {
2132
1497
  throw new Error(`Unsupported chain for fingerprint: ${chain}`);
2133
1498
  }
2134
1499
  return (0, import_hwk_adapter_core3.deriveDeviceFingerprint)(address);
2135
1500
  }
2136
- /**
2137
- * Operation owning the running job, so a mid-call UI request can name it.
2138
- * Undefined at cold start when no operation owns the job.
2139
- */
2140
- _activeOperationId() {
2141
- const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
2142
- if (!activeJobId) return void 0;
2143
- if ((0, import_hwk_adapter_core3.isHardwareOperationId)(activeJobId)) return activeJobId;
2144
- return this._operations.findActiveByConnectionKey(activeJobId)?.operationId;
2145
- }
2146
1501
  // Ledger WebUSB won't expose a locked device, so we can't auto-detect unlock.
2147
1502
  // The user must press Confirm after unlocking, which triggers a search retry.
2148
1503
  // If `signal` is provided, an abort cancels the pending UI request so the
@@ -2152,18 +1507,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2152
1507
  if (signal?.aborted) {
2153
1508
  _LedgerAdapter._throwIfAborted(signal);
2154
1509
  }
2155
- const operationId = this._activeOperationId();
2156
1510
  const waitPromise = this._uiRegistry.wait(
2157
- import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT,
2158
- { operationId }
1511
+ import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT
2159
1512
  );
2160
1513
  this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT, {
2161
1514
  type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_CONNECT,
2162
1515
  payload: {
2163
1516
  vendor: "ledger",
2164
1517
  reason: "device-not-found",
2165
- message: "Please connect and unlock your Ledger device",
2166
- operationId
1518
+ message: "Please connect and unlock your Ledger device"
2167
1519
  }
2168
1520
  });
2169
1521
  let payload;
@@ -2224,18 +1576,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2224
1576
  return { ...params, showOnDevice: true };
2225
1577
  }
2226
1578
  async _waitForBtcHighIndexConfirm(path, accountIndex) {
2227
- const operationId = this._activeOperationId();
2228
1579
  const waitPromise = this._uiRegistry.wait(
2229
- import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
2230
- { operationId }
1580
+ import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM
2231
1581
  );
2232
1582
  this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM, {
2233
1583
  type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
2234
1584
  payload: {
2235
1585
  vendor: "ledger",
2236
1586
  path,
2237
- accountIndex,
2238
- operationId
1587
+ accountIndex
2239
1588
  }
2240
1589
  });
2241
1590
  try {
@@ -2252,14 +1601,12 @@ var _LedgerAdapter = class _LedgerAdapter {
2252
1601
  // Ask the user whether to install a missing app (autoInstallApp flow).
2253
1602
  // Same register-then-emit ordering as the BTC high-index gate.
2254
1603
  async _waitForInstallAppConfirm(appName) {
2255
- const operationId = this._activeOperationId();
2256
1604
  const waitPromise = this._uiRegistry.wait(
2257
- import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP,
2258
- { operationId }
1605
+ import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP
2259
1606
  );
2260
1607
  this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP, {
2261
1608
  type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_INSTALL_APP,
2262
- payload: { vendor: "ledger", appName, operationId }
1609
+ payload: { vendor: "ledger", appName }
2263
1610
  });
2264
1611
  try {
2265
1612
  const payload = await waitPromise;
@@ -2275,17 +1622,16 @@ var _LedgerAdapter = class _LedgerAdapter {
2275
1622
  // Layer 1 entry. Caller signal only races the outer awaiter; the shared
2276
1623
  // `_doConnect` runs under its own internal controller so caller A's cancel
2277
1624
  // doesn't kill caller B's await.
2278
- async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false, preserveOperationId, context) {
1625
+ async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false) {
2279
1626
  if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
2280
- this._assertConnectorReady("connectDevice");
2281
- if (this._isBleConnection() && !connectId && !allowUsbEphemeralFallback) {
1627
+ if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
2282
1628
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
2283
1629
  code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
2284
1630
  });
2285
1631
  }
2286
1632
  if (connectId && this._sessions.has(connectId)) return connectId;
2287
1633
  if (!connectId && this._sessions.size > 0) {
2288
- if (!this._isBleConnection() && this._sessions.size > 1) {
1634
+ if (!isLedgerBleConnectionType(this.connector.connectionType) && this._sessions.size > 1) {
2289
1635
  throw Object.assign(
2290
1636
  new Error(
2291
1637
  "Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
@@ -2295,24 +1641,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2295
1641
  }
2296
1642
  return this._sessions.keys().next().value;
2297
1643
  }
2298
- if (!this._connectingPromise || this._doConnectAbortController?.signal.aborted) {
2299
- const controller = new AbortController();
2300
- this._doConnectAbortController = controller;
2301
- const innerSignal = controller.signal;
1644
+ if (!this._connectingPromise) {
1645
+ this._doConnectAbortController = new AbortController();
1646
+ const innerSignal = this._doConnectAbortController.signal;
2302
1647
  this._connectingPromise = (async () => {
2303
1648
  try {
2304
- return await this._doConnect(
2305
- innerSignal,
2306
- connectId,
2307
- allowUsbEphemeralFallback,
2308
- preserveOperationId,
2309
- context
2310
- );
1649
+ return await this._doConnect(innerSignal, connectId, allowUsbEphemeralFallback);
2311
1650
  } finally {
2312
- if (this._doConnectAbortController === controller) {
2313
- this._connectingPromise = null;
2314
- this._doConnectAbortController = null;
2315
- }
1651
+ this._connectingPromise = null;
1652
+ this._doConnectAbortController = null;
2316
1653
  }
2317
1654
  })();
2318
1655
  }
@@ -2321,57 +1658,22 @@ var _LedgerAdapter = class _LedgerAdapter {
2321
1658
  // Layer 1 main loop — the ONLY place in SDK that emits unlock dialog.
2322
1659
  // Bounded by MAX_DOCONNECT_CONFIRMS — after N Confirms with no progress,
2323
1660
  // throw DeviceNotFound so the user is kicked out of the loop.
2324
- async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false, preserveOperationId, context) {
2325
- _LedgerAdapter._throwIfAborted(internalSignal);
2326
- if (this.connector.availableTransports?.includes("usb") && this.connector.availableTransports.includes("ble")) {
2327
- this._activeConnectionType = "usb";
2328
- const usbDevices = await this._searchDevices({ transportType: "usb" }, internalSignal);
2329
- _LedgerAdapter._throwIfAborted(internalSignal);
2330
- if (usbDevices.length > 0) {
2331
- const knownUsb = context?.knownConnections?.find(
2332
- (connection) => connection.transport === "usb"
2333
- );
2334
- const usbTarget = knownUsb?.transport === "usb" ? knownUsb.connectId : usbDevices.find((device) => device.connectId === targetConnectId)?.connectId;
2335
- return this._connectFirstOrSelect(
2336
- usbDevices,
2337
- usbTarget,
2338
- allowUsbEphemeralFallback,
2339
- preserveOperationId,
2340
- context,
2341
- internalSignal
2342
- );
2343
- }
2344
- if (context?.supportedTransports && !context.supportedTransports.includes("ble")) {
2345
- throw (0, import_hwk_adapter_core3.createHwkError)({
2346
- code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
2347
- message: "This Ledger model has no Bluetooth transport; connect it over USB"
2348
- });
2349
- }
2350
- this._activeConnectionType = "ble";
2351
- const knownBle = context?.knownConnections?.find(
2352
- (connection) => connection.transport === "ble"
2353
- );
2354
- if (knownBle?.transport === "ble") {
2355
- return this._connectDeviceOrThrow(knownBle.connectId, preserveOperationId, internalSignal);
2356
- }
2357
- if (targetConnectId && context?.knownConnections === void 0) {
2358
- throw (0, import_hwk_adapter_core3.createHwkError)({
2359
- code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
2360
- message: "Ledger connection metadata is required before starting Bluetooth binding"
2361
- });
1661
+ async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false) {
1662
+ if (isLedgerBleConnectionType(this.connector.connectionType) && targetConnectId) {
1663
+ try {
1664
+ return await this._connectDeviceOrThrow(targetConnectId);
1665
+ } catch (err) {
1666
+ if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
1667
+ throw err;
1668
+ }
1669
+ this._discoveredDevices.delete(targetConnectId);
1670
+ if (isDeviceDisconnectedError(err)) {
1671
+ try {
1672
+ this.connector.reset?.();
1673
+ } catch {
1674
+ }
1675
+ }
2362
1676
  }
2363
- const bleDevices = await this._searchDevices({ transportType: "ble" }, internalSignal);
2364
- return this._connectFirstOrSelect(
2365
- bleDevices,
2366
- void 0,
2367
- allowUsbEphemeralFallback,
2368
- preserveOperationId,
2369
- context,
2370
- internalSignal
2371
- );
2372
- }
2373
- if (this._isBleConnection() && targetConnectId) {
2374
- return this._connectDeviceOrThrow(targetConnectId, preserveOperationId, internalSignal);
2375
1677
  }
2376
1678
  let confirms = 0;
2377
1679
  while (!internalSignal.aborted) {
@@ -2379,28 +1681,22 @@ var _LedgerAdapter = class _LedgerAdapter {
2379
1681
  type: import_hwk_adapter_core3.EConnectorInteraction.Searching,
2380
1682
  payload: { sessionId: "" }
2381
1683
  });
2382
- let devices = await this._searchDevices(void 0, internalSignal);
2383
- _LedgerAdapter._throwIfAborted(internalSignal);
1684
+ let devices = await this.searchDevices();
2384
1685
  if (devices.length === 0) {
2385
1686
  for (let i = 0; i < 3 && !internalSignal.aborted; i += 1) {
2386
1687
  await new Promise((resolve) => {
2387
1688
  setTimeout(resolve, import_hwk_adapter_core3.DEVICE_CONNECT_RETRY_DELAY_MS);
2388
1689
  });
2389
- _LedgerAdapter._throwIfAborted(internalSignal);
2390
- devices = await this._searchDevices(void 0, internalSignal);
2391
- _LedgerAdapter._throwIfAborted(internalSignal);
1690
+ devices = await this.searchDevices();
2392
1691
  if (devices.length > 0) break;
2393
1692
  }
2394
1693
  }
2395
- if (devices.length > 0 || this._isBleConnection() && allowUsbEphemeralFallback) {
1694
+ if (devices.length > 0) {
2396
1695
  try {
2397
1696
  return await this._connectFirstOrSelect(
2398
1697
  devices,
2399
1698
  targetConnectId,
2400
- allowUsbEphemeralFallback,
2401
- preserveOperationId,
2402
- context,
2403
- internalSignal
1699
+ allowUsbEphemeralFallback
2404
1700
  );
2405
1701
  } catch (err) {
2406
1702
  if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
@@ -2429,133 +1725,45 @@ var _LedgerAdapter = class _LedgerAdapter {
2429
1725
  _LedgerAdapter._throwIfAborted(internalSignal);
2430
1726
  throw new Error("_doConnect aborted");
2431
1727
  }
2432
- async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback, preserveOperationId, context, signal) {
2433
- _LedgerAdapter._throwIfAborted(signal);
1728
+ async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback = false) {
2434
1729
  if (targetConnectId) {
2435
1730
  const target = devices.find(
2436
1731
  (d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
2437
1732
  );
2438
1733
  if (target) {
2439
- return this._connectDeviceOrThrow(target.connectId, preserveOperationId, signal);
1734
+ return this._connectDeviceOrThrow(target.connectId);
2440
1735
  }
2441
- if (!this._isBleConnection() && devices.length === 1 && allowUsbEphemeralFallback) {
1736
+ if (!isLedgerBleConnectionType(this.connector.connectionType) && devices.length === 1 && allowUsbEphemeralFallback) {
2442
1737
  debugLog(
2443
1738
  `[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} for fingerprint-verified recovery`
2444
1739
  );
2445
- return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
1740
+ return this._connectDeviceOrThrow(devices[0].connectId);
2446
1741
  }
2447
- if (!this._isBleConnection() || !allowUsbEphemeralFallback) {
2448
- const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
2449
- code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
2450
- });
2451
- if (this._isBleConnection()) {
2452
- err._tag = ERROR_TAG.DeviceNotAdvertising;
2453
- }
2454
- throw err;
1742
+ const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
1743
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
1744
+ });
1745
+ if (isLedgerBleConnectionType(this.connector.connectionType)) {
1746
+ err._tag = ERROR_TAG.DeviceNotAdvertising;
2455
1747
  }
1748
+ throw err;
2456
1749
  }
2457
- const requiresBleSelection = this._isBleConnection();
2458
- if (requiresBleSelection && !allowUsbEphemeralFallback) {
1750
+ if (isLedgerBleConnectionType(this.connector.connectionType)) {
2459
1751
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
2460
1752
  code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
2461
1753
  });
2462
1754
  }
2463
- if (requiresBleSelection && context?.allowDeviceSelection !== false) {
2464
- const bindingSessionId = context?.bindingSessionId ?? this._uiRegistry.createRequestId();
2465
- if (context) context.bindingSessionId = bindingSessionId;
2466
- const allowUsbFallback = context?.bindingReason !== "manual-rebind" && Boolean(this.connector.availableTransports?.includes("usb"));
2467
- const knownUsb = context?.knownConnections?.find(
2468
- (connection) => connection.transport === "usb"
2469
- );
2470
- const usbConnectId = knownUsb?.transport === "usb" ? knownUsb.connectId : targetConnectId;
2471
- const { device, requestId } = await (0, import_hwk_adapter_core3.requestBleDeviceSelection)({
2472
- emitter: this.emitter,
2473
- registry: this._uiRegistry,
2474
- signal,
2475
- allowUsbFallback,
2476
- scan: async () => {
2477
- if (allowUsbFallback) {
2478
- const usbDevices = await this._searchDevices({ transportType: "usb" }, signal);
2479
- const candidate = usbDevices.find(
2480
- (device2) => device2.connectionType === "usb" && device2.connectId === usbConnectId
2481
- ) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
2482
- if (candidate) return [candidate];
2483
- }
2484
- return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId));
2485
- },
2486
- request: {
2487
- devices: devices.filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId)),
2488
- bindingSessionId,
2489
- rejectedConnectId: context?.rejectedConnectId,
2490
- context: {
2491
- kind: "bind-connection",
2492
- transport: "ble",
2493
- reason: context?.bindingReason ?? (targetConnectId ? "known-connection-unavailable" : "missing-binding")
2494
- },
2495
- extra: context?.extra,
2496
- operationId: preserveOperationId
2497
- }
2498
- });
2499
- if (device.connectionType === "usb") {
2500
- this._activeConnectionType = "usb";
2501
- if (context) context.selectedConnection = void 0;
2502
- if (preserveOperationId) this._pendingOperationBindings.delete(preserveOperationId);
2503
- this._bindingSelectionRequestId = requestId;
2504
- this._finishBleBinding("cancelled");
2505
- return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
2506
- }
2507
- if (context) context.selectedConnection = { connectId: device.connectId, requestId };
2508
- this._bindingSelectionRequestId = requestId;
2509
- return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
2510
- }
2511
- if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
2512
- if (context?.allowDeviceSelection === false || !this.emitter.listenerCount(import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE)) {
2513
- throw (0, import_hwk_adapter_core3.createHwkError)({
2514
- code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
2515
- message: "Select a Ledger device before continuing"
2516
- });
2517
- }
2518
- const requestId = this._uiRegistry.createRequestId();
2519
- const operationId = this._activeOperationId();
2520
- const waitPromise = this._uiRegistry.wait(
2521
- import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE,
2522
- { requestId, operationId }
2523
- );
2524
- this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE, {
2525
- type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_SELECT_DEVICE,
2526
- payload: {
2527
- devices,
2528
- requestId,
2529
- operationId,
2530
- context: requiresBleSelection ? {
2531
- kind: "bind-connection",
2532
- transport: "ble",
2533
- reason: targetConnectId ? "known-connection-unavailable" : "missing-binding"
2534
- } : { kind: "select-device", transport: "usb", reason: "multiple-candidates" },
2535
- extra: context?.extra
2536
- }
2537
- });
2538
- const { sdkConnectId } = await this._abortable(signal, waitPromise);
2539
- _LedgerAdapter._throwIfAborted(signal);
2540
- const selected = devices.find((device) => device.connectId === sdkConnectId);
2541
- if (!selected) {
2542
- throw Object.assign(new Error("Selected Ledger is no longer available"), {
2543
- code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
2544
- });
2545
- }
2546
- if (context && requiresBleSelection)
2547
- context.selectedConnection = { connectId: selected.connectId, requestId };
2548
- return this._connectDeviceOrThrow(selected.connectId, preserveOperationId, signal);
1755
+ if (devices.length > 1) {
1756
+ throw createMultipleUsbLedgerDevicesError();
2549
1757
  }
2550
1758
  if (devices.length !== 1) {
2551
1759
  throw Object.assign(new Error("Ledger device not found."), {
2552
1760
  code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound
2553
1761
  });
2554
1762
  }
2555
- return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
1763
+ return this._connectDeviceOrThrow(devices[0].connectId);
2556
1764
  }
2557
- async _connectDeviceOrThrow(chosenConnectId, preserveOperationId, signal) {
2558
- const result = await this._connectTarget(chosenConnectId, preserveOperationId, signal);
1765
+ async _connectDeviceOrThrow(chosenConnectId) {
1766
+ const result = await this.connectDevice(chosenConnectId);
2559
1767
  if (!result.success) {
2560
1768
  const payload = result.payload;
2561
1769
  const rethrow = Object.assign(new Error(payload.error), {
@@ -2566,7 +1774,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2566
1774
  }
2567
1775
  throw rethrow;
2568
1776
  }
2569
- return result.payload;
1777
+ return chosenConnectId;
2570
1778
  }
2571
1779
  /**
2572
1780
  * Call the connector with automatic session resolution and disconnect retry.
@@ -2596,133 +1804,33 @@ var _LedgerAdapter = class _LedgerAdapter {
2596
1804
  * lives in one place.
2597
1805
  */
2598
1806
  async _callConnector(sessionId, method, params, signal) {
2599
- this._assertConnectorReady(method);
2600
- if (signal?.aborted) throw this._abortReason(signal);
2601
- const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
2602
- let promise;
2603
- try {
2604
- promise = this.connector.call(sessionId, method, params).finally(releaseOperation);
2605
- } catch (error) {
2606
- releaseOperation();
2607
- throw error;
2608
- }
1807
+ const promise = this.connector.call(sessionId, method, params);
2609
1808
  const result = signal ? await this._abortable(signal, promise) : await promise;
2610
1809
  return this._unwrapConnectorResult(result);
2611
1810
  }
2612
- _assertConnectorReady(method) {
2613
- if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
2614
- throw _LedgerAdapter._createDeviceBusyError(method);
2615
- }
2616
- }
2617
- _runConnectorTeardown(task) {
2618
- const previous = this._connectorTeardownTail;
2619
- let releaseTail = () => void 0;
2620
- this._connectorTeardownTail = new Promise((resolve) => {
2621
- releaseTail = resolve;
2622
- });
2623
- this._pendingConnectorTeardowns += 1;
2624
- return (async () => {
2625
- try {
2626
- await previous;
2627
- await this._waitForConnectorOperationsToDrain();
2628
- await task();
2629
- } finally {
2630
- this._pendingConnectorTeardowns -= 1;
2631
- releaseTail();
2632
- }
2633
- })();
2634
- }
2635
- _waitForConnectorOperationsToDrain() {
2636
- if (this._unsettledConnectorOperations.size === 0) {
2637
- return Promise.resolve();
2638
- }
2639
- return new Promise((resolve) => {
2640
- this._connectorIdleWaiters.add(resolve);
2641
- });
2642
- }
2643
- _retainConnectorOperation(key) {
2644
- this._unsettledConnectorOperations.set(
2645
- key,
2646
- (this._unsettledConnectorOperations.get(key) ?? 0) + 1
2647
- );
2648
- let released = false;
2649
- return () => {
2650
- if (released) return;
2651
- released = true;
2652
- const remaining = (this._unsettledConnectorOperations.get(key) ?? 1) - 1;
2653
- if (remaining > 0) {
2654
- this._unsettledConnectorOperations.set(key, remaining);
2655
- } else {
2656
- this._unsettledConnectorOperations.delete(key);
2657
- }
2658
- if (this._unsettledConnectorOperations.size === 0) {
2659
- for (const resolve of this._connectorIdleWaiters) resolve();
2660
- this._connectorIdleWaiters.clear();
2661
- }
2662
- };
2663
- }
2664
1811
  async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
2665
- const positionalOperationId = (0, import_hwk_adapter_core3.isHardwareOperationId)(connectId) ? connectId : void 0;
2666
- if (positionalOperationId && commonParams?.operationId && positionalOperationId !== commonParams.operationId) {
2667
- throw (0, import_hwk_adapter_core3.createHwkError)({
2668
- code: import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
2669
- message: "Conflicting Ledger operation ids",
2670
- params: {
2671
- positionalOperationId,
2672
- commonOperationId: commonParams.operationId
2673
- }
2674
- });
2675
- }
2676
- const operationId = commonParams?.operationId ?? positionalOperationId;
2677
- const operation = operationId ? this._operations.resolve(operationId) : void 0;
2678
- const releaseOperationRetention = operationId ? this._operations.retain(operationId) : void 0;
2679
- const effectiveConnectId = operation?.connectId ?? connectId;
2680
- debugLog("[LedgerAdapter][REQ]", {
2681
- method,
2682
- connectId: effectiveConnectId || "(empty)",
2683
- params
2684
- });
2685
- const queueKey = ledgerQueueKey({ operationId, connectId: effectiveConnectId });
1812
+ debugLog("[LedgerAdapter][REQ]", { method, connectId: connectId || "(empty)", params });
1813
+ const queueKey = connectId || "__ledger_default__";
2686
1814
  try {
2687
1815
  const result = await this._jobQueue.enqueue(
2688
1816
  queueKey,
2689
- async (signal) => {
2690
- if (operationId) this._activeOperationJobs.add(operationId);
2691
- try {
2692
- return await this._runConnectorCall(
2693
- effectiveConnectId,
2694
- method,
2695
- params,
2696
- signal,
2697
- fingerprint,
2698
- permissionDeviceId,
2699
- commonParams,
2700
- installContext ?? {},
2701
- operationId
2702
- );
2703
- } catch (error) {
2704
- this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
2705
- throw error;
2706
- } finally {
2707
- if (operationId) {
2708
- this._activeOperationJobs.delete(operationId);
2709
- if (this._pendingOperationDisconnects.delete(operationId)) {
2710
- await this._releaseLostOperationConnection(operationId);
2711
- }
2712
- }
2713
- }
2714
- },
1817
+ async (signal) => this._runConnectorCall(
1818
+ connectId,
1819
+ method,
1820
+ params,
1821
+ signal,
1822
+ fingerprint,
1823
+ permissionDeviceId,
1824
+ commonParams,
1825
+ installContext
1826
+ ),
2715
1827
  {
2716
1828
  label: method,
2717
1829
  rejectIfBusy: true,
2718
1830
  busyError: _LedgerAdapter._createDeviceBusyError(method)
2719
1831
  }
2720
1832
  );
2721
- debugLog("[LedgerAdapter][RES]", {
2722
- method,
2723
- success: true,
2724
- payload: redactResultForLog(result)
2725
- });
1833
+ debugLog("[LedgerAdapter][RES]", { method, success: true, payload: result });
2726
1834
  return result;
2727
1835
  } catch (err) {
2728
1836
  const e = err;
@@ -2736,22 +1844,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2736
1844
  }
2737
1845
  });
2738
1846
  throw err;
2739
- } finally {
2740
- releaseOperationRetention?.();
2741
1847
  }
2742
1848
  }
2743
- /** Hermes/RN polyfills don't always populate signal.reason; fall back. */
2744
- _abortReason(signal) {
2745
- return signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
2746
- }
2747
1849
  /**
2748
1850
  * Race a promise against an abort signal. On abort, rejects with
2749
1851
  * signal.reason → instance _lastCancelReason → generic Error('Aborted').
2750
1852
  */
2751
1853
  _abortable(signal, promise) {
2752
- const getAbortReason = () => this._abortReason(signal);
1854
+ const getAbortReason = () => signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
2753
1855
  if (signal.aborted) {
2754
- void promise.catch(() => void 0);
2755
1856
  return Promise.reject(getAbortReason());
2756
1857
  }
2757
1858
  return new Promise((resolve, reject) => {
@@ -2778,15 +1879,13 @@ var _LedgerAdapter = class _LedgerAdapter {
2778
1879
  }
2779
1880
  }
2780
1881
  /** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
2781
- async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext, operationId, lockedRetryBudget = _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET) {
1882
+ async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext) {
2782
1883
  _LedgerAdapter._throwIfAborted(signal);
2783
- if (!operationId) {
2784
- await this._ensureDevicePermission(
2785
- connectId,
2786
- permissionDeviceId ?? fingerprint?.deviceId,
2787
- signal
2788
- );
2789
- }
1884
+ await this._ensureDevicePermission(
1885
+ connectId,
1886
+ permissionDeviceId ?? fingerprint?.deviceId,
1887
+ signal
1888
+ );
2790
1889
  _LedgerAdapter._throwIfAborted(signal);
2791
1890
  let effectiveParams = params;
2792
1891
  if (method === "btcGetPublicKey") {
@@ -2800,135 +1899,36 @@ var _LedgerAdapter = class _LedgerAdapter {
2800
1899
  effectiveParams = gatedParams;
2801
1900
  }
2802
1901
  const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
2803
- let businessCallStarted = false;
2804
- const verifiedBleTarget = connectId ? this._verifiedBleReconnectTargets.get(connectId) : void 0;
2805
- const knownTransport = this._isBleConnection() ? "ble" : "usb";
2806
- const hintedConnectId = commonParams?.knownConnections?.find(
2807
- (connection) => connection.transport === knownTransport
2808
- );
2809
- const inputConnectId = hintedConnectId && hintedConnectId.transport !== "qr" ? hintedConnectId.connectId : connectId;
2810
- const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && verifiedBleTarget?.chain === fingerprint.chain && verifiedBleTarget.fingerprint === fingerprint.deviceId ? verifiedBleTarget.connectId : inputConnectId;
2811
- const connectionAttempt = { ...commonParams };
2812
- const bundleConnection = installContext?.connection;
2813
- if (bundleConnection && this._sessions.get(bundleConnection.connectId) !== bundleConnection.sessionId) {
2814
- throw (0, import_hwk_adapter_core3.createHwkError)({
2815
- code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
2816
- message: "Ledger all-network connection ended"
2817
- });
2818
- }
2819
- let resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
2820
- preferredConnectId,
1902
+ const resolvedConnectId = await this.ensureConnected(
1903
+ connectId,
2821
1904
  signal,
2822
- allowUsbEphemeralFallback,
2823
- void 0,
2824
- connectionAttempt
1905
+ allowUsbEphemeralFallback
2825
1906
  );
2826
- let sessionId = this._sessions.get(resolvedConnectId);
2827
- if (sessionId && installContext && !installContext.connection) {
2828
- installContext.connection = { connectId: resolvedConnectId, sessionId };
2829
- }
1907
+ const sessionId = this._sessions.get(resolvedConnectId);
2830
1908
  if (!sessionId) {
2831
- if (operationId) {
2832
- this._operations.end(operationId, "disconnect");
2833
- throw (0, import_hwk_adapter_core3.createHwkError)({
2834
- code: import_hwk_adapter_core3.HardwareErrorCode.OperationEnded,
2835
- message: "Ledger operation connection is no longer active",
2836
- params: { operationId, reason: "disconnect" }
2837
- });
2838
- }
2839
1909
  throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
2840
1910
  _tag: ERROR_TAG.DeviceSessionNotFound
2841
1911
  });
2842
1912
  }
2843
1913
  try {
2844
1914
  if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2845
- for (; ; ) {
2846
- const fp = await this._abortable(
2847
- signal,
2848
- this._verifyDeviceFingerprintWithSession(
2849
- sessionId,
2850
- fingerprint.deviceId,
2851
- fingerprint.chain
2852
- )
2853
- );
2854
- if (fp.success) break;
2855
- const binding = operationId ? this._pendingOperationBindings.get(operationId) : connectionAttempt;
2856
- if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== resolvedConnectId) {
2857
- throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2858
- code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
2859
- });
2860
- }
2861
- binding.rejectedConnectIds ?? (binding.rejectedConnectIds = /* @__PURE__ */ new Set());
2862
- binding.rejectedConnectIds.add(resolvedConnectId);
2863
- binding.rejectedConnectId = resolvedConnectId;
2864
- this._sessions.delete(resolvedConnectId);
2865
- await this.connector.disconnect(sessionId);
2866
- if (operationId) this._pendingOperationDisconnects.delete(operationId);
2867
- _LedgerAdapter._throwIfAborted(signal);
2868
- resolvedConnectId = await this._connectFirstOrSelect(
2869
- [],
2870
- void 0,
2871
- true,
2872
- operationId,
2873
- binding,
2874
- signal
2875
- );
2876
- const selectedSession = this._sessions.get(resolvedConnectId);
2877
- const selectedDevice = this._discoveredDevices.get(resolvedConnectId);
2878
- if (!selectedSession || !selectedDevice)
2879
- throw (0, import_hwk_adapter_core3.createHwkError)({
2880
- code: import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
2881
- message: "Selected Ledger connection ended"
2882
- });
2883
- sessionId = selectedSession;
2884
- if (operationId)
2885
- this._operations.rebind(operationId, {
2886
- connectId: resolvedConnectId,
2887
- device: selectedDevice,
2888
- // Same source as `_createOperation`: the selected transport, not
2889
- // the device snapshot a session connect overwrote.
2890
- connectionType: this._isBleConnection() ? "ble" : "usb",
2891
- connectionKeys: [sessionId]
2892
- });
2893
- if (installContext)
2894
- installContext.connection = { connectId: resolvedConnectId, sessionId };
2895
- }
2896
- await this._publishVerifiedBleBinding(
2897
- resolvedConnectId,
2898
- fingerprint.chain,
2899
- fingerprint.deviceId,
2900
- connectionAttempt,
2901
- operationId,
2902
- signal
1915
+ const fp = await this._abortable(
1916
+ signal,
1917
+ this._verifyDeviceFingerprintWithSession(
1918
+ sessionId,
1919
+ fingerprint.deviceId,
1920
+ fingerprint.chain
1921
+ )
2903
1922
  );
2904
- if (!operationId && connectionAttempt.selectedConnection?.connectId === resolvedConnectId && this._isBleConnection()) {
2905
- this._verifiedBleReconnectTargets.set(connectId, {
2906
- connectId: resolvedConnectId,
2907
- chain: fingerprint.chain,
2908
- fingerprint: fingerprint.deviceId
1923
+ if (!fp.success) {
1924
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1925
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
2909
1926
  });
2910
1927
  }
2911
1928
  }
2912
- businessCallStarted = true;
2913
1929
  return await this._callConnector(sessionId, method, effectiveParams, signal);
2914
1930
  } catch (err) {
2915
1931
  if (signal.aborted) throw err;
2916
- if (isLostConnectionError(err)) {
2917
- this._discoveredDevices.delete(resolvedConnectId);
2918
- if (operationId) await this._releaseLostOperationConnection(operationId);
2919
- else {
2920
- this._sessions.delete(resolvedConnectId);
2921
- await this.connector.disconnect(sessionId).catch(() => void 0);
2922
- }
2923
- const ambiguous = businessCallStarted && !(0, import_hwk_adapter_core3.canReplayHardwareMethodAfterTransportFailure)(method);
2924
- const interactionParams = operationId ? { operationId, reason: "disconnect" } : void 0;
2925
- throw (0, import_hwk_adapter_core3.createHwkError)({
2926
- code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : mapLedgerError(err).code,
2927
- message: "Ledger operation connection was lost; start a new operation",
2928
- params: ambiguous ? (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(method, { operationId }) : interactionParams,
2929
- recovery: ambiguous ? { scope: "unknown" } : void 0
2930
- });
2931
- }
2932
1932
  const errObj = err;
2933
1933
  debugLog("[LedgerAdapter] connectorCall error:", method, {
2934
1934
  message: errObj?.message,
@@ -2940,60 +1940,6 @@ var _LedgerAdapter = class _LedgerAdapter {
2940
1940
  isNotAdvertising: isDeviceNotAdvertisingError(err),
2941
1941
  isStuckApp: isStuckAppStateError(err)
2942
1942
  });
2943
- const assertSessionCurrent = (message) => {
2944
- if (this._sessions.get(resolvedConnectId) === sessionId) return;
2945
- throw (0, import_hwk_adapter_core3.createHwkError)({
2946
- code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected,
2947
- message
2948
- });
2949
- };
2950
- if ((isDeviceLockedError(err) || errObj?.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceLocked) && lockedRetryBudget > 0) {
2951
- await this._waitForDeviceConnect(signal);
2952
- assertSessionCurrent("Ledger connection ended while waiting for unlock");
2953
- return this._runConnectorCall(
2954
- resolvedConnectId,
2955
- method,
2956
- effectiveParams,
2957
- signal,
2958
- fingerprint,
2959
- permissionDeviceId,
2960
- commonParams,
2961
- installContext,
2962
- operationId,
2963
- lockedRetryBudget - 1
2964
- );
2965
- }
2966
- if (businessCallStarted && isStuckAppStateError(err)) {
2967
- await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
2968
- assertSessionCurrent("Ledger connection ended during the app transition");
2969
- try {
2970
- return await this._callConnector(sessionId, method, effectiveParams, signal);
2971
- } catch (retryErr) {
2972
- if (isStuckAppStateError(retryErr)) throw err;
2973
- if (isLostConnectionError(retryErr)) {
2974
- if (operationId) await this._releaseLostOperationConnection(operationId);
2975
- else {
2976
- this._sessions.delete(resolvedConnectId);
2977
- this._discoveredDevices.delete(resolvedConnectId);
2978
- await this.connector.disconnect(sessionId).catch(() => void 0);
2979
- }
2980
- const ambiguous = !(0, import_hwk_adapter_core3.canReplayHardwareMethodAfterTransportFailure)(method);
2981
- throw (0, import_hwk_adapter_core3.createHwkError)({
2982
- code: operationId ? import_hwk_adapter_core3.HardwareErrorCode.OperationEnded : mapLedgerError(retryErr).code,
2983
- message: `Ledger ${method} may have completed before the connection was lost`,
2984
- params: ambiguous ? (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(method, { operationId, reason: "disconnect" }) : { operationId, reason: "disconnect" },
2985
- recovery: ambiguous ? { scope: "unknown" } : void 0
2986
- });
2987
- }
2988
- throw retryErr;
2989
- }
2990
- }
2991
- if (!operationId && errObj?.code === import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch) {
2992
- this._sessions.delete(resolvedConnectId);
2993
- this._discoveredDevices.delete(resolvedConnectId);
2994
- await this.connector.disconnect(sessionId).catch(() => void 0);
2995
- throw err;
2996
- }
2997
1943
  const autoInstallApp = commonParams?.autoInstallApp ?? this._defaultAutoInstallApp;
2998
1944
  const isAppMissing = isAppNotInstalledError(err) || err?.code === import_hwk_adapter_core3.HardwareErrorCode.AppNotInstalled;
2999
1945
  if (autoInstallApp && isAppMissing) {
@@ -3049,14 +1995,157 @@ var _LedgerAdapter = class _LedgerAdapter {
3049
1995
  fingerprint,
3050
1996
  permissionDeviceId,
3051
1997
  commonParams,
3052
- installContext,
3053
- operationId
1998
+ installContext
3054
1999
  );
3055
2000
  }
3056
2001
  }
2002
+ if (isStuckAppStateError(err)) {
2003
+ try {
2004
+ this._sessions.delete(resolvedConnectId);
2005
+ this._discoveredDevices.delete(resolvedConnectId);
2006
+ this.connector.reset?.();
2007
+ } catch {
2008
+ }
2009
+ debugLog(
2010
+ "[LedgerAdapter] stuck-app retry: method=",
2011
+ method,
2012
+ "delayMs=",
2013
+ _LedgerAdapter.STUCK_APP_RETRY_DELAY_MS,
2014
+ "_tag=",
2015
+ err?._tag
2016
+ );
2017
+ try {
2018
+ const retryResult = await this._retryAfterStuckApp(
2019
+ resolvedConnectId,
2020
+ method,
2021
+ effectiveParams,
2022
+ signal,
2023
+ err,
2024
+ fingerprint
2025
+ );
2026
+ debugLog("[LedgerAdapter] stuck-app retry succeeded: method=", method);
2027
+ return retryResult;
2028
+ } catch (retryErr) {
2029
+ if (signal.aborted) throw retryErr;
2030
+ if (isStuckAppStateError(retryErr)) {
2031
+ debugLog("[LedgerAdapter] stuck-app retry exhausted (2nd 6901): method=", method);
2032
+ throw err;
2033
+ }
2034
+ debugLog(
2035
+ "[LedgerAdapter] stuck-app retry threw non-stuck error: method=",
2036
+ method,
2037
+ "retryErrTag=",
2038
+ retryErr?._tag
2039
+ );
2040
+ throw retryErr;
2041
+ }
2042
+ }
2043
+ if (isDeviceLockedError(err) || isDeviceNotAdvertisingError(err) || isDeviceDisconnectedError(err)) {
2044
+ let lastErr = err;
2045
+ for (let attempt = 0; attempt < _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET; attempt += 1) {
2046
+ if (signal.aborted) throw lastErr;
2047
+ try {
2048
+ this._sessions.delete(resolvedConnectId);
2049
+ this._discoveredDevices.delete(resolvedConnectId);
2050
+ if (isDeviceDisconnectedError(lastErr)) {
2051
+ try {
2052
+ this.connector.reset?.();
2053
+ } catch {
2054
+ }
2055
+ }
2056
+ const reConnectId = await this.ensureConnected(
2057
+ resolvedConnectId,
2058
+ signal,
2059
+ allowUsbEphemeralFallback
2060
+ );
2061
+ const reSessionId = this._sessions.get(reConnectId);
2062
+ if (!reSessionId) throw lastErr;
2063
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2064
+ const fp = await this._abortable(
2065
+ signal,
2066
+ this._verifyDeviceFingerprintWithSession(
2067
+ reSessionId,
2068
+ fingerprint.deviceId,
2069
+ fingerprint.chain
2070
+ )
2071
+ );
2072
+ if (!fp.success) {
2073
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2074
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
2075
+ });
2076
+ }
2077
+ }
2078
+ return await this._callConnector(reSessionId, method, effectiveParams, signal);
2079
+ } catch (retryErr) {
2080
+ if (signal.aborted) throw retryErr;
2081
+ lastErr = retryErr;
2082
+ const canRetry = attempt < _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET - 1 && (isDeviceLockedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isDeviceDisconnectedError(retryErr));
2083
+ if (!canRetry) {
2084
+ throw retryErr;
2085
+ }
2086
+ }
2087
+ }
2088
+ throw lastErr;
2089
+ }
2090
+ if (isTimeoutError(err)) {
2091
+ debugLog("[LedgerAdapter] timeout, retrying with fresh connection...");
2092
+ this._discoveredDevices.delete(resolvedConnectId);
2093
+ return this._retryWithFreshConnection(
2094
+ resolvedConnectId,
2095
+ method,
2096
+ effectiveParams,
2097
+ signal,
2098
+ err,
2099
+ fingerprint
2100
+ );
2101
+ }
2102
+ if (isConnectionLevelError(err)) {
2103
+ debugLog("[LedgerAdapter] connection-level fail-closed reset");
2104
+ this._sessions.delete(resolvedConnectId);
2105
+ this._discoveredDevices.delete(resolvedConnectId);
2106
+ this.connector.reset();
2107
+ const codeNum = err?.code;
2108
+ throw Object.assign(err, {
2109
+ code: codeNum ?? import_hwk_adapter_core3.HardwareErrorCode.DeviceDisconnected
2110
+ });
2111
+ }
3057
2112
  throw err;
3058
2113
  }
3059
2114
  }
2115
+ /**
2116
+ * Stuck-app recovery: pause for the device's UI transition, then retry once.
2117
+ *
2118
+ * Caller has already cleared the session + reset connector. We wait so Stax
2119
+ * finishes its post-CloseApp animation, then go through ensureConnected +
2120
+ * fingerprint check + call exactly once. Caller decides what to do on a
2121
+ * second stuck-app hit.
2122
+ */
2123
+ async _retryAfterStuckApp(resolvedConnectId, method, params, signal, originalErr, fingerprint) {
2124
+ await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
2125
+ const retryConnectId = await this.ensureConnected(
2126
+ resolvedConnectId,
2127
+ signal,
2128
+ !!fingerprint?.deviceId && !fingerprint.skipFingerprint
2129
+ );
2130
+ const retrySessionId = this._sessions.get(retryConnectId);
2131
+ if (!retrySessionId) throw originalErr;
2132
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2133
+ const fp = await this._abortable(
2134
+ signal,
2135
+ this._verifyDeviceFingerprintWithSession(
2136
+ retrySessionId,
2137
+ fingerprint.deviceId,
2138
+ fingerprint.chain
2139
+ )
2140
+ );
2141
+ if (!fp.success) {
2142
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2143
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
2144
+ });
2145
+ }
2146
+ }
2147
+ return this._callConnector(retrySessionId, method, params, signal);
2148
+ }
3060
2149
  _sleepAbortable(ms, signal) {
3061
2150
  return new Promise((resolve, reject) => {
3062
2151
  if (signal.aborted) {
@@ -3075,22 +2164,111 @@ var _LedgerAdapter = class _LedgerAdapter {
3075
2164
  });
3076
2165
  }
3077
2166
  /**
3078
- * Ensure OS-level Bluetooth/USB permission; times out after 60s if the host never
3079
- * replies. Without connectId the request is environment-level, else device-level.
2167
+ * Clear stale session, reconnect, and retry the call.
2168
+ *
2169
+ * Timeout recovery starts with a full connector reset. After an APDU
2170
+ * timeout, DMK/transport state may still emit malformed responses; retrying
2171
+ * on the same DMK can poison the next chain switch.
2172
+ */
2173
+ async _retryWithFreshConnection(targetConnectId, method, params, signal, originalErr, fingerprint) {
2174
+ this.connector.reset();
2175
+ this._sessions.clear();
2176
+ this._discoveredDevices.clear();
2177
+ this._connectingPromise = null;
2178
+ const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
2179
+ const retryConnectId = await this.ensureConnected(
2180
+ targetConnectId,
2181
+ signal,
2182
+ allowUsbEphemeralFallback
2183
+ );
2184
+ const retrySessionId = this._sessions.get(retryConnectId);
2185
+ if (!retrySessionId) {
2186
+ throw originalErr;
2187
+ }
2188
+ try {
2189
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2190
+ const fp = await this._abortable(
2191
+ signal,
2192
+ this._verifyDeviceFingerprintWithSession(
2193
+ retrySessionId,
2194
+ fingerprint.deviceId,
2195
+ fingerprint.chain
2196
+ )
2197
+ );
2198
+ if (!fp.success) {
2199
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2200
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
2201
+ });
2202
+ }
2203
+ }
2204
+ return await this._callConnector(retrySessionId, method, params, signal);
2205
+ } catch (retryErr) {
2206
+ if (signal.aborted) throw retryErr;
2207
+ this.connector.reset();
2208
+ this._sessions.clear();
2209
+ this._discoveredDevices.clear();
2210
+ this._connectingPromise = null;
2211
+ if (!isDeviceDisconnectedError(retryErr) && !isTimeoutError(retryErr)) {
2212
+ throw retryErr;
2213
+ }
2214
+ debugLog(
2215
+ "[LedgerAdapter] fresh-session retry still failed; resetting connector and rebuilding DMK"
2216
+ );
2217
+ this.connector.reset();
2218
+ this._sessions.clear();
2219
+ this._discoveredDevices.clear();
2220
+ this._connectingPromise = null;
2221
+ const finalConnectId = await this.ensureConnected(
2222
+ targetConnectId,
2223
+ signal,
2224
+ allowUsbEphemeralFallback
2225
+ );
2226
+ const finalSessionId = this._sessions.get(finalConnectId);
2227
+ if (!finalSessionId) {
2228
+ throw originalErr;
2229
+ }
2230
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2231
+ const fp = await this._abortable(
2232
+ signal,
2233
+ this._verifyDeviceFingerprintWithSession(
2234
+ finalSessionId,
2235
+ fingerprint.deviceId,
2236
+ fingerprint.chain
2237
+ )
2238
+ );
2239
+ if (!fp.success) {
2240
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2241
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch
2242
+ });
2243
+ }
2244
+ }
2245
+ return this._callConnector(finalSessionId, method, params, signal);
2246
+ }
2247
+ }
2248
+ /**
2249
+ * Ensure OS-level device permission (Bluetooth / USB) before proceeding.
2250
+ *
2251
+ * Emits `REQUEST_DEVICE_PERMISSION` and awaits the consumer's
2252
+ * `RECEIVE_DEVICE_PERMISSION` reply (60s budget covers "probe → system
2253
+ * prompt → user tap" plus a generous margin). If the consumer never wires
2254
+ * a handler or never replies, the wait times out and the operation fails
2255
+ * fast so scanners/callers don't hang silently.
2256
+ *
2257
+ * - No connectId (searchDevices): environment-level permission
2258
+ * - With connectId (business methods): device-level permission
3080
2259
  */
3081
2260
  async _ensureDevicePermission(connectId, deviceId, signal) {
3082
2261
  if (signal?.aborted) {
3083
2262
  _LedgerAdapter._throwIfAborted(signal);
3084
2263
  }
3085
2264
  const transportType = this.activeTransport ?? "hid";
3086
- const operationId = this._activeOperationId();
3087
2265
  const waitPromise = this._uiRegistry.wait(
3088
2266
  import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
3089
- { timeoutMs: 6e4, operationId }
2267
+ { timeoutMs: 6e4 }
3090
2268
  );
3091
2269
  this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_PERMISSION, {
3092
2270
  type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
3093
- payload: { transportType, connectId, deviceId, operationId }
2271
+ payload: { transportType, connectId, deviceId }
3094
2272
  });
3095
2273
  let response;
3096
2274
  const onAbort = () => {
@@ -3135,16 +2313,8 @@ var _LedgerAdapter = class _LedgerAdapter {
3135
2313
  const tag = err && typeof err === "object" ? err._tag : void 0;
3136
2314
  if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
3137
2315
  const e = err;
3138
- const params = e.code === import_hwk_adapter_core3.HardwareErrorCode.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
3139
- return ledgerFailure(
3140
- e.code,
3141
- e.message ?? "Unknown error",
3142
- e.appName,
3143
- tag,
3144
- params,
3145
- void 0,
3146
- (0, import_hwk_adapter_core3.isHwkRecoveryHint)(e.recovery) ? e.recovery : void 0
3147
- );
2316
+ const params = e.code === import_hwk_adapter_core3.HardwareErrorCode.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
2317
+ return ledgerFailure(e.code, e.message ?? "Unknown error", e.appName, tag, params);
3148
2318
  }
3149
2319
  const mapped = mapLedgerError(err);
3150
2320
  return ledgerFailure(mapped.code, mapped.message, mapped.appName, tag);
@@ -3171,7 +2341,7 @@ var _LedgerAdapter = class _LedgerAdapter {
3171
2341
  deviceId: device.deviceId,
3172
2342
  connectId: device.connectId,
3173
2343
  label: device.name,
3174
- connectionType: device.connectionType ?? this.connector.connectionType,
2344
+ connectionType: this.connector.connectionType,
3175
2345
  rssi: device.rssi,
3176
2346
  isConnectable: device.isConnectable,
3177
2347
  serialNumber: device.serialNumber,
@@ -3196,7 +2366,7 @@ _LedgerAdapter.APP_INSTALL_PROGRESS_MIN_DELTA = 0.05;
3196
2366
  var LedgerAdapter = _LedgerAdapter;
3197
2367
 
3198
2368
  // src/connector/LedgerConnectorBase.ts
3199
- var import_hwk_adapter_core12 = require("@onekeyfe/hwk-adapter-core");
2369
+ var import_hwk_adapter_core13 = require("@onekeyfe/hwk-adapter-core");
3200
2370
 
3201
2371
  // src/device/LedgerDeviceManager.ts
3202
2372
  var LedgerDeviceManager = class {
@@ -3732,7 +2902,7 @@ var SignerManager = class _SignerManager {
3732
2902
  return (args) => new import_device_signer_kit_ethereum.SignerEthBuilder(args);
3733
2903
  }
3734
2904
  static _createContextModule() {
3735
- const contextModule = new import_context_module.ContextModuleBuilder({}).setChain(import_context_module.ContextModuleChainID.Ethereum).removeDefaultLoaders().build();
2905
+ const contextModule = new import_context_module.ContextModuleBuilder({}).removeDefaultLoaders().build();
3736
2906
  return _SignerManager.wrapBlindSigningReportNonBlocking(contextModule);
3737
2907
  }
3738
2908
  static wrapBlindSigningReportNonBlocking(contextModule) {
@@ -3770,27 +2940,6 @@ function collapseSignerInteraction(interaction) {
3770
2940
  return import_hwk_adapter_core5.EConnectorInteraction.ConfirmOnDevice;
3771
2941
  }
3772
2942
  }
3773
- function wireSignerToSession(ctx, sessionId, chain, signer) {
3774
- signer.onInteraction = (interaction) => {
3775
- debugLog(`[LedgerConnector] ${chain}.onInteraction:`, interaction);
3776
- ctx.emit("ui-event", {
3777
- type: collapseSignerInteraction(interaction),
3778
- payload: { sessionId }
3779
- });
3780
- };
3781
- signer.onRegisterCanceller = (cancel) => ctx.registerCanceller(sessionId, cancel);
3782
- return signer;
3783
- }
3784
- async function runSignerCall(ctx, sessionId, call) {
3785
- try {
3786
- return await call();
3787
- } catch (err) {
3788
- ctx.invalidateSession(sessionId);
3789
- throw ctx.wrapError(err);
3790
- } finally {
3791
- ctx.clearCanceller(sessionId);
3792
- }
3793
- }
3794
2943
 
3795
2944
  // src/connector/chains/evm.ts
3796
2945
  async function evmGetAddress(ctx, sessionId, params) {
@@ -4224,7 +3373,6 @@ function _applySignaturesToPsbt(psbtHex, signatures) {
4224
3373
 
4225
3374
  // src/connector/chains/sol.ts
4226
3375
  var import_hwk_adapter_core9 = require("@onekeyfe/hwk-adapter-core");
4227
- var import_bs58 = __toESM(require("bs58"));
4228
3376
 
4229
3377
  // src/signer/SignerSol.ts
4230
3378
  var SignerSol = class {
@@ -4308,21 +3456,8 @@ async function solSignMessage(ctx, sessionId, params) {
4308
3456
  const path = normalizePath(params.path);
4309
3457
  const messageBytes = (0, import_hwk_adapter_core9.hexToBytes)(params.message);
4310
3458
  try {
4311
- if (params.messageVersion === 1) {
4312
- const preparedMessage = (0, import_hwk_adapter_core9.prepareSolanaOffchainMessageV1)({
4313
- message: messageBytes,
4314
- requiredSigners: params.requiredSigners
4315
- });
4316
- const { SignMessageVersion } = await ctx.importLedgerKit(
4317
- "@ledgerhq/device-signer-kit-solana"
4318
- );
4319
- const result2 = await solSigner.signMessage(path, preparedMessage.serializedMessage, {
4320
- version: SignMessageVersion.Raw
4321
- });
4322
- return { signature: decodeBase58Signature(result2.signature) };
4323
- }
4324
3459
  const result = await solSigner.signMessage(path, messageBytes);
4325
- return { signature: decodeBase58EnvelopeSignature(result.signature) };
3460
+ return { signature: result.signature };
4326
3461
  } catch (err) {
4327
3462
  ctx.invalidateSession(sessionId);
4328
3463
  throw ctx.wrapError(err);
@@ -4330,27 +3465,11 @@ async function solSignMessage(ctx, sessionId, params) {
4330
3465
  ctx.clearCanceller(sessionId);
4331
3466
  }
4332
3467
  }
4333
- function decodeBase58Signature(signature) {
4334
- const bytes = import_bs58.default.decode(signature);
4335
- if (bytes.length !== 64) {
4336
- throw new Error(`Ledger Solana signature must be 64 bytes, received ${bytes.length}`);
4337
- }
4338
- return (0, import_hwk_adapter_core9.bytesToHex)(bytes);
4339
- }
4340
- function decodeBase58EnvelopeSignature(envelope) {
4341
- const bytes = import_bs58.default.decode(envelope);
4342
- if (bytes.length < 65 || bytes[0] !== 1) {
4343
- throw new Error("Ledger Solana signature envelope is invalid");
4344
- }
4345
- return (0, import_hwk_adapter_core9.bytesToHex)(bytes.subarray(1, 65));
4346
- }
4347
3468
  async function _createSolSigner(ctx, sessionId) {
4348
3469
  const dmk = await ctx.getOrCreateDmk();
4349
- const { ContextModuleBuilder: ContextModuleBuilder2, ContextModuleChainID: ContextModuleChainID2 } = await ctx.importLedgerKit(
4350
- "@ledgerhq/context-module"
4351
- );
3470
+ const { ContextModuleBuilder: ContextModuleBuilder2 } = await ctx.importLedgerKit("@ledgerhq/context-module");
4352
3471
  const { SignerSolanaBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-solana");
4353
- const contextModule = new ContextModuleBuilder2({}).setChain(ContextModuleChainID2.Solana).removeDefaultLoaders().build();
3472
+ const contextModule = new ContextModuleBuilder2({}).removeDefaultLoaders().build();
4354
3473
  const sdkSigner = new SignerSolanaBuilder({ dmk, sessionId }).withContextModule(contextModule).build();
4355
3474
  const signer = new SignerSol(sdkSigner);
4356
3475
  signer.onInteraction = (interaction) => {
@@ -4366,151 +3485,360 @@ async function _createSolSigner(ctx, sessionId) {
4366
3485
  return signer;
4367
3486
  }
4368
3487
 
4369
- // src/connector/chains/zcash.ts
4370
- var import_hwk_adapter_core10 = require("@onekeyfe/hwk-adapter-core");
3488
+ // src/connector/chains/tron.ts
3489
+ var import_hwk_adapter_core12 = require("@onekeyfe/hwk-adapter-core");
3490
+ var import_hw_app_trx = __toESM(require("@ledgerhq/hw-app-trx"));
4371
3491
 
4372
- // src/signer/SignerZcash.ts
4373
- var SignerZcash = class {
4374
- // eslint-disable-next-line no-useless-constructor, no-empty-function
4375
- constructor(_sdk) {
4376
- this._sdk = _sdk;
3492
+ // src/connector/chains/legacyChainCall.ts
3493
+ var import_hwk_adapter_core11 = require("@onekeyfe/hwk-adapter-core");
3494
+
3495
+ // src/app/AppManager.ts
3496
+ var import_device_management_kit2 = require("@ledgerhq/device-management-kit");
3497
+ var import_hwk_adapter_core10 = require("@onekeyfe/hwk-adapter-core");
3498
+ var APP_NAME_MAP = {
3499
+ ETH: "Ethereum",
3500
+ BTC: "Bitcoin",
3501
+ SOL: "Solana",
3502
+ TRX: "Tron",
3503
+ XRP: "XRP",
3504
+ ADA: "Cardano",
3505
+ DOT: "Polkadot",
3506
+ ATOM: "Cosmos"
3507
+ };
3508
+ var DASHBOARD_APP_NAME = "BOLOS";
3509
+ var AppManager = class {
3510
+ constructor(dmk, options) {
3511
+ this._dmk = dmk;
3512
+ this._waitMs = options?.waitMs ?? 1e3;
3513
+ this._maxRetries = options?.maxRetries ?? 10;
4377
3514
  }
4378
- /** GET_VK: UFVK string (default) or raw 96-byte Orchard FVK. */
4379
- async getFullViewingKey(derivationPath, options) {
4380
- const action = this._sdk.getFullViewingKey(derivationPath, options);
4381
- return deviceActionToPromise(
4382
- action,
4383
- this.onInteraction,
4384
- void 0,
4385
- this.onRegisterCanceller
3515
+ /**
3516
+ * Return the Ledger app name for a given chain ticker,
3517
+ * or undefined if the chain is not supported.
3518
+ */
3519
+ static getAppName(chain) {
3520
+ return APP_NAME_MAP[chain];
3521
+ }
3522
+ /**
3523
+ * Ensure the target app is open on the device identified by `sessionId`.
3524
+ *
3525
+ * Flow:
3526
+ * 1. Check the currently running app.
3527
+ * 2. If it is already the target, return immediately.
3528
+ * 3. If a different app is running (not dashboard), close it first.
3529
+ * 4. Open the target app.
3530
+ * 5. Poll until the device confirms the target app is running.
3531
+ */
3532
+ /**
3533
+ * @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
3534
+ * device is about to display "Open <app>" on screen and wait for the
3535
+ * user's button press. UI consumers should show their "open app" prompt
3536
+ * in response. NOT called when the target app is already open (no user
3537
+ * interaction needed in that case).
3538
+ *
3539
+ * Important: OpenAppCommand is blocking. It does not resolve until the user
3540
+ * has physically confirmed on the device, so anything that runs AFTER
3541
+ * `await this._openApp(...)` lands AFTER the prompt is already gone.
3542
+ * Hence the callback must fire BEFORE that await.
3543
+ */
3544
+ async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
3545
+ const currentApp = await this._getCurrentApp(sessionId);
3546
+ if (currentApp === targetAppName) {
3547
+ return;
3548
+ }
3549
+ if (!this._isDashboard(currentApp)) {
3550
+ await this._closeCurrentApp(sessionId);
3551
+ await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
3552
+ }
3553
+ onConfirmOnDevice?.();
3554
+ await this._openApp(sessionId, targetAppName);
3555
+ await this._waitForApp(sessionId, targetAppName);
3556
+ }
3557
+ // ---------------------------------------------------------------------------
3558
+ // Private helpers
3559
+ // ---------------------------------------------------------------------------
3560
+ async _getCurrentApp(sessionId) {
3561
+ const result = await this._dmk.sendCommand({
3562
+ sessionId,
3563
+ command: new import_device_management_kit2.GetAppAndVersionCommand()
3564
+ });
3565
+ if ((0, import_device_management_kit2.isSuccessCommandResult)(result)) {
3566
+ debugLog("[AppManager] currentApp:", result.data.name);
3567
+ return result.data.name;
3568
+ }
3569
+ const errResult = result;
3570
+ const dmkErr = errResult.error ?? {};
3571
+ const original = dmkErr.originalError;
3572
+ debugLog(
3573
+ "[AppManager] _getCurrentApp failed sessionId=",
3574
+ sessionId,
3575
+ "tag=",
3576
+ dmkErr._tag,
3577
+ "errorCode=",
3578
+ dmkErr.errorCode,
3579
+ "message=",
3580
+ dmkErr.message,
3581
+ "originalErrorMessage=",
3582
+ original?.message ?? String(original ?? "")
3583
+ );
3584
+ throw Object.assign(
3585
+ new Error(
3586
+ dmkErr.message ?? "Failed to get current app from device"
3587
+ ),
3588
+ {
3589
+ _tag: dmkErr._tag,
3590
+ errorCode: dmkErr.errorCode,
3591
+ originalError: original
3592
+ }
4386
3593
  );
4387
3594
  }
4388
- /** GET_SHIELDED_ADDRESS: single-Orchard-receiver UA for a 5-level transparent path. */
4389
- async getShieldedAddress(derivationPath, options) {
4390
- const action = this._sdk.getShieldedAddress(derivationPath, options);
4391
- return deviceActionToPromise(
4392
- action,
4393
- this.onInteraction,
4394
- void 0,
4395
- this.onRegisterCanceller
3595
+ async _openApp(sessionId, appName) {
3596
+ const result = await this._dmk.sendCommand({
3597
+ sessionId,
3598
+ command: new import_device_management_kit2.OpenAppCommand({ appName })
3599
+ });
3600
+ if (!(0, import_device_management_kit2.isSuccessCommandResult)(result)) {
3601
+ const dmkErr = result.error;
3602
+ const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
3603
+ const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
3604
+ debugLog(
3605
+ "[AppManager] openApp failed:",
3606
+ appName,
3607
+ "errorCode:",
3608
+ errorCode,
3609
+ "tag:",
3610
+ dmkErr._tag
3611
+ );
3612
+ let code;
3613
+ if (errorCode === "6807" || /unknown application/i.test(message)) {
3614
+ code = import_hwk_adapter_core10.HardwareErrorCode.AppNotInstalled;
3615
+ } else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
3616
+ code = import_hwk_adapter_core10.HardwareErrorCode.UserRejected;
3617
+ }
3618
+ throw Object.assign(new Error(`Failed to open "${appName}"`), {
3619
+ _tag: ERROR_TAG.OpenAppCommand,
3620
+ code,
3621
+ errorCode,
3622
+ statusCode: errorCode,
3623
+ appName,
3624
+ originalError: dmkErr
3625
+ });
3626
+ }
3627
+ }
3628
+ async _closeCurrentApp(sessionId) {
3629
+ debugLog("[AppManager] closeCurrentApp");
3630
+ await this._dmk.sendCommand({
3631
+ sessionId,
3632
+ command: new import_device_management_kit2.CloseAppCommand()
3633
+ });
3634
+ }
3635
+ /**
3636
+ * Poll the device until the expected app is reported as running,
3637
+ * or throw after `_maxRetries` attempts.
3638
+ */
3639
+ async _waitForApp(sessionId, expectedAppName) {
3640
+ let lastSeen = "";
3641
+ for (let i = 0; i < this._maxRetries; i++) {
3642
+ await this._wait();
3643
+ const current = await this._getCurrentApp(sessionId);
3644
+ lastSeen = current;
3645
+ if (current === expectedAppName) {
3646
+ return;
3647
+ }
3648
+ }
3649
+ debugLog(
3650
+ "[AppManager] waitForApp exhausted: expected=",
3651
+ expectedAppName,
3652
+ "lastSeen=",
3653
+ lastSeen
3654
+ );
3655
+ throw new Error(
3656
+ `Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
4396
3657
  );
4397
3658
  }
3659
+ _isDashboard(appName) {
3660
+ return appName === DASHBOARD_APP_NAME;
3661
+ }
3662
+ _wait() {
3663
+ return new Promise((resolve) => setTimeout(resolve, this._waitMs));
3664
+ }
4398
3665
  };
4399
3666
 
4400
- // src/connector/chains/zcash.ts
4401
- async function zcashGetFullViewingKey(ctx, sessionId, params) {
4402
- const signer = await _createZcashSigner(ctx, sessionId);
4403
- const path = normalizePath(params.path);
4404
- const mode = params.mode ?? "ufvk";
4405
- return runSignerCall(ctx, sessionId, async () => {
4406
- const result = await signer.getFullViewingKey(path, { mode });
4407
- if (result.mode === "ufvk") {
4408
- return { path: params.path, mode, ufvk: result.fullViewingKey };
4409
- }
4410
- return { path: params.path, mode, orchardFvk: (0, import_hwk_adapter_core10.bytesToHex)(result.fullViewingKey) };
4411
- });
3667
+ // src/connector/chains/legacyChainCall.ts
3668
+ function isLegacyWrongAppError(err, _appName) {
3669
+ return isWrongAppError(err);
4412
3670
  }
4413
- async function zcashGetShieldedAddress(ctx, sessionId, params) {
4414
- const signer = await _createZcashSigner(ctx, sessionId);
4415
- const path = normalizePath(params.path);
4416
- return runSignerCall(ctx, sessionId, async () => {
4417
- const result = await signer.getShieldedAddress(path, {
4418
- checkOnDevice: params.showOnDevice ?? false
3671
+ async function withLegacyChainCall(ctx, sessionId, options, action) {
3672
+ const { appName, needsConfirmation } = options;
3673
+ let openAppPromptShown = false;
3674
+ const onAppOpenPrompt = () => {
3675
+ openAppPromptShown = true;
3676
+ ctx.emit("ui-event", {
3677
+ type: import_hwk_adapter_core11.EConnectorInteraction.ConfirmOpenApp,
3678
+ payload: { sessionId }
4419
3679
  });
4420
- return { address: result.address, path: params.path };
4421
- });
4422
- }
4423
- async function _createZcashSigner(ctx, sessionId) {
4424
- const dmk = await ctx.getOrCreateDmk();
4425
- const { SignerZcashBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-zcash");
4426
- const sdkSigner = new SignerZcashBuilder({ dmk, sessionId }).build();
4427
- return wireSignerToSession(ctx, sessionId, "zcash", new SignerZcash(sdkSigner));
4428
- }
4429
-
4430
- // src/connector/chains/tron.ts
4431
- var import_hwk_adapter_core11 = require("@onekeyfe/hwk-adapter-core");
4432
-
4433
- // src/signer/SignerTron.ts
4434
- var SignerTron = class {
4435
- // eslint-disable-next-line no-useless-constructor, no-empty-function
4436
- constructor(_sdk) {
4437
- this._sdk = _sdk;
4438
- }
4439
- /** Base58 Tron address plus the uncompressed public key at `derivationPath`. */
4440
- async getAddress(derivationPath, options) {
4441
- const action = this._sdk.getAddress(derivationPath, options);
4442
- const result = await deviceActionToPromise(
4443
- action,
4444
- this.onInteraction,
4445
- void 0,
4446
- this.onRegisterCanceller
3680
+ };
3681
+ const closeOpenAppUiIfShown = () => {
3682
+ if (openAppPromptShown) {
3683
+ ctx.emit("ui-event", {
3684
+ type: import_hwk_adapter_core11.EConnectorInteraction.InteractionComplete,
3685
+ payload: { sessionId }
3686
+ });
3687
+ openAppPromptShown = false;
3688
+ }
3689
+ };
3690
+ try {
3691
+ await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
3692
+ } catch (err) {
3693
+ debugLog(
3694
+ "[LegacyChainCall] pre-flight ensureAppOpen failed:",
3695
+ appName,
3696
+ err?.message
4447
3697
  );
4448
- return { address: result.address, publicKey: result.publicKey };
3698
+ closeOpenAppUiIfShown();
3699
+ throw ctx.wrapError(err, { defaultAppName: appName });
3700
+ }
3701
+ const runOnce = async () => {
3702
+ let confirmEmitted = false;
3703
+ if (needsConfirmation) {
3704
+ ctx.emit("ui-event", {
3705
+ type: import_hwk_adapter_core11.EConnectorInteraction.ConfirmOnDevice,
3706
+ payload: { sessionId }
3707
+ });
3708
+ confirmEmitted = true;
3709
+ }
3710
+ try {
3711
+ return await action(sessionId);
3712
+ } finally {
3713
+ if (confirmEmitted || openAppPromptShown) {
3714
+ ctx.emit("ui-event", {
3715
+ type: import_hwk_adapter_core11.EConnectorInteraction.InteractionComplete,
3716
+ payload: { sessionId }
3717
+ });
3718
+ openAppPromptShown = false;
3719
+ }
3720
+ }
3721
+ };
3722
+ try {
3723
+ return await runOnce();
3724
+ } catch (err) {
3725
+ if (!isLegacyWrongAppError(err, appName)) {
3726
+ debugLog("[LegacyChainCall] non-wrong-app failure:", appName, err?.message);
3727
+ ctx.invalidateSession(sessionId);
3728
+ throw ctx.wrapError(err, { defaultAppName: appName });
3729
+ }
3730
+ debugLog("[LegacyChainCall] wrong-app detected, retrying:", appName);
3731
+ try {
3732
+ await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
3733
+ } catch (switchErr) {
3734
+ debugLog(
3735
+ "[LegacyChainCall] retry ensureAppOpen failed:",
3736
+ appName,
3737
+ switchErr?.message
3738
+ );
3739
+ closeOpenAppUiIfShown();
3740
+ throw ctx.wrapError(switchErr, { defaultAppName: appName });
3741
+ }
3742
+ ctx.clearAllSigners();
3743
+ const result = await runOnce();
3744
+ debugLog("[LegacyChainCall] retry succeeded:", appName);
3745
+ return result;
3746
+ }
3747
+ }
3748
+ async function _ensureAppOpen(ctx, sessionId, appName, onPrompt) {
3749
+ const dmk = await ctx.getOrCreateDmk();
3750
+ const appManager = new AppManager(dmk);
3751
+ await appManager.ensureAppOpen(sessionId, appName, onPrompt);
3752
+ }
3753
+
3754
+ // src/transport/DmkTransport.ts
3755
+ var import_hw_transport = __toESM(require("@ledgerhq/hw-transport"));
3756
+ var DmkTransport = class extends import_hw_transport.default {
3757
+ constructor(dmk, sessionId) {
3758
+ super();
3759
+ this._dmk = dmk;
3760
+ this._sessionId = sessionId;
4449
3761
  }
4450
- /** Sign a protobuf-encoded raw transaction. */
4451
- async signTransaction(derivationPath, transaction, options) {
4452
- const action = this._sdk.signTransaction(derivationPath, transaction, options);
4453
- return deviceActionToPromise(
4454
- action,
4455
- this.onInteraction,
4456
- void 0,
4457
- this.onRegisterCanceller
4458
- );
3762
+ async exchange(apdu) {
3763
+ const response = await this._dmk.sendApdu({
3764
+ sessionId: this._sessionId,
3765
+ apdu: new Uint8Array(apdu)
3766
+ });
3767
+ const { data, statusCode } = response;
3768
+ const result = Buffer.alloc(data.length + 2);
3769
+ if (data.length > 0) {
3770
+ result.set(data, 0);
3771
+ }
3772
+ result.set(statusCode, data.length);
3773
+ return result;
4459
3774
  }
4460
- /** Sign a personal message (TIP-191). */
4461
- async signPersonalMessage(derivationPath, message, options) {
4462
- const action = this._sdk.signPersonalMessage(derivationPath, message, options);
4463
- return deviceActionToPromise(
4464
- action,
4465
- this.onInteraction,
4466
- void 0,
4467
- this.onRegisterCanceller
4468
- );
3775
+ async close() {
4469
3776
  }
4470
3777
  };
4471
3778
 
4472
3779
  // src/connector/chains/tron.ts
4473
3780
  async function tronGetAddress(ctx, sessionId, params) {
4474
- const tronSigner = await _createTronSigner(ctx, sessionId);
4475
3781
  const path = normalizePath(params.path);
4476
- return runSignerCall(ctx, sessionId, async () => {
4477
- const result = await tronSigner.getAddress(path, {
4478
- checkOnDevice: params.showOnDevice ?? false
4479
- });
4480
- return { address: result.address, publicKey: result.publicKey, path: params.path };
4481
- });
3782
+ const showOnDevice = params.showOnDevice ?? false;
3783
+ return withLegacyChainCall(
3784
+ ctx,
3785
+ sessionId,
3786
+ {
3787
+ appName: "Tron",
3788
+ // Only show "confirm on device" UI when the device is actually going
3789
+ // to display the address for the user to verify.
3790
+ needsConfirmation: showOnDevice
3791
+ },
3792
+ async (sid) => {
3793
+ const trx = await _createTrx(ctx, sid);
3794
+ const result = await trx.getAddress(path, showOnDevice);
3795
+ return { address: result.address, publicKey: result.publicKey, path: params.path };
3796
+ }
3797
+ );
4482
3798
  }
4483
3799
  async function tronSignTransaction(ctx, sessionId, params) {
4484
3800
  if (!params.rawTxHex) {
4485
3801
  throw Object.assign(
4486
3802
  new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
4487
- { code: import_hwk_adapter_core11.HardwareErrorCode.InvalidParams }
3803
+ { code: import_hwk_adapter_core12.HardwareErrorCode.InvalidParams }
4488
3804
  );
4489
3805
  }
4490
- const tronSigner = await _createTronSigner(ctx, sessionId);
4491
3806
  const path = normalizePath(params.path);
4492
- return runSignerCall(ctx, sessionId, async () => {
4493
- const signature = await tronSigner.signTransaction(path, (0, import_hwk_adapter_core11.hexToBytes)(params.rawTxHex));
4494
- return { signature: (0, import_hwk_adapter_core11.bytesToHex)(signature) };
4495
- });
3807
+ return withLegacyChainCall(
3808
+ ctx,
3809
+ sessionId,
3810
+ { appName: "Tron", needsConfirmation: true },
3811
+ async (sid) => {
3812
+ const trx = await _createTrx(ctx, sid);
3813
+ const signature = await trx.signTransaction(
3814
+ path,
3815
+ params.rawTxHex,
3816
+ params.tokenSignatures ?? []
3817
+ );
3818
+ return { signature };
3819
+ }
3820
+ );
4496
3821
  }
4497
3822
  async function tronSignMessage(ctx, sessionId, params) {
4498
- const tronSigner = await _createTronSigner(ctx, sessionId);
4499
3823
  const path = normalizePath(params.path);
4500
- return runSignerCall(ctx, sessionId, async () => {
4501
- const signature = await tronSigner.signPersonalMessage(path, (0, import_hwk_adapter_core11.hexToBytes)(params.messageHex));
4502
- return { signature: (0, import_hwk_adapter_core11.bytesToHex)(signature) };
4503
- });
3824
+ return withLegacyChainCall(
3825
+ ctx,
3826
+ sessionId,
3827
+ { appName: "Tron", needsConfirmation: true },
3828
+ async (sid) => {
3829
+ const trx = await _createTrx(ctx, sid);
3830
+ const signature = await trx.signPersonalMessage(path, params.messageHex);
3831
+ return { signature };
3832
+ }
3833
+ );
4504
3834
  }
4505
- async function _createTronSigner(ctx, sessionId) {
3835
+ async function _createTrx(ctx, sessionId) {
4506
3836
  const dmk = await ctx.getOrCreateDmk();
4507
- const { SignerTrxBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-tron");
4508
- const sdkSigner = new SignerTrxBuilder({ dmk, sessionId }).build();
4509
- return wireSignerToSession(ctx, sessionId, "tron", new SignerTron(sdkSigner));
3837
+ return new import_hw_app_trx.default(new DmkTransport(dmk, sessionId));
4510
3838
  }
4511
3839
 
4512
3840
  // src/device-apps/customActions.ts
4513
- var import_device_management_kit2 = require("@ledgerhq/device-management-kit");
3841
+ var import_device_management_kit3 = require("@ledgerhq/device-management-kit");
4514
3842
  var import_rxjs = require("rxjs");
4515
3843
  var GetOsVersionDeviceAction = class {
4516
3844
  // eslint-disable-next-line no-useless-constructor, no-empty-function
@@ -4524,7 +3852,7 @@ var GetOsVersionDeviceAction = class {
4524
3852
  (async () => {
4525
3853
  try {
4526
3854
  subject.next({
4527
- status: import_device_management_kit2.DeviceActionStatus.Pending,
3855
+ status: import_device_management_kit3.DeviceActionStatus.Pending,
4528
3856
  intermediateValue: { requiredUserInteraction: "none" }
4529
3857
  });
4530
3858
  const result = await internalApi.sendCommand(new this._deps.GetOsVersionCommand());
@@ -4533,11 +3861,11 @@ var GetOsVersionDeviceAction = class {
4533
3861
  const errObj = result?.error;
4534
3862
  throw new Error(errObj?.message ?? "GetOsVersionCommand failed");
4535
3863
  }
4536
- subject.next({ status: import_device_management_kit2.DeviceActionStatus.Completed, output: result.data });
3864
+ subject.next({ status: import_device_management_kit3.DeviceActionStatus.Completed, output: result.data });
4537
3865
  subject.complete();
4538
3866
  } catch (err) {
4539
3867
  if (cancelled) return;
4540
- subject.next({ status: import_device_management_kit2.DeviceActionStatus.Error, error: err });
3868
+ subject.next({ status: import_device_management_kit3.DeviceActionStatus.Error, error: err });
4541
3869
  subject.complete();
4542
3870
  }
4543
3871
  })();
@@ -4545,7 +3873,7 @@ var GetOsVersionDeviceAction = class {
4545
3873
  observable: subject.asObservable(),
4546
3874
  cancel: () => {
4547
3875
  cancelled = true;
4548
- subject.next({ status: import_device_management_kit2.DeviceActionStatus.Stopped });
3876
+ subject.next({ status: import_device_management_kit3.DeviceActionStatus.Stopped });
4549
3877
  subject.complete();
4550
3878
  }
4551
3879
  };
@@ -4563,7 +3891,7 @@ var ListAvailableAppsDeviceAction = class {
4563
3891
  (async () => {
4564
3892
  try {
4565
3893
  subject.next({
4566
- status: import_device_management_kit2.DeviceActionStatus.Pending,
3894
+ status: import_device_management_kit3.DeviceActionStatus.Pending,
4567
3895
  intermediateValue: { requiredUserInteraction: "none" }
4568
3896
  });
4569
3897
  const osVersionResult = await internalApi.sendCommand(new this._GetOsVersionCommand());
@@ -4580,11 +3908,11 @@ var ListAvailableAppsDeviceAction = class {
4580
3908
  throw new Error(httpErr?.message ?? "Manager API getAppList failed");
4581
3909
  }
4582
3910
  const apps = either.extract();
4583
- subject.next({ status: import_device_management_kit2.DeviceActionStatus.Completed, output: apps });
3911
+ subject.next({ status: import_device_management_kit3.DeviceActionStatus.Completed, output: apps });
4584
3912
  subject.complete();
4585
3913
  } catch (err) {
4586
3914
  if (cancelled) return;
4587
- subject.next({ status: import_device_management_kit2.DeviceActionStatus.Error, error: err });
3915
+ subject.next({ status: import_device_management_kit3.DeviceActionStatus.Error, error: err });
4588
3916
  subject.complete();
4589
3917
  }
4590
3918
  })();
@@ -4592,7 +3920,7 @@ var ListAvailableAppsDeviceAction = class {
4592
3920
  observable: subject.asObservable(),
4593
3921
  cancel: () => {
4594
3922
  cancelled = true;
4595
- subject.next({ status: import_device_management_kit2.DeviceActionStatus.Stopped });
3923
+ subject.next({ status: import_device_management_kit3.DeviceActionStatus.Stopped });
4596
3924
  subject.complete();
4597
3925
  }
4598
3926
  };
@@ -4685,7 +4013,7 @@ var DeviceApps = class {
4685
4013
  seTargetId: v.seTargetId,
4686
4014
  mcuTargetId: v.mcuTargetId,
4687
4015
  seVersion: v.seVersion,
4688
- seFlagsHex: bytesToHex4(v.seFlags),
4016
+ seFlagsHex: bytesToHex2(v.seFlags),
4689
4017
  mcuSephVersion: v.mcuSephVersion,
4690
4018
  mcuBootloaderVersion: v.mcuBootloaderVersion,
4691
4019
  hwVersion: v.hwVersion
@@ -4759,7 +4087,7 @@ var DeviceApps = class {
4759
4087
  }
4760
4088
  }
4761
4089
  };
4762
- function bytesToHex4(bytes) {
4090
+ function bytesToHex2(bytes) {
4763
4091
  if (!bytes) return "";
4764
4092
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
4765
4093
  }
@@ -4802,11 +4130,10 @@ var METHOD_PREFIX_TO_APP_NAME = {
4802
4130
  evm: "Ethereum",
4803
4131
  btc: "Bitcoin",
4804
4132
  sol: "Solana",
4805
- tron: "Tron",
4806
- zcash: "Zcash"
4133
+ tron: "Tron"
4807
4134
  };
4808
4135
  var HARDWARE_ERROR_CODE_VALUES = new Set(
4809
- Object.values(import_hwk_adapter_core12.HardwareErrorCode).filter((value) => typeof value === "number")
4136
+ Object.values(import_hwk_adapter_core13.HardwareErrorCode).filter((value) => typeof value === "number")
4810
4137
  );
4811
4138
  var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
4812
4139
  var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
@@ -4843,10 +4170,6 @@ async function defaultLedgerKitImporter(pkg) {
4843
4170
  return import("@ledgerhq/device-signer-kit-bitcoin");
4844
4171
  case "@ledgerhq/device-signer-kit-solana":
4845
4172
  return import("@ledgerhq/device-signer-kit-solana");
4846
- case "@ledgerhq/device-signer-kit-tron":
4847
- return import("@ledgerhq/device-signer-kit-tron");
4848
- case "@ledgerhq/device-signer-kit-zcash":
4849
- return import("@ledgerhq/device-signer-kit-zcash");
4850
4173
  case "@ledgerhq/context-module":
4851
4174
  return import("@ledgerhq/context-module");
4852
4175
  default:
@@ -4906,7 +4229,6 @@ var LedgerConnectorBase = class {
4906
4229
  this._ctx = {
4907
4230
  emit: (event, data) => this._emit(event, data),
4908
4231
  invalidateSession: (sid) => this._invalidateSession(sid),
4909
- teardownSecureChannelSession: (sid) => this._teardownSecureChannelSession(sid),
4910
4232
  wrapError: (err, opts) => this._wrapError(err, opts),
4911
4233
  getOrCreateDmk: () => this._getOrCreateDmk(),
4912
4234
  getDeviceManager: () => this._getDeviceManager(),
@@ -4958,7 +4280,7 @@ var LedgerConnectorBase = class {
4958
4280
  async searchDevices() {
4959
4281
  const dm = await this._getDeviceManager();
4960
4282
  const descriptors = await this._discoverDescriptors(dm);
4961
- const resolvedDescriptors = descriptors.filter((d) => !(0, import_hwk_adapter_core12.isKnownNonTargetHardwareVendor)(d, "ledger")).map((d) => ({
4283
+ const resolvedDescriptors = descriptors.filter((d) => !(0, import_hwk_adapter_core13.isKnownNonTargetHardwareVendor)(d, "ledger")).map((d) => ({
4962
4284
  descriptor: d,
4963
4285
  connectId: this._resolveConnectId(d)
4964
4286
  }));
@@ -5006,7 +4328,7 @@ var LedgerConnectorBase = class {
5006
4328
  `Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
5007
4329
  );
5008
4330
  err._tag = ERROR_TAG.BlePairingTimeout;
5009
- err.code = import_hwk_adapter_core12.HardwareErrorCode.BlePairingTimeout;
4331
+ err.code = import_hwk_adapter_core13.HardwareErrorCode.BlePairingTimeout;
5010
4332
  reject(err);
5011
4333
  }, HANG_CEILING_MS);
5012
4334
  });
@@ -5039,7 +4361,7 @@ var LedgerConnectorBase = class {
5039
4361
  "Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
5040
4362
  );
5041
4363
  err._tag = ERROR_TAG.DeviceNotAdvertising;
5042
- err.code = import_hwk_adapter_core12.HardwareErrorCode.DeviceNotFound;
4364
+ err.code = import_hwk_adapter_core13.HardwareErrorCode.DeviceNotFound;
5043
4365
  throw err;
5044
4366
  };
5045
4367
  const doConnect = async (path) => {
@@ -5096,14 +4418,14 @@ var LedgerConnectorBase = class {
5096
4418
  this._resetSignersAndSessions();
5097
4419
  if (isLedgerBleConnectionType(this.connectionType)) {
5098
4420
  const tag = err?._tag;
5099
- if (isKnownConnectionTag(tag) && !isConnectionOpeningTag(tag)) {
4421
+ if (isKnownConnectionTag(tag)) {
5100
4422
  throw err;
5101
4423
  }
5102
4424
  const wrapped = new Error(
5103
4425
  "Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
5104
4426
  );
5105
4427
  wrapped._tag = ERROR_TAG.BleGattBondingFailed;
5106
- wrapped.code = import_hwk_adapter_core12.HardwareErrorCode.BlePairingTimeout;
4428
+ wrapped.code = import_hwk_adapter_core13.HardwareErrorCode.BlePairingTimeout;
5107
4429
  wrapped.originalError = err;
5108
4430
  throw wrapped;
5109
4431
  }
@@ -5181,7 +4503,7 @@ var LedgerConnectorBase = class {
5181
4503
  this._unwatchSessionState(sessionId);
5182
4504
  this._signerManager?.invalidate(sessionId);
5183
4505
  this._cancellers.get(sessionId)?.({
5184
- code: import_hwk_adapter_core12.HardwareErrorCode.DeviceDisconnected,
4506
+ code: import_hwk_adapter_core13.HardwareErrorCode.DeviceDisconnected,
5185
4507
  tag: "DeviceDisconnected",
5186
4508
  message: "Device disconnected"
5187
4509
  });
@@ -5205,9 +4527,9 @@ var LedgerConnectorBase = class {
5205
4527
  if (isAppStuckByApdu(err)) {
5206
4528
  return {
5207
4529
  success: false,
5208
- error: (0, import_hwk_adapter_core12.serializeConnectorError)(
4530
+ error: (0, import_hwk_adapter_core13.serializeConnectorError)(
5209
4531
  Object.assign(new Error("Ledger app is unresponsive"), {
5210
- code: import_hwk_adapter_core12.HardwareErrorCode.DeviceAppStuck,
4532
+ code: import_hwk_adapter_core13.HardwareErrorCode.DeviceAppStuck,
5211
4533
  _tag: ERROR_TAG.DeviceAppStuck,
5212
4534
  originalError: err
5213
4535
  })
@@ -5217,16 +4539,16 @@ var LedgerConnectorBase = class {
5217
4539
  if (isTransportStuck(err)) {
5218
4540
  return {
5219
4541
  success: false,
5220
- error: (0, import_hwk_adapter_core12.serializeConnectorError)(
4542
+ error: (0, import_hwk_adapter_core13.serializeConnectorError)(
5221
4543
  Object.assign(new Error("Device communication interrupted, please retry"), {
5222
- code: import_hwk_adapter_core12.HardwareErrorCode.TransportError,
4544
+ code: import_hwk_adapter_core13.HardwareErrorCode.TransportError,
5223
4545
  _tag: ERROR_TAG.DeviceTransportStuck,
5224
4546
  originalError: err
5225
4547
  })
5226
4548
  )
5227
4549
  };
5228
4550
  }
5229
- return { success: false, error: (0, import_hwk_adapter_core12.serializeConnectorError)(err) };
4551
+ return { success: false, error: (0, import_hwk_adapter_core13.serializeConnectorError)(err) };
5230
4552
  }
5231
4553
  }
5232
4554
  async _dispatch(sessionId, method, params) {
@@ -5265,11 +4587,6 @@ var LedgerConnectorBase = class {
5265
4587
  return solSignTransaction(ctx, sessionId, params);
5266
4588
  case "solSignMessage":
5267
4589
  return solSignMessage(ctx, sessionId, params);
5268
- // ZCASH
5269
- case "zcashGetFullViewingKey":
5270
- return zcashGetFullViewingKey(ctx, sessionId, params);
5271
- case "zcashGetShieldedAddress":
5272
- return zcashGetShieldedAddress(ctx, sessionId, params);
5273
4590
  // TRON
5274
4591
  case "tronGetAddress":
5275
4592
  return tronGetAddress(ctx, sessionId, params);
@@ -5294,7 +4611,7 @@ var LedgerConnectorBase = class {
5294
4611
  try {
5295
4612
  return await apps.install(p.appName, ({ progress }) => {
5296
4613
  ctx.emit("ui-event", {
5297
- type: import_hwk_adapter_core12.EConnectorInteraction.AppInstallProgress,
4614
+ type: import_hwk_adapter_core13.EConnectorInteraction.AppInstallProgress,
5298
4615
  payload: {
5299
4616
  sessionId,
5300
4617
  appName: p.appName,
@@ -5303,7 +4620,7 @@ var LedgerConnectorBase = class {
5303
4620
  });
5304
4621
  });
5305
4622
  } catch (err) {
5306
- ctx.teardownSecureChannelSession(sessionId);
4623
+ ctx.invalidateSession(sessionId);
5307
4624
  throw ctx.wrapError(err);
5308
4625
  } finally {
5309
4626
  ctx.clearCanceller(sessionId);
@@ -5393,7 +4710,7 @@ var LedgerConnectorBase = class {
5393
4710
  );
5394
4711
  return { isGenuine: output.isGenuine, deviceId };
5395
4712
  } catch (err) {
5396
- ctx.teardownSecureChannelSession(sessionId);
4713
+ ctx.invalidateSession(sessionId);
5397
4714
  throw ctx.wrapError(err);
5398
4715
  } finally {
5399
4716
  ctx.clearCanceller(sessionId);
@@ -5512,26 +4829,9 @@ var LedgerConnectorBase = class {
5512
4829
  }
5513
4830
  return this._deviceAppsManager;
5514
4831
  }
5515
- // DeviceAppsManager is a per-call factory with no cached session state, so
5516
- // there is nothing to invalidate for it here.
5517
4832
  _invalidateSession(sessionId) {
5518
4833
  this._signerManager?.invalidate(sessionId);
5519
4834
  }
5520
- /**
5521
- * Fire the canceller so DMK closes the secure-channel WebSocket. The DMK device
5522
- * session is kept: unlock/retry recovery reuses it.
5523
- */
5524
- _teardownSecureChannelSession(sessionId) {
5525
- const cancel = this._cancellers.get(sessionId);
5526
- this._cancellers.delete(sessionId);
5527
- if (cancel) {
5528
- try {
5529
- cancel();
5530
- } catch {
5531
- }
5532
- }
5533
- this._invalidateSession(sessionId);
5534
- }
5535
4835
  /**
5536
4836
  * Replace an old session with a new one after app switch.
5537
4837
  * Emits device-connect so the adapter updates its _sessions Map.
@@ -5616,7 +4916,7 @@ var LedgerConnectorBase = class {
5616
4916
  * at every catch site. Falls through unchanged for unknown methods.
5617
4917
  */
5618
4918
  _ctxForMethod(method) {
5619
- const prefix = /^(evm|btc|sol|tron|zcash)/.exec(method)?.[1];
4919
+ const prefix = /^(evm|btc|sol|tron)/.exec(method)?.[1];
5620
4920
  const defaultAppName = prefix ? METHOD_PREFIX_TO_APP_NAME[prefix] : void 0;
5621
4921
  if (!defaultAppName) return this._ctx;
5622
4922
  return {
@@ -5650,204 +4950,6 @@ var LedgerConnectorBase = class {
5650
4950
  return error;
5651
4951
  }
5652
4952
  };
5653
-
5654
- // src/transport/DmkTransport.ts
5655
- var import_hw_transport = __toESM(require("@ledgerhq/hw-transport"));
5656
- var DmkTransport = class extends import_hw_transport.default {
5657
- constructor(dmk, sessionId) {
5658
- super();
5659
- this._dmk = dmk;
5660
- this._sessionId = sessionId;
5661
- }
5662
- async exchange(apdu) {
5663
- const response = await this._dmk.sendApdu({
5664
- sessionId: this._sessionId,
5665
- apdu: new Uint8Array(apdu)
5666
- });
5667
- const { data, statusCode } = response;
5668
- const result = Buffer.alloc(data.length + 2);
5669
- if (data.length > 0) {
5670
- result.set(data, 0);
5671
- }
5672
- result.set(statusCode, data.length);
5673
- return result;
5674
- }
5675
- async close() {
5676
- }
5677
- };
5678
-
5679
- // src/app/AppManager.ts
5680
- var import_device_management_kit3 = require("@ledgerhq/device-management-kit");
5681
- var import_hwk_adapter_core13 = require("@onekeyfe/hwk-adapter-core");
5682
- var APP_NAME_MAP = {
5683
- ETH: "Ethereum",
5684
- BTC: "Bitcoin",
5685
- SOL: "Solana",
5686
- TRX: "Tron",
5687
- ZEC: "Zcash",
5688
- XRP: "XRP",
5689
- ADA: "Cardano",
5690
- DOT: "Polkadot",
5691
- ATOM: "Cosmos"
5692
- };
5693
- var DASHBOARD_APP_NAME = "BOLOS";
5694
- var AppManager = class {
5695
- constructor(dmk, options) {
5696
- this._dmk = dmk;
5697
- this._waitMs = options?.waitMs ?? 1e3;
5698
- this._maxRetries = options?.maxRetries ?? 10;
5699
- }
5700
- /**
5701
- * Return the Ledger app name for a given chain ticker,
5702
- * or undefined if the chain is not supported.
5703
- */
5704
- static getAppName(chain) {
5705
- return APP_NAME_MAP[chain];
5706
- }
5707
- /**
5708
- * Ensure the target app is open on the device identified by `sessionId`.
5709
- *
5710
- * Flow:
5711
- * 1. Check the currently running app.
5712
- * 2. If it is already the target, return immediately.
5713
- * 3. If a different app is running (not dashboard), close it first.
5714
- * 4. Open the target app.
5715
- * 5. Poll until the device confirms the target app is running.
5716
- */
5717
- /**
5718
- * @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
5719
- * device is about to display "Open <app>" on screen and wait for the
5720
- * user's button press. UI consumers should show their "open app" prompt
5721
- * in response. NOT called when the target app is already open (no user
5722
- * interaction needed in that case).
5723
- *
5724
- * Important: OpenAppCommand is blocking. It does not resolve until the user
5725
- * has physically confirmed on the device, so anything that runs AFTER
5726
- * `await this._openApp(...)` lands AFTER the prompt is already gone.
5727
- * Hence the callback must fire BEFORE that await.
5728
- */
5729
- async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
5730
- const currentApp = await this._getCurrentApp(sessionId);
5731
- if (currentApp === targetAppName) {
5732
- return;
5733
- }
5734
- if (!this._isDashboard(currentApp)) {
5735
- await this._closeCurrentApp(sessionId);
5736
- await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
5737
- }
5738
- onConfirmOnDevice?.();
5739
- await this._openApp(sessionId, targetAppName);
5740
- await this._waitForApp(sessionId, targetAppName);
5741
- }
5742
- // ---------------------------------------------------------------------------
5743
- // Private helpers
5744
- // ---------------------------------------------------------------------------
5745
- async _getCurrentApp(sessionId) {
5746
- const result = await this._dmk.sendCommand({
5747
- sessionId,
5748
- command: new import_device_management_kit3.GetAppAndVersionCommand()
5749
- });
5750
- if ((0, import_device_management_kit3.isSuccessCommandResult)(result)) {
5751
- debugLog("[AppManager] currentApp:", result.data.name);
5752
- return result.data.name;
5753
- }
5754
- const errResult = result;
5755
- const dmkErr = errResult.error ?? {};
5756
- const original = dmkErr.originalError;
5757
- debugLog(
5758
- "[AppManager] _getCurrentApp failed sessionId=",
5759
- sessionId,
5760
- "tag=",
5761
- dmkErr._tag,
5762
- "errorCode=",
5763
- dmkErr.errorCode,
5764
- "message=",
5765
- dmkErr.message,
5766
- "originalErrorMessage=",
5767
- original?.message ?? String(original ?? "")
5768
- );
5769
- throw Object.assign(
5770
- new Error(
5771
- dmkErr.message ?? "Failed to get current app from device"
5772
- ),
5773
- {
5774
- _tag: dmkErr._tag,
5775
- errorCode: dmkErr.errorCode,
5776
- originalError: original
5777
- }
5778
- );
5779
- }
5780
- async _openApp(sessionId, appName) {
5781
- const result = await this._dmk.sendCommand({
5782
- sessionId,
5783
- command: new import_device_management_kit3.OpenAppCommand({ appName })
5784
- });
5785
- if (!(0, import_device_management_kit3.isSuccessCommandResult)(result)) {
5786
- const dmkErr = result.error;
5787
- const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
5788
- const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
5789
- debugLog(
5790
- "[AppManager] openApp failed:",
5791
- appName,
5792
- "errorCode:",
5793
- errorCode,
5794
- "tag:",
5795
- dmkErr._tag
5796
- );
5797
- let code;
5798
- if (errorCode === "6807" || /unknown application/i.test(message)) {
5799
- code = import_hwk_adapter_core13.HardwareErrorCode.AppNotInstalled;
5800
- } else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
5801
- code = import_hwk_adapter_core13.HardwareErrorCode.UserRejected;
5802
- }
5803
- throw Object.assign(new Error(`Failed to open "${appName}"`), {
5804
- _tag: ERROR_TAG.OpenAppCommand,
5805
- code,
5806
- errorCode,
5807
- statusCode: errorCode,
5808
- appName,
5809
- originalError: dmkErr
5810
- });
5811
- }
5812
- }
5813
- async _closeCurrentApp(sessionId) {
5814
- debugLog("[AppManager] closeCurrentApp");
5815
- await this._dmk.sendCommand({
5816
- sessionId,
5817
- command: new import_device_management_kit3.CloseAppCommand()
5818
- });
5819
- }
5820
- /**
5821
- * Poll the device until the expected app is reported as running,
5822
- * or throw after `_maxRetries` attempts.
5823
- */
5824
- async _waitForApp(sessionId, expectedAppName) {
5825
- let lastSeen = "";
5826
- for (let i = 0; i < this._maxRetries; i++) {
5827
- await this._wait();
5828
- const current = await this._getCurrentApp(sessionId);
5829
- lastSeen = current;
5830
- if (current === expectedAppName) {
5831
- return;
5832
- }
5833
- }
5834
- debugLog(
5835
- "[AppManager] waitForApp exhausted: expected=",
5836
- expectedAppName,
5837
- "lastSeen=",
5838
- lastSeen
5839
- );
5840
- throw new Error(
5841
- `Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
5842
- );
5843
- }
5844
- _isDashboard(appName) {
5845
- return appName === DASHBOARD_APP_NAME;
5846
- }
5847
- _wait() {
5848
- return new Promise((resolve) => setTimeout(resolve, this._waitMs));
5849
- }
5850
- };
5851
4953
  // Annotate the CommonJS export names for ESM import in node:
5852
4954
  0 && (module.exports = {
5853
4955
  AppManager,
@@ -5859,7 +4961,6 @@ var AppManager = class {
5859
4961
  SignerEth,
5860
4962
  SignerManager,
5861
4963
  SignerSol,
5862
- SignerZcash,
5863
4964
  debugLog,
5864
4965
  deviceActionToPromise,
5865
4966
  isDeviceLockedError,