@onekeyfe/hwk-ledger-adapter 1.2.3-alpha.8 → 1.2.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.mjs CHANGED
@@ -6,41 +6,30 @@ import {
6
6
  DeviceJobQueue,
7
7
  EConnectorInteraction,
8
8
  HardwareErrorCode as HardwareErrorCode3,
9
- OperationRegistry,
10
- SDK,
11
9
  TypedEventEmitter,
12
10
  UI_REQUEST,
13
11
  UI_REQUEST_PREEMPTED_TAG,
14
12
  UiRequestRegistry,
15
- canReplayHardwareMethodAfterTransportFailure,
16
13
  createHwkError,
17
14
  deriveDeviceFingerprint,
18
15
  failure as failure2,
19
- isHardwareOperationId,
20
- isHwkRecoveryHint,
21
- operationMayHaveCompletedParams,
22
16
  rehydrateConnectorError,
23
- requestBleDeviceSelection,
24
- requestSaveDeviceBinding,
25
- resolveSearchTargetReusePolicy,
26
17
  success
27
18
  } from "@onekeyfe/hwk-adapter-core";
28
19
 
29
20
  // src/errors.ts
30
- import {
31
- HardwareErrorCode,
32
- defaultOriginForCode,
33
- defaultRecoveryForCode,
34
- enrichErrorMessage
35
- } from "@onekeyfe/hwk-adapter-core";
36
- function ledgerFailure(code, error, appName, tag, params, origin, recovery) {
21
+ import { HardwareErrorCode, enrichErrorMessage } from "@onekeyfe/hwk-adapter-core";
22
+ var MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE = "Multiple Ledger USB devices are connected. Please connect only one Ledger device and try again.";
23
+ function createMultipleUsbLedgerDevicesError() {
24
+ return Object.assign(new Error(MULTIPLE_USB_LEDGER_DEVICES_ERROR_MESSAGE), {
25
+ code: HardwareErrorCode.DeviceOneDeviceOnly
26
+ });
27
+ }
28
+ function ledgerFailure(code, error, appName, tag, params) {
37
29
  const payload = { error, code };
38
30
  if (appName !== void 0) payload.appName = appName;
39
31
  if (tag !== void 0) payload._tag = tag;
40
32
  if (params !== void 0) payload.params = params;
41
- const resolvedOrigin = origin ?? defaultOriginForCode(code);
42
- if (resolvedOrigin !== void 0) payload.origin = resolvedOrigin;
43
- payload.recovery = recovery ?? defaultRecoveryForCode(code);
44
33
  return { success: false, payload };
45
34
  }
46
35
  var LOCKED_ERROR_CODES = /* @__PURE__ */ new Set(["5515", "21781", "6982", "27010", "5303", "21251"]);
@@ -181,12 +170,7 @@ var ERROR_TAG = {
181
170
  UnknownDevice: "UnknownDeviceError",
182
171
  DeviceSessionRefresher: "DeviceSessionRefresherError",
183
172
  DeviceNotInitialized: "DeviceNotInitializedError",
184
- // DMK's class is named OpeningConnectionError but its `_tag` reads
185
- // "ConnectionOpeningError" — and its typings widen `_tag` to `string`, which
186
- // is why the class name looked authoritative. Both spellings are kept so a
187
- // DMK version that aligns them does not silently drop back to UnknownError.
188
- OpeningConnection: "ConnectionOpeningError",
189
- OpeningConnectionLegacy: "OpeningConnectionError",
173
+ OpeningConnection: "OpeningConnectionError",
190
174
  DeviceDisconnectedBeforeSendingApdu: "DeviceDisconnectedBeforeSendingApdu",
191
175
  DeviceDisconnectedWhileSending: "DeviceDisconnectedWhileSendingError",
192
176
  Disconnect: "DisconnectError",
@@ -199,18 +183,7 @@ var ERROR_TAG = {
199
183
  // DMK remote-network failures (manager-api HTTP / secure-channel WS).
200
184
  WebSocketConnection: "WebSocketConnectionError",
201
185
  HttpFetch: "FetchError",
202
- NetworkDA: "NetworkDAError",
203
- InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError",
204
- ApplicationsMetadataTask: "GetApplicationsMetadataTaskError",
205
- // DMK OS/secure-channel device actions. `SecureChannelError` is the residual
206
- // bucket left by SecureChannelError.mapInstallDAErrors() after the locked /
207
- // refused / already-installed / OOM cases have been split out, so it means
208
- // "the relay itself broke", not "the device answered".
209
- SecureChannel: "SecureChannelError",
210
- RefusedByUserDA: "RefusedByUserDAError",
211
- AppAlreadyInstalledDA: "AppAlreadyInstalledDAError",
212
- OutOfMemoryDA: "OutOfMemoryDAError",
213
- DeviceNotOnboarded: "DeviceNotOnboardedError"
186
+ InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError"
214
187
  };
215
188
  function isDeviceLockedError(err) {
216
189
  if (!err || typeof err !== "object") return false;
@@ -240,6 +213,7 @@ function isBlePairingFailureError(err) {
240
213
  return false;
241
214
  }
242
215
  var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
216
+ ERROR_TAG.DeviceLocked,
243
217
  ERROR_TAG.DeviceNotAdvertising,
244
218
  ERROR_TAG.BlePairingTimeout,
245
219
  ERROR_TAG.BleGattBondingFailed,
@@ -251,7 +225,6 @@ var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
251
225
  ERROR_TAG.DeviceSessionRefresher,
252
226
  ERROR_TAG.DeviceNotInitialized,
253
227
  ERROR_TAG.OpeningConnection,
254
- ERROR_TAG.OpeningConnectionLegacy,
255
228
  ERROR_TAG.DeviceDisconnectedBeforeSendingApdu,
256
229
  ERROR_TAG.DeviceDisconnectedWhileSending,
257
230
  ERROR_TAG.Disconnect,
@@ -266,10 +239,7 @@ var DEVICE_NOT_FOUND_TAGS = /* @__PURE__ */ new Set([
266
239
  // Map to DeviceNotFound so non-BLE-direct paths get a sensible error code.
267
240
  ERROR_TAG.DeviceNotInDiscoveryCache
268
241
  ]);
269
- var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([
270
- ERROR_TAG.OpeningConnection,
271
- ERROR_TAG.OpeningConnectionLegacy
272
- ]);
242
+ var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.OpeningConnection]);
273
243
  var DEVICE_DISCONNECTED_TAGS = /* @__PURE__ */ new Set([
274
244
  ERROR_TAG.DeviceNotRecognized,
275
245
  ERROR_TAG.DeviceSessionNotFound,
@@ -292,13 +262,6 @@ function isConnectionLevelError(err) {
292
262
  function isKnownConnectionTag(tag) {
293
263
  return typeof tag === "string" && CONNECTION_LEVEL_TAGS.has(tag);
294
264
  }
295
- var CONNECTION_OPENING_TAGS = /* @__PURE__ */ new Set([
296
- ERROR_TAG.OpeningConnection,
297
- ERROR_TAG.OpeningConnectionLegacy
298
- ]);
299
- function isConnectionOpeningTag(tag) {
300
- return typeof tag === "string" && CONNECTION_OPENING_TAGS.has(tag);
301
- }
302
265
  function hasStatusCode(err, codeSet) {
303
266
  if (!err || typeof err !== "object") return false;
304
267
  const e = err;
@@ -334,14 +297,6 @@ function hasInvalidArgumentCode(err) {
334
297
  if (e.error != null && e._tag && hasInvalidArgumentCode(e.error)) return true;
335
298
  return false;
336
299
  }
337
- function hasErrorTag(err, tags) {
338
- if (!err || typeof err !== "object") return false;
339
- const e = err;
340
- if (typeof e._tag === "string" && tags.has(e._tag)) return true;
341
- if (e.originalError != null && hasErrorTag(e.originalError, tags)) return true;
342
- if (e.error != null && e._tag && hasErrorTag(e.error, tags)) return true;
343
- return false;
344
- }
345
300
  function isDeviceNotFoundError(err) {
346
301
  if (!err || typeof err !== "object") return false;
347
302
  const tag = err._tag;
@@ -360,14 +315,10 @@ function isDeviceBusyError(err) {
360
315
  if (e.error != null && e._tag && isDeviceBusyError(e.error)) return true;
361
316
  return false;
362
317
  }
363
- var USER_REJECTED_TAGS = /* @__PURE__ */ new Set([
364
- ERROR_TAG.UserRefusedOnDevice,
365
- ERROR_TAG.RefusedByUserDA
366
- ]);
367
318
  function isUserRejectedError(err) {
368
319
  if (!err || typeof err !== "object") return false;
369
320
  const e = err;
370
- if (hasErrorTag(err, USER_REJECTED_TAGS)) return true;
321
+ if (e._tag === ERROR_TAG.UserRefusedOnDevice) return true;
371
322
  if (typeof e.message === "string" && /denied|rejected|refused/i.test(e.message)) return true;
372
323
  if (hasStatusCode(err, USER_REJECTED_CODES)) return true;
373
324
  return false;
@@ -398,37 +349,15 @@ function isAppNotInstalledError(err) {
398
349
  if (hasStatusCode(err, APP_NOT_INSTALLED_CODES)) return true;
399
350
  return false;
400
351
  }
401
- var OUT_OF_MEMORY_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.OutOfMemoryDA]);
402
352
  function isOutOfMemoryError(err) {
403
- return hasErrorTag(err, OUT_OF_MEMORY_TAGS);
404
- }
405
- var APP_ALREADY_INSTALLED_TAGS = /* @__PURE__ */ new Set([
406
- ERROR_TAG.AppAlreadyInstalledDA
407
- ]);
408
- function isAppAlreadyInstalledError(err) {
409
- return hasErrorTag(err, APP_ALREADY_INSTALLED_TAGS);
410
- }
411
- var DEVICE_NOT_ONBOARDED_TAGS = /* @__PURE__ */ new Set([
412
- ERROR_TAG.DeviceNotOnboarded
413
- ]);
414
- function isDeviceNotOnboardedError(err) {
415
- return hasErrorTag(err, DEVICE_NOT_ONBOARDED_TAGS);
416
- }
417
- var FIRMWARE_METADATA_TAGS = /* @__PURE__ */ new Set([
418
- ERROR_TAG.InvalidFirmwareMetadataResponse,
419
- ERROR_TAG.ApplicationsMetadataTask
420
- ]);
421
- function isFirmwareMetadataError(err) {
422
- return hasErrorTag(err, FIRMWARE_METADATA_TAGS);
423
- }
424
- var SECURE_CHANNEL_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.SecureChannel]);
425
- function isSecureChannelError(err) {
426
- return hasErrorTag(err, SECURE_CHANNEL_TAGS);
353
+ if (!err || typeof err !== "object") return false;
354
+ const e = err;
355
+ return e._tag === "OutOfMemoryDAError";
427
356
  }
428
357
  var NETWORK_ERROR_TAGS = /* @__PURE__ */ new Set([
429
358
  ERROR_TAG.WebSocketConnection,
430
359
  ERROR_TAG.HttpFetch,
431
- ERROR_TAG.NetworkDA
360
+ ERROR_TAG.InvalidFirmwareMetadataResponse
432
361
  ]);
433
362
  function isNetworkError(err) {
434
363
  if (!err || typeof err !== "object") return false;
@@ -478,34 +407,23 @@ function isTransportStuck(err) {
478
407
  function isStuckAppStateError(err) {
479
408
  return isAppStuckByApdu(err) || isTransportStuck(err);
480
409
  }
481
- var DMK_PLACEHOLDER_MESSAGE = "Unknown error.";
482
- function nestedErrorMessage(nested) {
483
- if (!nested || typeof nested !== "object") return void 0;
484
- const { message } = nested;
485
- if (typeof message !== "string") return void 0;
486
- const trimmed = message.trim();
487
- if (!trimmed || trimmed === DMK_PLACEHOLDER_MESSAGE) return void 0;
488
- return message;
489
- }
490
410
  function mapLedgerError(err, opts) {
491
411
  let originalMessage = "Unknown Ledger error";
492
412
  if (err instanceof Error) {
493
413
  originalMessage = err.message;
494
414
  } else if (err && typeof err === "object") {
495
415
  const e = err;
496
- originalMessage = String(
497
- e.message ?? nestedErrorMessage(e.originalError) ?? e._tag ?? e.type ?? JSON.stringify(err)
498
- );
416
+ originalMessage = String(e.message ?? e._tag ?? e.type ?? JSON.stringify(err));
499
417
  }
500
418
  let code;
501
419
  if (isDeviceLockedError(err)) {
502
420
  code = HardwareErrorCode.DeviceLocked;
503
421
  } else if (isDeviceNotAdvertisingError(err) || isDeviceNotFoundError(err)) {
504
422
  code = HardwareErrorCode.DeviceNotFound;
505
- } else if (isBlePairingFailureError(err)) {
506
- code = HardwareErrorCode.BlePairingTimeout;
507
423
  } else if (isDeviceBusyError(err)) {
508
424
  code = HardwareErrorCode.DeviceBusy;
425
+ } else if (isBlePairingFailureError(err)) {
426
+ code = HardwareErrorCode.BlePairingTimeout;
509
427
  } else if (isUserAbortedError(err)) {
510
428
  code = HardwareErrorCode.UserAborted;
511
429
  } else if (isUserRejectedError(err)) {
@@ -514,18 +432,10 @@ function mapLedgerError(err, opts) {
514
432
  code = HardwareErrorCode.WrongApp;
515
433
  } else if (isAppNotInstalledError(err)) {
516
434
  code = HardwareErrorCode.AppNotInstalled;
517
- } else if (isAppAlreadyInstalledError(err)) {
518
- code = HardwareErrorCode.AppAlreadyInstalled;
519
435
  } else if (isOutOfMemoryError(err)) {
520
436
  code = HardwareErrorCode.DeviceOutOfMemory;
521
- } else if (isDeviceNotOnboardedError(err)) {
522
- code = HardwareErrorCode.DeviceNotInitialized;
523
- } else if (isFirmwareMetadataError(err)) {
524
- code = HardwareErrorCode.LedgerFirmwareMetadataError;
525
437
  } else if (isNetworkError(err)) {
526
438
  code = HardwareErrorCode.NetworkError;
527
- } else if (isSecureChannelError(err)) {
528
- code = HardwareErrorCode.LedgerSecureChannelError;
529
439
  } else if (isDeviceDisconnectedError(err)) {
530
440
  code = HardwareErrorCode.DeviceDisconnected;
531
441
  } else if (isTimeoutError(err)) {
@@ -538,32 +448,11 @@ function mapLedgerError(err, opts) {
538
448
  }
539
449
  const errAppName = err && typeof err === "object" ? err.appName : void 0;
540
450
  const appName = errAppName ?? opts?.defaultAppName;
541
- return {
542
- code,
543
- message: enrichErrorMessage(code, originalMessage),
544
- origin: defaultOriginForCode(code),
545
- appName
546
- };
547
- }
548
-
549
- // src/utils/queueKey.ts
550
- var LEDGER_DEFAULT_QUEUE_KEY = "__ledger_default__";
551
- function ledgerQueueKey({
552
- operationId,
553
- connectId
554
- }) {
555
- return (operationId ?? connectId) || LEDGER_DEFAULT_QUEUE_KEY;
451
+ return { code, message: enrichErrorMessage(code, originalMessage), appName };
556
452
  }
557
453
 
558
454
  // src/adapter/methods/allNetworkGetAddress.ts
559
- import {
560
- HardwareErrorCode as HardwareErrorCode2,
561
- failure,
562
- isConnectionLost,
563
- isUserRefusal,
564
- resolveHardwareOperationTarget,
565
- runAllNetworkGetAddress
566
- } from "@onekeyfe/hwk-adapter-core";
455
+ import { HardwareErrorCode as HardwareErrorCode2, failure, runAllNetworkGetAddress } from "@onekeyfe/hwk-adapter-core";
567
456
 
568
457
  // src/utils/sdkEventBus.ts
569
458
  var listeners = /* @__PURE__ */ new Set();
@@ -620,102 +509,65 @@ var LEDGER_BTC_NETWORK_COIN_MAP = {
620
509
  var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "dogecoin"]);
621
510
  function createAllNetworkGetAddress({
622
511
  callChain,
623
- getChainFingerprint,
624
- retainOperation,
625
- errorToFailure,
626
- createCancelScope
512
+ getChainFingerprint
627
513
  }) {
628
514
  return async function allNetworkGetAddress(connectId, _deviceId, params) {
629
- debugLog("[LedgerAdapter][REQ]", {
630
- method: "allNetworkGetAddress",
631
- connectId,
632
- itemCount: params.bundle.length
633
- });
634
- const target = resolveHardwareOperationTarget(connectId, params.operationId, "ledger");
635
- if (!target.success) return target;
636
- const effectiveTargetId = target.payload.targetId ?? "";
637
- let releaseOperationRetention;
638
- try {
639
- releaseOperationRetention = target.payload.operationId ? retainOperation(target.payload.operationId) : void 0;
640
- } catch (error) {
641
- return errorToFailure(error);
642
- }
643
- const cancelScope = createCancelScope(
644
- ledgerQueueKey({ operationId: target.payload.operationId, connectId: effectiveTargetId })
645
- );
515
+ debugLog("[LedgerAdapter][REQ]", { method: "allNetworkGetAddress", connectId, params });
646
516
  const installContext = {};
647
517
  const commonParams = {
648
- autoInstallApp: params.autoInstallApp,
649
- operationId: target.payload.operationId,
650
- knownConnections: params.knownConnections,
651
- extra: params.extra,
652
- allowDeviceSelection: params.allowDeviceSelection
518
+ autoInstallApp: params.autoInstallApp
653
519
  };
654
520
  const chainFingerprints = /* @__PURE__ */ new Map();
655
- try {
656
- const result = await runAllNetworkGetAddress({
657
- connectId: effectiveTargetId,
658
- deviceId: _deviceId,
659
- params,
660
- normalizeItem: normalizeLedgerAllNetworkItem,
661
- buildUnsupportedNetworkResponse: (item) => isUnsupportedLedgerAllNetworkNetwork(item) ? buildUnsupportedNetworkResponse(item) : void 0,
662
- callItem: async ({ method, chain, item }) => {
663
- if (cancelScope.signal.aborted) return buildCancelledFailure(cancelScope.signal);
664
- const itemDeviceId = getItemDeviceId(item) ?? chainFingerprints.get(chain) ?? "";
665
- return callAllNetworkMethod(
666
- callChain,
667
- effectiveTargetId,
668
- itemDeviceId,
669
- method,
670
- item,
671
- commonParams,
672
- installContext
673
- );
674
- },
675
- attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
676
- getChainFingerprint,
677
- effectiveTargetId,
521
+ const result = await runAllNetworkGetAddress({
522
+ connectId,
523
+ deviceId: _deviceId,
524
+ params,
525
+ normalizeItem: normalizeLedgerAllNetworkItem,
526
+ buildUnsupportedNetworkResponse: (item) => isUnsupportedLedgerAllNetworkNetwork(item) ? buildUnsupportedNetworkResponse(item) : void 0,
527
+ callItem: async ({ method, chain, item }) => {
528
+ const itemDeviceId = getItemDeviceId(item) ?? chainFingerprints.get(chain) ?? "";
529
+ return callAllNetworkMethod(
530
+ callChain,
531
+ connectId,
532
+ itemDeviceId,
533
+ method,
678
534
  item,
679
- chain,
680
- payload,
681
- chainFingerprints,
682
- installContext,
683
- cancelScope.signal
684
- ),
685
- shouldAbortBundle: isTopLevelAllNetworkFailure,
686
- buildTopLevelFailure: (response) => {
687
- const code = response.payload?.code ?? HardwareErrorCode2.DeviceMismatch;
688
- return failure(
689
- code,
690
- response.payload?.error ?? "All-network get-address aborted",
691
- response.payload?.params
692
- );
693
- }
694
- });
695
- debugLog("[LedgerAdapter][RES]", {
696
- method: "allNetworkGetAddress",
697
- success: result.success,
698
- payload: result
699
- });
700
- return result;
701
- } finally {
702
- cancelScope.release();
703
- releaseOperationRetention?.();
704
- }
535
+ commonParams,
536
+ installContext
537
+ );
538
+ },
539
+ attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
540
+ getChainFingerprint,
541
+ connectId,
542
+ item,
543
+ chain,
544
+ payload,
545
+ chainFingerprints
546
+ ),
547
+ shouldAbortBundle: isTopLevelAllNetworkFailure,
548
+ buildTopLevelFailure: (response) => {
549
+ const code = response.payload?.code ?? HardwareErrorCode2.DeviceMismatch;
550
+ return failure(
551
+ code,
552
+ response.payload?.error ?? "All-network get-address aborted",
553
+ response.payload?.params
554
+ );
555
+ }
556
+ });
557
+ debugLog("[LedgerAdapter][RES]", {
558
+ method: "allNetworkGetAddress",
559
+ success: result.success,
560
+ payload: result
561
+ });
562
+ return result;
705
563
  };
706
564
  }
707
- function buildCancelledFailure(signal) {
708
- const reason = signal.reason;
709
- const code = typeof reason?.code === "number" ? reason.code : HardwareErrorCode2.UserAborted;
710
- const message = typeof reason?.message === "string" ? reason.message : "";
711
- return failure(code, message || "All-network get-address cancelled");
712
- }
713
565
  function isTopLevelAllNetworkFailure(response) {
714
566
  if (response.success) {
715
567
  return false;
716
568
  }
717
569
  const code = response.payload?.code;
718
- return code === HardwareErrorCode2.DeviceMismatch || isUserRefusal(code) || isConnectionLost(code);
570
+ return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.UserAborted || code === HardwareErrorCode2.UserRejected;
719
571
  }
720
572
  function getItemDeviceId(item) {
721
573
  const { deviceId } = item;
@@ -745,18 +597,8 @@ function normalizeLedgerAllNetworkItem(method, item) {
745
597
  const coin = LEDGER_BTC_NETWORK_COIN_MAP[item.network];
746
598
  return coin ? { ...item, coin } : item;
747
599
  }
748
- async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints, context, cancelSignal) {
749
- const knownFingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || "";
750
- if (!knownFingerprint && cancelSignal.aborted) {
751
- const cancelled = buildCancelledFailure(cancelSignal);
752
- return { ...item, success: false, payload: cancelled.payload };
753
- }
754
- const fingerprint = knownFingerprint || await bootstrapChainFingerprint(
755
- getChainFingerprint,
756
- context.connection?.connectId ?? connectId,
757
- chain,
758
- context
759
- );
600
+ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints) {
601
+ const fingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || await bootstrapChainFingerprint(getChainFingerprint, connectId, chain);
760
602
  if (!fingerprint) {
761
603
  return buildFingerprintBootstrapFailure(item, chain);
762
604
  }
@@ -777,8 +619,8 @@ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain,
777
619
  }
778
620
  };
779
621
  }
780
- async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain, context) {
781
- const response = await getChainFingerprint(connectId, chain, context);
622
+ async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain) {
623
+ const response = await getChainFingerprint(connectId, chain);
782
624
  return response.success ? response.payload : "";
783
625
  }
784
626
  function buildFingerprintBootstrapFailure(item, chain) {
@@ -882,32 +724,9 @@ function btcAccountIndexFromPath(path) {
882
724
  var _LedgerAdapter = class _LedgerAdapter {
883
725
  constructor(connector, options) {
884
726
  this.vendor = "ledger";
885
- // A cancel releases the DMK device action and its intent-queue slot, so the
886
- // next call is not blocked behind it. It still cannot retract a confirmation
887
- // screen the device is already showing.
888
- this.cancelCapability = "stops-waiting";
889
727
  this.emitter = new TypedEventEmitter();
890
- this._operations = new OperationRegistry({
891
- vendor: "ledger",
892
- onEnded: (operation, reason) => {
893
- const binding = this._pendingOperationBindings.get(operation.operationId);
894
- if (binding?.selectedConnection?.requestId === this._bindingSelectionRequestId) {
895
- this._finishBleBinding("cancelled");
896
- }
897
- this._pendingOperationBindings.delete(operation.operationId);
898
- this.emitter.emit(SDK.OPERATION_ENDED, {
899
- type: SDK.OPERATION_ENDED,
900
- payload: { operationId: operation.operationId, reason }
901
- });
902
- if (reason === "timeout") {
903
- void this._releaseOperationConnection(operation);
904
- }
905
- }
906
- });
907
728
  this._discoveredDevices = /* @__PURE__ */ new Map();
908
729
  this._sessions = /* @__PURE__ */ new Map();
909
- this._pendingOperationBindings = /* @__PURE__ */ new Map();
910
- this._verifiedBleReconnectTargets = /* @__PURE__ */ new Map();
911
730
  this._uiRegistry = new UiRequestRegistry();
912
731
  // BTC App rejects account index >= 100 unless display=true. Cached per
913
732
  // adapter instance: first 100+ path asks the user once via UI request,
@@ -921,31 +740,10 @@ var _LedgerAdapter = class _LedgerAdapter {
921
740
  this._deviceAuthenticityQueueTail = Promise.resolve();
922
741
  // Shared across concurrent callers — only `cancel()` aborts.
923
742
  this._doConnectAbortController = null;
924
- this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
925
- this._connectorIdleWaiters = /* @__PURE__ */ new Set();
926
- this._resetPromise = null;
927
- this._stateGeneration = 0;
928
- this._connectorTeardownTail = Promise.resolve();
929
- this._pendingConnectorTeardowns = 0;
930
- this._activeOperationJobs = /* @__PURE__ */ new Set();
931
- this._pendingOperationDisconnects = /* @__PURE__ */ new Set();
932
743
  this._installProgressLastEmittedValue = -Infinity;
933
744
  this.allNetworkGetAddress = createAllNetworkGetAddress({
934
745
  callChain: this.callChain.bind(this),
935
- getChainFingerprint: async (connectId, chain, context) => {
936
- try {
937
- const fingerprint = await this._computeChainFingerprint(
938
- chain,
939
- (method, params) => this.connectorCall(connectId, method, params, void 0, void 0, void 0, context)
940
- );
941
- return success(fingerprint);
942
- } catch (error) {
943
- return this.errorToFailure(error);
944
- }
945
- },
946
- retainOperation: (operationId) => this._operations.retain(operationId),
947
- errorToFailure: (error) => this.errorToFailure(error),
948
- createCancelScope: (queueKey) => this._jobQueue.createCancelScope(queueKey)
746
+ getChainFingerprint: (connectId, chain) => this.getChainFingerprint(connectId, "", chain)
949
747
  });
950
748
  // ---------------------------------------------------------------------------
951
749
  // Private helpers
@@ -955,7 +753,7 @@ var _LedgerAdapter = class _LedgerAdapter {
955
753
  *
956
754
  * - If a session already exists for the given connectId, reuse it.
957
755
  * - If ANY session exists (Ledger IDs are ephemeral), reuse it.
958
- * - Otherwise: search → one USB device auto-connects; multiple asks the host to choose.
756
+ * - Otherwise: search → exactly 1 USB device auto-connects; multiple or none throws.
959
757
  */
960
758
  // Mutex for ensureConnected — prevents concurrent calls from establishing duplicate connections
961
759
  this._connectingPromise = null;
@@ -972,12 +770,6 @@ var _LedgerAdapter = class _LedgerAdapter {
972
770
  });
973
771
  };
974
772
  this.deviceDisconnectHandler = (data) => {
975
- const activeOperation = this._operations.findActiveByConnectionKey(data.connectId);
976
- if (activeOperation && this._activeOperationJobs.has(activeOperation.operationId)) {
977
- this._pendingOperationDisconnects.add(activeOperation.operationId);
978
- } else {
979
- this._operations.endByConnectionKey(data.connectId, "disconnect");
980
- }
981
773
  this._discoveredDevices.delete(data.connectId);
982
774
  this._sessions.delete(data.connectId);
983
775
  this.emitter.emit(DEVICE.DISCONNECT, {
@@ -1034,28 +826,11 @@ var _LedgerAdapter = class _LedgerAdapter {
1034
826
  this._jobQueue = new DeviceJobQueue();
1035
827
  this.registerEventListeners();
1036
828
  }
1037
- _finishBleBinding(status) {
1038
- const selectionRequestId = this._bindingSelectionRequestId;
1039
- this._bindingSelectionRequestId = void 0;
1040
- if (selectionRequestId)
1041
- this.emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
1042
- type: UI_REQUEST.DEVICE_BINDING_STATUS,
1043
- payload: { selectionRequestId, status }
1044
- });
1045
- }
1046
- _isBleConnection() {
1047
- return isLedgerBleConnectionType(this._activeConnectionType ?? this.connector.connectionType);
1048
- }
1049
829
  // Transport
1050
830
  get activeTransport() {
1051
- return this._isBleConnection() ? "ble" : "hid";
831
+ return isLedgerBleConnectionType(this.connector.connectionType) ? "ble" : "hid";
1052
832
  }
1053
833
  getAvailableTransports() {
1054
- if (this.connector.availableTransports) {
1055
- return this.connector.availableTransports.map(
1056
- (transport) => transport === "ble" ? "ble" : "hid"
1057
- );
1058
- }
1059
834
  return this.activeTransport ? [this.activeTransport] : [];
1060
835
  }
1061
836
  // Connector is bound at construction; switching requires a new adapter.
@@ -1072,39 +847,21 @@ var _LedgerAdapter = class _LedgerAdapter {
1072
847
  * The next operation will re-discover and re-connect automatically.
1073
848
  */
1074
849
  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
850
  this._discoveredDevices.clear();
1085
851
  this._sessions.clear();
1086
- this._verifiedBleReconnectTargets.clear();
1087
852
  this._connectingPromise = null;
1088
- this._doConnectAbortController = null;
1089
853
  this._uiRegistry.reset();
1090
854
  this._jobQueue.clear();
1091
855
  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
856
  }
1104
857
  async dispose() {
1105
- await this._resetStateAndDisconnectSessions();
858
+ this._uiRegistry.reset();
859
+ this._jobQueue.clear();
1106
860
  this.unregisterEventListeners();
1107
861
  this.connector.reset();
862
+ this._discoveredDevices.clear();
863
+ this._sessions.clear();
864
+ this._btcHighIndexConfirmedThisSession = false;
1108
865
  this.emitter.removeAllListeners();
1109
866
  }
1110
867
  uiResponse(response) {
@@ -1114,32 +871,17 @@ var _LedgerAdapter = class _LedgerAdapter {
1114
871
  // Device management
1115
872
  // ---------------------------------------------------------------------------
1116
873
  async searchDevices(options) {
1117
- return this._searchDevices(options);
1118
- }
1119
- async _searchDevices(options, signal) {
1120
874
  debugLog("[LedgerAdapter][REQ]", { method: "searchDevices", params: options });
1121
875
  try {
1122
876
  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 createHwkError({
1139
- code: HardwareErrorCode3.UserAborted,
1140
- message: "Ledger discovery was reset"
1141
- });
877
+ this._doConnectAbortController?.abort();
878
+ this._sessions.clear();
879
+ this._connectingPromise = null;
880
+ this._doConnectAbortController = null;
881
+ this._btcHighIndexConfirmedThisSession = false;
1142
882
  }
883
+ await this._ensureDevicePermission();
884
+ const devices = await this.connector.searchDevices();
1143
885
  this._discoveredDevices.clear();
1144
886
  for (const d of devices) {
1145
887
  if (d.connectId) {
@@ -1147,7 +889,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1147
889
  }
1148
890
  }
1149
891
  if (this._discoveredDevices.size === 0) {
1150
- await this._ensureDevicePermission(void 0, void 0, signal);
892
+ await this._ensureDevicePermission();
1151
893
  }
1152
894
  const result = Array.from(this._discoveredDevices.values());
1153
895
  debugLog("[LedgerAdapter][RES]", {
@@ -1166,287 +908,41 @@ var _LedgerAdapter = class _LedgerAdapter {
1166
908
  throw err;
1167
909
  }
1168
910
  }
1169
- async searchDeviceTargets(options) {
1170
- const devices = await this.searchDevices(options);
1171
- return devices.map((device) => ({
1172
- searchTargetId: device.connectId,
1173
- searchTargetReusePolicy: 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
911
  // USB single-session invariant: evict all sessions, best-effort (see connectDevice).
1191
- async _evictAllSessions(preserveOperationId) {
1192
- this._operations.endAll("explicit", preserveOperationId);
912
+ async _evictAllSessions() {
1193
913
  if (this._sessions.size === 0) return;
1194
914
  const stale = [...this._sessions.values()];
1195
915
  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
- }
916
+ for (const sid of stale) {
917
+ try {
918
+ await this.connector.disconnect(sid);
919
+ } catch {
1202
920
  }
1203
- });
921
+ }
1204
922
  }
1205
923
  static _createDeviceBusyError(method) {
1206
924
  return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
1207
925
  code: HardwareErrorCode3.DeviceBusy
1208
926
  });
1209
927
  }
1210
- async connectDevice(searchTargetId) {
1211
- try {
1212
- return await this._jobQueue.enqueue(
1213
- searchTargetId || "__ledger_connect__",
1214
- async () => {
1215
- const connected = await this._connectTarget(searchTargetId);
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 failure2(HardwareErrorCode3.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 createHwkError({
1240
- code: HardwareErrorCode3.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 createHwkError({
1263
- code: HardwareErrorCode3.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 createHwkError({
1300
- code: HardwareErrorCode3.UnknownError,
1301
- message: "Bluetooth binding could not be saved",
1302
- origin: "host"
1303
- });
1304
- }
1305
- saved = true;
1306
- return 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 success(operation.operationId);
1398
- }
1399
- async _connectTarget(connectId, preserveOperationId, signal) {
928
+ async connectDevice(connectId) {
1400
929
  debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
1401
930
  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) {
931
+ if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
1408
932
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
1409
933
  code: HardwareErrorCode3.DeviceNotFound
1410
934
  });
1411
935
  }
1412
- await this._ensureDevicePermission(connectId, void 0, signal);
1413
- if (signal) _LedgerAdapter._throwIfAborted(signal);
1414
- if (this.connector.availableTransports && this.connector.availableTransports.length > 1) {
1415
- await this._evictAllSessions(preserveOperationId);
1416
- } else if (this._isBleConnection()) {
1417
- const previousSessionId = this._sessions.get(connectId);
1418
- this._operations.endByConnectionKey(connectId, "explicit", preserveOperationId);
1419
- if (previousSessionId) {
1420
- this._operations.endByConnectionKey(previousSessionId, "explicit", preserveOperationId);
1421
- this._sessions.delete(connectId);
1422
- await this.connector.disconnect(previousSessionId).catch(() => void 0);
1423
- }
1424
- } else {
1425
- await this._evictAllSessions(preserveOperationId);
1426
- }
1427
- if (signal) _LedgerAdapter._throwIfAborted(signal);
1428
- const stateGeneration = this._stateGeneration;
1429
- const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
1430
- let session;
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: HardwareErrorCode3.UserAborted
1439
- });
1440
- }
1441
- } finally {
1442
- releaseOperation();
936
+ if (!isLedgerBleConnectionType(this.connector.connectionType)) {
937
+ await this._evictAllSessions();
1443
938
  }
1444
- const resolvedConnectId = session.deviceInfo?.connectId || connectId;
1445
- this._sessions.set(resolvedConnectId, session.sessionId);
939
+ await this._ensureDevicePermission(connectId);
940
+ const session = await this.connector.connect(connectId);
941
+ this._sessions.set(connectId, session.sessionId);
1446
942
  if (session.deviceInfo) {
1447
- this._discoveredDevices.set(resolvedConnectId, session.deviceInfo);
943
+ this._discoveredDevices.set(connectId, session.deviceInfo);
1448
944
  }
1449
- const result = success(resolvedConnectId);
945
+ const result = success(connectId);
1450
946
  debugLog("[LedgerAdapter][RES]", { method: "connectDevice", success: true, payload: result });
1451
947
  return result;
1452
948
  } catch (err) {
@@ -1459,60 +955,30 @@ var _LedgerAdapter = class _LedgerAdapter {
1459
955
  return failureResult;
1460
956
  }
1461
957
  }
1462
- async releaseOperation(operationId) {
1463
- const operation = this._operations.find(operationId);
1464
- if (!operation) {
1465
- this._operations.resolve(operationId);
1466
- return;
1467
- }
1468
- const endedOperation = this._operations.end(operationId, "explicit");
1469
- if (!endedOperation) return;
1470
- const { connectId } = operation;
958
+ async disconnectDevice(connectId) {
1471
959
  debugLog("[LedgerAdapter][REQ]", {
1472
- method: "releaseOperation",
960
+ method: "disconnectDevice",
1473
961
  connectId,
1474
962
  params: { connectId }
1475
963
  });
1476
964
  try {
1477
- await this._releaseOperationConnection(endedOperation);
1478
- debugLog("[LedgerAdapter][RES]", { method: "releaseOperation", success: true });
965
+ const sessionId = this._sessions.get(connectId);
966
+ if (sessionId) {
967
+ await this.connector.disconnect(sessionId);
968
+ this._sessions.delete(connectId);
969
+ }
970
+ debugLog("[LedgerAdapter][RES]", { method: "disconnectDevice", success: true });
1479
971
  } catch (err) {
1480
972
  const e = err;
1481
973
  debugLog("[LedgerAdapter][RES]", {
1482
- method: "releaseOperation",
974
+ method: "disconnectDevice",
1483
975
  success: false,
1484
976
  error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
1485
977
  });
1486
978
  throw err;
1487
979
  }
1488
980
  }
1489
- async _releaseOperationConnection(operation) {
1490
- const sessionIds = /* @__PURE__ */ new Set();
1491
- for (const [connectId, sessionId] of this._sessions) {
1492
- if (connectId === operation.connectId || operation.connectionKeys.includes(connectId) || operation.connectionKeys.includes(sessionId)) {
1493
- this._sessions.delete(connectId);
1494
- sessionIds.add(sessionId);
1495
- }
1496
- }
1497
- for (const sessionId of sessionIds) {
1498
- await this._runConnectorTeardown(
1499
- () => this.connector.disconnect(sessionId).catch(() => void 0)
1500
- );
1501
- }
1502
- }
1503
- async _releaseLostOperationConnection(operationId) {
1504
- const endedOperation = this._operations.end(operationId, "disconnect");
1505
- if (!endedOperation) return;
1506
- this._discoveredDevices.delete(endedOperation.connectId);
1507
- await this._releaseOperationConnection(endedOperation);
1508
- }
1509
- async getDeviceInfo(connectIdOrOperationId, deviceId) {
1510
- let connectId;
1511
- try {
1512
- connectId = isHardwareOperationId(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
1513
- } catch (error) {
1514
- return this.errorToFailure(error);
1515
- }
981
+ async getDeviceInfo(connectId, deviceId) {
1516
982
  debugLog("[LedgerAdapter][REQ]", {
1517
983
  method: "getDeviceInfo",
1518
984
  connectId,
@@ -1550,6 +1016,9 @@ var _LedgerAdapter = class _LedgerAdapter {
1550
1016
  throw err;
1551
1017
  }
1552
1018
  }
1019
+ getSupportedChains() {
1020
+ return ["evm", "btc", "sol", "tron"];
1021
+ }
1553
1022
  // ---------------------------------------------------------------------------
1554
1023
  // Chain call helper
1555
1024
  // ---------------------------------------------------------------------------
@@ -1584,21 +1053,13 @@ var _LedgerAdapter = class _LedgerAdapter {
1584
1053
  if (params && typeof params === "object") {
1585
1054
  const {
1586
1055
  autoInstallApp,
1587
- operationId,
1588
1056
  passphraseState: _passphraseState,
1589
1057
  useEmptyPassphrase: _useEmptyPassphrase,
1590
- knownConnections,
1591
- extra,
1592
- allowDeviceSelection,
1593
1058
  ...rest
1594
1059
  } = params;
1595
1060
  return {
1596
1061
  commonParams: {
1597
- autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0,
1598
- operationId: typeof operationId === "string" ? operationId : void 0,
1599
- knownConnections,
1600
- extra,
1601
- allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0
1062
+ autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
1602
1063
  },
1603
1064
  rest
1604
1065
  };
@@ -1774,27 +1235,6 @@ var _LedgerAdapter = class _LedgerAdapter {
1774
1235
  );
1775
1236
  }
1776
1237
  // ---------------------------------------------------------------------------
1777
- // Zcash chain methods (viewing key + shielded address reads; Zcash app >= 3.8.0)
1778
- // ---------------------------------------------------------------------------
1779
- zcashGetFullViewingKey(connectId, deviceId, params) {
1780
- return this.callChainWithMergedParams(
1781
- connectId,
1782
- deviceId,
1783
- "zcash",
1784
- "zcashGetFullViewingKey",
1785
- params
1786
- );
1787
- }
1788
- zcashGetShieldedAddress(connectId, deviceId, params) {
1789
- return this.callChainWithMergedParams(
1790
- connectId,
1791
- deviceId,
1792
- "zcash",
1793
- "zcashGetShieldedAddress",
1794
- params
1795
- );
1796
- }
1797
- // ---------------------------------------------------------------------------
1798
1238
  // App management — OS-level Ledger app install / list. Bypasses fingerprint
1799
1239
  // and chain-handler dispatch; installApp progress is forwarded to the adapter
1800
1240
  // emitter via 'ui-event' AppInstallProgress events.
@@ -1883,7 +1323,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1883
1323
  );
1884
1324
  }
1885
1325
  await this.connector.configure({ ledgerGenuineCheckWebSocketUrl: relayUrl });
1886
- await this._resetStateAndDisconnectSessions();
1326
+ this.resetState();
1887
1327
  }
1888
1328
  const result = await this.connectorCall(connectId, "getDeviceGenuineCheck", {});
1889
1329
  if (!result.isGenuine) {
@@ -1909,10 +1349,10 @@ var _LedgerAdapter = class _LedgerAdapter {
1909
1349
  if (relayUrl) {
1910
1350
  try {
1911
1351
  await this.connector.configure?.({ ledgerGenuineCheckWebSocketUrl: void 0 });
1912
- await this._resetStateAndDisconnectSessions();
1352
+ this.resetState();
1913
1353
  } catch {
1914
1354
  this.connector.reset();
1915
- await this._resetStateAndDisconnectSessions();
1355
+ this.resetState();
1916
1356
  }
1917
1357
  }
1918
1358
  }
@@ -1924,20 +1364,6 @@ var _LedgerAdapter = class _LedgerAdapter {
1924
1364
  this.emitter.off(event, listener);
1925
1365
  }
1926
1366
  cancel(connectId) {
1927
- const namedOperationIsLive = (id) => {
1928
- try {
1929
- this._operations.resolve(id);
1930
- return true;
1931
- } catch {
1932
- return false;
1933
- }
1934
- };
1935
- if (isHardwareOperationId(connectId) && !namedOperationIsLive(connectId)) {
1936
- debugLog("[LedgerAdapter] cancel target already ended; nothing to cancel", {
1937
- connectId
1938
- });
1939
- return;
1940
- }
1941
1367
  const userAbortReason = Object.assign(new Error("User aborted operation"), {
1942
1368
  code: HardwareErrorCode3.UserAborted,
1943
1369
  _tag: ERROR_TAG.UserAborted
@@ -1948,50 +1374,14 @@ var _LedgerAdapter = class _LedgerAdapter {
1948
1374
  this._lastCancelReason = void 0;
1949
1375
  }
1950
1376
  }, 2e3);
1951
- const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
1952
- let operationId;
1953
- if (isHardwareOperationId(connectId)) {
1954
- operationId = connectId;
1955
- } else if (!connectId && isHardwareOperationId(activeJobId)) {
1956
- operationId = activeJobId;
1957
- }
1958
- let resolvedConnectId = connectId;
1959
- if (operationId) {
1960
- try {
1961
- resolvedConnectId = this._operations.resolve(operationId).connectId;
1962
- } catch {
1963
- resolvedConnectId = void 0;
1964
- }
1965
- }
1966
- const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
1967
- const pendingOperationId = operationId ?? interactionForPhysicalId?.operationId;
1968
- if (!connectId) {
1969
- this._uiRegistry.cancel();
1970
- } else if (pendingOperationId) {
1971
- this._uiRegistry.cancel(void 0, void 0, pendingOperationId);
1972
- }
1973
- this._finishBleBinding("cancelled");
1974
- if (!connectId) this._pendingOperationBindings.clear();
1975
- else if (pendingOperationId) {
1976
- this._pendingOperationBindings.delete(pendingOperationId);
1977
- }
1978
- const queueKeys = /* @__PURE__ */ new Set();
1979
- if (connectId) queueKeys.add(ledgerQueueKey({ connectId }));
1980
- if (pendingOperationId) queueKeys.add(ledgerQueueKey({ operationId: pendingOperationId }));
1981
- if (queueKeys.size) {
1982
- let cancelledAnyJob = false;
1983
- for (const key of queueKeys) {
1984
- cancelledAnyJob = this._jobQueue.cancelActiveAndPending(key, userAbortReason) || cancelledAnyJob;
1985
- }
1986
- debugLog("[LedgerAdapter] cancel routed to queue keys", {
1987
- queueKeys: [...queueKeys],
1988
- cancelledAnyJob
1989
- });
1377
+ this._uiRegistry.cancel();
1378
+ if (connectId) {
1379
+ this._jobQueue.cancelActiveAndPending(connectId, userAbortReason);
1990
1380
  } else {
1991
1381
  this._jobQueue.cancelActiveAndPending(void 0, userAbortReason);
1992
1382
  }
1993
- if (resolvedConnectId) {
1994
- const sessionId = this._sessions.get(resolvedConnectId) ?? resolvedConnectId;
1383
+ if (connectId) {
1384
+ const sessionId = this._sessions.get(connectId) ?? connectId;
1995
1385
  void this.connector.cancel(sessionId);
1996
1386
  } else {
1997
1387
  for (const sid of this._sessions.values()) void this.connector.cancel(sid);
@@ -2003,99 +1393,41 @@ var _LedgerAdapter = class _LedgerAdapter {
2003
1393
  // ---------------------------------------------------------------------------
2004
1394
  // Chain fingerprint
2005
1395
  // ---------------------------------------------------------------------------
2006
- /** A non-empty deviceId is an expected chain fingerprint, not a transport identifier. */
2007
1396
  async getChainFingerprint(connectId, deviceId, chain) {
2008
1397
  try {
2009
1398
  const fingerprint = await this._computeChainFingerprint(
2010
1399
  chain,
2011
1400
  (method, params) => this.connectorCall(connectId, method, params, void 0, deviceId)
2012
1401
  );
2013
- if (deviceId) {
2014
- if (fingerprint !== deviceId) {
2015
- return failure2(
2016
- HardwareErrorCode3.DeviceMismatch,
2017
- formatDeviceMismatchError(deviceId, fingerprint)
2018
- );
2019
- }
2020
- if (isHardwareOperationId(connectId)) {
2021
- const operation = this._operations.resolve(connectId);
2022
- await this._publishVerifiedBleBinding(
2023
- operation.connectId,
2024
- chain,
2025
- fingerprint,
2026
- void 0,
2027
- connectId
2028
- );
2029
- }
2030
- }
2031
1402
  return success(fingerprint);
2032
1403
  } catch (err) {
2033
1404
  debugError("[LedgerAdapter] getChainFingerprint error:", chain, err);
2034
1405
  return this.errorToFailure(err);
2035
1406
  }
2036
1407
  }
2037
- /** Discovery may select a BLE target before a later call verifies its wallet. */
2038
- async _publishVerifiedBleBinding(connectId, chain, fingerprint, attempt, operationId, signal) {
2039
- const binding = operationId ? this._pendingOperationBindings.get(operationId) : attempt;
2040
- if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== connectId) {
2041
- return false;
2042
- }
1408
+ /**
1409
+ * Verify fingerprint using an existing sessionId directly.
1410
+ * Safe to call inside connectorCall without causing queue deadlock.
1411
+ */
1412
+ async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
1413
+ if (!deviceId) return { success: true };
2043
1414
  try {
2044
- const outcome = await requestSaveDeviceBinding(
2045
- this.emitter,
2046
- this._uiRegistry,
2047
- {
2048
- selectionRequestId: binding.selectedConnection.requestId,
2049
- connection: { transport: "ble", connectId },
2050
- identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
2051
- extra: binding.extra,
2052
- operationId
2053
- },
2054
- signal
1415
+ const fingerprint = await this._computeChainFingerprint(
1416
+ chain,
1417
+ (method, params) => this._callConnector(sessionId, method, params)
2055
1418
  );
2056
- if (operationId) this._operations.resolve(operationId);
2057
- if (!outcome.saved) {
2058
- debugLog("[LedgerAdapter] BLE binding not persisted by host", {
2059
- connectId,
2060
- reason: outcome.reason
2061
- });
2062
- }
2063
- return outcome.saved;
2064
- } catch (error) {
2065
- if (operationId) {
2066
- this._pendingOperationBindings.delete(operationId);
2067
- this._operations.end(operationId, "explicit");
2068
- }
2069
- const sessionId = this._sessions.get(connectId);
2070
- this._sessions.delete(connectId);
2071
- if (sessionId) {
2072
- const teardown = this._runConnectorTeardown(
2073
- () => this.connector.disconnect(sessionId)
2074
- ).catch(() => void 0);
2075
- if (!signal?.aborted) await teardown;
2076
- }
2077
- throw error;
2078
- } finally {
2079
- if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
2080
- this._bindingSelectionRequestId = void 0;
1419
+ if (fingerprint === deviceId) {
1420
+ return { success: true };
2081
1421
  }
2082
- if (operationId) {
2083
- this._pendingOperationBindings.delete(operationId);
1422
+ return { success: false, expected: deviceId, actual: fingerprint };
1423
+ } catch (err) {
1424
+ const mapped = mapLedgerError(err);
1425
+ if (mapped.code === HardwareErrorCode3.WrongApp || mapped.code === HardwareErrorCode3.DeviceLocked) {
1426
+ return { success: true };
2084
1427
  }
1428
+ throw err;
2085
1429
  }
2086
1430
  }
2087
- /** Verify on the acquired session without re-entering the job queue. */
2088
- async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
2089
- if (!deviceId) return { success: true };
2090
- const fingerprint = await this._computeChainFingerprint(
2091
- chain,
2092
- (method, params) => this._callConnector(sessionId, method, params)
2093
- );
2094
- if (fingerprint === deviceId) {
2095
- return { success: true };
2096
- }
2097
- return { success: false, expected: deviceId, actual: fingerprint };
2098
- }
2099
1431
  /**
2100
1432
  * Compute the chain fingerprint via a caller-supplied call strategy.
2101
1433
  *
@@ -2123,27 +1455,12 @@ var _LedgerAdapter = class _LedgerAdapter {
2123
1455
  address = (await callMethod("solGetAddress", { path, showOnDevice: false })).address;
2124
1456
  } else if (chain === "tron") {
2125
1457
  address = (await callMethod("tronGetAddress", { path, showOnDevice: false })).address;
2126
- } else if (chain === "zcash") {
2127
- address = (await callMethod("zcashGetShieldedAddress", { path, showOnDevice: false })).address;
2128
1458
  } else {
2129
1459
  throw new Error(`Unsupported chain for fingerprint: ${chain}`);
2130
1460
  }
2131
1461
  return deriveDeviceFingerprint(address);
2132
1462
  }
2133
1463
  // Ledger WebUSB won't expose a locked device, so we can't auto-detect unlock.
2134
- /**
2135
- * The operation the running device job belongs to, so a UI request raised
2136
- * mid-call can name it. A call pinned to an operation queues under its id
2137
- * (`ledgerQueueKey`); a call without one queues under the connectId, and the
2138
- * live operation on that connection is still the owner. Only work with no
2139
- * operation at all (cold start) comes back undefined.
2140
- */
2141
- _activeOperationId() {
2142
- const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
2143
- if (!activeJobId) return void 0;
2144
- if (isHardwareOperationId(activeJobId)) return activeJobId;
2145
- return this._operations.findActiveByConnectionKey(activeJobId)?.operationId;
2146
- }
2147
1464
  // The user must press Confirm after unlocking, which triggers a search retry.
2148
1465
  // If `signal` is provided, an abort cancels the pending UI request so the
2149
1466
  // registry slot is released and a stale RECEIVE_DEVICE_CONNECT won't land in
@@ -2152,18 +1469,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2152
1469
  if (signal?.aborted) {
2153
1470
  _LedgerAdapter._throwIfAborted(signal);
2154
1471
  }
2155
- const operationId = this._activeOperationId();
2156
1472
  const waitPromise = this._uiRegistry.wait(
2157
- UI_REQUEST.REQUEST_DEVICE_CONNECT,
2158
- { operationId }
1473
+ UI_REQUEST.REQUEST_DEVICE_CONNECT
2159
1474
  );
2160
1475
  this.emitter.emit(UI_REQUEST.REQUEST_DEVICE_CONNECT, {
2161
1476
  type: UI_REQUEST.REQUEST_DEVICE_CONNECT,
2162
1477
  payload: {
2163
1478
  vendor: "ledger",
2164
1479
  reason: "device-not-found",
2165
- message: "Please connect and unlock your Ledger device",
2166
- operationId
1480
+ message: "Please connect and unlock your Ledger device"
2167
1481
  }
2168
1482
  });
2169
1483
  let payload;
@@ -2224,18 +1538,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2224
1538
  return { ...params, showOnDevice: true };
2225
1539
  }
2226
1540
  async _waitForBtcHighIndexConfirm(path, accountIndex) {
2227
- const operationId = this._activeOperationId();
2228
1541
  const waitPromise = this._uiRegistry.wait(
2229
- UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
2230
- { operationId }
1542
+ UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM
2231
1543
  );
2232
1544
  this.emitter.emit(UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM, {
2233
1545
  type: UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
2234
1546
  payload: {
2235
1547
  vendor: "ledger",
2236
1548
  path,
2237
- accountIndex,
2238
- operationId
1549
+ accountIndex
2239
1550
  }
2240
1551
  });
2241
1552
  try {
@@ -2252,14 +1563,12 @@ var _LedgerAdapter = class _LedgerAdapter {
2252
1563
  // Ask the user whether to install a missing app (autoInstallApp flow).
2253
1564
  // Same register-then-emit ordering as the BTC high-index gate.
2254
1565
  async _waitForInstallAppConfirm(appName) {
2255
- const operationId = this._activeOperationId();
2256
1566
  const waitPromise = this._uiRegistry.wait(
2257
- UI_REQUEST.REQUEST_INSTALL_APP,
2258
- { operationId }
1567
+ UI_REQUEST.REQUEST_INSTALL_APP
2259
1568
  );
2260
1569
  this.emitter.emit(UI_REQUEST.REQUEST_INSTALL_APP, {
2261
1570
  type: UI_REQUEST.REQUEST_INSTALL_APP,
2262
- payload: { vendor: "ledger", appName, operationId }
1571
+ payload: { vendor: "ledger", appName }
2263
1572
  });
2264
1573
  try {
2265
1574
  const payload = await waitPromise;
@@ -2275,17 +1584,16 @@ var _LedgerAdapter = class _LedgerAdapter {
2275
1584
  // Layer 1 entry. Caller signal only races the outer awaiter; the shared
2276
1585
  // `_doConnect` runs under its own internal controller so caller A's cancel
2277
1586
  // doesn't kill caller B's await.
2278
- async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false, preserveOperationId, context) {
1587
+ async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false) {
2279
1588
  if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
2280
- this._assertConnectorReady("connectDevice");
2281
- if (this._isBleConnection() && !connectId && !allowUsbEphemeralFallback) {
1589
+ if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
2282
1590
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
2283
1591
  code: HardwareErrorCode3.DeviceNotFound
2284
1592
  });
2285
1593
  }
2286
1594
  if (connectId && this._sessions.has(connectId)) return connectId;
2287
1595
  if (!connectId && this._sessions.size > 0) {
2288
- if (!this._isBleConnection() && this._sessions.size > 1) {
1596
+ if (!isLedgerBleConnectionType(this.connector.connectionType) && this._sessions.size > 1) {
2289
1597
  throw Object.assign(
2290
1598
  new Error(
2291
1599
  "Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
@@ -2295,24 +1603,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2295
1603
  }
2296
1604
  return this._sessions.keys().next().value;
2297
1605
  }
2298
- if (!this._connectingPromise || this._doConnectAbortController?.signal.aborted) {
2299
- const controller = new AbortController();
2300
- this._doConnectAbortController = controller;
2301
- const innerSignal = controller.signal;
1606
+ if (!this._connectingPromise) {
1607
+ this._doConnectAbortController = new AbortController();
1608
+ const innerSignal = this._doConnectAbortController.signal;
2302
1609
  this._connectingPromise = (async () => {
2303
1610
  try {
2304
- return await this._doConnect(
2305
- innerSignal,
2306
- connectId,
2307
- allowUsbEphemeralFallback,
2308
- preserveOperationId,
2309
- context
2310
- );
1611
+ return await this._doConnect(innerSignal, connectId, allowUsbEphemeralFallback);
2311
1612
  } finally {
2312
- if (this._doConnectAbortController === controller) {
2313
- this._connectingPromise = null;
2314
- this._doConnectAbortController = null;
2315
- }
1613
+ this._connectingPromise = null;
1614
+ this._doConnectAbortController = null;
2316
1615
  }
2317
1616
  })();
2318
1617
  }
@@ -2321,51 +1620,22 @@ var _LedgerAdapter = class _LedgerAdapter {
2321
1620
  // Layer 1 main loop — the ONLY place in SDK that emits unlock dialog.
2322
1621
  // Bounded by MAX_DOCONNECT_CONFIRMS — after N Confirms with no progress,
2323
1622
  // 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
- this._activeConnectionType = "ble";
2345
- const knownBle = context?.knownConnections?.find(
2346
- (connection) => connection.transport === "ble"
2347
- );
2348
- if (knownBle?.transport === "ble") {
2349
- return this._connectDeviceOrThrow(knownBle.connectId, preserveOperationId, internalSignal);
2350
- }
2351
- if (targetConnectId && context?.knownConnections === void 0) {
2352
- throw createHwkError({
2353
- code: HardwareErrorCode3.DeviceNotFound,
2354
- message: "Ledger connection metadata is required before starting Bluetooth binding"
2355
- });
1623
+ async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false) {
1624
+ if (isLedgerBleConnectionType(this.connector.connectionType) && targetConnectId) {
1625
+ try {
1626
+ return await this._connectDeviceOrThrow(targetConnectId);
1627
+ } catch (err) {
1628
+ if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
1629
+ throw err;
1630
+ }
1631
+ this._discoveredDevices.delete(targetConnectId);
1632
+ if (isDeviceDisconnectedError(err)) {
1633
+ try {
1634
+ this.connector.reset?.();
1635
+ } catch {
1636
+ }
1637
+ }
2356
1638
  }
2357
- const bleDevices = await this._searchDevices({ transportType: "ble" }, internalSignal);
2358
- return this._connectFirstOrSelect(
2359
- bleDevices,
2360
- void 0,
2361
- allowUsbEphemeralFallback,
2362
- preserveOperationId,
2363
- context,
2364
- internalSignal
2365
- );
2366
- }
2367
- if (this._isBleConnection() && targetConnectId) {
2368
- return this._connectDeviceOrThrow(targetConnectId, preserveOperationId, internalSignal);
2369
1639
  }
2370
1640
  let confirms = 0;
2371
1641
  while (!internalSignal.aborted) {
@@ -2373,28 +1643,22 @@ var _LedgerAdapter = class _LedgerAdapter {
2373
1643
  type: EConnectorInteraction.Searching,
2374
1644
  payload: { sessionId: "" }
2375
1645
  });
2376
- let devices = await this._searchDevices(void 0, internalSignal);
2377
- _LedgerAdapter._throwIfAborted(internalSignal);
1646
+ let devices = await this.searchDevices();
2378
1647
  if (devices.length === 0) {
2379
1648
  for (let i = 0; i < 3 && !internalSignal.aborted; i += 1) {
2380
1649
  await new Promise((resolve) => {
2381
1650
  setTimeout(resolve, DEVICE_CONNECT_RETRY_DELAY_MS);
2382
1651
  });
2383
- _LedgerAdapter._throwIfAborted(internalSignal);
2384
- devices = await this._searchDevices(void 0, internalSignal);
2385
- _LedgerAdapter._throwIfAborted(internalSignal);
1652
+ devices = await this.searchDevices();
2386
1653
  if (devices.length > 0) break;
2387
1654
  }
2388
1655
  }
2389
- if (devices.length > 0 || this._isBleConnection() && allowUsbEphemeralFallback) {
1656
+ if (devices.length > 0) {
2390
1657
  try {
2391
1658
  return await this._connectFirstOrSelect(
2392
1659
  devices,
2393
1660
  targetConnectId,
2394
- allowUsbEphemeralFallback,
2395
- preserveOperationId,
2396
- context,
2397
- internalSignal
1661
+ allowUsbEphemeralFallback
2398
1662
  );
2399
1663
  } catch (err) {
2400
1664
  if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
@@ -2423,136 +1687,45 @@ var _LedgerAdapter = class _LedgerAdapter {
2423
1687
  _LedgerAdapter._throwIfAborted(internalSignal);
2424
1688
  throw new Error("_doConnect aborted");
2425
1689
  }
2426
- async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback, preserveOperationId, context, signal) {
2427
- _LedgerAdapter._throwIfAborted(signal);
1690
+ async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback = false) {
2428
1691
  if (targetConnectId) {
2429
1692
  const target = devices.find(
2430
1693
  (d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
2431
1694
  );
2432
1695
  if (target) {
2433
- return this._connectDeviceOrThrow(target.connectId, preserveOperationId, signal);
1696
+ return this._connectDeviceOrThrow(target.connectId);
2434
1697
  }
2435
- if (!this._isBleConnection() && devices.length === 1 && allowUsbEphemeralFallback) {
1698
+ if (!isLedgerBleConnectionType(this.connector.connectionType) && devices.length === 1 && allowUsbEphemeralFallback) {
2436
1699
  debugLog(
2437
1700
  `[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} for fingerprint-verified recovery`
2438
1701
  );
2439
- return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
1702
+ return this._connectDeviceOrThrow(devices[0].connectId);
2440
1703
  }
2441
- if (!this._isBleConnection() || !allowUsbEphemeralFallback) {
2442
- const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
2443
- code: HardwareErrorCode3.DeviceNotFound
2444
- });
2445
- if (this._isBleConnection()) {
2446
- err._tag = ERROR_TAG.DeviceNotAdvertising;
2447
- }
2448
- throw err;
1704
+ const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
1705
+ code: HardwareErrorCode3.DeviceNotFound
1706
+ });
1707
+ if (isLedgerBleConnectionType(this.connector.connectionType)) {
1708
+ err._tag = ERROR_TAG.DeviceNotAdvertising;
2449
1709
  }
1710
+ throw err;
2450
1711
  }
2451
- const requiresBleSelection = this._isBleConnection();
2452
- if (requiresBleSelection && !allowUsbEphemeralFallback) {
1712
+ if (isLedgerBleConnectionType(this.connector.connectionType)) {
2453
1713
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
2454
1714
  code: HardwareErrorCode3.DeviceNotFound
2455
1715
  });
2456
1716
  }
2457
- if (requiresBleSelection && context?.allowDeviceSelection !== false) {
2458
- const bindingSessionId = context?.bindingSessionId ?? this._uiRegistry.createRequestId();
2459
- if (context) context.bindingSessionId = bindingSessionId;
2460
- const allowUsbFallback = context?.bindingReason !== "manual-rebind" && Boolean(this.connector.availableTransports?.includes("usb"));
2461
- const knownUsb = context?.knownConnections?.find(
2462
- (connection) => connection.transport === "usb"
2463
- );
2464
- const usbConnectId = knownUsb?.transport === "usb" ? knownUsb.connectId : targetConnectId;
2465
- const { device, requestId } = await requestBleDeviceSelection({
2466
- emitter: this.emitter,
2467
- registry: this._uiRegistry,
2468
- signal,
2469
- allowUsbFallback,
2470
- scan: async () => {
2471
- if (allowUsbFallback) {
2472
- const usbDevices = await this._searchDevices({ transportType: "usb" }, signal);
2473
- const candidate = usbDevices.find(
2474
- (device2) => device2.connectionType === "usb" && device2.connectId === usbConnectId
2475
- ) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
2476
- if (candidate) return [candidate];
2477
- }
2478
- return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId));
2479
- },
2480
- request: {
2481
- devices: devices.filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId)),
2482
- bindingSessionId,
2483
- rejectedConnectId: context?.rejectedConnectId,
2484
- context: {
2485
- kind: "bind-connection",
2486
- transport: "ble",
2487
- reason: context?.bindingReason ?? (targetConnectId ? "known-connection-unavailable" : "missing-binding")
2488
- },
2489
- extra: context?.extra,
2490
- operationId: preserveOperationId
2491
- }
2492
- });
2493
- if (device.connectionType === "usb") {
2494
- this._activeConnectionType = "usb";
2495
- if (context) context.selectedConnection = void 0;
2496
- if (preserveOperationId) this._pendingOperationBindings.delete(preserveOperationId);
2497
- this._bindingSelectionRequestId = void 0;
2498
- this.emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
2499
- type: UI_REQUEST.DEVICE_BINDING_STATUS,
2500
- payload: { selectionRequestId: requestId, status: "cancelled" }
2501
- });
2502
- return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
2503
- }
2504
- if (context) context.selectedConnection = { connectId: device.connectId, requestId };
2505
- this._bindingSelectionRequestId = requestId;
2506
- return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
2507
- }
2508
- if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
2509
- if (context?.allowDeviceSelection === false || !this.emitter.listenerCount(UI_REQUEST.REQUEST_SELECT_DEVICE)) {
2510
- throw createHwkError({
2511
- code: HardwareErrorCode3.DeviceNotFound,
2512
- message: "Select a Ledger device before continuing"
2513
- });
2514
- }
2515
- const requestId = this._uiRegistry.createRequestId();
2516
- const operationId = this._activeOperationId();
2517
- const waitPromise = this._uiRegistry.wait(
2518
- UI_REQUEST.REQUEST_SELECT_DEVICE,
2519
- { requestId, operationId }
2520
- );
2521
- this.emitter.emit(UI_REQUEST.REQUEST_SELECT_DEVICE, {
2522
- type: UI_REQUEST.REQUEST_SELECT_DEVICE,
2523
- payload: {
2524
- devices,
2525
- requestId,
2526
- operationId,
2527
- context: requiresBleSelection ? {
2528
- kind: "bind-connection",
2529
- transport: "ble",
2530
- reason: targetConnectId ? "known-connection-unavailable" : "missing-binding"
2531
- } : { kind: "select-device", transport: "usb", reason: "multiple-candidates" },
2532
- extra: context?.extra
2533
- }
2534
- });
2535
- const { sdkConnectId } = await (signal ? this._abortable(signal, waitPromise) : waitPromise);
2536
- if (signal) _LedgerAdapter._throwIfAborted(signal);
2537
- const selected = devices.find((device) => device.connectId === sdkConnectId);
2538
- if (!selected) {
2539
- throw Object.assign(new Error("Selected Ledger is no longer available"), {
2540
- code: HardwareErrorCode3.DeviceNotFound
2541
- });
2542
- }
2543
- if (context && requiresBleSelection)
2544
- context.selectedConnection = { connectId: selected.connectId, requestId };
2545
- return this._connectDeviceOrThrow(selected.connectId, preserveOperationId, signal);
1717
+ if (devices.length > 1) {
1718
+ throw createMultipleUsbLedgerDevicesError();
2546
1719
  }
2547
1720
  if (devices.length !== 1) {
2548
1721
  throw Object.assign(new Error("Ledger device not found."), {
2549
1722
  code: HardwareErrorCode3.DeviceNotFound
2550
1723
  });
2551
1724
  }
2552
- return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
1725
+ return this._connectDeviceOrThrow(devices[0].connectId);
2553
1726
  }
2554
- async _connectDeviceOrThrow(chosenConnectId, preserveOperationId, signal) {
2555
- const result = await this._connectTarget(chosenConnectId, preserveOperationId, signal);
1727
+ async _connectDeviceOrThrow(chosenConnectId) {
1728
+ const result = await this.connectDevice(chosenConnectId);
2556
1729
  if (!result.success) {
2557
1730
  const payload = result.payload;
2558
1731
  const rethrow = Object.assign(new Error(payload.error), {
@@ -2563,7 +1736,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2563
1736
  }
2564
1737
  throw rethrow;
2565
1738
  }
2566
- return result.payload;
1739
+ return chosenConnectId;
2567
1740
  }
2568
1741
  /**
2569
1742
  * Call the connector with automatic session resolution and disconnect retry.
@@ -2593,122 +1766,26 @@ var _LedgerAdapter = class _LedgerAdapter {
2593
1766
  * lives in one place.
2594
1767
  */
2595
1768
  async _callConnector(sessionId, method, params, signal) {
2596
- this._assertConnectorReady(method);
2597
- if (signal?.aborted) throw this._abortReason(signal);
2598
- const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
2599
- let promise;
2600
- try {
2601
- promise = this.connector.call(sessionId, method, params).finally(releaseOperation);
2602
- } catch (error) {
2603
- releaseOperation();
2604
- throw error;
2605
- }
1769
+ const promise = this.connector.call(sessionId, method, params);
2606
1770
  const result = signal ? await this._abortable(signal, promise) : await promise;
2607
1771
  return this._unwrapConnectorResult(result);
2608
1772
  }
2609
- _assertConnectorReady(method) {
2610
- if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
2611
- throw _LedgerAdapter._createDeviceBusyError(method);
2612
- }
2613
- }
2614
- _runConnectorTeardown(task) {
2615
- const previous = this._connectorTeardownTail;
2616
- let releaseTail = () => void 0;
2617
- this._connectorTeardownTail = new Promise((resolve) => {
2618
- releaseTail = resolve;
2619
- });
2620
- this._pendingConnectorTeardowns += 1;
2621
- return (async () => {
2622
- try {
2623
- await previous;
2624
- await this._waitForConnectorOperationsToDrain();
2625
- await task();
2626
- } finally {
2627
- this._pendingConnectorTeardowns -= 1;
2628
- releaseTail();
2629
- }
2630
- })();
2631
- }
2632
- _waitForConnectorOperationsToDrain() {
2633
- if (this._unsettledConnectorOperations.size === 0) {
2634
- return Promise.resolve();
2635
- }
2636
- return new Promise((resolve) => {
2637
- this._connectorIdleWaiters.add(resolve);
2638
- });
2639
- }
2640
- _retainConnectorOperation(key) {
2641
- this._unsettledConnectorOperations.set(
2642
- key,
2643
- (this._unsettledConnectorOperations.get(key) ?? 0) + 1
2644
- );
2645
- let released = false;
2646
- return () => {
2647
- if (released) return;
2648
- released = true;
2649
- const remaining = (this._unsettledConnectorOperations.get(key) ?? 1) - 1;
2650
- if (remaining > 0) {
2651
- this._unsettledConnectorOperations.set(key, remaining);
2652
- } else {
2653
- this._unsettledConnectorOperations.delete(key);
2654
- }
2655
- if (this._unsettledConnectorOperations.size === 0) {
2656
- for (const resolve of this._connectorIdleWaiters) resolve();
2657
- this._connectorIdleWaiters.clear();
2658
- }
2659
- };
2660
- }
2661
1773
  async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
2662
- const positionalOperationId = isHardwareOperationId(connectId) ? connectId : void 0;
2663
- if (positionalOperationId && commonParams?.operationId && positionalOperationId !== commonParams.operationId) {
2664
- throw createHwkError({
2665
- code: HardwareErrorCode3.InvalidParams,
2666
- message: "Conflicting Ledger operation ids",
2667
- params: {
2668
- positionalOperationId,
2669
- commonOperationId: commonParams.operationId
2670
- }
2671
- });
2672
- }
2673
- const operationId = commonParams?.operationId ?? positionalOperationId;
2674
- const operation = operationId ? this._operations.resolve(operationId) : void 0;
2675
- const releaseOperationRetention = operationId ? this._operations.retain(operationId) : void 0;
2676
- const effectiveConnectId = operation?.connectId ?? connectId;
2677
- debugLog("[LedgerAdapter][REQ]", {
2678
- method,
2679
- connectId: effectiveConnectId || "(empty)",
2680
- params
2681
- });
2682
- const queueKey = ledgerQueueKey({ operationId, connectId: effectiveConnectId });
1774
+ debugLog("[LedgerAdapter][REQ]", { method, connectId: connectId || "(empty)", params });
1775
+ const queueKey = connectId || "__ledger_default__";
2683
1776
  try {
2684
1777
  const result = await this._jobQueue.enqueue(
2685
1778
  queueKey,
2686
- async (signal) => {
2687
- if (operationId) this._activeOperationJobs.add(operationId);
2688
- try {
2689
- return await this._runConnectorCall(
2690
- effectiveConnectId,
2691
- method,
2692
- params,
2693
- signal,
2694
- fingerprint,
2695
- permissionDeviceId,
2696
- commonParams,
2697
- installContext ?? {},
2698
- operationId
2699
- );
2700
- } catch (error) {
2701
- this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
2702
- throw error;
2703
- } finally {
2704
- if (operationId) {
2705
- this._activeOperationJobs.delete(operationId);
2706
- if (this._pendingOperationDisconnects.delete(operationId)) {
2707
- await this._releaseLostOperationConnection(operationId);
2708
- }
2709
- }
2710
- }
2711
- },
1779
+ async (signal) => this._runConnectorCall(
1780
+ connectId,
1781
+ method,
1782
+ params,
1783
+ signal,
1784
+ fingerprint,
1785
+ permissionDeviceId,
1786
+ commonParams,
1787
+ installContext
1788
+ ),
2712
1789
  {
2713
1790
  label: method,
2714
1791
  rejectIfBusy: true,
@@ -2729,22 +1806,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2729
1806
  }
2730
1807
  });
2731
1808
  throw err;
2732
- } finally {
2733
- releaseOperationRetention?.();
2734
1809
  }
2735
1810
  }
2736
1811
  /**
2737
1812
  * Race a promise against an abort signal. On abort, rejects with
2738
1813
  * signal.reason → instance _lastCancelReason → generic Error('Aborted').
2739
1814
  */
2740
- /** Hermes/RN polyfills don't always populate signal.reason; fall back. */
2741
- _abortReason(signal) {
2742
- return signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
2743
- }
2744
1815
  _abortable(signal, promise) {
2745
- const getAbortReason = () => this._abortReason(signal);
1816
+ const getAbortReason = () => signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
2746
1817
  if (signal.aborted) {
2747
- void promise.catch(() => void 0);
2748
1818
  return Promise.reject(getAbortReason());
2749
1819
  }
2750
1820
  return new Promise((resolve, reject) => {
@@ -2771,15 +1841,13 @@ var _LedgerAdapter = class _LedgerAdapter {
2771
1841
  }
2772
1842
  }
2773
1843
  /** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
2774
- async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext, operationId, lockedRetryBudget = _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET) {
1844
+ async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext) {
2775
1845
  _LedgerAdapter._throwIfAborted(signal);
2776
- if (!operationId) {
2777
- await this._ensureDevicePermission(
2778
- connectId,
2779
- permissionDeviceId ?? fingerprint?.deviceId,
2780
- signal
2781
- );
2782
- }
1846
+ await this._ensureDevicePermission(
1847
+ connectId,
1848
+ permissionDeviceId ?? fingerprint?.deviceId,
1849
+ signal
1850
+ );
2783
1851
  _LedgerAdapter._throwIfAborted(signal);
2784
1852
  let effectiveParams = params;
2785
1853
  if (method === "btcGetPublicKey") {
@@ -2793,135 +1861,36 @@ var _LedgerAdapter = class _LedgerAdapter {
2793
1861
  effectiveParams = gatedParams;
2794
1862
  }
2795
1863
  const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
2796
- let businessCallStarted = false;
2797
- const verifiedBleTarget = connectId ? this._verifiedBleReconnectTargets.get(connectId) : void 0;
2798
- const knownTransport = this._isBleConnection() ? "ble" : "usb";
2799
- const hintedConnectId = commonParams?.knownConnections?.find(
2800
- (connection) => connection.transport === knownTransport
2801
- );
2802
- const inputConnectId = hintedConnectId && hintedConnectId.transport !== "qr" ? hintedConnectId.connectId : connectId;
2803
- const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && verifiedBleTarget?.chain === fingerprint.chain && verifiedBleTarget.fingerprint === fingerprint.deviceId ? verifiedBleTarget.connectId : inputConnectId;
2804
- const connectionAttempt = { ...commonParams };
2805
- const bundleConnection = installContext?.connection;
2806
- if (bundleConnection && this._sessions.get(bundleConnection.connectId) !== bundleConnection.sessionId) {
2807
- throw createHwkError({
2808
- code: HardwareErrorCode3.DeviceDisconnected,
2809
- message: "Ledger all-network connection ended"
2810
- });
2811
- }
2812
- let resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
2813
- preferredConnectId,
1864
+ const resolvedConnectId = await this.ensureConnected(
1865
+ connectId,
2814
1866
  signal,
2815
- allowUsbEphemeralFallback,
2816
- void 0,
2817
- connectionAttempt
1867
+ allowUsbEphemeralFallback
2818
1868
  );
2819
- let sessionId = this._sessions.get(resolvedConnectId);
2820
- if (sessionId && installContext && !installContext.connection) {
2821
- installContext.connection = { connectId: resolvedConnectId, sessionId };
2822
- }
1869
+ const sessionId = this._sessions.get(resolvedConnectId);
2823
1870
  if (!sessionId) {
2824
- if (operationId) {
2825
- this._operations.end(operationId, "disconnect");
2826
- throw createHwkError({
2827
- code: HardwareErrorCode3.OperationEnded,
2828
- message: "Ledger operation connection is no longer active",
2829
- params: { operationId, reason: "disconnect" }
2830
- });
2831
- }
2832
1871
  throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
2833
1872
  _tag: ERROR_TAG.DeviceSessionNotFound
2834
1873
  });
2835
1874
  }
2836
1875
  try {
2837
1876
  if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2838
- for (; ; ) {
2839
- const fp = await this._abortable(
2840
- signal,
2841
- this._verifyDeviceFingerprintWithSession(
2842
- sessionId,
2843
- fingerprint.deviceId,
2844
- fingerprint.chain
2845
- )
2846
- );
2847
- if (fp.success) break;
2848
- const binding = operationId ? this._pendingOperationBindings.get(operationId) : connectionAttempt;
2849
- if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== resolvedConnectId) {
2850
- throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2851
- code: HardwareErrorCode3.DeviceMismatch
2852
- });
2853
- }
2854
- binding.rejectedConnectIds ?? (binding.rejectedConnectIds = /* @__PURE__ */ new Set());
2855
- binding.rejectedConnectIds.add(resolvedConnectId);
2856
- binding.rejectedConnectId = resolvedConnectId;
2857
- this._sessions.delete(resolvedConnectId);
2858
- await this.connector.disconnect(sessionId);
2859
- if (operationId) this._pendingOperationDisconnects.delete(operationId);
2860
- _LedgerAdapter._throwIfAborted(signal);
2861
- resolvedConnectId = await this._connectFirstOrSelect(
2862
- [],
2863
- void 0,
2864
- true,
2865
- operationId,
2866
- binding,
2867
- signal
2868
- );
2869
- const selectedSession = this._sessions.get(resolvedConnectId);
2870
- const selectedDevice = this._discoveredDevices.get(resolvedConnectId);
2871
- if (!selectedSession || !selectedDevice)
2872
- throw createHwkError({
2873
- code: HardwareErrorCode3.DeviceDisconnected,
2874
- message: "Selected Ledger connection ended"
2875
- });
2876
- sessionId = selectedSession;
2877
- if (operationId)
2878
- this._operations.rebind(operationId, {
2879
- connectId: resolvedConnectId,
2880
- device: selectedDevice,
2881
- // Same source as `_createOperation`: the selected transport, not
2882
- // the device snapshot a session connect overwrote.
2883
- connectionType: this._isBleConnection() ? "ble" : "usb",
2884
- connectionKeys: [sessionId]
2885
- });
2886
- if (installContext)
2887
- installContext.connection = { connectId: resolvedConnectId, sessionId };
2888
- }
2889
- await this._publishVerifiedBleBinding(
2890
- resolvedConnectId,
2891
- fingerprint.chain,
2892
- fingerprint.deviceId,
2893
- connectionAttempt,
2894
- operationId,
2895
- signal
1877
+ const fp = await this._abortable(
1878
+ signal,
1879
+ this._verifyDeviceFingerprintWithSession(
1880
+ sessionId,
1881
+ fingerprint.deviceId,
1882
+ fingerprint.chain
1883
+ )
2896
1884
  );
2897
- if (!operationId && connectionAttempt.selectedConnection?.connectId === resolvedConnectId && this._isBleConnection()) {
2898
- this._verifiedBleReconnectTargets.set(connectId, {
2899
- connectId: resolvedConnectId,
2900
- chain: fingerprint.chain,
2901
- fingerprint: fingerprint.deviceId
1885
+ if (!fp.success) {
1886
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1887
+ code: HardwareErrorCode3.DeviceMismatch
2902
1888
  });
2903
1889
  }
2904
1890
  }
2905
- businessCallStarted = true;
2906
1891
  return await this._callConnector(sessionId, method, effectiveParams, signal);
2907
1892
  } catch (err) {
2908
1893
  if (signal.aborted) throw err;
2909
- if (isDeviceDisconnectedError(err) || isDeviceNotAdvertisingError(err) || isTimeoutError(err) || isConnectionLevelError(err)) {
2910
- this._discoveredDevices.delete(resolvedConnectId);
2911
- if (operationId) await this._releaseLostOperationConnection(operationId);
2912
- else {
2913
- this._sessions.delete(resolvedConnectId);
2914
- await this.connector.disconnect(sessionId).catch(() => void 0);
2915
- }
2916
- const ambiguous = businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(method);
2917
- const interactionParams = operationId ? { operationId, reason: "disconnect" } : void 0;
2918
- throw createHwkError({
2919
- code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(err).code,
2920
- message: "Ledger operation connection was lost; start a new operation",
2921
- params: ambiguous ? operationMayHaveCompletedParams(method, { operationId }) : interactionParams,
2922
- recovery: ambiguous ? { scope: "unknown" } : void 0
2923
- });
2924
- }
2925
1894
  const errObj = err;
2926
1895
  debugLog("[LedgerAdapter] connectorCall error:", method, {
2927
1896
  message: errObj?.message,
@@ -2933,65 +1902,6 @@ var _LedgerAdapter = class _LedgerAdapter {
2933
1902
  isNotAdvertising: isDeviceNotAdvertisingError(err),
2934
1903
  isStuckApp: isStuckAppStateError(err)
2935
1904
  });
2936
- if ((isDeviceLockedError(err) || errObj?.code === HardwareErrorCode3.DeviceLocked) && lockedRetryBudget > 0) {
2937
- await this._waitForDeviceConnect(signal);
2938
- if (this._sessions.get(resolvedConnectId) !== sessionId) {
2939
- throw createHwkError({
2940
- code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
2941
- message: "Ledger connection ended while waiting for unlock"
2942
- });
2943
- }
2944
- return this._runConnectorCall(
2945
- resolvedConnectId,
2946
- method,
2947
- effectiveParams,
2948
- signal,
2949
- fingerprint,
2950
- permissionDeviceId,
2951
- commonParams,
2952
- installContext,
2953
- operationId,
2954
- lockedRetryBudget - 1
2955
- );
2956
- }
2957
- if (businessCallStarted && isStuckAppStateError(err)) {
2958
- await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
2959
- if (this._sessions.get(resolvedConnectId) !== sessionId) {
2960
- throw createHwkError({
2961
- code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
2962
- message: "Ledger connection ended during the app transition"
2963
- });
2964
- }
2965
- try {
2966
- return await this._callConnector(sessionId, method, effectiveParams, signal);
2967
- } catch (retryErr) {
2968
- if (isStuckAppStateError(retryErr)) throw err;
2969
- if (isDeviceDisconnectedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isTimeoutError(retryErr) || isConnectionLevelError(retryErr)) {
2970
- if (operationId) await this._releaseLostOperationConnection(operationId);
2971
- else {
2972
- this._sessions.delete(resolvedConnectId);
2973
- this._discoveredDevices.delete(resolvedConnectId);
2974
- await this.connector.disconnect(sessionId).catch(() => void 0);
2975
- }
2976
- throw createHwkError({
2977
- code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(retryErr).code,
2978
- message: `Ledger ${method} may have completed before the connection was lost`,
2979
- params: !canReplayHardwareMethodAfterTransportFailure(method) ? operationMayHaveCompletedParams(method, {
2980
- operationId,
2981
- reason: "disconnect"
2982
- }) : { operationId, reason: "disconnect" },
2983
- recovery: !canReplayHardwareMethodAfterTransportFailure(method) ? { scope: "unknown" } : void 0
2984
- });
2985
- }
2986
- throw retryErr;
2987
- }
2988
- }
2989
- if (!operationId && err?.code === HardwareErrorCode3.DeviceMismatch) {
2990
- this._sessions.delete(resolvedConnectId);
2991
- this._discoveredDevices.delete(resolvedConnectId);
2992
- await this.connector.disconnect(sessionId).catch(() => void 0);
2993
- throw err;
2994
- }
2995
1905
  const autoInstallApp = commonParams?.autoInstallApp ?? this._defaultAutoInstallApp;
2996
1906
  const isAppMissing = isAppNotInstalledError(err) || err?.code === HardwareErrorCode3.AppNotInstalled;
2997
1907
  if (autoInstallApp && isAppMissing) {
@@ -3047,14 +1957,157 @@ var _LedgerAdapter = class _LedgerAdapter {
3047
1957
  fingerprint,
3048
1958
  permissionDeviceId,
3049
1959
  commonParams,
3050
- installContext,
3051
- operationId
1960
+ installContext
3052
1961
  );
3053
1962
  }
3054
1963
  }
1964
+ if (isStuckAppStateError(err)) {
1965
+ try {
1966
+ this._sessions.delete(resolvedConnectId);
1967
+ this._discoveredDevices.delete(resolvedConnectId);
1968
+ this.connector.reset?.();
1969
+ } catch {
1970
+ }
1971
+ debugLog(
1972
+ "[LedgerAdapter] stuck-app retry: method=",
1973
+ method,
1974
+ "delayMs=",
1975
+ _LedgerAdapter.STUCK_APP_RETRY_DELAY_MS,
1976
+ "_tag=",
1977
+ err?._tag
1978
+ );
1979
+ try {
1980
+ const retryResult = await this._retryAfterStuckApp(
1981
+ resolvedConnectId,
1982
+ method,
1983
+ effectiveParams,
1984
+ signal,
1985
+ err,
1986
+ fingerprint
1987
+ );
1988
+ debugLog("[LedgerAdapter] stuck-app retry succeeded: method=", method);
1989
+ return retryResult;
1990
+ } catch (retryErr) {
1991
+ if (signal.aborted) throw retryErr;
1992
+ if (isStuckAppStateError(retryErr)) {
1993
+ debugLog("[LedgerAdapter] stuck-app retry exhausted (2nd 6901): method=", method);
1994
+ throw err;
1995
+ }
1996
+ debugLog(
1997
+ "[LedgerAdapter] stuck-app retry threw non-stuck error: method=",
1998
+ method,
1999
+ "retryErrTag=",
2000
+ retryErr?._tag
2001
+ );
2002
+ throw retryErr;
2003
+ }
2004
+ }
2005
+ if (isDeviceLockedError(err) || isDeviceNotAdvertisingError(err) || isDeviceDisconnectedError(err)) {
2006
+ let lastErr = err;
2007
+ for (let attempt = 0; attempt < _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET; attempt += 1) {
2008
+ if (signal.aborted) throw lastErr;
2009
+ try {
2010
+ this._sessions.delete(resolvedConnectId);
2011
+ this._discoveredDevices.delete(resolvedConnectId);
2012
+ if (isDeviceDisconnectedError(lastErr)) {
2013
+ try {
2014
+ this.connector.reset?.();
2015
+ } catch {
2016
+ }
2017
+ }
2018
+ const reConnectId = await this.ensureConnected(
2019
+ resolvedConnectId,
2020
+ signal,
2021
+ allowUsbEphemeralFallback
2022
+ );
2023
+ const reSessionId = this._sessions.get(reConnectId);
2024
+ if (!reSessionId) throw lastErr;
2025
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2026
+ const fp = await this._abortable(
2027
+ signal,
2028
+ this._verifyDeviceFingerprintWithSession(
2029
+ reSessionId,
2030
+ fingerprint.deviceId,
2031
+ fingerprint.chain
2032
+ )
2033
+ );
2034
+ if (!fp.success) {
2035
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2036
+ code: HardwareErrorCode3.DeviceMismatch
2037
+ });
2038
+ }
2039
+ }
2040
+ return await this._callConnector(reSessionId, method, effectiveParams, signal);
2041
+ } catch (retryErr) {
2042
+ if (signal.aborted) throw retryErr;
2043
+ lastErr = retryErr;
2044
+ const canRetry = attempt < _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET - 1 && (isDeviceLockedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isDeviceDisconnectedError(retryErr));
2045
+ if (!canRetry) {
2046
+ throw retryErr;
2047
+ }
2048
+ }
2049
+ }
2050
+ throw lastErr;
2051
+ }
2052
+ if (isTimeoutError(err)) {
2053
+ debugLog("[LedgerAdapter] timeout, retrying with fresh connection...");
2054
+ this._discoveredDevices.delete(resolvedConnectId);
2055
+ return this._retryWithFreshConnection(
2056
+ resolvedConnectId,
2057
+ method,
2058
+ effectiveParams,
2059
+ signal,
2060
+ err,
2061
+ fingerprint
2062
+ );
2063
+ }
2064
+ if (isConnectionLevelError(err)) {
2065
+ debugLog("[LedgerAdapter] connection-level fail-closed reset");
2066
+ this._sessions.delete(resolvedConnectId);
2067
+ this._discoveredDevices.delete(resolvedConnectId);
2068
+ this.connector.reset();
2069
+ const codeNum = err?.code;
2070
+ throw Object.assign(err, {
2071
+ code: codeNum ?? HardwareErrorCode3.DeviceDisconnected
2072
+ });
2073
+ }
3055
2074
  throw err;
3056
2075
  }
3057
2076
  }
2077
+ /**
2078
+ * Stuck-app recovery: pause for the device's UI transition, then retry once.
2079
+ *
2080
+ * Caller has already cleared the session + reset connector. We wait so Stax
2081
+ * finishes its post-CloseApp animation, then go through ensureConnected +
2082
+ * fingerprint check + call exactly once. Caller decides what to do on a
2083
+ * second stuck-app hit.
2084
+ */
2085
+ async _retryAfterStuckApp(resolvedConnectId, method, params, signal, originalErr, fingerprint) {
2086
+ await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
2087
+ const retryConnectId = await this.ensureConnected(
2088
+ resolvedConnectId,
2089
+ signal,
2090
+ !!fingerprint?.deviceId && !fingerprint.skipFingerprint
2091
+ );
2092
+ const retrySessionId = this._sessions.get(retryConnectId);
2093
+ if (!retrySessionId) throw originalErr;
2094
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2095
+ const fp = await this._abortable(
2096
+ signal,
2097
+ this._verifyDeviceFingerprintWithSession(
2098
+ retrySessionId,
2099
+ fingerprint.deviceId,
2100
+ fingerprint.chain
2101
+ )
2102
+ );
2103
+ if (!fp.success) {
2104
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2105
+ code: HardwareErrorCode3.DeviceMismatch
2106
+ });
2107
+ }
2108
+ }
2109
+ return this._callConnector(retrySessionId, method, params, signal);
2110
+ }
3058
2111
  _sleepAbortable(ms, signal) {
3059
2112
  return new Promise((resolve, reject) => {
3060
2113
  if (signal.aborted) {
@@ -3072,6 +2125,88 @@ var _LedgerAdapter = class _LedgerAdapter {
3072
2125
  signal.addEventListener("abort", onAbort, { once: true });
3073
2126
  });
3074
2127
  }
2128
+ /**
2129
+ * Clear stale session, reconnect, and retry the call.
2130
+ *
2131
+ * Timeout recovery starts with a full connector reset. After an APDU
2132
+ * timeout, DMK/transport state may still emit malformed responses; retrying
2133
+ * on the same DMK can poison the next chain switch.
2134
+ */
2135
+ async _retryWithFreshConnection(targetConnectId, method, params, signal, originalErr, fingerprint) {
2136
+ this.connector.reset();
2137
+ this._sessions.clear();
2138
+ this._discoveredDevices.clear();
2139
+ this._connectingPromise = null;
2140
+ const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
2141
+ const retryConnectId = await this.ensureConnected(
2142
+ targetConnectId,
2143
+ signal,
2144
+ allowUsbEphemeralFallback
2145
+ );
2146
+ const retrySessionId = this._sessions.get(retryConnectId);
2147
+ if (!retrySessionId) {
2148
+ throw originalErr;
2149
+ }
2150
+ try {
2151
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2152
+ const fp = await this._abortable(
2153
+ signal,
2154
+ this._verifyDeviceFingerprintWithSession(
2155
+ retrySessionId,
2156
+ fingerprint.deviceId,
2157
+ fingerprint.chain
2158
+ )
2159
+ );
2160
+ if (!fp.success) {
2161
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2162
+ code: HardwareErrorCode3.DeviceMismatch
2163
+ });
2164
+ }
2165
+ }
2166
+ return await this._callConnector(retrySessionId, method, params, signal);
2167
+ } catch (retryErr) {
2168
+ if (signal.aborted) throw retryErr;
2169
+ this.connector.reset();
2170
+ this._sessions.clear();
2171
+ this._discoveredDevices.clear();
2172
+ this._connectingPromise = null;
2173
+ if (!isDeviceDisconnectedError(retryErr) && !isTimeoutError(retryErr)) {
2174
+ throw retryErr;
2175
+ }
2176
+ debugLog(
2177
+ "[LedgerAdapter] fresh-session retry still failed; resetting connector and rebuilding DMK"
2178
+ );
2179
+ this.connector.reset();
2180
+ this._sessions.clear();
2181
+ this._discoveredDevices.clear();
2182
+ this._connectingPromise = null;
2183
+ const finalConnectId = await this.ensureConnected(
2184
+ targetConnectId,
2185
+ signal,
2186
+ allowUsbEphemeralFallback
2187
+ );
2188
+ const finalSessionId = this._sessions.get(finalConnectId);
2189
+ if (!finalSessionId) {
2190
+ throw originalErr;
2191
+ }
2192
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2193
+ const fp = await this._abortable(
2194
+ signal,
2195
+ this._verifyDeviceFingerprintWithSession(
2196
+ finalSessionId,
2197
+ fingerprint.deviceId,
2198
+ fingerprint.chain
2199
+ )
2200
+ );
2201
+ if (!fp.success) {
2202
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2203
+ code: HardwareErrorCode3.DeviceMismatch
2204
+ });
2205
+ }
2206
+ }
2207
+ return this._callConnector(finalSessionId, method, params, signal);
2208
+ }
2209
+ }
3075
2210
  /**
3076
2211
  * Ensure OS-level device permission (Bluetooth / USB) before proceeding.
3077
2212
  *
@@ -3089,14 +2224,13 @@ var _LedgerAdapter = class _LedgerAdapter {
3089
2224
  _LedgerAdapter._throwIfAborted(signal);
3090
2225
  }
3091
2226
  const transportType = this.activeTransport ?? "hid";
3092
- const operationId = this._activeOperationId();
3093
2227
  const waitPromise = this._uiRegistry.wait(
3094
2228
  UI_REQUEST.REQUEST_DEVICE_PERMISSION,
3095
- { timeoutMs: 6e4, operationId }
2229
+ { timeoutMs: 6e4 }
3096
2230
  );
3097
2231
  this.emitter.emit(UI_REQUEST.REQUEST_DEVICE_PERMISSION, {
3098
2232
  type: UI_REQUEST.REQUEST_DEVICE_PERMISSION,
3099
- payload: { transportType, connectId, deviceId, operationId }
2233
+ payload: { transportType, connectId, deviceId }
3100
2234
  });
3101
2235
  let response;
3102
2236
  const onAbort = () => {
@@ -3142,15 +2276,7 @@ var _LedgerAdapter = class _LedgerAdapter {
3142
2276
  if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
3143
2277
  const e = err;
3144
2278
  const params = e.code === HardwareErrorCode3.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
3145
- return ledgerFailure(
3146
- e.code,
3147
- e.message ?? "Unknown error",
3148
- e.appName,
3149
- tag,
3150
- params,
3151
- void 0,
3152
- isHwkRecoveryHint(e.recovery) ? e.recovery : void 0
3153
- );
2279
+ return ledgerFailure(e.code, e.message ?? "Unknown error", e.appName, tag, params);
3154
2280
  }
3155
2281
  const mapped = mapLedgerError(err);
3156
2282
  return ledgerFailure(mapped.code, mapped.message, mapped.appName, tag);
@@ -3177,7 +2303,7 @@ var _LedgerAdapter = class _LedgerAdapter {
3177
2303
  deviceId: device.deviceId,
3178
2304
  connectId: device.connectId,
3179
2305
  label: device.name,
3180
- connectionType: device.connectionType ?? this.connector.connectionType,
2306
+ connectionType: this.connector.connectionType,
3181
2307
  rssi: device.rssi,
3182
2308
  isConnectable: device.isConnectable,
3183
2309
  serialNumber: device.serialNumber,
@@ -3203,8 +2329,8 @@ var LedgerAdapter = _LedgerAdapter;
3203
2329
 
3204
2330
  // src/connector/LedgerConnectorBase.ts
3205
2331
  import {
3206
- EConnectorInteraction as EConnectorInteraction3,
3207
- HardwareErrorCode as HardwareErrorCode7,
2332
+ EConnectorInteraction as EConnectorInteraction4,
2333
+ HardwareErrorCode as HardwareErrorCode8,
3208
2334
  isKnownNonTargetHardwareVendor,
3209
2335
  serializeConnectorError
3210
2336
  } from "@onekeyfe/hwk-adapter-core";
@@ -3477,7 +2603,7 @@ var LedgerDeviceManager = class {
3477
2603
 
3478
2604
  // src/signer/SignerManager.ts
3479
2605
  import { SignerEthBuilder } from "@ledgerhq/device-signer-kit-ethereum";
3480
- import { ContextModuleBuilder, ContextModuleChainID } from "@ledgerhq/context-module";
2606
+ import { ContextModuleBuilder } from "@ledgerhq/context-module";
3481
2607
 
3482
2608
  // src/signer/SignerEth.ts
3483
2609
  import { hexToBytes } from "@onekeyfe/hwk-adapter-core";
@@ -3743,7 +2869,7 @@ var SignerManager = class _SignerManager {
3743
2869
  return (args) => new SignerEthBuilder(args);
3744
2870
  }
3745
2871
  static _createContextModule() {
3746
- const contextModule = new ContextModuleBuilder({}).setChain(ContextModuleChainID.Ethereum).removeDefaultLoaders().build();
2872
+ const contextModule = new ContextModuleBuilder({}).removeDefaultLoaders().build();
3747
2873
  return _SignerManager.wrapBlindSigningReportNonBlocking(contextModule);
3748
2874
  }
3749
2875
  static wrapBlindSigningReportNonBlocking(contextModule) {
@@ -4213,8 +3339,7 @@ function _applySignaturesToPsbt(psbtHex, signatures) {
4213
3339
  }
4214
3340
 
4215
3341
  // src/connector/chains/sol.ts
4216
- import { bytesToHex, hexToBytes as hexToBytes3, prepareSolanaOffchainMessageV1 } from "@onekeyfe/hwk-adapter-core";
4217
- import bs58 from "bs58";
3342
+ import { bytesToHex, hexToBytes as hexToBytes3 } from "@onekeyfe/hwk-adapter-core";
4218
3343
 
4219
3344
  // src/signer/SignerSol.ts
4220
3345
  var SignerSol = class {
@@ -4298,21 +3423,8 @@ async function solSignMessage(ctx, sessionId, params) {
4298
3423
  const path = normalizePath(params.path);
4299
3424
  const messageBytes = hexToBytes3(params.message);
4300
3425
  try {
4301
- if (params.messageVersion === 1) {
4302
- const preparedMessage = prepareSolanaOffchainMessageV1({
4303
- message: messageBytes,
4304
- requiredSigners: params.requiredSigners
4305
- });
4306
- const { SignMessageVersion } = await ctx.importLedgerKit(
4307
- "@ledgerhq/device-signer-kit-solana"
4308
- );
4309
- const result2 = await solSigner.signMessage(path, preparedMessage.serializedMessage, {
4310
- version: SignMessageVersion.Raw
4311
- });
4312
- return { signature: decodeBase58Signature(result2.signature) };
4313
- }
4314
3426
  const result = await solSigner.signMessage(path, messageBytes);
4315
- return { signature: decodeBase58EnvelopeSignature(result.signature) };
3427
+ return { signature: result.signature };
4316
3428
  } catch (err) {
4317
3429
  ctx.invalidateSession(sessionId);
4318
3430
  throw ctx.wrapError(err);
@@ -4320,27 +3432,11 @@ async function solSignMessage(ctx, sessionId, params) {
4320
3432
  ctx.clearCanceller(sessionId);
4321
3433
  }
4322
3434
  }
4323
- function decodeBase58Signature(signature) {
4324
- const bytes = bs58.decode(signature);
4325
- if (bytes.length !== 64) {
4326
- throw new Error(`Ledger Solana signature must be 64 bytes, received ${bytes.length}`);
4327
- }
4328
- return bytesToHex(bytes);
4329
- }
4330
- function decodeBase58EnvelopeSignature(envelope) {
4331
- const bytes = bs58.decode(envelope);
4332
- if (bytes.length < 65 || bytes[0] !== 1) {
4333
- throw new Error("Ledger Solana signature envelope is invalid");
4334
- }
4335
- return bytesToHex(bytes.subarray(1, 65));
4336
- }
4337
3435
  async function _createSolSigner(ctx, sessionId) {
4338
3436
  const dmk = await ctx.getOrCreateDmk();
4339
- const { ContextModuleBuilder: ContextModuleBuilder2, ContextModuleChainID: ContextModuleChainID2 } = await ctx.importLedgerKit(
4340
- "@ledgerhq/context-module"
4341
- );
3437
+ const { ContextModuleBuilder: ContextModuleBuilder2 } = await ctx.importLedgerKit("@ledgerhq/context-module");
4342
3438
  const { SignerSolanaBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-solana");
4343
- const contextModule = new ContextModuleBuilder2({}).setChain(ContextModuleChainID2.Solana).removeDefaultLoaders().build();
3439
+ const contextModule = new ContextModuleBuilder2({}).removeDefaultLoaders().build();
4344
3440
  const sdkSigner = new SignerSolanaBuilder({ dmk, sessionId }).withContextModule(contextModule).build();
4345
3441
  const signer = new SignerSol(sdkSigner);
4346
3442
  signer.onInteraction = (interaction) => {
@@ -4356,192 +3452,361 @@ async function _createSolSigner(ctx, sessionId) {
4356
3452
  return signer;
4357
3453
  }
4358
3454
 
4359
- // src/connector/chains/zcash.ts
4360
- import { bytesToHex as bytesToHex2 } from "@onekeyfe/hwk-adapter-core";
3455
+ // src/connector/chains/tron.ts
3456
+ import { HardwareErrorCode as HardwareErrorCode7 } from "@onekeyfe/hwk-adapter-core";
3457
+ import Trx from "@ledgerhq/hw-app-trx";
4361
3458
 
4362
- // src/signer/SignerZcash.ts
4363
- var SignerZcash = class {
4364
- // eslint-disable-next-line no-useless-constructor, no-empty-function
4365
- constructor(_sdk) {
4366
- this._sdk = _sdk;
3459
+ // src/connector/chains/legacyChainCall.ts
3460
+ import { EConnectorInteraction as EConnectorInteraction3 } from "@onekeyfe/hwk-adapter-core";
3461
+
3462
+ // src/app/AppManager.ts
3463
+ import {
3464
+ CloseAppCommand,
3465
+ GetAppAndVersionCommand,
3466
+ OpenAppCommand,
3467
+ isSuccessCommandResult
3468
+ } from "@ledgerhq/device-management-kit";
3469
+ import { HardwareErrorCode as HardwareErrorCode6 } from "@onekeyfe/hwk-adapter-core";
3470
+ var APP_NAME_MAP = {
3471
+ ETH: "Ethereum",
3472
+ BTC: "Bitcoin",
3473
+ SOL: "Solana",
3474
+ TRX: "Tron",
3475
+ XRP: "XRP",
3476
+ ADA: "Cardano",
3477
+ DOT: "Polkadot",
3478
+ ATOM: "Cosmos"
3479
+ };
3480
+ var DASHBOARD_APP_NAME = "BOLOS";
3481
+ var AppManager = class {
3482
+ constructor(dmk, options) {
3483
+ this._dmk = dmk;
3484
+ this._waitMs = options?.waitMs ?? 1e3;
3485
+ this._maxRetries = options?.maxRetries ?? 10;
4367
3486
  }
4368
- /** GET_VK: UFVK string (default) or raw 96-byte Orchard FVK. */
4369
- async getFullViewingKey(derivationPath, options) {
4370
- const action = this._sdk.getFullViewingKey(derivationPath, options);
4371
- return deviceActionToPromise(
4372
- action,
4373
- this.onInteraction,
4374
- void 0,
4375
- this.onRegisterCanceller
3487
+ /**
3488
+ * Return the Ledger app name for a given chain ticker,
3489
+ * or undefined if the chain is not supported.
3490
+ */
3491
+ static getAppName(chain) {
3492
+ return APP_NAME_MAP[chain];
3493
+ }
3494
+ /**
3495
+ * Ensure the target app is open on the device identified by `sessionId`.
3496
+ *
3497
+ * Flow:
3498
+ * 1. Check the currently running app.
3499
+ * 2. If it is already the target, return immediately.
3500
+ * 3. If a different app is running (not dashboard), close it first.
3501
+ * 4. Open the target app.
3502
+ * 5. Poll until the device confirms the target app is running.
3503
+ */
3504
+ /**
3505
+ * @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
3506
+ * device is about to display "Open <app>" on screen and wait for the
3507
+ * user's button press. UI consumers should show their "open app" prompt
3508
+ * in response. NOT called when the target app is already open (no user
3509
+ * interaction needed in that case).
3510
+ *
3511
+ * Important: OpenAppCommand is blocking. It does not resolve until the user
3512
+ * has physically confirmed on the device, so anything that runs AFTER
3513
+ * `await this._openApp(...)` lands AFTER the prompt is already gone.
3514
+ * Hence the callback must fire BEFORE that await.
3515
+ */
3516
+ async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
3517
+ const currentApp = await this._getCurrentApp(sessionId);
3518
+ if (currentApp === targetAppName) {
3519
+ return;
3520
+ }
3521
+ if (!this._isDashboard(currentApp)) {
3522
+ await this._closeCurrentApp(sessionId);
3523
+ await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
3524
+ }
3525
+ onConfirmOnDevice?.();
3526
+ await this._openApp(sessionId, targetAppName);
3527
+ await this._waitForApp(sessionId, targetAppName);
3528
+ }
3529
+ // ---------------------------------------------------------------------------
3530
+ // Private helpers
3531
+ // ---------------------------------------------------------------------------
3532
+ async _getCurrentApp(sessionId) {
3533
+ const result = await this._dmk.sendCommand({
3534
+ sessionId,
3535
+ command: new GetAppAndVersionCommand()
3536
+ });
3537
+ if (isSuccessCommandResult(result)) {
3538
+ debugLog("[AppManager] currentApp:", result.data.name);
3539
+ return result.data.name;
3540
+ }
3541
+ const errResult = result;
3542
+ const dmkErr = errResult.error ?? {};
3543
+ const original = dmkErr.originalError;
3544
+ debugLog(
3545
+ "[AppManager] _getCurrentApp failed sessionId=",
3546
+ sessionId,
3547
+ "tag=",
3548
+ dmkErr._tag,
3549
+ "errorCode=",
3550
+ dmkErr.errorCode,
3551
+ "message=",
3552
+ dmkErr.message,
3553
+ "originalErrorMessage=",
3554
+ original?.message ?? String(original ?? "")
3555
+ );
3556
+ throw Object.assign(
3557
+ new Error(
3558
+ dmkErr.message ?? "Failed to get current app from device"
3559
+ ),
3560
+ {
3561
+ _tag: dmkErr._tag,
3562
+ errorCode: dmkErr.errorCode,
3563
+ originalError: original
3564
+ }
4376
3565
  );
4377
3566
  }
4378
- /** GET_SHIELDED_ADDRESS: single-Orchard-receiver UA for a 5-level transparent path. */
4379
- async getShieldedAddress(derivationPath, options) {
4380
- const action = this._sdk.getShieldedAddress(derivationPath, options);
4381
- return deviceActionToPromise(
4382
- action,
4383
- this.onInteraction,
4384
- void 0,
4385
- this.onRegisterCanceller
3567
+ async _openApp(sessionId, appName) {
3568
+ const result = await this._dmk.sendCommand({
3569
+ sessionId,
3570
+ command: new OpenAppCommand({ appName })
3571
+ });
3572
+ if (!isSuccessCommandResult(result)) {
3573
+ const dmkErr = result.error;
3574
+ const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
3575
+ const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
3576
+ debugLog(
3577
+ "[AppManager] openApp failed:",
3578
+ appName,
3579
+ "errorCode:",
3580
+ errorCode,
3581
+ "tag:",
3582
+ dmkErr._tag
3583
+ );
3584
+ let code;
3585
+ if (errorCode === "6807" || /unknown application/i.test(message)) {
3586
+ code = HardwareErrorCode6.AppNotInstalled;
3587
+ } else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
3588
+ code = HardwareErrorCode6.UserRejected;
3589
+ }
3590
+ throw Object.assign(new Error(`Failed to open "${appName}"`), {
3591
+ _tag: ERROR_TAG.OpenAppCommand,
3592
+ code,
3593
+ errorCode,
3594
+ statusCode: errorCode,
3595
+ appName,
3596
+ originalError: dmkErr
3597
+ });
3598
+ }
3599
+ }
3600
+ async _closeCurrentApp(sessionId) {
3601
+ debugLog("[AppManager] closeCurrentApp");
3602
+ await this._dmk.sendCommand({
3603
+ sessionId,
3604
+ command: new CloseAppCommand()
3605
+ });
3606
+ }
3607
+ /**
3608
+ * Poll the device until the expected app is reported as running,
3609
+ * or throw after `_maxRetries` attempts.
3610
+ */
3611
+ async _waitForApp(sessionId, expectedAppName) {
3612
+ let lastSeen = "";
3613
+ for (let i = 0; i < this._maxRetries; i++) {
3614
+ await this._wait();
3615
+ const current = await this._getCurrentApp(sessionId);
3616
+ lastSeen = current;
3617
+ if (current === expectedAppName) {
3618
+ return;
3619
+ }
3620
+ }
3621
+ debugLog(
3622
+ "[AppManager] waitForApp exhausted: expected=",
3623
+ expectedAppName,
3624
+ "lastSeen=",
3625
+ lastSeen
4386
3626
  );
3627
+ throw new Error(
3628
+ `Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
3629
+ );
3630
+ }
3631
+ _isDashboard(appName) {
3632
+ return appName === DASHBOARD_APP_NAME;
3633
+ }
3634
+ _wait() {
3635
+ return new Promise((resolve) => setTimeout(resolve, this._waitMs));
4387
3636
  }
4388
3637
  };
4389
3638
 
4390
- // src/connector/chains/zcash.ts
4391
- async function zcashGetFullViewingKey(ctx, sessionId, params) {
4392
- const signer = await _createZcashSigner(ctx, sessionId);
4393
- const path = normalizePath(params.path);
4394
- const mode = params.mode ?? "ufvk";
4395
- try {
4396
- const result = await signer.getFullViewingKey(path, { mode });
4397
- if (result.mode === "ufvk") {
4398
- return { path: params.path, mode, ufvk: result.fullViewingKey };
3639
+ // src/connector/chains/legacyChainCall.ts
3640
+ function isLegacyWrongAppError(err, _appName) {
3641
+ return isWrongAppError(err);
3642
+ }
3643
+ async function withLegacyChainCall(ctx, sessionId, options, action) {
3644
+ const { appName, needsConfirmation } = options;
3645
+ let openAppPromptShown = false;
3646
+ const onAppOpenPrompt = () => {
3647
+ openAppPromptShown = true;
3648
+ ctx.emit("ui-event", {
3649
+ type: EConnectorInteraction3.ConfirmOpenApp,
3650
+ payload: { sessionId }
3651
+ });
3652
+ };
3653
+ const closeOpenAppUiIfShown = () => {
3654
+ if (openAppPromptShown) {
3655
+ ctx.emit("ui-event", {
3656
+ type: EConnectorInteraction3.InteractionComplete,
3657
+ payload: { sessionId }
3658
+ });
3659
+ openAppPromptShown = false;
4399
3660
  }
4400
- return { path: params.path, mode, orchardFvk: bytesToHex2(result.fullViewingKey) };
3661
+ };
3662
+ try {
3663
+ await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
4401
3664
  } catch (err) {
4402
- ctx.invalidateSession(sessionId);
4403
- throw ctx.wrapError(err);
4404
- } finally {
4405
- ctx.clearCanceller(sessionId);
4406
- }
4407
- }
4408
- async function zcashGetShieldedAddress(ctx, sessionId, params) {
4409
- const signer = await _createZcashSigner(ctx, sessionId);
4410
- const path = normalizePath(params.path);
3665
+ debugLog(
3666
+ "[LegacyChainCall] pre-flight ensureAppOpen failed:",
3667
+ appName,
3668
+ err?.message
3669
+ );
3670
+ closeOpenAppUiIfShown();
3671
+ throw ctx.wrapError(err, { defaultAppName: appName });
3672
+ }
3673
+ const runOnce = async () => {
3674
+ let confirmEmitted = false;
3675
+ if (needsConfirmation) {
3676
+ ctx.emit("ui-event", {
3677
+ type: EConnectorInteraction3.ConfirmOnDevice,
3678
+ payload: { sessionId }
3679
+ });
3680
+ confirmEmitted = true;
3681
+ }
3682
+ try {
3683
+ return await action(sessionId);
3684
+ } finally {
3685
+ if (confirmEmitted || openAppPromptShown) {
3686
+ ctx.emit("ui-event", {
3687
+ type: EConnectorInteraction3.InteractionComplete,
3688
+ payload: { sessionId }
3689
+ });
3690
+ openAppPromptShown = false;
3691
+ }
3692
+ }
3693
+ };
4411
3694
  try {
4412
- const result = await signer.getShieldedAddress(path, {
4413
- checkOnDevice: params.showOnDevice ?? false
4414
- });
4415
- return { address: result.address, path: params.path };
3695
+ return await runOnce();
4416
3696
  } catch (err) {
4417
- ctx.invalidateSession(sessionId);
4418
- throw ctx.wrapError(err);
4419
- } finally {
4420
- ctx.clearCanceller(sessionId);
3697
+ if (!isLegacyWrongAppError(err, appName)) {
3698
+ debugLog("[LegacyChainCall] non-wrong-app failure:", appName, err?.message);
3699
+ ctx.invalidateSession(sessionId);
3700
+ throw ctx.wrapError(err, { defaultAppName: appName });
3701
+ }
3702
+ debugLog("[LegacyChainCall] wrong-app detected, retrying:", appName);
3703
+ try {
3704
+ await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
3705
+ } catch (switchErr) {
3706
+ debugLog(
3707
+ "[LegacyChainCall] retry ensureAppOpen failed:",
3708
+ appName,
3709
+ switchErr?.message
3710
+ );
3711
+ closeOpenAppUiIfShown();
3712
+ throw ctx.wrapError(switchErr, { defaultAppName: appName });
3713
+ }
3714
+ ctx.clearAllSigners();
3715
+ const result = await runOnce();
3716
+ debugLog("[LegacyChainCall] retry succeeded:", appName);
3717
+ return result;
4421
3718
  }
4422
3719
  }
4423
- async function _createZcashSigner(ctx, sessionId) {
3720
+ async function _ensureAppOpen(ctx, sessionId, appName, onPrompt) {
4424
3721
  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
- const signer = new SignerZcash(sdkSigner);
4428
- signer.onInteraction = (interaction) => {
4429
- debugLog("[LedgerConnector] zcash.onInteraction:", interaction);
4430
- ctx.emit("ui-event", {
4431
- type: collapseSignerInteraction(interaction),
4432
- payload: { sessionId }
4433
- });
4434
- };
4435
- signer.onRegisterCanceller = (cancel) => ctx.registerCanceller(sessionId, cancel);
4436
- return signer;
3722
+ const appManager = new AppManager(dmk);
3723
+ await appManager.ensureAppOpen(sessionId, appName, onPrompt);
4437
3724
  }
4438
3725
 
4439
- // src/connector/chains/tron.ts
4440
- import { HardwareErrorCode as HardwareErrorCode6, bytesToHex as bytesToHex3, hexToBytes as hexToBytes4 } from "@onekeyfe/hwk-adapter-core";
4441
-
4442
- // src/signer/SignerTron.ts
4443
- var SignerTron = class {
4444
- // eslint-disable-next-line no-useless-constructor, no-empty-function
4445
- constructor(_sdk) {
4446
- this._sdk = _sdk;
4447
- }
4448
- /** Base58 Tron address plus the uncompressed public key at `derivationPath`. */
4449
- async getAddress(derivationPath, options) {
4450
- const action = this._sdk.getAddress(derivationPath, options);
4451
- const result = await deviceActionToPromise(
4452
- action,
4453
- this.onInteraction,
4454
- void 0,
4455
- this.onRegisterCanceller
4456
- );
4457
- return { address: result.address, publicKey: result.publicKey };
4458
- }
4459
- /** Sign a protobuf-encoded raw transaction. */
4460
- async signTransaction(derivationPath, transaction, options) {
4461
- const action = this._sdk.signTransaction(derivationPath, transaction, options);
4462
- return deviceActionToPromise(
4463
- action,
4464
- this.onInteraction,
4465
- void 0,
4466
- this.onRegisterCanceller
4467
- );
3726
+ // src/transport/DmkTransport.ts
3727
+ import Transport from "@ledgerhq/hw-transport";
3728
+ var DmkTransport = class extends Transport {
3729
+ constructor(dmk, sessionId) {
3730
+ super();
3731
+ this._dmk = dmk;
3732
+ this._sessionId = sessionId;
4468
3733
  }
4469
- /** Sign a personal message (TIP-191). */
4470
- async signPersonalMessage(derivationPath, message, options) {
4471
- const action = this._sdk.signPersonalMessage(derivationPath, message, options);
4472
- return deviceActionToPromise(
4473
- action,
4474
- this.onInteraction,
4475
- void 0,
4476
- this.onRegisterCanceller
4477
- );
3734
+ async exchange(apdu) {
3735
+ const response = await this._dmk.sendApdu({
3736
+ sessionId: this._sessionId,
3737
+ apdu: new Uint8Array(apdu)
3738
+ });
3739
+ const { data, statusCode } = response;
3740
+ const result = Buffer.alloc(data.length + 2);
3741
+ if (data.length > 0) {
3742
+ result.set(data, 0);
3743
+ }
3744
+ result.set(statusCode, data.length);
3745
+ return result;
3746
+ }
3747
+ async close() {
4478
3748
  }
4479
3749
  };
4480
3750
 
4481
3751
  // src/connector/chains/tron.ts
4482
3752
  async function tronGetAddress(ctx, sessionId, params) {
4483
- const tronSigner = await _createTronSigner(ctx, sessionId);
4484
3753
  const path = normalizePath(params.path);
4485
- try {
4486
- const result = await tronSigner.getAddress(path, {
4487
- checkOnDevice: params.showOnDevice ?? false
4488
- });
4489
- return { address: result.address, publicKey: result.publicKey, path: params.path };
4490
- } catch (err) {
4491
- ctx.invalidateSession(sessionId);
4492
- throw ctx.wrapError(err);
4493
- } finally {
4494
- ctx.clearCanceller(sessionId);
4495
- }
3754
+ const showOnDevice = params.showOnDevice ?? false;
3755
+ return withLegacyChainCall(
3756
+ ctx,
3757
+ sessionId,
3758
+ {
3759
+ appName: "Tron",
3760
+ // Only show "confirm on device" UI when the device is actually going
3761
+ // to display the address for the user to verify.
3762
+ needsConfirmation: showOnDevice
3763
+ },
3764
+ async (sid) => {
3765
+ const trx = await _createTrx(ctx, sid);
3766
+ const result = await trx.getAddress(path, showOnDevice);
3767
+ return { address: result.address, publicKey: result.publicKey, path: params.path };
3768
+ }
3769
+ );
4496
3770
  }
4497
3771
  async function tronSignTransaction(ctx, sessionId, params) {
4498
3772
  if (!params.rawTxHex) {
4499
3773
  throw Object.assign(
4500
3774
  new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
4501
- { code: HardwareErrorCode6.InvalidParams }
3775
+ { code: HardwareErrorCode7.InvalidParams }
4502
3776
  );
4503
3777
  }
4504
- const tronSigner = await _createTronSigner(ctx, sessionId);
4505
3778
  const path = normalizePath(params.path);
4506
- try {
4507
- const signature = await tronSigner.signTransaction(path, hexToBytes4(params.rawTxHex));
4508
- return { signature: bytesToHex3(signature) };
4509
- } catch (err) {
4510
- ctx.invalidateSession(sessionId);
4511
- throw ctx.wrapError(err);
4512
- } finally {
4513
- ctx.clearCanceller(sessionId);
4514
- }
3779
+ return withLegacyChainCall(
3780
+ ctx,
3781
+ sessionId,
3782
+ { appName: "Tron", needsConfirmation: true },
3783
+ async (sid) => {
3784
+ const trx = await _createTrx(ctx, sid);
3785
+ const signature = await trx.signTransaction(
3786
+ path,
3787
+ params.rawTxHex,
3788
+ params.tokenSignatures ?? []
3789
+ );
3790
+ return { signature };
3791
+ }
3792
+ );
4515
3793
  }
4516
3794
  async function tronSignMessage(ctx, sessionId, params) {
4517
- const tronSigner = await _createTronSigner(ctx, sessionId);
4518
3795
  const path = normalizePath(params.path);
4519
- try {
4520
- const signature = await tronSigner.signPersonalMessage(path, hexToBytes4(params.messageHex));
4521
- return { signature: bytesToHex3(signature) };
4522
- } catch (err) {
4523
- ctx.invalidateSession(sessionId);
4524
- throw ctx.wrapError(err);
4525
- } finally {
4526
- ctx.clearCanceller(sessionId);
4527
- }
3796
+ return withLegacyChainCall(
3797
+ ctx,
3798
+ sessionId,
3799
+ { appName: "Tron", needsConfirmation: true },
3800
+ async (sid) => {
3801
+ const trx = await _createTrx(ctx, sid);
3802
+ const signature = await trx.signPersonalMessage(path, params.messageHex);
3803
+ return { signature };
3804
+ }
3805
+ );
4528
3806
  }
4529
- async function _createTronSigner(ctx, sessionId) {
3807
+ async function _createTrx(ctx, sessionId) {
4530
3808
  const dmk = await ctx.getOrCreateDmk();
4531
- const { SignerTrxBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-tron");
4532
- const sdkSigner = new SignerTrxBuilder({ dmk, sessionId }).build();
4533
- const signer = new SignerTron(sdkSigner);
4534
- signer.onInteraction = (interaction) => {
4535
- debugLog("[LedgerConnector] tron.onInteraction:", interaction);
4536
- ctx.emit("ui-event", {
4537
- type: collapseSignerInteraction(interaction),
4538
- payload: { sessionId }
4539
- });
4540
- };
4541
- signer.onRegisterCanceller = (cancel) => {
4542
- ctx.registerCanceller(sessionId, cancel);
4543
- };
4544
- return signer;
3809
+ return new Trx(new DmkTransport(dmk, sessionId));
4545
3810
  }
4546
3811
 
4547
3812
  // src/device-apps/customActions.ts
@@ -4720,7 +3985,7 @@ var DeviceApps = class {
4720
3985
  seTargetId: v.seTargetId,
4721
3986
  mcuTargetId: v.mcuTargetId,
4722
3987
  seVersion: v.seVersion,
4723
- seFlagsHex: bytesToHex4(v.seFlags),
3988
+ seFlagsHex: bytesToHex2(v.seFlags),
4724
3989
  mcuSephVersion: v.mcuSephVersion,
4725
3990
  mcuBootloaderVersion: v.mcuBootloaderVersion,
4726
3991
  hwVersion: v.hwVersion
@@ -4794,7 +4059,7 @@ var DeviceApps = class {
4794
4059
  }
4795
4060
  }
4796
4061
  };
4797
- function bytesToHex4(bytes) {
4062
+ function bytesToHex2(bytes) {
4798
4063
  if (!bytes) return "";
4799
4064
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
4800
4065
  }
@@ -4837,11 +4102,10 @@ var METHOD_PREFIX_TO_APP_NAME = {
4837
4102
  evm: "Ethereum",
4838
4103
  btc: "Bitcoin",
4839
4104
  sol: "Solana",
4840
- tron: "Tron",
4841
- zcash: "Zcash"
4105
+ tron: "Tron"
4842
4106
  };
4843
4107
  var HARDWARE_ERROR_CODE_VALUES = new Set(
4844
- Object.values(HardwareErrorCode7).filter((value) => typeof value === "number")
4108
+ Object.values(HardwareErrorCode8).filter((value) => typeof value === "number")
4845
4109
  );
4846
4110
  var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
4847
4111
  var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
@@ -4878,10 +4142,6 @@ async function defaultLedgerKitImporter(pkg) {
4878
4142
  return import("@ledgerhq/device-signer-kit-bitcoin");
4879
4143
  case "@ledgerhq/device-signer-kit-solana":
4880
4144
  return import("@ledgerhq/device-signer-kit-solana");
4881
- case "@ledgerhq/device-signer-kit-tron":
4882
- return import("@ledgerhq/device-signer-kit-tron");
4883
- case "@ledgerhq/device-signer-kit-zcash":
4884
- return import("@ledgerhq/device-signer-kit-zcash");
4885
4145
  case "@ledgerhq/context-module":
4886
4146
  return import("@ledgerhq/context-module");
4887
4147
  default:
@@ -4941,7 +4201,6 @@ var LedgerConnectorBase = class {
4941
4201
  this._ctx = {
4942
4202
  emit: (event, data) => this._emit(event, data),
4943
4203
  invalidateSession: (sid) => this._invalidateSession(sid),
4944
- teardownSecureChannelSession: (sid) => this._teardownSecureChannelSession(sid),
4945
4204
  wrapError: (err, opts) => this._wrapError(err, opts),
4946
4205
  getOrCreateDmk: () => this._getOrCreateDmk(),
4947
4206
  getDeviceManager: () => this._getDeviceManager(),
@@ -5041,7 +4300,7 @@ var LedgerConnectorBase = class {
5041
4300
  `Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
5042
4301
  );
5043
4302
  err._tag = ERROR_TAG.BlePairingTimeout;
5044
- err.code = HardwareErrorCode7.BlePairingTimeout;
4303
+ err.code = HardwareErrorCode8.BlePairingTimeout;
5045
4304
  reject(err);
5046
4305
  }, HANG_CEILING_MS);
5047
4306
  });
@@ -5074,7 +4333,7 @@ var LedgerConnectorBase = class {
5074
4333
  "Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
5075
4334
  );
5076
4335
  err._tag = ERROR_TAG.DeviceNotAdvertising;
5077
- err.code = HardwareErrorCode7.DeviceNotFound;
4336
+ err.code = HardwareErrorCode8.DeviceNotFound;
5078
4337
  throw err;
5079
4338
  };
5080
4339
  const doConnect = async (path) => {
@@ -5131,14 +4390,14 @@ var LedgerConnectorBase = class {
5131
4390
  this._resetSignersAndSessions();
5132
4391
  if (isLedgerBleConnectionType(this.connectionType)) {
5133
4392
  const tag = err?._tag;
5134
- if (isKnownConnectionTag(tag) && !isConnectionOpeningTag(tag)) {
4393
+ if (isKnownConnectionTag(tag)) {
5135
4394
  throw err;
5136
4395
  }
5137
4396
  const wrapped = new Error(
5138
4397
  "Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
5139
4398
  );
5140
4399
  wrapped._tag = ERROR_TAG.BleGattBondingFailed;
5141
- wrapped.code = HardwareErrorCode7.BlePairingTimeout;
4400
+ wrapped.code = HardwareErrorCode8.BlePairingTimeout;
5142
4401
  wrapped.originalError = err;
5143
4402
  throw wrapped;
5144
4403
  }
@@ -5216,7 +4475,7 @@ var LedgerConnectorBase = class {
5216
4475
  this._unwatchSessionState(sessionId);
5217
4476
  this._signerManager?.invalidate(sessionId);
5218
4477
  this._cancellers.get(sessionId)?.({
5219
- code: HardwareErrorCode7.DeviceDisconnected,
4478
+ code: HardwareErrorCode8.DeviceDisconnected,
5220
4479
  tag: "DeviceDisconnected",
5221
4480
  message: "Device disconnected"
5222
4481
  });
@@ -5242,7 +4501,7 @@ var LedgerConnectorBase = class {
5242
4501
  success: false,
5243
4502
  error: serializeConnectorError(
5244
4503
  Object.assign(new Error("Ledger app is unresponsive"), {
5245
- code: HardwareErrorCode7.DeviceAppStuck,
4504
+ code: HardwareErrorCode8.DeviceAppStuck,
5246
4505
  _tag: ERROR_TAG.DeviceAppStuck,
5247
4506
  originalError: err
5248
4507
  })
@@ -5254,7 +4513,7 @@ var LedgerConnectorBase = class {
5254
4513
  success: false,
5255
4514
  error: serializeConnectorError(
5256
4515
  Object.assign(new Error("Device communication interrupted, please retry"), {
5257
- code: HardwareErrorCode7.TransportError,
4516
+ code: HardwareErrorCode8.TransportError,
5258
4517
  _tag: ERROR_TAG.DeviceTransportStuck,
5259
4518
  originalError: err
5260
4519
  })
@@ -5300,11 +4559,6 @@ var LedgerConnectorBase = class {
5300
4559
  return solSignTransaction(ctx, sessionId, params);
5301
4560
  case "solSignMessage":
5302
4561
  return solSignMessage(ctx, sessionId, params);
5303
- // ZCASH
5304
- case "zcashGetFullViewingKey":
5305
- return zcashGetFullViewingKey(ctx, sessionId, params);
5306
- case "zcashGetShieldedAddress":
5307
- return zcashGetShieldedAddress(ctx, sessionId, params);
5308
4562
  // TRON
5309
4563
  case "tronGetAddress":
5310
4564
  return tronGetAddress(ctx, sessionId, params);
@@ -5329,7 +4583,7 @@ var LedgerConnectorBase = class {
5329
4583
  try {
5330
4584
  return await apps.install(p.appName, ({ progress }) => {
5331
4585
  ctx.emit("ui-event", {
5332
- type: EConnectorInteraction3.AppInstallProgress,
4586
+ type: EConnectorInteraction4.AppInstallProgress,
5333
4587
  payload: {
5334
4588
  sessionId,
5335
4589
  appName: p.appName,
@@ -5338,7 +4592,7 @@ var LedgerConnectorBase = class {
5338
4592
  });
5339
4593
  });
5340
4594
  } catch (err) {
5341
- ctx.teardownSecureChannelSession(sessionId);
4595
+ ctx.invalidateSession(sessionId);
5342
4596
  throw ctx.wrapError(err);
5343
4597
  } finally {
5344
4598
  ctx.clearCanceller(sessionId);
@@ -5428,7 +4682,7 @@ var LedgerConnectorBase = class {
5428
4682
  );
5429
4683
  return { isGenuine: output.isGenuine, deviceId };
5430
4684
  } catch (err) {
5431
- ctx.teardownSecureChannelSession(sessionId);
4685
+ ctx.invalidateSession(sessionId);
5432
4686
  throw ctx.wrapError(err);
5433
4687
  } finally {
5434
4688
  ctx.clearCanceller(sessionId);
@@ -5547,34 +4801,9 @@ var LedgerConnectorBase = class {
5547
4801
  }
5548
4802
  return this._deviceAppsManager;
5549
4803
  }
5550
- // DeviceAppsManager is intentionally absent here: it is a per-call factory
5551
- // with no cached session state, so there is nothing to invalidate. Cancelling
5552
- // the device action is what closes the secure channel.
5553
4804
  _invalidateSession(sessionId) {
5554
4805
  this._signerManager?.invalidate(sessionId);
5555
4806
  }
5556
- /**
5557
- * Teardown for the OS-level actions that open a manager-api secure channel
5558
- * (install, uninstall, genuine check). Cancelling the device action is what
5559
- * stops DMK's xstate actor, which unsubscribes the secure-channel observable
5560
- * and closes its WebSocket; dropping the canceller without firing it leaves
5561
- * that teardown to run only if the action settled on its own.
5562
- *
5563
- * The DMK device session is deliberately kept: the secure channel is a
5564
- * separate WebSocket per call, and disconnecting here would break the bounded
5565
- * unlock/retry recovery the adapter runs on the original session.
5566
- */
5567
- _teardownSecureChannelSession(sessionId) {
5568
- const cancel = this._cancellers.get(sessionId);
5569
- this._cancellers.delete(sessionId);
5570
- if (cancel) {
5571
- try {
5572
- cancel();
5573
- } catch {
5574
- }
5575
- }
5576
- this._invalidateSession(sessionId);
5577
- }
5578
4807
  /**
5579
4808
  * Replace an old session with a new one after app switch.
5580
4809
  * Emits device-connect so the adapter updates its _sessions Map.
@@ -5659,7 +4888,7 @@ var LedgerConnectorBase = class {
5659
4888
  * at every catch site. Falls through unchanged for unknown methods.
5660
4889
  */
5661
4890
  _ctxForMethod(method) {
5662
- const prefix = /^(evm|btc|sol|tron|zcash)/.exec(method)?.[1];
4891
+ const prefix = /^(evm|btc|sol|tron)/.exec(method)?.[1];
5663
4892
  const defaultAppName = prefix ? METHOD_PREFIX_TO_APP_NAME[prefix] : void 0;
5664
4893
  if (!defaultAppName) return this._ctx;
5665
4894
  return {
@@ -5693,209 +4922,6 @@ var LedgerConnectorBase = class {
5693
4922
  return error;
5694
4923
  }
5695
4924
  };
5696
-
5697
- // src/transport/DmkTransport.ts
5698
- import Transport from "@ledgerhq/hw-transport";
5699
- var DmkTransport = class extends Transport {
5700
- constructor(dmk, sessionId) {
5701
- super();
5702
- this._dmk = dmk;
5703
- this._sessionId = sessionId;
5704
- }
5705
- async exchange(apdu) {
5706
- const response = await this._dmk.sendApdu({
5707
- sessionId: this._sessionId,
5708
- apdu: new Uint8Array(apdu)
5709
- });
5710
- const { data, statusCode } = response;
5711
- const result = Buffer.alloc(data.length + 2);
5712
- if (data.length > 0) {
5713
- result.set(data, 0);
5714
- }
5715
- result.set(statusCode, data.length);
5716
- return result;
5717
- }
5718
- async close() {
5719
- }
5720
- };
5721
-
5722
- // src/app/AppManager.ts
5723
- import {
5724
- CloseAppCommand,
5725
- GetAppAndVersionCommand,
5726
- OpenAppCommand,
5727
- isSuccessCommandResult
5728
- } from "@ledgerhq/device-management-kit";
5729
- import { HardwareErrorCode as HardwareErrorCode8 } from "@onekeyfe/hwk-adapter-core";
5730
- var APP_NAME_MAP = {
5731
- ETH: "Ethereum",
5732
- BTC: "Bitcoin",
5733
- SOL: "Solana",
5734
- TRX: "Tron",
5735
- ZEC: "Zcash",
5736
- XRP: "XRP",
5737
- ADA: "Cardano",
5738
- DOT: "Polkadot",
5739
- ATOM: "Cosmos"
5740
- };
5741
- var DASHBOARD_APP_NAME = "BOLOS";
5742
- var AppManager = class {
5743
- constructor(dmk, options) {
5744
- this._dmk = dmk;
5745
- this._waitMs = options?.waitMs ?? 1e3;
5746
- this._maxRetries = options?.maxRetries ?? 10;
5747
- }
5748
- /**
5749
- * Return the Ledger app name for a given chain ticker,
5750
- * or undefined if the chain is not supported.
5751
- */
5752
- static getAppName(chain) {
5753
- return APP_NAME_MAP[chain];
5754
- }
5755
- /**
5756
- * Ensure the target app is open on the device identified by `sessionId`.
5757
- *
5758
- * Flow:
5759
- * 1. Check the currently running app.
5760
- * 2. If it is already the target, return immediately.
5761
- * 3. If a different app is running (not dashboard), close it first.
5762
- * 4. Open the target app.
5763
- * 5. Poll until the device confirms the target app is running.
5764
- */
5765
- /**
5766
- * @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
5767
- * device is about to display "Open <app>" on screen and wait for the
5768
- * user's button press. UI consumers should show their "open app" prompt
5769
- * in response. NOT called when the target app is already open (no user
5770
- * interaction needed in that case).
5771
- *
5772
- * Important: OpenAppCommand is blocking. It does not resolve until the user
5773
- * has physically confirmed on the device, so anything that runs AFTER
5774
- * `await this._openApp(...)` lands AFTER the prompt is already gone.
5775
- * Hence the callback must fire BEFORE that await.
5776
- */
5777
- async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
5778
- const currentApp = await this._getCurrentApp(sessionId);
5779
- if (currentApp === targetAppName) {
5780
- return;
5781
- }
5782
- if (!this._isDashboard(currentApp)) {
5783
- await this._closeCurrentApp(sessionId);
5784
- await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
5785
- }
5786
- onConfirmOnDevice?.();
5787
- await this._openApp(sessionId, targetAppName);
5788
- await this._waitForApp(sessionId, targetAppName);
5789
- }
5790
- // ---------------------------------------------------------------------------
5791
- // Private helpers
5792
- // ---------------------------------------------------------------------------
5793
- async _getCurrentApp(sessionId) {
5794
- const result = await this._dmk.sendCommand({
5795
- sessionId,
5796
- command: new GetAppAndVersionCommand()
5797
- });
5798
- if (isSuccessCommandResult(result)) {
5799
- debugLog("[AppManager] currentApp:", result.data.name);
5800
- return result.data.name;
5801
- }
5802
- const errResult = result;
5803
- const dmkErr = errResult.error ?? {};
5804
- const original = dmkErr.originalError;
5805
- debugLog(
5806
- "[AppManager] _getCurrentApp failed sessionId=",
5807
- sessionId,
5808
- "tag=",
5809
- dmkErr._tag,
5810
- "errorCode=",
5811
- dmkErr.errorCode,
5812
- "message=",
5813
- dmkErr.message,
5814
- "originalErrorMessage=",
5815
- original?.message ?? String(original ?? "")
5816
- );
5817
- throw Object.assign(
5818
- new Error(
5819
- dmkErr.message ?? "Failed to get current app from device"
5820
- ),
5821
- {
5822
- _tag: dmkErr._tag,
5823
- errorCode: dmkErr.errorCode,
5824
- originalError: original
5825
- }
5826
- );
5827
- }
5828
- async _openApp(sessionId, appName) {
5829
- const result = await this._dmk.sendCommand({
5830
- sessionId,
5831
- command: new OpenAppCommand({ appName })
5832
- });
5833
- if (!isSuccessCommandResult(result)) {
5834
- const dmkErr = result.error;
5835
- const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
5836
- const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
5837
- debugLog(
5838
- "[AppManager] openApp failed:",
5839
- appName,
5840
- "errorCode:",
5841
- errorCode,
5842
- "tag:",
5843
- dmkErr._tag
5844
- );
5845
- let code;
5846
- if (errorCode === "6807" || /unknown application/i.test(message)) {
5847
- code = HardwareErrorCode8.AppNotInstalled;
5848
- } else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
5849
- code = HardwareErrorCode8.UserRejected;
5850
- }
5851
- throw Object.assign(new Error(`Failed to open "${appName}"`), {
5852
- _tag: ERROR_TAG.OpenAppCommand,
5853
- code,
5854
- errorCode,
5855
- statusCode: errorCode,
5856
- appName,
5857
- originalError: dmkErr
5858
- });
5859
- }
5860
- }
5861
- async _closeCurrentApp(sessionId) {
5862
- debugLog("[AppManager] closeCurrentApp");
5863
- await this._dmk.sendCommand({
5864
- sessionId,
5865
- command: new CloseAppCommand()
5866
- });
5867
- }
5868
- /**
5869
- * Poll the device until the expected app is reported as running,
5870
- * or throw after `_maxRetries` attempts.
5871
- */
5872
- async _waitForApp(sessionId, expectedAppName) {
5873
- let lastSeen = "";
5874
- for (let i = 0; i < this._maxRetries; i++) {
5875
- await this._wait();
5876
- const current = await this._getCurrentApp(sessionId);
5877
- lastSeen = current;
5878
- if (current === expectedAppName) {
5879
- return;
5880
- }
5881
- }
5882
- debugLog(
5883
- "[AppManager] waitForApp exhausted: expected=",
5884
- expectedAppName,
5885
- "lastSeen=",
5886
- lastSeen
5887
- );
5888
- throw new Error(
5889
- `Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
5890
- );
5891
- }
5892
- _isDashboard(appName) {
5893
- return appName === DASHBOARD_APP_NAME;
5894
- }
5895
- _wait() {
5896
- return new Promise((resolve) => setTimeout(resolve, this._waitMs));
5897
- }
5898
- };
5899
4925
  export {
5900
4926
  AppManager,
5901
4927
  DmkTransport,
@@ -5906,7 +4932,6 @@ export {
5906
4932
  SignerEth,
5907
4933
  SignerManager,
5908
4934
  SignerSol,
5909
- SignerZcash,
5910
4935
  debugLog,
5911
4936
  deviceActionToPromise,
5912
4937
  isDeviceLockedError,