@onekeyfe/hwk-ledger-adapter 1.2.6-alpha.0 → 1.2.6-alpha.2

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,10 +170,7 @@ var ERROR_TAG = {
181
170
  UnknownDevice: "UnknownDeviceError",
182
171
  DeviceSessionRefresher: "DeviceSessionRefresherError",
183
172
  DeviceNotInitialized: "DeviceNotInitializedError",
184
- // DMK class OpeningConnectionError carries `_tag` "ConnectionOpeningError"; both
185
- // spellings are kept so a DMK release that aligns them still classifies.
186
- OpeningConnection: "ConnectionOpeningError",
187
- OpeningConnectionLegacy: "OpeningConnectionError",
173
+ OpeningConnection: "OpeningConnectionError",
188
174
  DeviceDisconnectedBeforeSendingApdu: "DeviceDisconnectedBeforeSendingApdu",
189
175
  DeviceDisconnectedWhileSending: "DeviceDisconnectedWhileSendingError",
190
176
  Disconnect: "DisconnectError",
@@ -197,16 +183,7 @@ var ERROR_TAG = {
197
183
  // DMK remote-network failures (manager-api HTTP / secure-channel WS).
198
184
  WebSocketConnection: "WebSocketConnectionError",
199
185
  HttpFetch: "FetchError",
200
- NetworkDA: "NetworkDAError",
201
- InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError",
202
- ApplicationsMetadataTask: "GetApplicationsMetadataTaskError",
203
- // DMK OS device actions. SecureChannelError is what mapInstallDAErrors() leaves
204
- // after splitting out device answers, i.e. the relay itself broke.
205
- SecureChannel: "SecureChannelError",
206
- RefusedByUserDA: "RefusedByUserDAError",
207
- AppAlreadyInstalledDA: "AppAlreadyInstalledDAError",
208
- OutOfMemoryDA: "OutOfMemoryDAError",
209
- DeviceNotOnboarded: "DeviceNotOnboardedError"
186
+ InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError"
210
187
  };
211
188
  function isDeviceLockedError(err) {
212
189
  if (!err || typeof err !== "object") return false;
@@ -236,6 +213,7 @@ function isBlePairingFailureError(err) {
236
213
  return false;
237
214
  }
238
215
  var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
216
+ ERROR_TAG.DeviceLocked,
239
217
  ERROR_TAG.DeviceNotAdvertising,
240
218
  ERROR_TAG.BlePairingTimeout,
241
219
  ERROR_TAG.BleGattBondingFailed,
@@ -247,7 +225,6 @@ var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
247
225
  ERROR_TAG.DeviceSessionRefresher,
248
226
  ERROR_TAG.DeviceNotInitialized,
249
227
  ERROR_TAG.OpeningConnection,
250
- ERROR_TAG.OpeningConnectionLegacy,
251
228
  ERROR_TAG.DeviceDisconnectedBeforeSendingApdu,
252
229
  ERROR_TAG.DeviceDisconnectedWhileSending,
253
230
  ERROR_TAG.Disconnect,
@@ -262,10 +239,7 @@ var DEVICE_NOT_FOUND_TAGS = /* @__PURE__ */ new Set([
262
239
  // Map to DeviceNotFound so non-BLE-direct paths get a sensible error code.
263
240
  ERROR_TAG.DeviceNotInDiscoveryCache
264
241
  ]);
265
- var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([
266
- ERROR_TAG.OpeningConnection,
267
- ERROR_TAG.OpeningConnectionLegacy
268
- ]);
242
+ var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.OpeningConnection]);
269
243
  var DEVICE_DISCONNECTED_TAGS = /* @__PURE__ */ new Set([
270
244
  ERROR_TAG.DeviceNotRecognized,
271
245
  ERROR_TAG.DeviceSessionNotFound,
@@ -277,18 +251,17 @@ var DEVICE_DISCONNECTED_TAGS = /* @__PURE__ */ new Set([
277
251
  ERROR_TAG.WebHIDDisconnect
278
252
  ]);
279
253
  function isConnectionLevelError(err) {
280
- return hasErrorTag(err, CONNECTION_LEVEL_TAGS);
254
+ if (!err || typeof err !== "object") return false;
255
+ const tag = err._tag;
256
+ if (tag && CONNECTION_LEVEL_TAGS.has(tag)) return true;
257
+ const e = err;
258
+ if (e.originalError != null && isConnectionLevelError(e.originalError)) return true;
259
+ if (e.error != null && e._tag && isConnectionLevelError(e.error)) return true;
260
+ return false;
281
261
  }
282
262
  function isKnownConnectionTag(tag) {
283
263
  return typeof tag === "string" && CONNECTION_LEVEL_TAGS.has(tag);
284
264
  }
285
- var CONNECTION_OPENING_TAGS = /* @__PURE__ */ new Set([
286
- ERROR_TAG.OpeningConnection,
287
- ERROR_TAG.OpeningConnectionLegacy
288
- ]);
289
- function isConnectionOpeningTag(tag) {
290
- return typeof tag === "string" && CONNECTION_OPENING_TAGS.has(tag);
291
- }
292
265
  function hasStatusCode(err, codeSet) {
293
266
  if (!err || typeof err !== "object") return false;
294
267
  const e = err;
@@ -324,28 +297,28 @@ function hasInvalidArgumentCode(err) {
324
297
  if (e.error != null && e._tag && hasInvalidArgumentCode(e.error)) return true;
325
298
  return false;
326
299
  }
327
- function hasErrorTag(err, tags) {
300
+ function isDeviceNotFoundError(err) {
328
301
  if (!err || typeof err !== "object") return false;
302
+ const tag = err._tag;
303
+ if (tag && DEVICE_NOT_FOUND_TAGS.has(tag)) return true;
329
304
  const e = err;
330
- if (typeof e._tag === "string" && tags.has(e._tag)) return true;
331
- if (e.originalError != null && hasErrorTag(e.originalError, tags)) return true;
332
- if (e.error != null && e._tag && hasErrorTag(e.error, tags)) return true;
305
+ if (e.originalError != null && isDeviceNotFoundError(e.originalError)) return true;
306
+ if (e.error != null && e._tag && isDeviceNotFoundError(e.error)) return true;
333
307
  return false;
334
308
  }
335
- function isDeviceNotFoundError(err) {
336
- return hasErrorTag(err, DEVICE_NOT_FOUND_TAGS);
337
- }
338
309
  function isDeviceBusyError(err) {
339
- return hasErrorTag(err, DEVICE_BUSY_TAGS);
310
+ if (!err || typeof err !== "object") return false;
311
+ const tag = err._tag;
312
+ if (tag && DEVICE_BUSY_TAGS.has(tag)) return true;
313
+ const e = err;
314
+ if (e.originalError != null && isDeviceBusyError(e.originalError)) return true;
315
+ if (e.error != null && e._tag && isDeviceBusyError(e.error)) return true;
316
+ return false;
340
317
  }
341
- var USER_REJECTED_TAGS = /* @__PURE__ */ new Set([
342
- ERROR_TAG.UserRefusedOnDevice,
343
- ERROR_TAG.RefusedByUserDA
344
- ]);
345
318
  function isUserRejectedError(err) {
346
319
  if (!err || typeof err !== "object") return false;
347
320
  const e = err;
348
- if (hasErrorTag(err, USER_REJECTED_TAGS)) return true;
321
+ if (e._tag === ERROR_TAG.UserRefusedOnDevice) return true;
349
322
  if (typeof e.message === "string" && /denied|rejected|refused/i.test(e.message)) return true;
350
323
  if (hasStatusCode(err, USER_REJECTED_CODES)) return true;
351
324
  return false;
@@ -376,33 +349,15 @@ function isAppNotInstalledError(err) {
376
349
  if (hasStatusCode(err, APP_NOT_INSTALLED_CODES)) return true;
377
350
  return false;
378
351
  }
379
- var OUT_OF_MEMORY_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.OutOfMemoryDA]);
380
352
  function isOutOfMemoryError(err) {
381
- return hasErrorTag(err, OUT_OF_MEMORY_TAGS);
382
- }
383
- var APP_ALREADY_INSTALLED_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.AppAlreadyInstalledDA]);
384
- function isAppAlreadyInstalledError(err) {
385
- return hasErrorTag(err, APP_ALREADY_INSTALLED_TAGS);
386
- }
387
- var DEVICE_NOT_ONBOARDED_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.DeviceNotOnboarded]);
388
- function isDeviceNotOnboardedError(err) {
389
- return hasErrorTag(err, DEVICE_NOT_ONBOARDED_TAGS);
390
- }
391
- var FIRMWARE_METADATA_TAGS = /* @__PURE__ */ new Set([
392
- ERROR_TAG.InvalidFirmwareMetadataResponse,
393
- ERROR_TAG.ApplicationsMetadataTask
394
- ]);
395
- function isFirmwareMetadataError(err) {
396
- return hasErrorTag(err, FIRMWARE_METADATA_TAGS);
397
- }
398
- var SECURE_CHANNEL_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.SecureChannel]);
399
- function isSecureChannelError(err) {
400
- return hasErrorTag(err, SECURE_CHANNEL_TAGS);
353
+ if (!err || typeof err !== "object") return false;
354
+ const e = err;
355
+ return e._tag === "OutOfMemoryDAError";
401
356
  }
402
357
  var NETWORK_ERROR_TAGS = /* @__PURE__ */ new Set([
403
358
  ERROR_TAG.WebSocketConnection,
404
359
  ERROR_TAG.HttpFetch,
405
- ERROR_TAG.NetworkDA
360
+ ERROR_TAG.InvalidFirmwareMetadataResponse
406
361
  ]);
407
362
  function isNetworkError(err) {
408
363
  if (!err || typeof err !== "object") return false;
@@ -452,34 +407,23 @@ function isTransportStuck(err) {
452
407
  function isStuckAppStateError(err) {
453
408
  return isAppStuckByApdu(err) || isTransportStuck(err);
454
409
  }
455
- var DMK_PLACEHOLDER_MESSAGE = "Unknown error.";
456
- function nestedErrorMessage(nested) {
457
- if (!nested || typeof nested !== "object") return void 0;
458
- const { message } = nested;
459
- if (typeof message !== "string") return void 0;
460
- const trimmed = message.trim();
461
- if (!trimmed || trimmed === DMK_PLACEHOLDER_MESSAGE) return void 0;
462
- return message;
463
- }
464
410
  function mapLedgerError(err, opts) {
465
411
  let originalMessage = "Unknown Ledger error";
466
412
  if (err instanceof Error) {
467
413
  originalMessage = err.message;
468
414
  } else if (err && typeof err === "object") {
469
415
  const e = err;
470
- originalMessage = String(
471
- e.message ?? nestedErrorMessage(e.originalError) ?? e._tag ?? e.type ?? JSON.stringify(err)
472
- );
416
+ originalMessage = String(e.message ?? e._tag ?? e.type ?? JSON.stringify(err));
473
417
  }
474
418
  let code;
475
419
  if (isDeviceLockedError(err)) {
476
420
  code = HardwareErrorCode.DeviceLocked;
477
421
  } else if (isDeviceNotAdvertisingError(err) || isDeviceNotFoundError(err)) {
478
422
  code = HardwareErrorCode.DeviceNotFound;
479
- } else if (isBlePairingFailureError(err)) {
480
- code = HardwareErrorCode.BlePairingTimeout;
481
423
  } else if (isDeviceBusyError(err)) {
482
424
  code = HardwareErrorCode.DeviceBusy;
425
+ } else if (isBlePairingFailureError(err)) {
426
+ code = HardwareErrorCode.BlePairingTimeout;
483
427
  } else if (isUserAbortedError(err)) {
484
428
  code = HardwareErrorCode.UserAborted;
485
429
  } else if (isUserRejectedError(err)) {
@@ -488,18 +432,10 @@ function mapLedgerError(err, opts) {
488
432
  code = HardwareErrorCode.WrongApp;
489
433
  } else if (isAppNotInstalledError(err)) {
490
434
  code = HardwareErrorCode.AppNotInstalled;
491
- } else if (isAppAlreadyInstalledError(err)) {
492
- code = HardwareErrorCode.AppAlreadyInstalled;
493
435
  } else if (isOutOfMemoryError(err)) {
494
436
  code = HardwareErrorCode.DeviceOutOfMemory;
495
- } else if (isDeviceNotOnboardedError(err)) {
496
- code = HardwareErrorCode.DeviceNotInitialized;
497
- } else if (isFirmwareMetadataError(err)) {
498
- code = HardwareErrorCode.LedgerFirmwareMetadataError;
499
437
  } else if (isNetworkError(err)) {
500
438
  code = HardwareErrorCode.NetworkError;
501
- } else if (isSecureChannelError(err)) {
502
- code = HardwareErrorCode.LedgerSecureChannelError;
503
439
  } else if (isDeviceDisconnectedError(err)) {
504
440
  code = HardwareErrorCode.DeviceDisconnected;
505
441
  } else if (isTimeoutError(err)) {
@@ -512,32 +448,11 @@ function mapLedgerError(err, opts) {
512
448
  }
513
449
  const errAppName = err && typeof err === "object" ? err.appName : void 0;
514
450
  const appName = errAppName ?? opts?.defaultAppName;
515
- return {
516
- code,
517
- message: enrichErrorMessage(code, originalMessage),
518
- origin: defaultOriginForCode(code),
519
- appName
520
- };
521
- }
522
-
523
- // src/utils/queueKey.ts
524
- var LEDGER_DEFAULT_QUEUE_KEY = "__ledger_default__";
525
- function ledgerQueueKey({
526
- operationId,
527
- connectId
528
- }) {
529
- return (operationId ?? connectId) || LEDGER_DEFAULT_QUEUE_KEY;
451
+ return { code, message: enrichErrorMessage(code, originalMessage), appName };
530
452
  }
531
453
 
532
454
  // src/adapter/methods/allNetworkGetAddress.ts
533
- import {
534
- HardwareErrorCode as HardwareErrorCode2,
535
- failure,
536
- isConnectionLost,
537
- isUserRefusal,
538
- resolveHardwareOperationTarget,
539
- runAllNetworkGetAddress
540
- } from "@onekeyfe/hwk-adapter-core";
455
+ import { HardwareErrorCode as HardwareErrorCode2, failure, runAllNetworkGetAddress } from "@onekeyfe/hwk-adapter-core";
541
456
 
542
457
  // src/utils/sdkEventBus.ts
543
458
  var listeners = /* @__PURE__ */ new Set();
@@ -594,103 +509,65 @@ var LEDGER_BTC_NETWORK_COIN_MAP = {
594
509
  var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "dogecoin"]);
595
510
  function createAllNetworkGetAddress({
596
511
  callChain,
597
- getChainFingerprint,
598
- retainOperation,
599
- errorToFailure,
600
- createCancelScope
512
+ getChainFingerprint
601
513
  }) {
602
514
  return async function allNetworkGetAddress(connectId, _deviceId, params) {
603
- debugLog("[LedgerAdapter][REQ]", {
604
- method: "allNetworkGetAddress",
605
- connectId,
606
- itemCount: params.bundle.length
607
- });
608
- const target = resolveHardwareOperationTarget(connectId, params.operationId, "ledger");
609
- if (!target.success) return target;
610
- const effectiveTargetId = target.payload.targetId ?? "";
611
- let releaseOperationRetention;
612
- try {
613
- releaseOperationRetention = target.payload.operationId ? retainOperation(target.payload.operationId) : void 0;
614
- } catch (error) {
615
- return errorToFailure(error);
616
- }
617
- const cancelScope = createCancelScope(
618
- ledgerQueueKey({ operationId: target.payload.operationId, connectId: effectiveTargetId })
619
- );
515
+ debugLog("[LedgerAdapter][REQ]", { method: "allNetworkGetAddress", connectId, params });
620
516
  const installContext = {};
621
517
  const commonParams = {
622
- autoInstallApp: params.autoInstallApp,
623
- operationId: target.payload.operationId,
624
- knownConnections: params.knownConnections,
625
- extra: params.extra,
626
- allowDeviceSelection: params.allowDeviceSelection,
627
- supportedTransports: params.supportedTransports
518
+ autoInstallApp: params.autoInstallApp
628
519
  };
629
520
  const chainFingerprints = /* @__PURE__ */ new Map();
630
- try {
631
- const result = await runAllNetworkGetAddress({
632
- connectId: effectiveTargetId,
633
- deviceId: _deviceId,
634
- params,
635
- normalizeItem: normalizeLedgerAllNetworkItem,
636
- buildUnsupportedNetworkResponse: (item) => isUnsupportedLedgerAllNetworkNetwork(item) ? buildUnsupportedNetworkResponse(item) : void 0,
637
- callItem: async ({ method, chain, item }) => {
638
- if (cancelScope.signal.aborted) return buildCancelledFailure(cancelScope.signal);
639
- const itemDeviceId = getItemDeviceId(item) ?? chainFingerprints.get(chain) ?? "";
640
- return callAllNetworkMethod(
641
- callChain,
642
- effectiveTargetId,
643
- itemDeviceId,
644
- method,
645
- item,
646
- commonParams,
647
- installContext
648
- );
649
- },
650
- attachIdentity: async ({ item, chain, payload }) => attachLedgerIdentity(
651
- getChainFingerprint,
652
- 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,
653
534
  item,
654
- chain,
655
- payload,
656
- chainFingerprints,
657
- installContext,
658
- cancelScope.signal
659
- ),
660
- shouldAbortBundle: isTopLevelAllNetworkFailure,
661
- buildTopLevelFailure: (response) => {
662
- const code = response.payload?.code ?? HardwareErrorCode2.DeviceMismatch;
663
- return failure(
664
- code,
665
- response.payload?.error ?? "All-network get-address aborted",
666
- response.payload?.params
667
- );
668
- }
669
- });
670
- debugLog("[LedgerAdapter][RES]", {
671
- method: "allNetworkGetAddress",
672
- success: result.success,
673
- payload: result
674
- });
675
- return result;
676
- } finally {
677
- cancelScope.release();
678
- releaseOperationRetention?.();
679
- }
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;
680
563
  };
681
564
  }
682
- function buildCancelledFailure(signal) {
683
- const reason = signal.reason;
684
- const code = typeof reason?.code === "number" ? reason.code : HardwareErrorCode2.UserAborted;
685
- const message = typeof reason?.message === "string" ? reason.message : "";
686
- return failure(code, message || "All-network get-address cancelled");
687
- }
688
565
  function isTopLevelAllNetworkFailure(response) {
689
566
  if (response.success) {
690
567
  return false;
691
568
  }
692
569
  const code = response.payload?.code;
693
- return code === HardwareErrorCode2.DeviceMismatch || isUserRefusal(code) || isConnectionLost(code);
570
+ return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.UserAborted || code === HardwareErrorCode2.UserRejected;
694
571
  }
695
572
  function getItemDeviceId(item) {
696
573
  const { deviceId } = item;
@@ -720,18 +597,8 @@ function normalizeLedgerAllNetworkItem(method, item) {
720
597
  const coin = LEDGER_BTC_NETWORK_COIN_MAP[item.network];
721
598
  return coin ? { ...item, coin } : item;
722
599
  }
723
- async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints, context, cancelSignal) {
724
- const knownFingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || "";
725
- if (!knownFingerprint && cancelSignal.aborted) {
726
- const cancelled = buildCancelledFailure(cancelSignal);
727
- return { ...item, success: false, payload: cancelled.payload };
728
- }
729
- const fingerprint = knownFingerprint || await bootstrapChainFingerprint(
730
- getChainFingerprint,
731
- context.connection?.connectId ?? connectId,
732
- chain,
733
- context
734
- );
600
+ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints) {
601
+ const fingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || await bootstrapChainFingerprint(getChainFingerprint, connectId, chain);
735
602
  if (!fingerprint) {
736
603
  return buildFingerprintBootstrapFailure(item, chain);
737
604
  }
@@ -752,8 +619,8 @@ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain,
752
619
  }
753
620
  };
754
621
  }
755
- async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain, context) {
756
- const response = await getChainFingerprint(connectId, chain, context);
622
+ async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain) {
623
+ const response = await getChainFingerprint(connectId, chain);
757
624
  return response.success ? response.payload : "";
758
625
  }
759
626
  function buildFingerprintBootstrapFailure(item, chain) {
@@ -846,16 +713,6 @@ function isLedgerBleDescriptor(connectionType, descriptor) {
846
713
  function formatDeviceMismatchError(expected, actual) {
847
714
  return `Wrong device: expected ${expected}, got ${actual}`;
848
715
  }
849
- var LOG_REDACTED_RESULT_KEYS = ["ufvk", "orchardFvk"];
850
- function redactResultForLog(result) {
851
- if (!result || typeof result !== "object") return result;
852
- const keys = LOG_REDACTED_RESULT_KEYS.filter((key) => key in result);
853
- if (!keys.length) return result;
854
- return { ...result, ...Object.fromEntries(keys.map((key) => [key, "[redacted]"])) };
855
- }
856
- function isLostConnectionError(err) {
857
- return isDeviceDisconnectedError(err) || isDeviceNotAdvertisingError(err) || isTimeoutError(err) || isConnectionLevelError(err);
858
- }
859
716
  var BTC_HIGH_INDEX_THRESHOLD = 100;
860
717
  function btcAccountIndexFromPath(path) {
861
718
  const segments = path.replace(/^m\//, "").split("/");
@@ -867,35 +724,9 @@ function btcAccountIndexFromPath(path) {
867
724
  var _LedgerAdapter = class _LedgerAdapter {
868
725
  constructor(connector, options) {
869
726
  this.vendor = "ledger";
870
- // Cancel frees the DMK action and its intent-queue slot, but cannot retract a
871
- // confirmation screen the device is already showing.
872
- this.cancelCapability = "stops-waiting";
873
727
  this.emitter = new TypedEventEmitter();
874
- this._operations = new OperationRegistry({
875
- vendor: "ledger",
876
- onEnded: (operation, reason) => {
877
- this._discardPendingBinding(this._pendingOperationBindings.get(operation.operationId));
878
- this._pendingOperationBindings.delete(operation.operationId);
879
- this.emitter.emit(SDK.OPERATION_ENDED, {
880
- type: SDK.OPERATION_ENDED,
881
- payload: { operationId: operation.operationId, reason }
882
- });
883
- if (reason === "timeout") {
884
- void this._releaseOperationConnection(operation);
885
- }
886
- }
887
- });
888
728
  this._discoveredDevices = /* @__PURE__ */ new Map();
889
729
  this._sessions = /* @__PURE__ */ new Map();
890
- // An operation's BLE pick is saved by its first call that gets past the wallet check.
891
- this._pendingOperationBindings = /* @__PURE__ */ new Map();
892
- // A one-shot call's unchecked BLE pick, by connectId. It outlives the call only
893
- // through an unlock retry; any other failure drops it with its session.
894
- this._pendingPickBindings = /* @__PURE__ */ new Map();
895
- // Transport each session was opened on, by sessionId. The adapter-wide
896
- // `_activeConnectionType` moves with every discovery and can go stale.
897
- this._sessionTransports = /* @__PURE__ */ new Map();
898
- this._selectedBleReconnectTargets = /* @__PURE__ */ new Map();
899
730
  this._uiRegistry = new UiRequestRegistry();
900
731
  // BTC App rejects account index >= 100 unless display=true. Cached per
901
732
  // adapter instance: first 100+ path asks the user once via UI request,
@@ -909,31 +740,10 @@ var _LedgerAdapter = class _LedgerAdapter {
909
740
  this._deviceAuthenticityQueueTail = Promise.resolve();
910
741
  // Shared across concurrent callers — only `cancel()` aborts.
911
742
  this._doConnectAbortController = null;
912
- this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
913
- this._connectorIdleWaiters = /* @__PURE__ */ new Set();
914
- this._resetPromise = null;
915
- this._stateGeneration = 0;
916
- this._connectorTeardownTail = Promise.resolve();
917
- this._pendingConnectorTeardowns = 0;
918
- this._activeOperationJobs = /* @__PURE__ */ new Set();
919
- this._pendingOperationDisconnects = /* @__PURE__ */ new Set();
920
743
  this._installProgressLastEmittedValue = -Infinity;
921
744
  this.allNetworkGetAddress = createAllNetworkGetAddress({
922
745
  callChain: this.callChain.bind(this),
923
- getChainFingerprint: async (connectId, chain, context) => {
924
- try {
925
- const fingerprint = await this._computeChainFingerprint(
926
- chain,
927
- (method, params) => this.connectorCall(connectId, method, params, void 0, void 0, void 0, context)
928
- );
929
- return success(fingerprint);
930
- } catch (error) {
931
- return this.errorToFailure(error);
932
- }
933
- },
934
- retainOperation: (operationId) => this._operations.retain(operationId),
935
- errorToFailure: (error) => this.errorToFailure(error),
936
- createCancelScope: (queueKey) => this._jobQueue.createCancelScope(queueKey)
746
+ getChainFingerprint: (connectId, chain) => this.getChainFingerprint(connectId, "", chain)
937
747
  });
938
748
  // ---------------------------------------------------------------------------
939
749
  // Private helpers
@@ -943,7 +753,7 @@ var _LedgerAdapter = class _LedgerAdapter {
943
753
  *
944
754
  * - If a session already exists for the given connectId, reuse it.
945
755
  * - If ANY session exists (Ledger IDs are ephemeral), reuse it.
946
- * - 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.
947
757
  */
948
758
  // Mutex for ensureConnected — prevents concurrent calls from establishing duplicate connections
949
759
  this._connectingPromise = null;
@@ -960,12 +770,6 @@ var _LedgerAdapter = class _LedgerAdapter {
960
770
  });
961
771
  };
962
772
  this.deviceDisconnectHandler = (data) => {
963
- const activeOperation = this._operations.findActiveByConnectionKey(data.connectId);
964
- if (activeOperation && this._activeOperationJobs.has(activeOperation.operationId)) {
965
- this._pendingOperationDisconnects.add(activeOperation.operationId);
966
- } else {
967
- this._operations.endByConnectionKey(data.connectId, "disconnect");
968
- }
969
773
  this._discoveredDevices.delete(data.connectId);
970
774
  this._sessions.delete(data.connectId);
971
775
  this.emitter.emit(DEVICE.DISCONNECT, {
@@ -974,8 +778,10 @@ var _LedgerAdapter = class _LedgerAdapter {
974
778
  });
975
779
  };
976
780
  // Forward connector `ui-event` to the public hw.emitter so consumers only
977
- // need to subscribe in one place. The AppInstallProgress variant re-keys
978
- // sessionId to connectId via `_sessions`, and drops if no mapping exists.
781
+ // need to subscribe in one place. For the AppInstallProgress variant we
782
+ // re-key sessionId → connectId via the live _sessions map; if no mapping
783
+ // exists (race during teardown) we drop. All other variants pass through
784
+ // unchanged.
979
785
  this.uiEventForwarder = (event) => {
980
786
  if (event.type === EConnectorInteraction.AppInstallProgress) {
981
787
  let connectId;
@@ -1020,34 +826,11 @@ var _LedgerAdapter = class _LedgerAdapter {
1020
826
  this._jobQueue = new DeviceJobQueue();
1021
827
  this.registerEventListeners();
1022
828
  }
1023
- _discardPendingBinding(owner, status = "cancelled") {
1024
- const pending = owner?.pendingBinding;
1025
- if (!owner || !pending) return;
1026
- owner.pendingBinding = void 0;
1027
- if (pending.requestId === this._bindingSelectionRequestId) this._finishBleBinding(status);
1028
- }
1029
- _finishBleBinding(status) {
1030
- const selectionRequestId = this._bindingSelectionRequestId;
1031
- this._bindingSelectionRequestId = void 0;
1032
- if (selectionRequestId)
1033
- this.emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
1034
- type: UI_REQUEST.DEVICE_BINDING_STATUS,
1035
- payload: { selectionRequestId, status }
1036
- });
1037
- }
1038
- _isBleConnection() {
1039
- return isLedgerBleConnectionType(this._activeConnectionType ?? this.connector.connectionType);
1040
- }
1041
829
  // Transport
1042
830
  get activeTransport() {
1043
- return this._isBleConnection() ? "ble" : "hid";
831
+ return isLedgerBleConnectionType(this.connector.connectionType) ? "ble" : "hid";
1044
832
  }
1045
833
  getAvailableTransports() {
1046
- if (this.connector.availableTransports) {
1047
- return this.connector.availableTransports.map(
1048
- (transport) => transport === "ble" ? "ble" : "hid"
1049
- );
1050
- }
1051
834
  return this.activeTransport ? [this.activeTransport] : [];
1052
835
  }
1053
836
  // Connector is bound at construction; switching requires a new adapter.
@@ -1064,42 +847,21 @@ var _LedgerAdapter = class _LedgerAdapter {
1064
847
  * The next operation will re-discover and re-connect automatically.
1065
848
  */
1066
849
  resetState() {
1067
- void this._resetStateAndDisconnectSessions();
1068
- }
1069
- _resetStateAndDisconnectSessions() {
1070
- if (this._resetPromise) return this._resetPromise;
1071
- const sessionIds = new Set(this._sessions.values());
1072
- this._stateGeneration += 1;
1073
- this._finishBleBinding("cancelled");
1074
- this._operations.endAll("runtime-reset");
1075
- this._doConnectAbortController?.abort();
1076
850
  this._discoveredDevices.clear();
1077
851
  this._sessions.clear();
1078
- this._sessionTransports.clear();
1079
- this._pendingOperationBindings.clear();
1080
- this._pendingPickBindings.clear();
1081
- this._selectedBleReconnectTargets.clear();
1082
852
  this._connectingPromise = null;
1083
- this._doConnectAbortController = null;
1084
853
  this._uiRegistry.reset();
1085
854
  this._jobQueue.clear();
1086
855
  this._btcHighIndexConfirmedThisSession = false;
1087
- const resetPromise = this._runConnectorTeardown(async () => {
1088
- for (const sessionId of sessionIds) {
1089
- await this.connector.disconnect(sessionId).catch(() => void 0);
1090
- }
1091
- });
1092
- this._resetPromise = resetPromise;
1093
- return resetPromise.finally(() => {
1094
- if (this._resetPromise === resetPromise) {
1095
- this._resetPromise = null;
1096
- }
1097
- });
1098
856
  }
1099
857
  async dispose() {
1100
- await this._resetStateAndDisconnectSessions();
858
+ this._uiRegistry.reset();
859
+ this._jobQueue.clear();
1101
860
  this.unregisterEventListeners();
1102
861
  this.connector.reset();
862
+ this._discoveredDevices.clear();
863
+ this._sessions.clear();
864
+ this._btcHighIndexConfirmedThisSession = false;
1103
865
  this.emitter.removeAllListeners();
1104
866
  }
1105
867
  uiResponse(response) {
@@ -1109,32 +871,17 @@ var _LedgerAdapter = class _LedgerAdapter {
1109
871
  // Device management
1110
872
  // ---------------------------------------------------------------------------
1111
873
  async searchDevices(options) {
1112
- return this._searchDevices(options);
1113
- }
1114
- async _searchDevices(options, signal) {
1115
874
  debugLog("[LedgerAdapter][REQ]", { method: "searchDevices", params: options });
1116
875
  try {
1117
876
  if (options?.resetSession) {
1118
- await this._resetStateAndDisconnectSessions();
1119
- } else {
1120
- await this._connectorTeardownTail;
1121
- }
1122
- await this._ensureDevicePermission(void 0, void 0, signal);
1123
- if (signal) _LedgerAdapter._throwIfAborted(signal);
1124
- const stateGeneration = this._stateGeneration;
1125
- const devices = await this.connector.searchDevices(
1126
- options?.transportType ? {
1127
- transportType: options.transportType,
1128
- waitForAll: options.waitForAllTransports
1129
- } : void 0
1130
- );
1131
- if (signal) _LedgerAdapter._throwIfAborted(signal);
1132
- if (stateGeneration !== this._stateGeneration) {
1133
- throw createHwkError({
1134
- code: HardwareErrorCode3.UserAborted,
1135
- message: "Ledger discovery was reset"
1136
- });
877
+ this._doConnectAbortController?.abort();
878
+ this._sessions.clear();
879
+ this._connectingPromise = null;
880
+ this._doConnectAbortController = null;
881
+ this._btcHighIndexConfirmedThisSession = false;
1137
882
  }
883
+ await this._ensureDevicePermission();
884
+ const devices = await this.connector.searchDevices();
1138
885
  this._discoveredDevices.clear();
1139
886
  for (const d of devices) {
1140
887
  if (d.connectId) {
@@ -1142,7 +889,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1142
889
  }
1143
890
  }
1144
891
  if (this._discoveredDevices.size === 0) {
1145
- await this._ensureDevicePermission(void 0, void 0, signal);
892
+ await this._ensureDevicePermission();
1146
893
  }
1147
894
  const result = Array.from(this._discoveredDevices.values());
1148
895
  debugLog("[LedgerAdapter][RES]", {
@@ -1161,280 +908,41 @@ var _LedgerAdapter = class _LedgerAdapter {
1161
908
  throw err;
1162
909
  }
1163
910
  }
1164
- async searchDeviceTargets(options) {
1165
- const devices = await this.searchDevices(options);
1166
- return devices.map((device) => ({
1167
- searchTargetId: device.connectId,
1168
- searchTargetReusePolicy: resolveSearchTargetReusePolicy(device),
1169
- vendor: "ledger",
1170
- connectionType: device.connectionType,
1171
- kind: "physical",
1172
- label: device.label,
1173
- model: device.model,
1174
- modelName: device.modelName,
1175
- serialNumber: device.serialNumber
1176
- }));
1177
- }
1178
- async listConnectionTargets(options) {
1179
- const targets = await this.searchDeviceTargets(options);
1180
- return targets.map(({ searchTargetId, ...target }) => ({
1181
- ...target,
1182
- targetId: searchTargetId
1183
- }));
1184
- }
1185
911
  // USB single-session invariant: evict all sessions, best-effort (see connectDevice).
1186
- async _evictAllSessions(preserveOperationId) {
1187
- this._operations.endAll("explicit", preserveOperationId);
912
+ async _evictAllSessions() {
1188
913
  if (this._sessions.size === 0) return;
1189
914
  const stale = [...this._sessions.values()];
1190
915
  this._sessions.clear();
1191
- for (const sid of stale) this._sessionTransports.delete(sid);
1192
- await this._runConnectorTeardown(async () => {
1193
- for (const sid of stale) {
1194
- try {
1195
- await this.connector.disconnect(sid);
1196
- } catch {
1197
- }
916
+ for (const sid of stale) {
917
+ try {
918
+ await this.connector.disconnect(sid);
919
+ } catch {
1198
920
  }
1199
- });
921
+ }
1200
922
  }
1201
923
  static _createDeviceBusyError(method) {
1202
924
  return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
1203
925
  code: HardwareErrorCode3.DeviceBusy
1204
926
  });
1205
927
  }
1206
- async connectDevice(searchTargetId) {
1207
- try {
1208
- return await this._jobQueue.enqueue(
1209
- searchTargetId || "__ledger_connect__",
1210
- async (signal) => {
1211
- const connected = await this._connectTarget(searchTargetId, void 0, signal);
1212
- if (!connected.success) return connected;
1213
- return this._createOperation(searchTargetId, connected.payload);
1214
- },
1215
- {
1216
- label: "connectDevice",
1217
- rejectIfBusy: true,
1218
- busyError: _LedgerAdapter._createDeviceBusyError("connectDevice")
1219
- }
1220
- );
1221
- } catch (error) {
1222
- return this.errorToFailure(error);
1223
- }
1224
- }
1225
- /** Binds the picked BLE device; with a recorded wallet identity it must match first. */
1226
- async bindBleDevice(params) {
1227
- const identity = params.identity?.vendor === "ledger" ? params.identity : void 0;
1228
- try {
1229
- return await this._jobQueue.enqueue(
1230
- "__ledger_bind_ble__",
1231
- async (signal) => {
1232
- if (!this.getAvailableTransports().includes("ble")) {
1233
- throw createHwkError({
1234
- code: HardwareErrorCode3.TransportNotAvailable,
1235
- message: "Ledger Bluetooth transport is not available"
1236
- });
1237
- }
1238
- this._activeConnectionType = "ble";
1239
- await this._ensureDevicePermission(void 0, void 0, signal);
1240
- const attempt = {
1241
- extra: params.extra,
1242
- bindingReason: "manual-rebind",
1243
- deferBindingSave: Boolean(identity)
1244
- };
1245
- try {
1246
- const connectId = await this._connectFirstOrSelect(
1247
- [],
1248
- void 0,
1249
- true,
1250
- void 0,
1251
- attempt,
1252
- signal
1253
- );
1254
- const pending = attempt.pendingBinding;
1255
- if (identity && pending) {
1256
- attempt.pendingBinding = void 0;
1257
- const sessionId = this._sessions.get(connectId);
1258
- if (!sessionId) {
1259
- throw createHwkError({
1260
- code: HardwareErrorCode3.DeviceDisconnected,
1261
- message: "Selected Ledger connection ended"
1262
- });
1263
- }
1264
- const fingerprint = await this._computeChainFingerprint(
1265
- identity.chain,
1266
- (method, callParams) => this._runConnectorCall(
1267
- connectId,
1268
- method,
1269
- callParams,
1270
- signal,
1271
- void 0,
1272
- void 0,
1273
- { autoInstallApp: true },
1274
- { connection: { connectId, sessionId } }
1275
- )
1276
- );
1277
- if (fingerprint !== identity.value) {
1278
- await this._dropSession(connectId, signal);
1279
- throw createHwkError({
1280
- code: HardwareErrorCode3.DeviceMismatch,
1281
- message: formatDeviceMismatchError(identity.value, fingerprint)
1282
- });
1283
- }
1284
- await this._saveSelectedBleBinding(
1285
- connectId,
1286
- pending.requestId,
1287
- attempt,
1288
- void 0,
1289
- signal,
1290
- identity
1291
- );
1292
- }
1293
- if (!attempt.bindingSaved) {
1294
- await this._dropSession(connectId, signal);
1295
- throw createHwkError({
1296
- code: HardwareErrorCode3.UnknownError,
1297
- message: "Bluetooth binding could not be saved",
1298
- origin: "host"
1299
- });
1300
- }
1301
- return success(connectId);
1302
- } catch (error) {
1303
- this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
1304
- throw error;
1305
- }
1306
- },
1307
- {
1308
- label: "bindBleDevice",
1309
- rejectIfBusy: true,
1310
- busyError: _LedgerAdapter._createDeviceBusyError("bindBleDevice")
1311
- }
1312
- );
1313
- } catch (error) {
1314
- return this.errorToFailure(error);
1315
- }
1316
- }
1317
- async acquireOperation(connectId, context) {
1318
- try {
1319
- return await this._jobQueue.enqueue(
1320
- connectId || "__ledger_acquire__",
1321
- async (signal) => {
1322
- const transport = this._isBleConnection() ? "ble" : "usb";
1323
- const hint = context.knownConnections?.find(
1324
- (connection) => connection.transport === transport
1325
- );
1326
- const target = hint && hint.transport !== "qr" ? hint.connectId : connectId;
1327
- await this._ensureDevicePermission(target, void 0, signal);
1328
- const attempt = {
1329
- ...context,
1330
- extra: context.extra ? { ...context.extra } : void 0,
1331
- // The operation's calls decide whether the pick is the right wallet.
1332
- deferBindingSave: true
1333
- };
1334
- try {
1335
- const resolvedConnectId = await this.ensureConnected(
1336
- target,
1337
- signal,
1338
- true,
1339
- void 0,
1340
- attempt
1341
- );
1342
- _LedgerAdapter._throwIfAborted(signal);
1343
- const result = this._createOperation(connectId, resolvedConnectId);
1344
- if (result.success && attempt.pendingBinding) {
1345
- this._pendingOperationBindings.set(result.payload, attempt);
1346
- }
1347
- return result;
1348
- } catch (error) {
1349
- this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
1350
- throw error;
1351
- }
1352
- },
1353
- {
1354
- label: "acquireOperation",
1355
- rejectIfBusy: true,
1356
- busyError: _LedgerAdapter._createDeviceBusyError("acquireOperation")
1357
- }
1358
- );
1359
- } catch (error) {
1360
- return this.errorToFailure(error);
1361
- }
1362
- }
1363
- _createOperation(searchTargetId, resolvedConnectId) {
1364
- this._operations.endByConnectionKey(resolvedConnectId, "explicit");
1365
- const sessionId = this._sessions.get(resolvedConnectId);
1366
- if (sessionId) this._operations.endByConnectionKey(sessionId, "explicit");
1367
- const connectionType = sessionId && this._sessionTransports.get(sessionId) || (this._isBleConnection() ? "ble" : "usb");
1368
- const device = this._discoveredDevices.get(resolvedConnectId) ?? {
1369
- vendor: "ledger",
1370
- model: "unknown",
1371
- firmwareVersion: "",
1372
- deviceId: "",
1373
- connectId: resolvedConnectId,
1374
- connectionType
1375
- };
1376
- const operation = this._operations.create({
1377
- searchTargetId,
1378
- connectId: resolvedConnectId,
1379
- device,
1380
- connectionType,
1381
- connectionKeys: [this._sessions.get(resolvedConnectId) ?? ""]
1382
- });
1383
- return success(operation.operationId);
1384
- }
1385
- async _connectTarget(connectId, preserveOperationId, signal) {
928
+ async connectDevice(connectId) {
1386
929
  debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
1387
930
  try {
1388
- this._assertConnectorReady("connectDevice");
1389
- const discoveredType = this._discoveredDevices.get(connectId)?.connectionType;
1390
- if (discoveredType === "usb" || discoveredType === "ble") {
1391
- this._activeConnectionType = discoveredType;
1392
- }
1393
- if (this._isBleConnection() && !connectId) {
931
+ if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
1394
932
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
1395
933
  code: HardwareErrorCode3.DeviceNotFound
1396
934
  });
1397
935
  }
1398
- await this._ensureDevicePermission(connectId, void 0, signal);
1399
- if (signal) _LedgerAdapter._throwIfAborted(signal);
1400
- const isMultiTransport = (this.connector.availableTransports?.length ?? 0) > 1;
1401
- if (!isMultiTransport && this._isBleConnection()) {
1402
- const previousSessionId = this._sessions.get(connectId);
1403
- this._operations.endByConnectionKey(connectId, "explicit", preserveOperationId);
1404
- if (previousSessionId) {
1405
- this._operations.endByConnectionKey(previousSessionId, "explicit", preserveOperationId);
1406
- this._sessions.delete(connectId);
1407
- await this.connector.disconnect(previousSessionId).catch(() => void 0);
1408
- }
1409
- } else {
1410
- await this._evictAllSessions(preserveOperationId);
1411
- }
1412
- if (signal) _LedgerAdapter._throwIfAborted(signal);
1413
- const stateGeneration = this._stateGeneration;
1414
- const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
1415
- let session;
1416
- this._connectingConnectId = connectId;
1417
- try {
1418
- session = this.connector.availableTransports?.length ? await this.connector.connect(connectId, {
1419
- transportType: this._activeConnectionType ?? "usb"
1420
- }) : await this.connector.connect(connectId);
1421
- if (signal?.aborted || stateGeneration !== this._stateGeneration) {
1422
- await this.connector.disconnect(session.sessionId).catch(() => void 0);
1423
- throw Object.assign(new Error("Ledger connection aborted"), {
1424
- code: HardwareErrorCode3.UserAborted
1425
- });
1426
- }
1427
- } finally {
1428
- if (this._connectingConnectId === connectId) this._connectingConnectId = void 0;
1429
- releaseOperation();
936
+ if (!isLedgerBleConnectionType(this.connector.connectionType)) {
937
+ await this._evictAllSessions();
1430
938
  }
1431
- const resolvedConnectId = session.deviceInfo?.connectId || connectId;
1432
- this._sessions.set(resolvedConnectId, session.sessionId);
1433
- this._sessionTransports.set(session.sessionId, this._isBleConnection() ? "ble" : "usb");
939
+ await this._ensureDevicePermission(connectId);
940
+ const session = await this.connector.connect(connectId);
941
+ this._sessions.set(connectId, session.sessionId);
1434
942
  if (session.deviceInfo) {
1435
- this._discoveredDevices.set(resolvedConnectId, session.deviceInfo);
943
+ this._discoveredDevices.set(connectId, session.deviceInfo);
1436
944
  }
1437
- const result = success(resolvedConnectId);
945
+ const result = success(connectId);
1438
946
  debugLog("[LedgerAdapter][RES]", { method: "connectDevice", success: true, payload: result });
1439
947
  return result;
1440
948
  } catch (err) {
@@ -1447,60 +955,30 @@ var _LedgerAdapter = class _LedgerAdapter {
1447
955
  return failureResult;
1448
956
  }
1449
957
  }
1450
- async releaseOperation(operationId) {
1451
- const operation = this._operations.find(operationId);
1452
- if (!operation) {
1453
- this._operations.resolve(operationId);
1454
- return;
1455
- }
1456
- const endedOperation = this._operations.end(operationId, "explicit");
1457
- if (!endedOperation) return;
1458
- const { connectId } = operation;
958
+ async disconnectDevice(connectId) {
1459
959
  debugLog("[LedgerAdapter][REQ]", {
1460
- method: "releaseOperation",
960
+ method: "disconnectDevice",
1461
961
  connectId,
1462
962
  params: { connectId }
1463
963
  });
1464
964
  try {
1465
- await this._releaseOperationConnection(endedOperation);
1466
- 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 });
1467
971
  } catch (err) {
1468
972
  const e = err;
1469
973
  debugLog("[LedgerAdapter][RES]", {
1470
- method: "releaseOperation",
974
+ method: "disconnectDevice",
1471
975
  success: false,
1472
976
  error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
1473
977
  });
1474
978
  throw err;
1475
979
  }
1476
980
  }
1477
- async _releaseOperationConnection(operation) {
1478
- const sessionIds = /* @__PURE__ */ new Set();
1479
- for (const [connectId, sessionId] of this._sessions) {
1480
- if (connectId === operation.connectId || operation.connectionKeys.includes(connectId) || operation.connectionKeys.includes(sessionId)) {
1481
- this._sessions.delete(connectId);
1482
- sessionIds.add(sessionId);
1483
- }
1484
- }
1485
- for (const sessionId of sessionIds) {
1486
- await this._runConnectorTeardown(
1487
- () => this.connector.disconnect(sessionId).catch(() => void 0)
1488
- );
1489
- }
1490
- }
1491
- async _releaseLostOperationConnection(operationId) {
1492
- const endedOperation = this._operations.end(operationId, "disconnect");
1493
- if (!endedOperation) return;
1494
- this._discoveredDevices.delete(endedOperation.connectId);
1495
- await this._releaseOperationConnection(endedOperation);
1496
- }
1497
- async getDeviceInfo(connectIdOrOperationId, deviceId) {
1498
- let connectId;
1499
- try {
1500
- connectId = isHardwareOperationId(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
1501
- } catch (error) {
1502
- return this.errorToFailure(error);
1503
- }
981
+ async getDeviceInfo(connectId, deviceId) {
1504
982
  debugLog("[LedgerAdapter][REQ]", {
1505
983
  method: "getDeviceInfo",
1506
984
  connectId,
@@ -1535,9 +1013,12 @@ var _LedgerAdapter = class _LedgerAdapter {
1535
1013
  success: false,
1536
1014
  error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
1537
1015
  });
1538
- return this.errorToFailure(err);
1016
+ throw err;
1539
1017
  }
1540
1018
  }
1019
+ getSupportedChains() {
1020
+ return ["evm", "btc", "sol", "tron"];
1021
+ }
1541
1022
  // ---------------------------------------------------------------------------
1542
1023
  // Chain call helper
1543
1024
  // ---------------------------------------------------------------------------
@@ -1572,23 +1053,13 @@ var _LedgerAdapter = class _LedgerAdapter {
1572
1053
  if (params && typeof params === "object") {
1573
1054
  const {
1574
1055
  autoInstallApp,
1575
- operationId,
1576
1056
  passphraseState: _passphraseState,
1577
1057
  useEmptyPassphrase: _useEmptyPassphrase,
1578
- knownConnections,
1579
- extra,
1580
- allowDeviceSelection,
1581
- supportedTransports,
1582
1058
  ...rest
1583
1059
  } = params;
1584
1060
  return {
1585
1061
  commonParams: {
1586
- autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0,
1587
- operationId: typeof operationId === "string" ? operationId : void 0,
1588
- knownConnections,
1589
- extra,
1590
- allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0,
1591
- supportedTransports
1062
+ autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
1592
1063
  },
1593
1064
  rest
1594
1065
  };
@@ -1764,28 +1235,9 @@ var _LedgerAdapter = class _LedgerAdapter {
1764
1235
  );
1765
1236
  }
1766
1237
  // ---------------------------------------------------------------------------
1767
- // Zcash chain methods (viewing key + shielded address reads; Zcash app >= 3.8.0)
1768
- // ---------------------------------------------------------------------------
1769
- zcashGetFullViewingKey(connectId, deviceId, params) {
1770
- return this.callChainWithMergedParams(
1771
- connectId,
1772
- deviceId,
1773
- "zcash",
1774
- "zcashGetFullViewingKey",
1775
- params
1776
- );
1777
- }
1778
- zcashGetShieldedAddress(connectId, deviceId, params) {
1779
- return this.callChainWithMergedParams(
1780
- connectId,
1781
- deviceId,
1782
- "zcash",
1783
- "zcashGetShieldedAddress",
1784
- params
1785
- );
1786
- }
1787
- // ---------------------------------------------------------------------------
1788
- // App management: OS-level app install/list, no fingerprint or chain dispatch.
1238
+ // App management — OS-level Ledger app install / list. Bypasses fingerprint
1239
+ // and chain-handler dispatch; installApp progress is forwarded to the adapter
1240
+ // emitter via 'ui-event' AppInstallProgress events.
1789
1241
  // ---------------------------------------------------------------------------
1790
1242
  async installApp(connectId, appName) {
1791
1243
  try {
@@ -1871,7 +1323,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1871
1323
  );
1872
1324
  }
1873
1325
  await this.connector.configure({ ledgerGenuineCheckWebSocketUrl: relayUrl });
1874
- await this._resetStateAndDisconnectSessions();
1326
+ this.resetState();
1875
1327
  }
1876
1328
  const result = await this.connectorCall(connectId, "getDeviceGenuineCheck", {});
1877
1329
  if (!result.isGenuine) {
@@ -1897,10 +1349,10 @@ var _LedgerAdapter = class _LedgerAdapter {
1897
1349
  if (relayUrl) {
1898
1350
  try {
1899
1351
  await this.connector.configure?.({ ledgerGenuineCheckWebSocketUrl: void 0 });
1900
- await this._resetStateAndDisconnectSessions();
1352
+ this.resetState();
1901
1353
  } catch {
1902
1354
  this.connector.reset();
1903
- await this._resetStateAndDisconnectSessions();
1355
+ this.resetState();
1904
1356
  }
1905
1357
  }
1906
1358
  }
@@ -1912,20 +1364,6 @@ var _LedgerAdapter = class _LedgerAdapter {
1912
1364
  this.emitter.off(event, listener);
1913
1365
  }
1914
1366
  cancel(connectId) {
1915
- const namedOperationIsLive = (id) => {
1916
- try {
1917
- this._operations.resolve(id);
1918
- return true;
1919
- } catch {
1920
- return false;
1921
- }
1922
- };
1923
- if (isHardwareOperationId(connectId) && !namedOperationIsLive(connectId)) {
1924
- debugLog("[LedgerAdapter] cancel target already ended; nothing to cancel", {
1925
- connectId
1926
- });
1927
- return;
1928
- }
1929
1367
  const userAbortReason = Object.assign(new Error("User aborted operation"), {
1930
1368
  code: HardwareErrorCode3.UserAborted,
1931
1369
  _tag: ERROR_TAG.UserAborted
@@ -1936,48 +1374,14 @@ var _LedgerAdapter = class _LedgerAdapter {
1936
1374
  this._lastCancelReason = void 0;
1937
1375
  }
1938
1376
  }, 2e3);
1939
- const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
1940
- let operationId;
1941
- if (isHardwareOperationId(connectId)) {
1942
- operationId = connectId;
1943
- } else if (!connectId && isHardwareOperationId(activeJobId)) {
1944
- operationId = activeJobId;
1945
- }
1946
- let resolvedConnectId = connectId;
1947
- if (operationId) {
1948
- try {
1949
- resolvedConnectId = this._operations.resolve(operationId).connectId;
1950
- } catch {
1951
- resolvedConnectId = void 0;
1952
- }
1953
- }
1954
- const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
1955
- const pendingOperationId = operationId ?? interactionForPhysicalId?.operationId;
1956
- if (!connectId) {
1957
- this._uiRegistry.cancel();
1958
- } else if (pendingOperationId) {
1959
- this._uiRegistry.cancel(void 0, void 0, pendingOperationId);
1960
- }
1961
- this._finishBleBinding("cancelled");
1962
- if (!connectId) this._pendingOperationBindings.clear();
1963
- else if (pendingOperationId) this._pendingOperationBindings.delete(pendingOperationId);
1964
- const queueKeys = /* @__PURE__ */ new Set();
1965
- if (connectId) queueKeys.add(ledgerQueueKey({ connectId }));
1966
- if (pendingOperationId) queueKeys.add(ledgerQueueKey({ operationId: pendingOperationId }));
1967
- if (queueKeys.size) {
1968
- let cancelledAnyJob = false;
1969
- for (const key of queueKeys) {
1970
- cancelledAnyJob = this._jobQueue.cancelActiveAndPending(key, userAbortReason) || cancelledAnyJob;
1971
- }
1972
- debugLog("[LedgerAdapter] cancel routed to queue keys", {
1973
- queueKeys: [...queueKeys],
1974
- cancelledAnyJob
1975
- });
1377
+ this._uiRegistry.cancel();
1378
+ if (connectId) {
1379
+ this._jobQueue.cancelActiveAndPending(connectId, userAbortReason);
1976
1380
  } else {
1977
1381
  this._jobQueue.cancelActiveAndPending(void 0, userAbortReason);
1978
1382
  }
1979
- if (resolvedConnectId) {
1980
- const sessionId = this._sessions.get(resolvedConnectId) ?? resolvedConnectId;
1383
+ if (connectId) {
1384
+ const sessionId = this._sessions.get(connectId) ?? connectId;
1981
1385
  void this.connector.cancel(sessionId);
1982
1386
  } else {
1983
1387
  for (const sid of this._sessions.values()) void this.connector.cancel(sid);
@@ -1985,86 +1389,45 @@ var _LedgerAdapter = class _LedgerAdapter {
1985
1389
  if (this._connectingPromise) {
1986
1390
  this._doConnectAbortController?.abort(userAbortReason);
1987
1391
  }
1988
- const connectingConnectId = this._connectingConnectId;
1989
- if (connectingConnectId) void this.connector.cancel(connectingConnectId);
1990
1392
  }
1991
1393
  // ---------------------------------------------------------------------------
1992
1394
  // Chain fingerprint
1993
1395
  // ---------------------------------------------------------------------------
1994
- /** A non-empty deviceId is an expected chain fingerprint, not a transport identifier. */
1995
1396
  async getChainFingerprint(connectId, deviceId, chain) {
1996
1397
  try {
1997
1398
  const fingerprint = await this._computeChainFingerprint(
1998
1399
  chain,
1999
1400
  (method, params) => this.connectorCall(connectId, method, params, void 0, deviceId)
2000
1401
  );
2001
- if (deviceId) {
2002
- if (fingerprint !== deviceId) {
2003
- return failure2(
2004
- HardwareErrorCode3.DeviceMismatch,
2005
- formatDeviceMismatchError(deviceId, fingerprint)
2006
- );
2007
- }
2008
- }
2009
1402
  return success(fingerprint);
2010
1403
  } catch (err) {
2011
1404
  debugError("[LedgerAdapter] getChainFingerprint error:", chain, err);
2012
1405
  return this.errorToFailure(err);
2013
1406
  }
2014
1407
  }
2015
- /** Saves a picked BLE endpoint; `identity` is set when its wallet was just checked. */
2016
- async _saveSelectedBleBinding(connectId, selectionRequestId, context, operationId, signal, identity) {
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 };
2017
1414
  try {
2018
- const outcome = await requestSaveDeviceBinding(
2019
- this.emitter,
2020
- this._uiRegistry,
2021
- {
2022
- selectionRequestId,
2023
- connection: { transport: "ble", connectId },
2024
- ...identity ? { identity } : {},
2025
- extra: context?.extra,
2026
- operationId
2027
- },
2028
- signal
1415
+ const fingerprint = await this._computeChainFingerprint(
1416
+ chain,
1417
+ (method, params) => this._callConnector(sessionId, method, params)
2029
1418
  );
2030
- if (context) context.bindingSaved = outcome.saved;
2031
- if (!outcome.saved) {
2032
- debugLog("[LedgerAdapter] BLE binding not persisted by host", {
2033
- connectId,
2034
- reason: outcome.reason
2035
- });
1419
+ if (fingerprint === deviceId) {
1420
+ return { success: true };
2036
1421
  }
2037
- } catch (error) {
2038
- await this._dropSession(connectId, signal);
2039
- throw error;
2040
- } finally {
2041
- if (this._bindingSelectionRequestId === selectionRequestId) {
2042
- this._bindingSelectionRequestId = void 0;
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 };
2043
1427
  }
1428
+ throw err;
2044
1429
  }
2045
1430
  }
2046
- async _dropSession(connectId, signal) {
2047
- const sessionId = this._sessions.get(connectId);
2048
- this._sessions.delete(connectId);
2049
- if (!sessionId) return;
2050
- this._sessionTransports.delete(sessionId);
2051
- const teardown = this._runConnectorTeardown(() => this.connector.disconnect(sessionId)).catch(
2052
- () => void 0
2053
- );
2054
- if (!signal.aborted) await teardown;
2055
- }
2056
- /** Verify on the acquired session without re-entering the job queue. */
2057
- async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
2058
- if (!deviceId) return { success: true };
2059
- const fingerprint = await this._computeChainFingerprint(
2060
- chain,
2061
- (method, params) => this._callConnector(sessionId, method, params)
2062
- );
2063
- if (fingerprint === deviceId) {
2064
- return { success: true };
2065
- }
2066
- return { success: false, expected: deviceId, actual: fingerprint };
2067
- }
2068
1431
  /**
2069
1432
  * Compute the chain fingerprint via a caller-supplied call strategy.
2070
1433
  *
@@ -2092,23 +1455,11 @@ var _LedgerAdapter = class _LedgerAdapter {
2092
1455
  address = (await callMethod("solGetAddress", { path, showOnDevice: false })).address;
2093
1456
  } else if (chain === "tron") {
2094
1457
  address = (await callMethod("tronGetAddress", { path, showOnDevice: false })).address;
2095
- } else if (chain === "zcash") {
2096
- address = (await callMethod("zcashGetShieldedAddress", { path, showOnDevice: false })).address;
2097
1458
  } else {
2098
1459
  throw new Error(`Unsupported chain for fingerprint: ${chain}`);
2099
1460
  }
2100
1461
  return deriveDeviceFingerprint(address);
2101
1462
  }
2102
- /**
2103
- * Operation owning the running job, so a mid-call UI request can name it.
2104
- * Undefined at cold start when no operation owns the job.
2105
- */
2106
- _activeOperationId() {
2107
- const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
2108
- if (!activeJobId) return void 0;
2109
- if (isHardwareOperationId(activeJobId)) return activeJobId;
2110
- return this._operations.findActiveByConnectionKey(activeJobId)?.operationId;
2111
- }
2112
1463
  // Ledger WebUSB won't expose a locked device, so we can't auto-detect unlock.
2113
1464
  // The user must press Confirm after unlocking, which triggers a search retry.
2114
1465
  // If `signal` is provided, an abort cancels the pending UI request so the
@@ -2118,18 +1469,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2118
1469
  if (signal?.aborted) {
2119
1470
  _LedgerAdapter._throwIfAborted(signal);
2120
1471
  }
2121
- const operationId = this._activeOperationId();
2122
1472
  const waitPromise = this._uiRegistry.wait(
2123
- UI_REQUEST.REQUEST_DEVICE_CONNECT,
2124
- { operationId }
1473
+ UI_REQUEST.REQUEST_DEVICE_CONNECT
2125
1474
  );
2126
1475
  this.emitter.emit(UI_REQUEST.REQUEST_DEVICE_CONNECT, {
2127
1476
  type: UI_REQUEST.REQUEST_DEVICE_CONNECT,
2128
1477
  payload: {
2129
1478
  vendor: "ledger",
2130
1479
  reason: "device-not-found",
2131
- message: "Please connect and unlock your Ledger device",
2132
- operationId
1480
+ message: "Please connect and unlock your Ledger device"
2133
1481
  }
2134
1482
  });
2135
1483
  let payload;
@@ -2190,18 +1538,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2190
1538
  return { ...params, showOnDevice: true };
2191
1539
  }
2192
1540
  async _waitForBtcHighIndexConfirm(path, accountIndex) {
2193
- const operationId = this._activeOperationId();
2194
1541
  const waitPromise = this._uiRegistry.wait(
2195
- UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
2196
- { operationId }
1542
+ UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM
2197
1543
  );
2198
1544
  this.emitter.emit(UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM, {
2199
1545
  type: UI_REQUEST.REQUEST_BTC_HIGH_INDEX_CONFIRM,
2200
1546
  payload: {
2201
1547
  vendor: "ledger",
2202
1548
  path,
2203
- accountIndex,
2204
- operationId
1549
+ accountIndex
2205
1550
  }
2206
1551
  });
2207
1552
  try {
@@ -2218,14 +1563,12 @@ var _LedgerAdapter = class _LedgerAdapter {
2218
1563
  // Ask the user whether to install a missing app (autoInstallApp flow).
2219
1564
  // Same register-then-emit ordering as the BTC high-index gate.
2220
1565
  async _waitForInstallAppConfirm(appName) {
2221
- const operationId = this._activeOperationId();
2222
1566
  const waitPromise = this._uiRegistry.wait(
2223
- UI_REQUEST.REQUEST_INSTALL_APP,
2224
- { operationId }
1567
+ UI_REQUEST.REQUEST_INSTALL_APP
2225
1568
  );
2226
1569
  this.emitter.emit(UI_REQUEST.REQUEST_INSTALL_APP, {
2227
1570
  type: UI_REQUEST.REQUEST_INSTALL_APP,
2228
- payload: { vendor: "ledger", appName, operationId }
1571
+ payload: { vendor: "ledger", appName }
2229
1572
  });
2230
1573
  try {
2231
1574
  const payload = await waitPromise;
@@ -2241,17 +1584,16 @@ var _LedgerAdapter = class _LedgerAdapter {
2241
1584
  // Layer 1 entry. Caller signal only races the outer awaiter; the shared
2242
1585
  // `_doConnect` runs under its own internal controller so caller A's cancel
2243
1586
  // doesn't kill caller B's await.
2244
- async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false, preserveOperationId, context) {
1587
+ async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false) {
2245
1588
  if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
2246
- this._assertConnectorReady("connectDevice");
2247
- if (this._isBleConnection() && !connectId && !allowUsbEphemeralFallback) {
1589
+ if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
2248
1590
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
2249
1591
  code: HardwareErrorCode3.DeviceNotFound
2250
1592
  });
2251
1593
  }
2252
1594
  if (connectId && this._sessions.has(connectId)) return connectId;
2253
1595
  if (!connectId && this._sessions.size > 0) {
2254
- if (!this._isBleConnection() && this._sessions.size > 1) {
1596
+ if (!isLedgerBleConnectionType(this.connector.connectionType) && this._sessions.size > 1) {
2255
1597
  throw Object.assign(
2256
1598
  new Error(
2257
1599
  "Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
@@ -2261,24 +1603,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2261
1603
  }
2262
1604
  return this._sessions.keys().next().value;
2263
1605
  }
2264
- if (!this._connectingPromise || this._doConnectAbortController?.signal.aborted) {
2265
- const controller = new AbortController();
2266
- this._doConnectAbortController = controller;
2267
- const innerSignal = controller.signal;
1606
+ if (!this._connectingPromise) {
1607
+ this._doConnectAbortController = new AbortController();
1608
+ const innerSignal = this._doConnectAbortController.signal;
2268
1609
  this._connectingPromise = (async () => {
2269
1610
  try {
2270
- return await this._doConnect(
2271
- innerSignal,
2272
- connectId,
2273
- allowUsbEphemeralFallback,
2274
- preserveOperationId,
2275
- context
2276
- );
1611
+ return await this._doConnect(innerSignal, connectId, allowUsbEphemeralFallback);
2277
1612
  } finally {
2278
- if (this._doConnectAbortController === controller) {
2279
- this._connectingPromise = null;
2280
- this._doConnectAbortController = null;
2281
- }
1613
+ this._connectingPromise = null;
1614
+ this._doConnectAbortController = null;
2282
1615
  }
2283
1616
  })();
2284
1617
  }
@@ -2287,57 +1620,22 @@ var _LedgerAdapter = class _LedgerAdapter {
2287
1620
  // Layer 1 main loop — the ONLY place in SDK that emits unlock dialog.
2288
1621
  // Bounded by MAX_DOCONNECT_CONFIRMS — after N Confirms with no progress,
2289
1622
  // throw DeviceNotFound so the user is kicked out of the loop.
2290
- async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false, preserveOperationId, context) {
2291
- _LedgerAdapter._throwIfAborted(internalSignal);
2292
- if (this.connector.availableTransports?.includes("usb") && this.connector.availableTransports.includes("ble")) {
2293
- this._activeConnectionType = "usb";
2294
- const usbDevices = await this._searchDevices({ transportType: "usb" }, internalSignal);
2295
- _LedgerAdapter._throwIfAborted(internalSignal);
2296
- if (usbDevices.length > 0) {
2297
- const knownUsb = context?.knownConnections?.find(
2298
- (connection) => connection.transport === "usb"
2299
- );
2300
- const usbTarget = knownUsb?.transport === "usb" ? knownUsb.connectId : usbDevices.find((device) => device.connectId === targetConnectId)?.connectId;
2301
- return this._connectFirstOrSelect(
2302
- usbDevices,
2303
- usbTarget,
2304
- allowUsbEphemeralFallback,
2305
- preserveOperationId,
2306
- context,
2307
- internalSignal
2308
- );
2309
- }
2310
- if (context?.supportedTransports && !context.supportedTransports.includes("ble")) {
2311
- throw createHwkError({
2312
- code: HardwareErrorCode3.DeviceNotFound,
2313
- message: "This Ledger model has no Bluetooth transport; connect it over USB"
2314
- });
2315
- }
2316
- this._activeConnectionType = "ble";
2317
- const knownBle = context?.knownConnections?.find(
2318
- (connection) => connection.transport === "ble"
2319
- );
2320
- if (knownBle?.transport === "ble") {
2321
- return this._connectDeviceOrThrow(knownBle.connectId, preserveOperationId, internalSignal);
2322
- }
2323
- if (targetConnectId && context?.knownConnections === void 0) {
2324
- throw createHwkError({
2325
- code: HardwareErrorCode3.DeviceNotFound,
2326
- message: "Ledger connection metadata is required before starting Bluetooth binding"
2327
- });
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
+ }
2328
1638
  }
2329
- const bleDevices = await this._searchDevices({ transportType: "ble" }, internalSignal);
2330
- return this._connectFirstOrSelect(
2331
- bleDevices,
2332
- void 0,
2333
- allowUsbEphemeralFallback,
2334
- preserveOperationId,
2335
- context,
2336
- internalSignal
2337
- );
2338
- }
2339
- if (this._isBleConnection() && targetConnectId) {
2340
- return this._connectDeviceOrThrow(targetConnectId, preserveOperationId, internalSignal);
2341
1639
  }
2342
1640
  let confirms = 0;
2343
1641
  while (!internalSignal.aborted) {
@@ -2345,28 +1643,22 @@ var _LedgerAdapter = class _LedgerAdapter {
2345
1643
  type: EConnectorInteraction.Searching,
2346
1644
  payload: { sessionId: "" }
2347
1645
  });
2348
- let devices = await this._searchDevices(void 0, internalSignal);
2349
- _LedgerAdapter._throwIfAborted(internalSignal);
1646
+ let devices = await this.searchDevices();
2350
1647
  if (devices.length === 0) {
2351
1648
  for (let i = 0; i < 3 && !internalSignal.aborted; i += 1) {
2352
1649
  await new Promise((resolve) => {
2353
1650
  setTimeout(resolve, DEVICE_CONNECT_RETRY_DELAY_MS);
2354
1651
  });
2355
- _LedgerAdapter._throwIfAborted(internalSignal);
2356
- devices = await this._searchDevices(void 0, internalSignal);
2357
- _LedgerAdapter._throwIfAborted(internalSignal);
1652
+ devices = await this.searchDevices();
2358
1653
  if (devices.length > 0) break;
2359
1654
  }
2360
1655
  }
2361
- if (devices.length > 0 || this._isBleConnection() && allowUsbEphemeralFallback) {
1656
+ if (devices.length > 0) {
2362
1657
  try {
2363
1658
  return await this._connectFirstOrSelect(
2364
1659
  devices,
2365
1660
  targetConnectId,
2366
- allowUsbEphemeralFallback,
2367
- preserveOperationId,
2368
- context,
2369
- internalSignal
1661
+ allowUsbEphemeralFallback
2370
1662
  );
2371
1663
  } catch (err) {
2372
1664
  if (!isDeviceLockedError(err) && !isDeviceNotAdvertisingError(err) && !isDeviceDisconnectedError(err)) {
@@ -2395,147 +1687,45 @@ var _LedgerAdapter = class _LedgerAdapter {
2395
1687
  _LedgerAdapter._throwIfAborted(internalSignal);
2396
1688
  throw new Error("_doConnect aborted");
2397
1689
  }
2398
- async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback, preserveOperationId, context, signal) {
2399
- _LedgerAdapter._throwIfAborted(signal);
1690
+ async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback = false) {
2400
1691
  if (targetConnectId) {
2401
1692
  const target = devices.find(
2402
1693
  (d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
2403
1694
  );
2404
1695
  if (target) {
2405
- return this._connectDeviceOrThrow(target.connectId, preserveOperationId, signal);
1696
+ return this._connectDeviceOrThrow(target.connectId);
2406
1697
  }
2407
- if (!this._isBleConnection() && devices.length === 1 && allowUsbEphemeralFallback) {
1698
+ if (!isLedgerBleConnectionType(this.connector.connectionType) && devices.length === 1 && allowUsbEphemeralFallback) {
2408
1699
  debugLog(
2409
1700
  `[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} for fingerprint-verified recovery`
2410
1701
  );
2411
- return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
1702
+ return this._connectDeviceOrThrow(devices[0].connectId);
2412
1703
  }
2413
- if (!this._isBleConnection() || !allowUsbEphemeralFallback) {
2414
- const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
2415
- code: HardwareErrorCode3.DeviceNotFound
2416
- });
2417
- if (this._isBleConnection()) {
2418
- err._tag = ERROR_TAG.DeviceNotAdvertising;
2419
- }
2420
- 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;
2421
1709
  }
1710
+ throw err;
2422
1711
  }
2423
- const requiresBleSelection = this._isBleConnection();
2424
- if (requiresBleSelection && !allowUsbEphemeralFallback) {
1712
+ if (isLedgerBleConnectionType(this.connector.connectionType)) {
2425
1713
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
2426
1714
  code: HardwareErrorCode3.DeviceNotFound
2427
1715
  });
2428
1716
  }
2429
- if (requiresBleSelection && context?.allowDeviceSelection !== false) {
2430
- const bindingSessionId = context?.bindingSessionId ?? this._uiRegistry.createRequestId();
2431
- if (context) context.bindingSessionId = bindingSessionId;
2432
- const allowUsbFallback = context?.bindingReason !== "manual-rebind" && Boolean(this.connector.availableTransports?.includes("usb"));
2433
- const knownUsb = context?.knownConnections?.find(
2434
- (connection) => connection.transport === "usb"
2435
- );
2436
- const usbConnectId = knownUsb?.transport === "usb" ? knownUsb.connectId : targetConnectId;
2437
- const { device, requestId } = await requestBleDeviceSelection({
2438
- emitter: this.emitter,
2439
- registry: this._uiRegistry,
2440
- signal,
2441
- allowUsbFallback,
2442
- scan: async () => {
2443
- if (allowUsbFallback) {
2444
- const usbDevices = await this._searchDevices({ transportType: "usb" }, signal);
2445
- const candidate = usbDevices.find(
2446
- (device2) => device2.connectionType === "usb" && device2.connectId === usbConnectId
2447
- ) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
2448
- if (candidate) return [candidate];
2449
- }
2450
- return this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal);
2451
- },
2452
- request: {
2453
- devices,
2454
- bindingSessionId,
2455
- context: {
2456
- kind: "bind-connection",
2457
- transport: "ble",
2458
- reason: context?.bindingReason ?? (targetConnectId ? "known-connection-unavailable" : "missing-binding")
2459
- },
2460
- extra: context?.extra,
2461
- operationId: preserveOperationId
2462
- }
2463
- });
2464
- if (device.connectionType === "usb") {
2465
- this._activeConnectionType = "usb";
2466
- if (context) context.selectedConnection = void 0;
2467
- this._bindingSelectionRequestId = requestId;
2468
- this._finishBleBinding("cancelled");
2469
- return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
2470
- }
2471
- this._bindingSelectionRequestId = requestId;
2472
- const connected = await this._connectDeviceOrThrow(
2473
- device.connectId,
2474
- preserveOperationId,
2475
- signal
2476
- );
2477
- if (context) context.selectedConnection = { connectId: connected, requestId };
2478
- if (context?.deferBindingSave) {
2479
- context.pendingBinding = { connectId: connected, requestId };
2480
- } else {
2481
- await this._saveSelectedBleBinding(
2482
- connected,
2483
- requestId,
2484
- context,
2485
- preserveOperationId,
2486
- signal
2487
- );
2488
- }
2489
- return connected;
2490
- }
2491
- if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
2492
- if (context?.allowDeviceSelection === false || !this.emitter.listenerCount(UI_REQUEST.REQUEST_SELECT_DEVICE)) {
2493
- throw createHwkError({
2494
- code: HardwareErrorCode3.DeviceNotFound,
2495
- message: "Select a Ledger device before continuing"
2496
- });
2497
- }
2498
- const requestId = this._uiRegistry.createRequestId();
2499
- const operationId = this._activeOperationId();
2500
- const waitPromise = this._uiRegistry.wait(
2501
- UI_REQUEST.REQUEST_SELECT_DEVICE,
2502
- { requestId, operationId }
2503
- );
2504
- this.emitter.emit(UI_REQUEST.REQUEST_SELECT_DEVICE, {
2505
- type: UI_REQUEST.REQUEST_SELECT_DEVICE,
2506
- payload: {
2507
- devices,
2508
- requestId,
2509
- operationId,
2510
- context: requiresBleSelection ? {
2511
- kind: "bind-connection",
2512
- transport: "ble",
2513
- reason: targetConnectId ? "known-connection-unavailable" : "missing-binding"
2514
- } : { kind: "select-device", transport: "usb", reason: "multiple-candidates" },
2515
- extra: context?.extra
2516
- }
2517
- });
2518
- const { sdkConnectId } = await this._abortable(signal, waitPromise);
2519
- _LedgerAdapter._throwIfAborted(signal);
2520
- const selected = devices.find((device) => device.connectId === sdkConnectId);
2521
- if (!selected) {
2522
- throw Object.assign(new Error("Selected Ledger is no longer available"), {
2523
- code: HardwareErrorCode3.DeviceNotFound
2524
- });
2525
- }
2526
- if (context && requiresBleSelection)
2527
- context.selectedConnection = { connectId: selected.connectId, requestId };
2528
- return this._connectDeviceOrThrow(selected.connectId, preserveOperationId, signal);
1717
+ if (devices.length > 1) {
1718
+ throw createMultipleUsbLedgerDevicesError();
2529
1719
  }
2530
1720
  if (devices.length !== 1) {
2531
1721
  throw Object.assign(new Error("Ledger device not found."), {
2532
1722
  code: HardwareErrorCode3.DeviceNotFound
2533
1723
  });
2534
1724
  }
2535
- return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
1725
+ return this._connectDeviceOrThrow(devices[0].connectId);
2536
1726
  }
2537
- async _connectDeviceOrThrow(chosenConnectId, preserveOperationId, signal) {
2538
- const result = await this._connectTarget(chosenConnectId, preserveOperationId, signal);
1727
+ async _connectDeviceOrThrow(chosenConnectId) {
1728
+ const result = await this.connectDevice(chosenConnectId);
2539
1729
  if (!result.success) {
2540
1730
  const payload = result.payload;
2541
1731
  const rethrow = Object.assign(new Error(payload.error), {
@@ -2546,7 +1736,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2546
1736
  }
2547
1737
  throw rethrow;
2548
1738
  }
2549
- return result.payload;
1739
+ return chosenConnectId;
2550
1740
  }
2551
1741
  /**
2552
1742
  * Call the connector with automatic session resolution and disconnect retry.
@@ -2576,139 +1766,33 @@ var _LedgerAdapter = class _LedgerAdapter {
2576
1766
  * lives in one place.
2577
1767
  */
2578
1768
  async _callConnector(sessionId, method, params, signal) {
2579
- this._assertConnectorReady(method);
2580
- if (signal?.aborted) throw this._abortReason(signal);
2581
- const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
2582
- let promise;
2583
- try {
2584
- promise = this.connector.call(sessionId, method, params).finally(releaseOperation);
2585
- } catch (error) {
2586
- releaseOperation();
2587
- throw error;
2588
- }
1769
+ const promise = this.connector.call(sessionId, method, params);
2589
1770
  const result = signal ? await this._abortable(signal, promise) : await promise;
2590
1771
  return this._unwrapConnectorResult(result);
2591
1772
  }
2592
- _assertConnectorReady(method) {
2593
- if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
2594
- throw _LedgerAdapter._createDeviceBusyError(method);
2595
- }
2596
- }
2597
- _runConnectorTeardown(task) {
2598
- const previous = this._connectorTeardownTail;
2599
- let releaseTail = () => void 0;
2600
- this._connectorTeardownTail = new Promise((resolve) => {
2601
- releaseTail = resolve;
2602
- });
2603
- this._pendingConnectorTeardowns += 1;
2604
- return (async () => {
2605
- try {
2606
- await previous;
2607
- await this._waitForConnectorOperationsToDrain();
2608
- await task();
2609
- } finally {
2610
- this._pendingConnectorTeardowns -= 1;
2611
- releaseTail();
2612
- }
2613
- })();
2614
- }
2615
- _waitForConnectorOperationsToDrain() {
2616
- if (this._unsettledConnectorOperations.size === 0) {
2617
- return Promise.resolve();
2618
- }
2619
- return new Promise((resolve) => {
2620
- this._connectorIdleWaiters.add(resolve);
2621
- });
2622
- }
2623
- _retainConnectorOperation(key) {
2624
- this._unsettledConnectorOperations.set(
2625
- key,
2626
- (this._unsettledConnectorOperations.get(key) ?? 0) + 1
2627
- );
2628
- let released = false;
2629
- return () => {
2630
- if (released) return;
2631
- released = true;
2632
- const remaining = (this._unsettledConnectorOperations.get(key) ?? 1) - 1;
2633
- if (remaining > 0) {
2634
- this._unsettledConnectorOperations.set(key, remaining);
2635
- } else {
2636
- this._unsettledConnectorOperations.delete(key);
2637
- }
2638
- if (this._unsettledConnectorOperations.size === 0) {
2639
- for (const resolve of this._connectorIdleWaiters) resolve();
2640
- this._connectorIdleWaiters.clear();
2641
- }
2642
- };
2643
- }
2644
1773
  async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
2645
- const positionalOperationId = isHardwareOperationId(connectId) ? connectId : void 0;
2646
- if (positionalOperationId && commonParams?.operationId && positionalOperationId !== commonParams.operationId) {
2647
- throw createHwkError({
2648
- code: HardwareErrorCode3.InvalidParams,
2649
- message: "Conflicting Ledger operation ids",
2650
- params: {
2651
- positionalOperationId,
2652
- commonOperationId: commonParams.operationId
2653
- }
2654
- });
2655
- }
2656
- const operationId = commonParams?.operationId ?? positionalOperationId;
2657
- const operation = operationId ? this._operations.resolve(operationId) : void 0;
2658
- const releaseOperationRetention = operationId ? this._operations.retain(operationId) : void 0;
2659
- const effectiveConnectId = operation?.connectId ?? connectId;
2660
- debugLog("[LedgerAdapter][REQ]", {
2661
- method,
2662
- connectId: effectiveConnectId || "(empty)",
2663
- params
2664
- });
2665
- const queueKey = ledgerQueueKey({ operationId, connectId: effectiveConnectId });
1774
+ debugLog("[LedgerAdapter][REQ]", { method, connectId: connectId || "(empty)", params });
1775
+ const queueKey = connectId || "__ledger_default__";
2666
1776
  try {
2667
1777
  const result = await this._jobQueue.enqueue(
2668
1778
  queueKey,
2669
- async (signal) => {
2670
- if (operationId) this._activeOperationJobs.add(operationId);
2671
- try {
2672
- return await this._runConnectorCall(
2673
- effectiveConnectId,
2674
- method,
2675
- params,
2676
- signal,
2677
- fingerprint,
2678
- permissionDeviceId,
2679
- commonParams,
2680
- installContext ?? {},
2681
- operationId
2682
- );
2683
- } catch (error) {
2684
- const status = signal.aborted ? "cancelled" : "failed";
2685
- for (const [pickedConnectId, owner] of this._pendingPickBindings) {
2686
- this._pendingPickBindings.delete(pickedConnectId);
2687
- this._discardPendingBinding(owner, status);
2688
- await this._dropSession(pickedConnectId, signal);
2689
- }
2690
- this._finishBleBinding(status);
2691
- throw error;
2692
- } finally {
2693
- if (operationId) {
2694
- this._activeOperationJobs.delete(operationId);
2695
- if (this._pendingOperationDisconnects.delete(operationId)) {
2696
- await this._releaseLostOperationConnection(operationId);
2697
- }
2698
- }
2699
- }
2700
- },
1779
+ async (signal) => this._runConnectorCall(
1780
+ connectId,
1781
+ method,
1782
+ params,
1783
+ signal,
1784
+ fingerprint,
1785
+ permissionDeviceId,
1786
+ commonParams,
1787
+ installContext
1788
+ ),
2701
1789
  {
2702
1790
  label: method,
2703
1791
  rejectIfBusy: true,
2704
1792
  busyError: _LedgerAdapter._createDeviceBusyError(method)
2705
1793
  }
2706
1794
  );
2707
- debugLog("[LedgerAdapter][RES]", {
2708
- method,
2709
- success: true,
2710
- payload: redactResultForLog(result)
2711
- });
1795
+ debugLog("[LedgerAdapter][RES]", { method, success: true, payload: result });
2712
1796
  return result;
2713
1797
  } catch (err) {
2714
1798
  const e = err;
@@ -2722,22 +1806,15 @@ var _LedgerAdapter = class _LedgerAdapter {
2722
1806
  }
2723
1807
  });
2724
1808
  throw err;
2725
- } finally {
2726
- releaseOperationRetention?.();
2727
1809
  }
2728
1810
  }
2729
- /** Hermes/RN polyfills don't always populate signal.reason; fall back. */
2730
- _abortReason(signal) {
2731
- return signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
2732
- }
2733
1811
  /**
2734
1812
  * Race a promise against an abort signal. On abort, rejects with
2735
1813
  * signal.reason → instance _lastCancelReason → generic Error('Aborted').
2736
1814
  */
2737
1815
  _abortable(signal, promise) {
2738
- const getAbortReason = () => this._abortReason(signal);
1816
+ const getAbortReason = () => signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
2739
1817
  if (signal.aborted) {
2740
- void promise.catch(() => void 0);
2741
1818
  return Promise.reject(getAbortReason());
2742
1819
  }
2743
1820
  return new Promise((resolve, reject) => {
@@ -2764,15 +1841,13 @@ var _LedgerAdapter = class _LedgerAdapter {
2764
1841
  }
2765
1842
  }
2766
1843
  /** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
2767
- 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) {
2768
1845
  _LedgerAdapter._throwIfAborted(signal);
2769
- if (!operationId) {
2770
- await this._ensureDevicePermission(
2771
- connectId,
2772
- permissionDeviceId ?? fingerprint?.deviceId,
2773
- signal
2774
- );
2775
- }
1846
+ await this._ensureDevicePermission(
1847
+ connectId,
1848
+ permissionDeviceId ?? fingerprint?.deviceId,
1849
+ signal
1850
+ );
2776
1851
  _LedgerAdapter._throwIfAborted(signal);
2777
1852
  let effectiveParams = params;
2778
1853
  if (method === "btcGetPublicKey") {
@@ -2786,60 +1861,19 @@ var _LedgerAdapter = class _LedgerAdapter {
2786
1861
  effectiveParams = gatedParams;
2787
1862
  }
2788
1863
  const allowUsbEphemeralFallback = !!fingerprint?.deviceId && !fingerprint.skipFingerprint;
2789
- let businessCallStarted = false;
2790
- const selectedBleTarget = connectId ? this._selectedBleReconnectTargets.get(connectId) : void 0;
2791
- const knownTransport = this._isBleConnection() ? "ble" : "usb";
2792
- const hintedConnectId = commonParams?.knownConnections?.find(
2793
- (connection) => connection.transport === knownTransport
2794
- );
2795
- const inputConnectId = hintedConnectId && hintedConnectId.transport !== "qr" ? hintedConnectId.connectId : connectId;
2796
- const preferredConnectId = fingerprint && !fingerprint.skipFingerprint && selectedBleTarget?.chain === fingerprint.chain && selectedBleTarget.fingerprint === fingerprint.deviceId ? selectedBleTarget.connectId : inputConnectId;
2797
- const checksWallet = Boolean(
2798
- fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId
2799
- );
2800
- const connectionAttempt = {
2801
- ...commonParams,
2802
- deferBindingSave: checksWallet
2803
- };
2804
- const bundleConnection = installContext?.connection;
2805
- if (bundleConnection && this._sessions.get(bundleConnection.connectId) !== bundleConnection.sessionId) {
2806
- throw createHwkError({
2807
- code: HardwareErrorCode3.DeviceDisconnected,
2808
- message: "Ledger all-network connection ended"
2809
- });
2810
- }
2811
- const resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
2812
- preferredConnectId,
1864
+ const resolvedConnectId = await this.ensureConnected(
1865
+ connectId,
2813
1866
  signal,
2814
- allowUsbEphemeralFallback,
2815
- void 0,
2816
- connectionAttempt
1867
+ allowUsbEphemeralFallback
2817
1868
  );
2818
1869
  const sessionId = this._sessions.get(resolvedConnectId);
2819
- if (sessionId && installContext && !installContext.connection) {
2820
- installContext.connection = { connectId: resolvedConnectId, sessionId };
2821
- }
2822
1870
  if (!sessionId) {
2823
- if (operationId) {
2824
- this._operations.end(operationId, "disconnect");
2825
- throw createHwkError({
2826
- code: HardwareErrorCode3.OperationEnded,
2827
- message: "Ledger operation connection is no longer active",
2828
- params: { operationId, reason: "disconnect" }
2829
- });
2830
- }
2831
1871
  throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
2832
1872
  _tag: ERROR_TAG.DeviceSessionNotFound
2833
1873
  });
2834
1874
  }
2835
1875
  try {
2836
- const isBleSession = this._sessionTransports.get(sessionId) === "ble";
2837
- if (!operationId && connectionAttempt.pendingBinding) {
2838
- this._pendingPickBindings.set(resolvedConnectId, connectionAttempt);
2839
- }
2840
- const bindingOwner = operationId ? this._pendingOperationBindings.get(operationId) : this._pendingPickBindings.get(resolvedConnectId);
2841
- const pendingBinding = bindingOwner?.pendingBinding?.connectId === resolvedConnectId ? bindingOwner.pendingBinding : void 0;
2842
- if (fingerprint && checksWallet) {
1876
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
2843
1877
  const fp = await this._abortable(
2844
1878
  signal,
2845
1879
  this._verifyDeviceFingerprintWithSession(
@@ -2849,65 +1883,14 @@ var _LedgerAdapter = class _LedgerAdapter {
2849
1883
  )
2850
1884
  );
2851
1885
  if (!fp.success) {
2852
- if (pendingBinding) {
2853
- this._discardPendingBinding(bindingOwner, "failed");
2854
- this._pendingPickBindings.delete(resolvedConnectId);
2855
- if (operationId) {
2856
- this._pendingOperationBindings.delete(operationId);
2857
- this._operations.end(operationId, "explicit");
2858
- await this._dropSession(resolvedConnectId, signal);
2859
- }
2860
- }
2861
1886
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2862
1887
  code: HardwareErrorCode3.DeviceMismatch
2863
1888
  });
2864
1889
  }
2865
1890
  }
2866
- if (pendingBinding && bindingOwner) {
2867
- bindingOwner.pendingBinding = void 0;
2868
- if (operationId) this._pendingOperationBindings.delete(operationId);
2869
- else this._pendingPickBindings.delete(resolvedConnectId);
2870
- await this._saveSelectedBleBinding(
2871
- pendingBinding.connectId,
2872
- pendingBinding.requestId,
2873
- bindingOwner,
2874
- operationId,
2875
- signal,
2876
- fingerprint && checksWallet ? {
2877
- vendor: "ledger",
2878
- type: "chainFingerprint",
2879
- chain: fingerprint.chain,
2880
- value: fingerprint.deviceId
2881
- } : void 0
2882
- );
2883
- }
2884
- if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId && !operationId && isBleSession && connectionAttempt.selectedConnection?.connectId === resolvedConnectId) {
2885
- this._selectedBleReconnectTargets.set(connectId, {
2886
- connectId: resolvedConnectId,
2887
- chain: fingerprint.chain,
2888
- fingerprint: fingerprint.deviceId
2889
- });
2890
- }
2891
- businessCallStarted = true;
2892
1891
  return await this._callConnector(sessionId, method, effectiveParams, signal);
2893
1892
  } catch (err) {
2894
1893
  if (signal.aborted) throw err;
2895
- if (isLostConnectionError(err)) {
2896
- this._discoveredDevices.delete(resolvedConnectId);
2897
- if (operationId) await this._releaseLostOperationConnection(operationId);
2898
- else {
2899
- this._sessions.delete(resolvedConnectId);
2900
- await this.connector.disconnect(sessionId).catch(() => void 0);
2901
- }
2902
- const ambiguous = businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(method);
2903
- const interactionParams = operationId ? { operationId, reason: "disconnect" } : void 0;
2904
- throw createHwkError({
2905
- code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(err).code,
2906
- message: "Ledger operation connection was lost; start a new operation",
2907
- params: ambiguous ? operationMayHaveCompletedParams(method, { operationId }) : interactionParams,
2908
- recovery: ambiguous ? { scope: "unknown" } : void 0
2909
- });
2910
- }
2911
1894
  const errObj = err;
2912
1895
  debugLog("[LedgerAdapter] connectorCall error:", method, {
2913
1896
  message: errObj?.message,
@@ -2919,61 +1902,6 @@ var _LedgerAdapter = class _LedgerAdapter {
2919
1902
  isNotAdvertising: isDeviceNotAdvertisingError(err),
2920
1903
  isStuckApp: isStuckAppStateError(err)
2921
1904
  });
2922
- const assertSessionCurrent = (message) => {
2923
- if (this._sessions.get(resolvedConnectId) === sessionId) return;
2924
- throw createHwkError({
2925
- code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
2926
- message
2927
- });
2928
- };
2929
- if ((isDeviceLockedError(err) || errObj?.code === HardwareErrorCode3.DeviceLocked) && lockedRetryBudget > 0) {
2930
- await this._waitForDeviceConnect(signal);
2931
- assertSessionCurrent("Ledger connection ended while waiting for unlock");
2932
- return this._runConnectorCall(
2933
- resolvedConnectId,
2934
- method,
2935
- effectiveParams,
2936
- signal,
2937
- fingerprint,
2938
- permissionDeviceId,
2939
- commonParams,
2940
- installContext,
2941
- operationId,
2942
- lockedRetryBudget - 1
2943
- );
2944
- }
2945
- if (businessCallStarted && isStuckAppStateError(err)) {
2946
- await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
2947
- assertSessionCurrent("Ledger connection ended during the app transition");
2948
- try {
2949
- return await this._callConnector(sessionId, method, effectiveParams, signal);
2950
- } catch (retryErr) {
2951
- if (isStuckAppStateError(retryErr)) throw err;
2952
- if (isLostConnectionError(retryErr)) {
2953
- if (operationId) await this._releaseLostOperationConnection(operationId);
2954
- else {
2955
- this._sessions.delete(resolvedConnectId);
2956
- this._discoveredDevices.delete(resolvedConnectId);
2957
- await this.connector.disconnect(sessionId).catch(() => void 0);
2958
- }
2959
- const ambiguous = !canReplayHardwareMethodAfterTransportFailure(method);
2960
- throw createHwkError({
2961
- code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(retryErr).code,
2962
- message: `Ledger ${method} may have completed before the connection was lost`,
2963
- params: ambiguous ? operationMayHaveCompletedParams(method, { operationId, reason: "disconnect" }) : { operationId, reason: "disconnect" },
2964
- recovery: ambiguous ? { scope: "unknown" } : void 0
2965
- });
2966
- }
2967
- throw retryErr;
2968
- }
2969
- }
2970
- if (!operationId && errObj?.code === HardwareErrorCode3.DeviceMismatch) {
2971
- this._sessions.delete(resolvedConnectId);
2972
- this._sessionTransports.delete(sessionId);
2973
- this._discoveredDevices.delete(resolvedConnectId);
2974
- await this.connector.disconnect(sessionId).catch(() => void 0);
2975
- throw err;
2976
- }
2977
1905
  const autoInstallApp = commonParams?.autoInstallApp ?? this._defaultAutoInstallApp;
2978
1906
  const isAppMissing = isAppNotInstalledError(err) || err?.code === HardwareErrorCode3.AppNotInstalled;
2979
1907
  if (autoInstallApp && isAppMissing) {
@@ -3029,14 +1957,157 @@ var _LedgerAdapter = class _LedgerAdapter {
3029
1957
  fingerprint,
3030
1958
  permissionDeviceId,
3031
1959
  commonParams,
3032
- installContext,
3033
- operationId
1960
+ installContext
3034
1961
  );
3035
1962
  }
3036
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
+ }
3037
2074
  throw err;
3038
2075
  }
3039
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
+ }
3040
2111
  _sleepAbortable(ms, signal) {
3041
2112
  return new Promise((resolve, reject) => {
3042
2113
  if (signal.aborted) {
@@ -3055,22 +2126,111 @@ var _LedgerAdapter = class _LedgerAdapter {
3055
2126
  });
3056
2127
  }
3057
2128
  /**
3058
- * Ensure OS-level Bluetooth/USB permission; times out after 60s if the host never
3059
- * replies. Without connectId the request is environment-level, else device-level.
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
+ }
2210
+ /**
2211
+ * Ensure OS-level device permission (Bluetooth / USB) before proceeding.
2212
+ *
2213
+ * Emits `REQUEST_DEVICE_PERMISSION` and awaits the consumer's
2214
+ * `RECEIVE_DEVICE_PERMISSION` reply (60s budget covers "probe → system
2215
+ * prompt → user tap" plus a generous margin). If the consumer never wires
2216
+ * a handler or never replies, the wait times out and the operation fails
2217
+ * fast so scanners/callers don't hang silently.
2218
+ *
2219
+ * - No connectId (searchDevices): environment-level permission
2220
+ * - With connectId (business methods): device-level permission
3060
2221
  */
3061
2222
  async _ensureDevicePermission(connectId, deviceId, signal) {
3062
2223
  if (signal?.aborted) {
3063
2224
  _LedgerAdapter._throwIfAborted(signal);
3064
2225
  }
3065
2226
  const transportType = this.activeTransport ?? "hid";
3066
- const operationId = this._activeOperationId();
3067
2227
  const waitPromise = this._uiRegistry.wait(
3068
2228
  UI_REQUEST.REQUEST_DEVICE_PERMISSION,
3069
- { timeoutMs: 6e4, operationId }
2229
+ { timeoutMs: 6e4 }
3070
2230
  );
3071
2231
  this.emitter.emit(UI_REQUEST.REQUEST_DEVICE_PERMISSION, {
3072
2232
  type: UI_REQUEST.REQUEST_DEVICE_PERMISSION,
3073
- payload: { transportType, connectId, deviceId, operationId }
2233
+ payload: { transportType, connectId, deviceId }
3074
2234
  });
3075
2235
  let response;
3076
2236
  const onAbort = () => {
@@ -3116,15 +2276,7 @@ var _LedgerAdapter = class _LedgerAdapter {
3116
2276
  if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
3117
2277
  const e = err;
3118
2278
  const params = e.code === HardwareErrorCode3.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
3119
- return ledgerFailure(
3120
- e.code,
3121
- e.message ?? "Unknown error",
3122
- e.appName,
3123
- tag,
3124
- params,
3125
- void 0,
3126
- isHwkRecoveryHint(e.recovery) ? e.recovery : void 0
3127
- );
2279
+ return ledgerFailure(e.code, e.message ?? "Unknown error", e.appName, tag, params);
3128
2280
  }
3129
2281
  const mapped = mapLedgerError(err);
3130
2282
  return ledgerFailure(mapped.code, mapped.message, mapped.appName, tag);
@@ -3151,7 +2303,7 @@ var _LedgerAdapter = class _LedgerAdapter {
3151
2303
  deviceId: device.deviceId,
3152
2304
  connectId: device.connectId,
3153
2305
  label: device.name,
3154
- connectionType: device.connectionType ?? this.connector.connectionType,
2306
+ connectionType: this.connector.connectionType,
3155
2307
  rssi: device.rssi,
3156
2308
  isConnectable: device.isConnectable,
3157
2309
  serialNumber: device.serialNumber,
@@ -3177,8 +2329,8 @@ var LedgerAdapter = _LedgerAdapter;
3177
2329
 
3178
2330
  // src/connector/LedgerConnectorBase.ts
3179
2331
  import {
3180
- EConnectorInteraction as EConnectorInteraction3,
3181
- HardwareErrorCode as HardwareErrorCode7,
2332
+ EConnectorInteraction as EConnectorInteraction4,
2333
+ HardwareErrorCode as HardwareErrorCode8,
3182
2334
  isKnownNonTargetHardwareVendor,
3183
2335
  serializeConnectorError
3184
2336
  } from "@onekeyfe/hwk-adapter-core";
@@ -3451,7 +2603,7 @@ var LedgerDeviceManager = class {
3451
2603
 
3452
2604
  // src/signer/SignerManager.ts
3453
2605
  import { SignerEthBuilder } from "@ledgerhq/device-signer-kit-ethereum";
3454
- import { ContextModuleBuilder, ContextModuleChainID } from "@ledgerhq/context-module";
2606
+ import { ContextModuleBuilder } from "@ledgerhq/context-module";
3455
2607
 
3456
2608
  // src/signer/SignerEth.ts
3457
2609
  import { hexToBytes } from "@onekeyfe/hwk-adapter-core";
@@ -3717,7 +2869,7 @@ var SignerManager = class _SignerManager {
3717
2869
  return (args) => new SignerEthBuilder(args);
3718
2870
  }
3719
2871
  static _createContextModule() {
3720
- const contextModule = new ContextModuleBuilder({}).setChain(ContextModuleChainID.Ethereum).removeDefaultLoaders().build();
2872
+ const contextModule = new ContextModuleBuilder({}).removeDefaultLoaders().build();
3721
2873
  return _SignerManager.wrapBlindSigningReportNonBlocking(contextModule);
3722
2874
  }
3723
2875
  static wrapBlindSigningReportNonBlocking(contextModule) {
@@ -3740,40 +2892,19 @@ import { HardwareErrorCode as HardwareErrorCode4, padHex64, stripHex } from "@on
3740
2892
 
3741
2893
  // src/connector/chains/utils.ts
3742
2894
  import { EConnectorInteraction as EConnectorInteraction2 } from "@onekeyfe/hwk-adapter-core";
3743
- function normalizePath(path) {
3744
- return path.startsWith("m/") ? path.slice(2) : path;
3745
- }
3746
- function collapseSignerInteraction(interaction) {
3747
- switch (interaction) {
3748
- case "confirm-open-app":
3749
- return EConnectorInteraction2.ConfirmOpenApp;
3750
- case "unlock-device":
3751
- return EConnectorInteraction2.UnlockDevice;
3752
- case "interaction-complete":
3753
- return EConnectorInteraction2.InteractionComplete;
3754
- default:
3755
- return EConnectorInteraction2.ConfirmOnDevice;
3756
- }
3757
- }
3758
- function wireSignerToSession(ctx, sessionId, chain, signer) {
3759
- signer.onInteraction = (interaction) => {
3760
- debugLog(`[LedgerConnector] ${chain}.onInteraction:`, interaction);
3761
- ctx.emit("ui-event", {
3762
- type: collapseSignerInteraction(interaction),
3763
- payload: { sessionId }
3764
- });
3765
- };
3766
- signer.onRegisterCanceller = (cancel) => ctx.registerCanceller(sessionId, cancel);
3767
- return signer;
3768
- }
3769
- async function runSignerCall(ctx, sessionId, call) {
3770
- try {
3771
- return await call();
3772
- } catch (err) {
3773
- ctx.invalidateSession(sessionId);
3774
- throw ctx.wrapError(err);
3775
- } finally {
3776
- ctx.clearCanceller(sessionId);
2895
+ function normalizePath(path) {
2896
+ return path.startsWith("m/") ? path.slice(2) : path;
2897
+ }
2898
+ function collapseSignerInteraction(interaction) {
2899
+ switch (interaction) {
2900
+ case "confirm-open-app":
2901
+ return EConnectorInteraction2.ConfirmOpenApp;
2902
+ case "unlock-device":
2903
+ return EConnectorInteraction2.UnlockDevice;
2904
+ case "interaction-complete":
2905
+ return EConnectorInteraction2.InteractionComplete;
2906
+ default:
2907
+ return EConnectorInteraction2.ConfirmOnDevice;
3777
2908
  }
3778
2909
  }
3779
2910
 
@@ -4208,8 +3339,7 @@ function _applySignaturesToPsbt(psbtHex, signatures) {
4208
3339
  }
4209
3340
 
4210
3341
  // src/connector/chains/sol.ts
4211
- import { bytesToHex, hexToBytes as hexToBytes3, prepareSolanaOffchainMessageV1 } from "@onekeyfe/hwk-adapter-core";
4212
- import bs58 from "bs58";
3342
+ import { bytesToHex, hexToBytes as hexToBytes3 } from "@onekeyfe/hwk-adapter-core";
4213
3343
 
4214
3344
  // src/signer/SignerSol.ts
4215
3345
  var SignerSol = class {
@@ -4293,21 +3423,8 @@ async function solSignMessage(ctx, sessionId, params) {
4293
3423
  const path = normalizePath(params.path);
4294
3424
  const messageBytes = hexToBytes3(params.message);
4295
3425
  try {
4296
- if (params.messageVersion === 1) {
4297
- const preparedMessage = prepareSolanaOffchainMessageV1({
4298
- message: messageBytes,
4299
- requiredSigners: params.requiredSigners
4300
- });
4301
- const { SignMessageVersion } = await ctx.importLedgerKit(
4302
- "@ledgerhq/device-signer-kit-solana"
4303
- );
4304
- const result2 = await solSigner.signMessage(path, preparedMessage.serializedMessage, {
4305
- version: SignMessageVersion.Raw
4306
- });
4307
- return { signature: decodeBase58Signature(result2.signature) };
4308
- }
4309
3426
  const result = await solSigner.signMessage(path, messageBytes);
4310
- return { signature: decodeBase58EnvelopeSignature(result.signature) };
3427
+ return { signature: result.signature };
4311
3428
  } catch (err) {
4312
3429
  ctx.invalidateSession(sessionId);
4313
3430
  throw ctx.wrapError(err);
@@ -4315,27 +3432,11 @@ async function solSignMessage(ctx, sessionId, params) {
4315
3432
  ctx.clearCanceller(sessionId);
4316
3433
  }
4317
3434
  }
4318
- function decodeBase58Signature(signature) {
4319
- const bytes = bs58.decode(signature);
4320
- if (bytes.length !== 64) {
4321
- throw new Error(`Ledger Solana signature must be 64 bytes, received ${bytes.length}`);
4322
- }
4323
- return bytesToHex(bytes);
4324
- }
4325
- function decodeBase58EnvelopeSignature(envelope) {
4326
- const bytes = bs58.decode(envelope);
4327
- if (bytes.length < 65 || bytes[0] !== 1) {
4328
- throw new Error("Ledger Solana signature envelope is invalid");
4329
- }
4330
- return bytesToHex(bytes.subarray(1, 65));
4331
- }
4332
3435
  async function _createSolSigner(ctx, sessionId) {
4333
3436
  const dmk = await ctx.getOrCreateDmk();
4334
- const { ContextModuleBuilder: ContextModuleBuilder2, ContextModuleChainID: ContextModuleChainID2 } = await ctx.importLedgerKit(
4335
- "@ledgerhq/context-module"
4336
- );
3437
+ const { ContextModuleBuilder: ContextModuleBuilder2 } = await ctx.importLedgerKit("@ledgerhq/context-module");
4337
3438
  const { SignerSolanaBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-solana");
4338
- const contextModule = new ContextModuleBuilder2({}).setChain(ContextModuleChainID2.Solana).removeDefaultLoaders().build();
3439
+ const contextModule = new ContextModuleBuilder2({}).removeDefaultLoaders().build();
4339
3440
  const sdkSigner = new SignerSolanaBuilder({ dmk, sessionId }).withContextModule(contextModule).build();
4340
3441
  const signer = new SignerSol(sdkSigner);
4341
3442
  signer.onInteraction = (interaction) => {
@@ -4351,147 +3452,361 @@ async function _createSolSigner(ctx, sessionId) {
4351
3452
  return signer;
4352
3453
  }
4353
3454
 
4354
- // src/connector/chains/zcash.ts
4355
- 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";
4356
3458
 
4357
- // src/signer/SignerZcash.ts
4358
- var SignerZcash = class {
4359
- // eslint-disable-next-line no-useless-constructor, no-empty-function
4360
- constructor(_sdk) {
4361
- 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;
4362
3486
  }
4363
- /** GET_VK: UFVK string (default) or raw 96-byte Orchard FVK. */
4364
- async getFullViewingKey(derivationPath, options) {
4365
- const action = this._sdk.getFullViewingKey(derivationPath, options);
4366
- return deviceActionToPromise(
4367
- action,
4368
- this.onInteraction,
4369
- void 0,
4370
- 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
+ }
4371
3565
  );
4372
3566
  }
4373
- /** GET_SHIELDED_ADDRESS: single-Orchard-receiver UA for a 5-level transparent path. */
4374
- async getShieldedAddress(derivationPath, options) {
4375
- const action = this._sdk.getShieldedAddress(derivationPath, options);
4376
- return deviceActionToPromise(
4377
- action,
4378
- this.onInteraction,
4379
- void 0,
4380
- 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
3626
+ );
3627
+ throw new Error(
3628
+ `Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
4381
3629
  );
4382
3630
  }
3631
+ _isDashboard(appName) {
3632
+ return appName === DASHBOARD_APP_NAME;
3633
+ }
3634
+ _wait() {
3635
+ return new Promise((resolve) => setTimeout(resolve, this._waitMs));
3636
+ }
4383
3637
  };
4384
3638
 
4385
- // src/connector/chains/zcash.ts
4386
- async function zcashGetFullViewingKey(ctx, sessionId, params) {
4387
- const signer = await _createZcashSigner(ctx, sessionId);
4388
- const path = normalizePath(params.path);
4389
- const mode = params.mode ?? "ufvk";
4390
- return runSignerCall(ctx, sessionId, async () => {
4391
- const result = await signer.getFullViewingKey(path, { mode });
4392
- if (result.mode === "ufvk") {
4393
- return { path: params.path, mode, ufvk: result.fullViewingKey };
4394
- }
4395
- return { path: params.path, mode, orchardFvk: bytesToHex2(result.fullViewingKey) };
4396
- });
3639
+ // src/connector/chains/legacyChainCall.ts
3640
+ function isLegacyWrongAppError(err, _appName) {
3641
+ return isWrongAppError(err);
4397
3642
  }
4398
- async function zcashGetShieldedAddress(ctx, sessionId, params) {
4399
- const signer = await _createZcashSigner(ctx, sessionId);
4400
- const path = normalizePath(params.path);
4401
- return runSignerCall(ctx, sessionId, async () => {
4402
- const result = await signer.getShieldedAddress(path, {
4403
- checkOnDevice: params.showOnDevice ?? false
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 }
4404
3651
  });
4405
- return { address: result.address, path: params.path };
4406
- });
3652
+ };
3653
+ const closeOpenAppUiIfShown = () => {
3654
+ if (openAppPromptShown) {
3655
+ ctx.emit("ui-event", {
3656
+ type: EConnectorInteraction3.InteractionComplete,
3657
+ payload: { sessionId }
3658
+ });
3659
+ openAppPromptShown = false;
3660
+ }
3661
+ };
3662
+ try {
3663
+ await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
3664
+ } catch (err) {
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
+ };
3694
+ try {
3695
+ return await runOnce();
3696
+ } catch (err) {
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;
3718
+ }
4407
3719
  }
4408
- async function _createZcashSigner(ctx, sessionId) {
3720
+ async function _ensureAppOpen(ctx, sessionId, appName, onPrompt) {
4409
3721
  const dmk = await ctx.getOrCreateDmk();
4410
- const { SignerZcashBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-zcash");
4411
- const sdkSigner = new SignerZcashBuilder({ dmk, sessionId }).build();
4412
- return wireSignerToSession(ctx, sessionId, "zcash", new SignerZcash(sdkSigner));
3722
+ const appManager = new AppManager(dmk);
3723
+ await appManager.ensureAppOpen(sessionId, appName, onPrompt);
4413
3724
  }
4414
3725
 
4415
- // src/connector/chains/tron.ts
4416
- import { HardwareErrorCode as HardwareErrorCode6, bytesToHex as bytesToHex3, hexToBytes as hexToBytes4 } from "@onekeyfe/hwk-adapter-core";
4417
-
4418
- // src/signer/SignerTron.ts
4419
- var SignerTron = class {
4420
- // eslint-disable-next-line no-useless-constructor, no-empty-function
4421
- constructor(_sdk) {
4422
- this._sdk = _sdk;
4423
- }
4424
- /** Base58 Tron address plus the uncompressed public key at `derivationPath`. */
4425
- async getAddress(derivationPath, options) {
4426
- const action = this._sdk.getAddress(derivationPath, options);
4427
- const result = await deviceActionToPromise(
4428
- action,
4429
- this.onInteraction,
4430
- void 0,
4431
- this.onRegisterCanceller
4432
- );
4433
- return { address: result.address, publicKey: result.publicKey };
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;
4434
3733
  }
4435
- /** Sign a protobuf-encoded raw transaction. */
4436
- async signTransaction(derivationPath, transaction, options) {
4437
- const action = this._sdk.signTransaction(derivationPath, transaction, options);
4438
- return deviceActionToPromise(
4439
- action,
4440
- this.onInteraction,
4441
- void 0,
4442
- this.onRegisterCanceller
4443
- );
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;
4444
3746
  }
4445
- /** Sign a personal message (TIP-191). */
4446
- async signPersonalMessage(derivationPath, message, options) {
4447
- const action = this._sdk.signPersonalMessage(derivationPath, message, options);
4448
- return deviceActionToPromise(
4449
- action,
4450
- this.onInteraction,
4451
- void 0,
4452
- this.onRegisterCanceller
4453
- );
3747
+ async close() {
4454
3748
  }
4455
3749
  };
4456
3750
 
4457
3751
  // src/connector/chains/tron.ts
4458
3752
  async function tronGetAddress(ctx, sessionId, params) {
4459
- const tronSigner = await _createTronSigner(ctx, sessionId);
4460
3753
  const path = normalizePath(params.path);
4461
- return runSignerCall(ctx, sessionId, async () => {
4462
- const result = await tronSigner.getAddress(path, {
4463
- checkOnDevice: params.showOnDevice ?? false
4464
- });
4465
- return { address: result.address, publicKey: result.publicKey, path: params.path };
4466
- });
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
+ );
4467
3770
  }
4468
3771
  async function tronSignTransaction(ctx, sessionId, params) {
4469
3772
  if (!params.rawTxHex) {
4470
3773
  throw Object.assign(
4471
3774
  new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
4472
- { code: HardwareErrorCode6.InvalidParams }
3775
+ { code: HardwareErrorCode7.InvalidParams }
4473
3776
  );
4474
3777
  }
4475
- const tronSigner = await _createTronSigner(ctx, sessionId);
4476
3778
  const path = normalizePath(params.path);
4477
- return runSignerCall(ctx, sessionId, async () => {
4478
- const signature = await tronSigner.signTransaction(path, hexToBytes4(params.rawTxHex));
4479
- return { signature: bytesToHex3(signature) };
4480
- });
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
+ );
4481
3793
  }
4482
3794
  async function tronSignMessage(ctx, sessionId, params) {
4483
- const tronSigner = await _createTronSigner(ctx, sessionId);
4484
3795
  const path = normalizePath(params.path);
4485
- return runSignerCall(ctx, sessionId, async () => {
4486
- const signature = await tronSigner.signPersonalMessage(path, hexToBytes4(params.messageHex));
4487
- return { signature: bytesToHex3(signature) };
4488
- });
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
+ );
4489
3806
  }
4490
- async function _createTronSigner(ctx, sessionId) {
3807
+ async function _createTrx(ctx, sessionId) {
4491
3808
  const dmk = await ctx.getOrCreateDmk();
4492
- const { SignerTrxBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-tron");
4493
- const sdkSigner = new SignerTrxBuilder({ dmk, sessionId }).build();
4494
- return wireSignerToSession(ctx, sessionId, "tron", new SignerTron(sdkSigner));
3809
+ return new Trx(new DmkTransport(dmk, sessionId));
4495
3810
  }
4496
3811
 
4497
3812
  // src/device-apps/customActions.ts
@@ -4670,7 +3985,7 @@ var DeviceApps = class {
4670
3985
  seTargetId: v.seTargetId,
4671
3986
  mcuTargetId: v.mcuTargetId,
4672
3987
  seVersion: v.seVersion,
4673
- seFlagsHex: bytesToHex4(v.seFlags),
3988
+ seFlagsHex: bytesToHex2(v.seFlags),
4674
3989
  mcuSephVersion: v.mcuSephVersion,
4675
3990
  mcuBootloaderVersion: v.mcuBootloaderVersion,
4676
3991
  hwVersion: v.hwVersion
@@ -4744,7 +4059,7 @@ var DeviceApps = class {
4744
4059
  }
4745
4060
  }
4746
4061
  };
4747
- function bytesToHex4(bytes) {
4062
+ function bytesToHex2(bytes) {
4748
4063
  if (!bytes) return "";
4749
4064
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
4750
4065
  }
@@ -4787,11 +4102,10 @@ var METHOD_PREFIX_TO_APP_NAME = {
4787
4102
  evm: "Ethereum",
4788
4103
  btc: "Bitcoin",
4789
4104
  sol: "Solana",
4790
- tron: "Tron",
4791
- zcash: "Zcash"
4105
+ tron: "Tron"
4792
4106
  };
4793
4107
  var HARDWARE_ERROR_CODE_VALUES = new Set(
4794
- Object.values(HardwareErrorCode7).filter((value) => typeof value === "number")
4108
+ Object.values(HardwareErrorCode8).filter((value) => typeof value === "number")
4795
4109
  );
4796
4110
  var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
4797
4111
  var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
@@ -4828,10 +4142,6 @@ async function defaultLedgerKitImporter(pkg) {
4828
4142
  return import("@ledgerhq/device-signer-kit-bitcoin");
4829
4143
  case "@ledgerhq/device-signer-kit-solana":
4830
4144
  return import("@ledgerhq/device-signer-kit-solana");
4831
- case "@ledgerhq/device-signer-kit-tron":
4832
- return import("@ledgerhq/device-signer-kit-tron");
4833
- case "@ledgerhq/device-signer-kit-zcash":
4834
- return import("@ledgerhq/device-signer-kit-zcash");
4835
4145
  case "@ledgerhq/context-module":
4836
4146
  return import("@ledgerhq/context-module");
4837
4147
  default:
@@ -4891,7 +4201,6 @@ var LedgerConnectorBase = class {
4891
4201
  this._ctx = {
4892
4202
  emit: (event, data) => this._emit(event, data),
4893
4203
  invalidateSession: (sid) => this._invalidateSession(sid),
4894
- teardownSecureChannelSession: (sid) => this._teardownSecureChannelSession(sid),
4895
4204
  wrapError: (err, opts) => this._wrapError(err, opts),
4896
4205
  getOrCreateDmk: () => this._getOrCreateDmk(),
4897
4206
  getDeviceManager: () => this._getDeviceManager(),
@@ -4991,7 +4300,7 @@ var LedgerConnectorBase = class {
4991
4300
  `Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
4992
4301
  );
4993
4302
  err._tag = ERROR_TAG.BlePairingTimeout;
4994
- err.code = HardwareErrorCode7.BlePairingTimeout;
4303
+ err.code = HardwareErrorCode8.BlePairingTimeout;
4995
4304
  reject(err);
4996
4305
  }, HANG_CEILING_MS);
4997
4306
  });
@@ -5024,7 +4333,7 @@ var LedgerConnectorBase = class {
5024
4333
  "Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
5025
4334
  );
5026
4335
  err._tag = ERROR_TAG.DeviceNotAdvertising;
5027
- err.code = HardwareErrorCode7.DeviceNotFound;
4336
+ err.code = HardwareErrorCode8.DeviceNotFound;
5028
4337
  throw err;
5029
4338
  };
5030
4339
  const doConnect = async (path) => {
@@ -5081,14 +4390,14 @@ var LedgerConnectorBase = class {
5081
4390
  this._resetSignersAndSessions();
5082
4391
  if (isLedgerBleConnectionType(this.connectionType)) {
5083
4392
  const tag = err?._tag;
5084
- if (isKnownConnectionTag(tag) && !isConnectionOpeningTag(tag)) {
4393
+ if (isKnownConnectionTag(tag)) {
5085
4394
  throw err;
5086
4395
  }
5087
4396
  const wrapped = new Error(
5088
4397
  "Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
5089
4398
  );
5090
4399
  wrapped._tag = ERROR_TAG.BleGattBondingFailed;
5091
- wrapped.code = HardwareErrorCode7.BlePairingTimeout;
4400
+ wrapped.code = HardwareErrorCode8.BlePairingTimeout;
5092
4401
  wrapped.originalError = err;
5093
4402
  throw wrapped;
5094
4403
  }
@@ -5166,7 +4475,7 @@ var LedgerConnectorBase = class {
5166
4475
  this._unwatchSessionState(sessionId);
5167
4476
  this._signerManager?.invalidate(sessionId);
5168
4477
  this._cancellers.get(sessionId)?.({
5169
- code: HardwareErrorCode7.DeviceDisconnected,
4478
+ code: HardwareErrorCode8.DeviceDisconnected,
5170
4479
  tag: "DeviceDisconnected",
5171
4480
  message: "Device disconnected"
5172
4481
  });
@@ -5192,7 +4501,7 @@ var LedgerConnectorBase = class {
5192
4501
  success: false,
5193
4502
  error: serializeConnectorError(
5194
4503
  Object.assign(new Error("Ledger app is unresponsive"), {
5195
- code: HardwareErrorCode7.DeviceAppStuck,
4504
+ code: HardwareErrorCode8.DeviceAppStuck,
5196
4505
  _tag: ERROR_TAG.DeviceAppStuck,
5197
4506
  originalError: err
5198
4507
  })
@@ -5204,7 +4513,7 @@ var LedgerConnectorBase = class {
5204
4513
  success: false,
5205
4514
  error: serializeConnectorError(
5206
4515
  Object.assign(new Error("Device communication interrupted, please retry"), {
5207
- code: HardwareErrorCode7.TransportError,
4516
+ code: HardwareErrorCode8.TransportError,
5208
4517
  _tag: ERROR_TAG.DeviceTransportStuck,
5209
4518
  originalError: err
5210
4519
  })
@@ -5250,11 +4559,6 @@ var LedgerConnectorBase = class {
5250
4559
  return solSignTransaction(ctx, sessionId, params);
5251
4560
  case "solSignMessage":
5252
4561
  return solSignMessage(ctx, sessionId, params);
5253
- // ZCASH
5254
- case "zcashGetFullViewingKey":
5255
- return zcashGetFullViewingKey(ctx, sessionId, params);
5256
- case "zcashGetShieldedAddress":
5257
- return zcashGetShieldedAddress(ctx, sessionId, params);
5258
4562
  // TRON
5259
4563
  case "tronGetAddress":
5260
4564
  return tronGetAddress(ctx, sessionId, params);
@@ -5279,7 +4583,7 @@ var LedgerConnectorBase = class {
5279
4583
  try {
5280
4584
  return await apps.install(p.appName, ({ progress }) => {
5281
4585
  ctx.emit("ui-event", {
5282
- type: EConnectorInteraction3.AppInstallProgress,
4586
+ type: EConnectorInteraction4.AppInstallProgress,
5283
4587
  payload: {
5284
4588
  sessionId,
5285
4589
  appName: p.appName,
@@ -5288,7 +4592,7 @@ var LedgerConnectorBase = class {
5288
4592
  });
5289
4593
  });
5290
4594
  } catch (err) {
5291
- ctx.teardownSecureChannelSession(sessionId);
4595
+ ctx.invalidateSession(sessionId);
5292
4596
  throw ctx.wrapError(err);
5293
4597
  } finally {
5294
4598
  ctx.clearCanceller(sessionId);
@@ -5378,7 +4682,7 @@ var LedgerConnectorBase = class {
5378
4682
  );
5379
4683
  return { isGenuine: output.isGenuine, deviceId };
5380
4684
  } catch (err) {
5381
- ctx.teardownSecureChannelSession(sessionId);
4685
+ ctx.invalidateSession(sessionId);
5382
4686
  throw ctx.wrapError(err);
5383
4687
  } finally {
5384
4688
  ctx.clearCanceller(sessionId);
@@ -5497,26 +4801,9 @@ var LedgerConnectorBase = class {
5497
4801
  }
5498
4802
  return this._deviceAppsManager;
5499
4803
  }
5500
- // DeviceAppsManager is a per-call factory with no cached session state, so
5501
- // there is nothing to invalidate for it here.
5502
4804
  _invalidateSession(sessionId) {
5503
4805
  this._signerManager?.invalidate(sessionId);
5504
4806
  }
5505
- /**
5506
- * Fire the canceller so DMK closes the secure-channel WebSocket. The DMK device
5507
- * session is kept: unlock/retry recovery reuses it.
5508
- */
5509
- _teardownSecureChannelSession(sessionId) {
5510
- const cancel = this._cancellers.get(sessionId);
5511
- this._cancellers.delete(sessionId);
5512
- if (cancel) {
5513
- try {
5514
- cancel();
5515
- } catch {
5516
- }
5517
- }
5518
- this._invalidateSession(sessionId);
5519
- }
5520
4807
  /**
5521
4808
  * Replace an old session with a new one after app switch.
5522
4809
  * Emits device-connect so the adapter updates its _sessions Map.
@@ -5601,7 +4888,7 @@ var LedgerConnectorBase = class {
5601
4888
  * at every catch site. Falls through unchanged for unknown methods.
5602
4889
  */
5603
4890
  _ctxForMethod(method) {
5604
- const prefix = /^(evm|btc|sol|tron|zcash)/.exec(method)?.[1];
4891
+ const prefix = /^(evm|btc|sol|tron)/.exec(method)?.[1];
5605
4892
  const defaultAppName = prefix ? METHOD_PREFIX_TO_APP_NAME[prefix] : void 0;
5606
4893
  if (!defaultAppName) return this._ctx;
5607
4894
  return {
@@ -5635,209 +4922,6 @@ var LedgerConnectorBase = class {
5635
4922
  return error;
5636
4923
  }
5637
4924
  };
5638
-
5639
- // src/transport/DmkTransport.ts
5640
- import Transport from "@ledgerhq/hw-transport";
5641
- var DmkTransport = class extends Transport {
5642
- constructor(dmk, sessionId) {
5643
- super();
5644
- this._dmk = dmk;
5645
- this._sessionId = sessionId;
5646
- }
5647
- async exchange(apdu) {
5648
- const response = await this._dmk.sendApdu({
5649
- sessionId: this._sessionId,
5650
- apdu: new Uint8Array(apdu)
5651
- });
5652
- const { data, statusCode } = response;
5653
- const result = Buffer.alloc(data.length + 2);
5654
- if (data.length > 0) {
5655
- result.set(data, 0);
5656
- }
5657
- result.set(statusCode, data.length);
5658
- return result;
5659
- }
5660
- async close() {
5661
- }
5662
- };
5663
-
5664
- // src/app/AppManager.ts
5665
- import {
5666
- CloseAppCommand,
5667
- GetAppAndVersionCommand,
5668
- OpenAppCommand,
5669
- isSuccessCommandResult
5670
- } from "@ledgerhq/device-management-kit";
5671
- import { HardwareErrorCode as HardwareErrorCode8 } from "@onekeyfe/hwk-adapter-core";
5672
- var APP_NAME_MAP = {
5673
- ETH: "Ethereum",
5674
- BTC: "Bitcoin",
5675
- SOL: "Solana",
5676
- TRX: "Tron",
5677
- ZEC: "Zcash",
5678
- XRP: "XRP",
5679
- ADA: "Cardano",
5680
- DOT: "Polkadot",
5681
- ATOM: "Cosmos"
5682
- };
5683
- var DASHBOARD_APP_NAME = "BOLOS";
5684
- var AppManager = class {
5685
- constructor(dmk, options) {
5686
- this._dmk = dmk;
5687
- this._waitMs = options?.waitMs ?? 1e3;
5688
- this._maxRetries = options?.maxRetries ?? 10;
5689
- }
5690
- /**
5691
- * Return the Ledger app name for a given chain ticker,
5692
- * or undefined if the chain is not supported.
5693
- */
5694
- static getAppName(chain) {
5695
- return APP_NAME_MAP[chain];
5696
- }
5697
- /**
5698
- * Ensure the target app is open on the device identified by `sessionId`.
5699
- *
5700
- * Flow:
5701
- * 1. Check the currently running app.
5702
- * 2. If it is already the target, return immediately.
5703
- * 3. If a different app is running (not dashboard), close it first.
5704
- * 4. Open the target app.
5705
- * 5. Poll until the device confirms the target app is running.
5706
- */
5707
- /**
5708
- * @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
5709
- * device is about to display "Open <app>" on screen and wait for the
5710
- * user's button press. UI consumers should show their "open app" prompt
5711
- * in response. NOT called when the target app is already open (no user
5712
- * interaction needed in that case).
5713
- *
5714
- * Important: OpenAppCommand is blocking. It does not resolve until the user
5715
- * has physically confirmed on the device, so anything that runs AFTER
5716
- * `await this._openApp(...)` lands AFTER the prompt is already gone.
5717
- * Hence the callback must fire BEFORE that await.
5718
- */
5719
- async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
5720
- const currentApp = await this._getCurrentApp(sessionId);
5721
- if (currentApp === targetAppName) {
5722
- return;
5723
- }
5724
- if (!this._isDashboard(currentApp)) {
5725
- await this._closeCurrentApp(sessionId);
5726
- await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
5727
- }
5728
- onConfirmOnDevice?.();
5729
- await this._openApp(sessionId, targetAppName);
5730
- await this._waitForApp(sessionId, targetAppName);
5731
- }
5732
- // ---------------------------------------------------------------------------
5733
- // Private helpers
5734
- // ---------------------------------------------------------------------------
5735
- async _getCurrentApp(sessionId) {
5736
- const result = await this._dmk.sendCommand({
5737
- sessionId,
5738
- command: new GetAppAndVersionCommand()
5739
- });
5740
- if (isSuccessCommandResult(result)) {
5741
- debugLog("[AppManager] currentApp:", result.data.name);
5742
- return result.data.name;
5743
- }
5744
- const errResult = result;
5745
- const dmkErr = errResult.error ?? {};
5746
- const original = dmkErr.originalError;
5747
- debugLog(
5748
- "[AppManager] _getCurrentApp failed sessionId=",
5749
- sessionId,
5750
- "tag=",
5751
- dmkErr._tag,
5752
- "errorCode=",
5753
- dmkErr.errorCode,
5754
- "message=",
5755
- dmkErr.message,
5756
- "originalErrorMessage=",
5757
- original?.message ?? String(original ?? "")
5758
- );
5759
- throw Object.assign(
5760
- new Error(
5761
- dmkErr.message ?? "Failed to get current app from device"
5762
- ),
5763
- {
5764
- _tag: dmkErr._tag,
5765
- errorCode: dmkErr.errorCode,
5766
- originalError: original
5767
- }
5768
- );
5769
- }
5770
- async _openApp(sessionId, appName) {
5771
- const result = await this._dmk.sendCommand({
5772
- sessionId,
5773
- command: new OpenAppCommand({ appName })
5774
- });
5775
- if (!isSuccessCommandResult(result)) {
5776
- const dmkErr = result.error;
5777
- const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
5778
- const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
5779
- debugLog(
5780
- "[AppManager] openApp failed:",
5781
- appName,
5782
- "errorCode:",
5783
- errorCode,
5784
- "tag:",
5785
- dmkErr._tag
5786
- );
5787
- let code;
5788
- if (errorCode === "6807" || /unknown application/i.test(message)) {
5789
- code = HardwareErrorCode8.AppNotInstalled;
5790
- } else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
5791
- code = HardwareErrorCode8.UserRejected;
5792
- }
5793
- throw Object.assign(new Error(`Failed to open "${appName}"`), {
5794
- _tag: ERROR_TAG.OpenAppCommand,
5795
- code,
5796
- errorCode,
5797
- statusCode: errorCode,
5798
- appName,
5799
- originalError: dmkErr
5800
- });
5801
- }
5802
- }
5803
- async _closeCurrentApp(sessionId) {
5804
- debugLog("[AppManager] closeCurrentApp");
5805
- await this._dmk.sendCommand({
5806
- sessionId,
5807
- command: new CloseAppCommand()
5808
- });
5809
- }
5810
- /**
5811
- * Poll the device until the expected app is reported as running,
5812
- * or throw after `_maxRetries` attempts.
5813
- */
5814
- async _waitForApp(sessionId, expectedAppName) {
5815
- let lastSeen = "";
5816
- for (let i = 0; i < this._maxRetries; i++) {
5817
- await this._wait();
5818
- const current = await this._getCurrentApp(sessionId);
5819
- lastSeen = current;
5820
- if (current === expectedAppName) {
5821
- return;
5822
- }
5823
- }
5824
- debugLog(
5825
- "[AppManager] waitForApp exhausted: expected=",
5826
- expectedAppName,
5827
- "lastSeen=",
5828
- lastSeen
5829
- );
5830
- throw new Error(
5831
- `Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
5832
- );
5833
- }
5834
- _isDashboard(appName) {
5835
- return appName === DASHBOARD_APP_NAME;
5836
- }
5837
- _wait() {
5838
- return new Promise((resolve) => setTimeout(resolve, this._waitMs));
5839
- }
5840
- };
5841
4925
  export {
5842
4926
  AppManager,
5843
4927
  DmkTransport,
@@ -5848,7 +4932,6 @@ export {
5848
4932
  SignerEth,
5849
4933
  SignerManager,
5850
4934
  SignerSol,
5851
- SignerZcash,
5852
4935
  debugLog,
5853
4936
  deviceActionToPromise,
5854
4937
  isDeviceLockedError,