@onekeyfe/hwk-ledger-adapter 1.2.0-alpha.12 → 1.2.0-alpha.13

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
@@ -2,15 +2,17 @@
2
2
  import {
3
3
  CHAIN_FINGERPRINT_PATHS,
4
4
  DEVICE,
5
+ DEVICE_CONNECT_RETRY_DELAY_MS,
5
6
  DeviceJobQueue,
6
7
  EConnectorInteraction,
7
- HardwareErrorCode as HardwareErrorCode2,
8
+ HardwareErrorCode as HardwareErrorCode3,
8
9
  TypedEventEmitter,
9
10
  UI_REQUEST,
10
11
  UI_REQUEST_PREEMPTED_TAG,
11
12
  UiRequestRegistry,
13
+ createHwkError,
12
14
  deriveDeviceFingerprint,
13
- failure,
15
+ failure as failure2,
14
16
  rehydrateConnectorError,
15
17
  success
16
18
  } from "@onekeyfe/hwk-adapter-core";
@@ -35,30 +37,37 @@ var USER_REJECTED_CODES = /* @__PURE__ */ new Set(["6985", "27013"]);
35
37
  var WRONG_APP_CODES = /* @__PURE__ */ new Set(["6e00", "28160", "6d00", "27904", "6a83", "27267"]);
36
38
  var APP_NOT_INSTALLED_CODES = /* @__PURE__ */ new Set(["6807", "26631"]);
37
39
  var STEP_BLIND_SIGN_TRANSACTION_FALLBACK = "signer.eth.steps.blindSignTransactionFallback";
40
+ var STEP_DETECT_BLIND_SIGNING = "signer.eth.steps.detectBlindSigning";
41
+ var BLIND_SIGNING_STEPS = /* @__PURE__ */ new Set([
42
+ STEP_BLIND_SIGN_TRANSACTION_FALLBACK,
43
+ STEP_DETECT_BLIND_SIGNING
44
+ ]);
45
+ function normalizeApduHex(value) {
46
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 65535) {
47
+ return value.toString(16).padStart(4, "0");
48
+ }
49
+ if (typeof value !== "string") return null;
50
+ const raw = value.toLowerCase().replace(/^0x/, "");
51
+ return /^[0-9a-f]{1,4}$/i.test(raw) ? raw.padStart(4, "0") : null;
52
+ }
38
53
  function getEthAppErrorCode(err) {
39
54
  if (!err || typeof err !== "object") return null;
40
55
  const e = err;
41
- if (e._tag === ERROR_TAG.EthAppCommand && typeof e.errorCode === "string") {
42
- return e.errorCode.toLowerCase();
56
+ if (e._tag === ERROR_TAG.EthAppCommand) {
57
+ const code = normalizeApduHex(e.errorCode) ?? normalizeApduHex(e.statusCode) ?? normalizeApduHex(e.code);
58
+ if (code) return code;
43
59
  }
44
60
  const orig = e.originalError;
45
- if (orig?._tag === ERROR_TAG.EthAppCommand && typeof orig.errorCode === "string") {
46
- return orig.errorCode.toLowerCase();
61
+ if (orig?._tag === ERROR_TAG.EthAppCommand) {
62
+ return normalizeApduHex(orig.errorCode) ?? normalizeApduHex(orig.statusCode) ?? normalizeApduHex(orig.code);
47
63
  }
48
64
  return null;
49
65
  }
50
66
  function extractApduHex(err) {
51
67
  if (!err || typeof err !== "object") return null;
52
68
  const e = err;
53
- if (typeof e.errorCode === "string" && /^[0-9a-f]+$/i.test(e.errorCode)) {
54
- return e.errorCode.toLowerCase();
55
- }
56
- if (typeof e.statusCode === "number" && Number.isFinite(e.statusCode)) {
57
- return e.statusCode.toString(16).padStart(4, "0");
58
- }
59
- if (typeof e.statusCode === "string" && /^[0-9a-f]+$/i.test(e.statusCode)) {
60
- return e.statusCode.toLowerCase();
61
- }
69
+ const direct = normalizeApduHex(e.errorCode) ?? normalizeApduHex(e.statusCode);
70
+ if (direct) return direct;
62
71
  if (e.originalError != null) {
63
72
  const nested = extractApduHex(e.originalError);
64
73
  if (nested) return nested;
@@ -117,10 +126,15 @@ function classifyEthAppError(err) {
117
126
  const ethCode = getEthAppErrorCode(err);
118
127
  if (!ethCode) return null;
119
128
  if (ethCode === "6a80") {
120
- return hasBlindSignFallbackStep(err) ? HardwareErrorCode.EvmBlindSigningRequired : null;
129
+ return hasBlindSigningStep(err) ? HardwareErrorCode.EvmBlindSigningRequired : null;
121
130
  }
122
131
  return mapEthAppErrorCode(ethCode);
123
132
  }
133
+ function classifyBlindSigningDetectionError(err) {
134
+ if (!hasBlindSigningStep(err)) return null;
135
+ if (hasInvalidArgumentCode(err)) return HardwareErrorCode.EvmBlindSigningRequired;
136
+ return null;
137
+ }
124
138
  var ERROR_TAG = {
125
139
  // SDK-mint
126
140
  DeviceNotAdvertising: "DeviceNotAdvertisingError",
@@ -136,6 +150,10 @@ var ERROR_TAG = {
136
150
  // chain app wedged (APDU 0x6901)
137
151
  DeviceTransportStuck: "DeviceTransportStuck",
138
152
  // DMK transport queue wedged
153
+ // installApp resolved success but the app is still missing on device —
154
+ // DMK quirk. Surfaced as a distinct tag so callers can tell it apart from
155
+ // the original DMK-thrown AppNotInstalled.
156
+ AppInstallVerifyFailed: "AppInstallVerifyFailedError",
139
157
  // DMK-reuse (DMK throws same string; we synthesize too)
140
158
  DeviceLocked: "DeviceLockedError",
141
159
  DeviceNotRecognized: "DeviceNotRecognizedError",
@@ -161,7 +179,11 @@ var ERROR_TAG = {
161
179
  // ble-plx surfaces this when GATT notification setup fails — typical
162
180
  // outcome when the user doesn't confirm pairing on the device, or the
163
181
  // existing bond is invalid. Observed in production after ~30s.
164
- PairingRefused: "PairingRefusedError"
182
+ PairingRefused: "PairingRefusedError",
183
+ // DMK remote-network failures (manager-api HTTP / secure-channel WS).
184
+ WebSocketConnection: "WebSocketConnectionError",
185
+ HttpFetch: "FetchError",
186
+ InvalidFirmwareMetadataResponse: "InvalidGetFirmwareMetadataResponseError"
165
187
  };
166
188
  function isDeviceLockedError(err) {
167
189
  if (!err || typeof err !== "object") return false;
@@ -249,15 +271,30 @@ function hasStatusCode(err, codeSet) {
249
271
  if (e.error != null && e._tag && hasStatusCode(e.error, codeSet)) return true;
250
272
  return false;
251
273
  }
252
- function hasBlindSignFallbackStep(err) {
274
+ function hasDeviceActionStep(err, step) {
253
275
  if (!err || typeof err !== "object") return false;
254
276
  const e = err;
255
- if (e._lastStep === STEP_BLIND_SIGN_TRANSACTION_FALLBACK) return true;
256
- if (Array.isArray(e._deviceActionSteps) && e._deviceActionSteps.includes(STEP_BLIND_SIGN_TRANSACTION_FALLBACK)) {
277
+ if (e._lastStep === step) return true;
278
+ if (Array.isArray(e._deviceActionSteps) && e._deviceActionSteps.includes(step)) {
257
279
  return true;
258
280
  }
259
- if (e.originalError != null && hasBlindSignFallbackStep(e.originalError)) return true;
260
- if (e.error != null && e._tag && hasBlindSignFallbackStep(e.error)) return true;
281
+ if (e.originalError != null && hasDeviceActionStep(e.originalError, step)) return true;
282
+ if (e.error != null && e._tag && hasDeviceActionStep(e.error, step)) return true;
283
+ return false;
284
+ }
285
+ function hasBlindSigningStep(err) {
286
+ for (const step of BLIND_SIGNING_STEPS) {
287
+ if (hasDeviceActionStep(err, step)) return true;
288
+ }
289
+ return false;
290
+ }
291
+ function hasInvalidArgumentCode(err) {
292
+ if (!err || typeof err !== "object") return false;
293
+ const e = err;
294
+ if (e.code === "INVALID_ARGUMENT") return true;
295
+ if (typeof e.message === "string" && e.message.includes("code=INVALID_ARGUMENT")) return true;
296
+ if (e.originalError != null && hasInvalidArgumentCode(e.originalError)) return true;
297
+ if (e.error != null && e._tag && hasInvalidArgumentCode(e.error)) return true;
261
298
  return false;
262
299
  }
263
300
  function isDeviceNotFoundError(err) {
@@ -286,6 +323,11 @@ function isUserRejectedError(err) {
286
323
  if (hasStatusCode(err, USER_REJECTED_CODES)) return true;
287
324
  return false;
288
325
  }
326
+ function isUserAbortedError(err) {
327
+ if (!err || typeof err !== "object") return false;
328
+ const e = err;
329
+ return e._tag === ERROR_TAG.UserAborted;
330
+ }
289
331
  function isWrongAppError(err) {
290
332
  if (!err || typeof err !== "object") return false;
291
333
  const e = err;
@@ -312,6 +354,20 @@ function isOutOfMemoryError(err) {
312
354
  const e = err;
313
355
  return e._tag === "OutOfMemoryDAError";
314
356
  }
357
+ var NETWORK_ERROR_TAGS = /* @__PURE__ */ new Set([
358
+ ERROR_TAG.WebSocketConnection,
359
+ ERROR_TAG.HttpFetch,
360
+ ERROR_TAG.InvalidFirmwareMetadataResponse
361
+ ]);
362
+ function isNetworkError(err) {
363
+ if (!err || typeof err !== "object") return false;
364
+ const e = err;
365
+ const tag = e._tag;
366
+ if (typeof tag === "string" && NETWORK_ERROR_TAGS.has(tag)) return true;
367
+ if (e.originalError != null && isNetworkError(e.originalError)) return true;
368
+ if (e.error != null && isNetworkError(e.error)) return true;
369
+ return false;
370
+ }
315
371
  function isDeviceDisconnectedError(err) {
316
372
  if (!err || typeof err !== "object") return false;
317
373
  const e = err;
@@ -368,6 +424,8 @@ function mapLedgerError(err, opts) {
368
424
  code = HardwareErrorCode.DeviceBusy;
369
425
  } else if (isBlePairingFailureError(err)) {
370
426
  code = HardwareErrorCode.BlePairingTimeout;
427
+ } else if (isUserAbortedError(err)) {
428
+ code = HardwareErrorCode.UserAborted;
371
429
  } else if (isUserRejectedError(err)) {
372
430
  code = HardwareErrorCode.UserRejected;
373
431
  } else if (isWrongAppError(err)) {
@@ -376,12 +434,14 @@ function mapLedgerError(err, opts) {
376
434
  code = HardwareErrorCode.AppNotInstalled;
377
435
  } else if (isOutOfMemoryError(err)) {
378
436
  code = HardwareErrorCode.DeviceOutOfMemory;
437
+ } else if (isNetworkError(err)) {
438
+ code = HardwareErrorCode.NetworkError;
379
439
  } else if (isDeviceDisconnectedError(err)) {
380
440
  code = HardwareErrorCode.DeviceDisconnected;
381
441
  } else if (isTimeoutError(err)) {
382
442
  code = HardwareErrorCode.OperationTimeout;
383
443
  } else {
384
- const ethMapped = classifyEthAppError(err);
444
+ const ethMapped = classifyEthAppError(err) ?? classifyBlindSigningDetectionError(err);
385
445
  const apduHex = ethMapped ? null : extractApduHex(err);
386
446
  const chainMapped = apduHex ? mapSolanaAppError(apduHex) ?? mapTronAppError(apduHex) ?? mapBtcAppError(apduHex) : null;
387
447
  code = ethMapped ?? chainMapped ?? HardwareErrorCode.UnknownError;
@@ -391,17 +451,8 @@ function mapLedgerError(err, opts) {
391
451
  return { code, message: enrichErrorMessage(code, originalMessage), appName };
392
452
  }
393
453
 
394
- // src/utils/ledgerDmkTransport.ts
395
- function isLedgerDmkBleTransport(transport) {
396
- const normalized = transport?.toUpperCase();
397
- return normalized === "BLE" || normalized?.endsWith("_BLE") === true;
398
- }
399
- function isLedgerBleConnectionType(connectionType) {
400
- return connectionType === "ble";
401
- }
402
- function isLedgerBleDescriptor(connectionType, descriptor) {
403
- return isLedgerBleConnectionType(connectionType) || isLedgerDmkBleTransport(descriptor.transport);
404
- }
454
+ // src/adapter/methods/allNetworkGetAddress.ts
455
+ import { HardwareErrorCode as HardwareErrorCode2, failure, runAllNetworkGetAddress } from "@onekeyfe/hwk-adapter-core";
405
456
 
406
457
  // src/utils/sdkEventBus.ts
407
458
  var listeners = /* @__PURE__ */ new Set();
@@ -448,6 +499,216 @@ function debugError(...args) {
448
499
  emitLog("error", ...args);
449
500
  }
450
501
 
502
+ // src/adapter/methods/allNetworkGetAddress.ts
503
+ var LEDGER_BTC_NETWORK_COIN_MAP = {
504
+ tbtc: "Testnet",
505
+ bch: "Bcash",
506
+ ltc: "Litecoin",
507
+ neurai: "Neurai"
508
+ };
509
+ var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "dogecoin"]);
510
+ function createAllNetworkGetAddress({
511
+ callChain,
512
+ getChainFingerprint
513
+ }) {
514
+ return async function allNetworkGetAddress(connectId, _deviceId, params) {
515
+ debugLog("[LedgerAdapter][REQ]", { method: "allNetworkGetAddress", connectId, params });
516
+ const installContext = {};
517
+ const commonParams = {
518
+ autoInstallApp: params.autoInstallApp
519
+ };
520
+ const chainFingerprints = /* @__PURE__ */ new Map();
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,
534
+ item,
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;
563
+ };
564
+ }
565
+ function isTopLevelAllNetworkFailure(response) {
566
+ if (response.success) {
567
+ return false;
568
+ }
569
+ const code = response.payload?.code;
570
+ return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.UserAborted || code === HardwareErrorCode2.UserRejected;
571
+ }
572
+ function getItemDeviceId(item) {
573
+ const { deviceId } = item;
574
+ return typeof deviceId === "string" && deviceId.length > 0 ? deviceId : void 0;
575
+ }
576
+ function isUnsupportedLedgerAllNetworkNetwork(item) {
577
+ return LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS.has(item.network.toLowerCase());
578
+ }
579
+ function buildUnsupportedNetworkResponse(item) {
580
+ return {
581
+ ...item,
582
+ success: false,
583
+ payload: {
584
+ code: HardwareErrorCode2.ChainNotSupported,
585
+ error: `Ledger allNetwork does not support ${item.network.toLowerCase() === "doge" ? "Dogecoin" : item.network}`
586
+ }
587
+ };
588
+ }
589
+ function normalizeLedgerAllNetworkItem(method, item) {
590
+ if (method !== "btcGetAddress" && method !== "btcGetPublicKey") {
591
+ return item;
592
+ }
593
+ const itemWithCoin = item;
594
+ if (itemWithCoin.coin) {
595
+ return item;
596
+ }
597
+ const coin = LEDGER_BTC_NETWORK_COIN_MAP[item.network];
598
+ return coin ? { ...item, coin } : item;
599
+ }
600
+ async function attachLedgerIdentity(getChainFingerprint, connectId, item, chain, payload, chainFingerprints) {
601
+ const fingerprint = getItemDeviceId(item) || chainFingerprints.get(chain) || await bootstrapChainFingerprint(getChainFingerprint, connectId, chain);
602
+ if (!fingerprint) {
603
+ return buildFingerprintBootstrapFailure(item, chain);
604
+ }
605
+ chainFingerprints.set(chain, fingerprint);
606
+ return {
607
+ ...item,
608
+ success: true,
609
+ payload: {
610
+ ...payload,
611
+ deviceIdentity: {
612
+ vendor: "ledger",
613
+ type: "chainFingerprint",
614
+ chain,
615
+ value: fingerprint
616
+ },
617
+ chainFingerprint: fingerprint,
618
+ chainFingerprintChain: chain
619
+ }
620
+ };
621
+ }
622
+ async function bootstrapChainFingerprint(getChainFingerprint, connectId, chain) {
623
+ const response = await getChainFingerprint(connectId, chain);
624
+ return response.success ? response.payload : "";
625
+ }
626
+ function buildFingerprintBootstrapFailure(item, chain) {
627
+ return {
628
+ ...item,
629
+ success: false,
630
+ payload: {
631
+ code: HardwareErrorCode2.DeviceMismatch,
632
+ error: `Could not establish chain fingerprint for ${chain} after device call; refusing to return unverifiable result. Please retry.`
633
+ }
634
+ };
635
+ }
636
+ async function callAllNetworkMethod(callChain, connectId, deviceId, method, item, commonParams, installContext) {
637
+ switch (method) {
638
+ case "evmGetAddress":
639
+ return callChain(
640
+ connectId,
641
+ deviceId,
642
+ "evm",
643
+ "evmGetAddress",
644
+ item,
645
+ commonParams,
646
+ false,
647
+ installContext
648
+ );
649
+ case "btcGetAddress":
650
+ return callChain(
651
+ connectId,
652
+ deviceId,
653
+ "btc",
654
+ "btcGetAddress",
655
+ item,
656
+ commonParams,
657
+ false,
658
+ installContext
659
+ );
660
+ case "btcGetPublicKey":
661
+ return callChain(
662
+ connectId,
663
+ deviceId,
664
+ "btc",
665
+ "btcGetPublicKey",
666
+ item,
667
+ commonParams,
668
+ false,
669
+ installContext
670
+ );
671
+ case "solGetAddress":
672
+ return callChain(
673
+ connectId,
674
+ deviceId,
675
+ "sol",
676
+ "solGetAddress",
677
+ item,
678
+ commonParams,
679
+ false,
680
+ installContext
681
+ );
682
+ case "tronGetAddress":
683
+ return callChain(
684
+ connectId,
685
+ deviceId,
686
+ "tron",
687
+ "tronGetAddress",
688
+ item,
689
+ commonParams,
690
+ false,
691
+ installContext
692
+ );
693
+ default:
694
+ throw Object.assign(new Error(`Unsupported allNetwork method: ${method}`), {
695
+ code: HardwareErrorCode2.InvalidParams
696
+ });
697
+ }
698
+ }
699
+
700
+ // src/utils/ledgerDmkTransport.ts
701
+ function isLedgerDmkBleTransport(transport) {
702
+ const normalized = transport?.toUpperCase();
703
+ return normalized === "BLE" || normalized?.endsWith("_BLE") === true;
704
+ }
705
+ function isLedgerBleConnectionType(connectionType) {
706
+ return connectionType === "ble";
707
+ }
708
+ function isLedgerBleDescriptor(connectionType, descriptor) {
709
+ return isLedgerBleConnectionType(connectionType) || isLedgerDmkBleTransport(descriptor.transport);
710
+ }
711
+
451
712
  // src/adapter/LedgerAdapter.ts
452
713
  function formatDeviceMismatchError(expected, actual) {
453
714
  return `Wrong device: expected ${expected}, got ${actual}`;
@@ -475,6 +736,11 @@ var _LedgerAdapter = class _LedgerAdapter {
475
736
  this._btcHighIndexConfirmedThisSession = false;
476
737
  // Shared across concurrent callers — only `cancel()` aborts.
477
738
  this._doConnectAbortController = null;
739
+ this._installProgressLastEmittedValue = -Infinity;
740
+ this.allNetworkGetAddress = createAllNetworkGetAddress({
741
+ callChain: this.callChain.bind(this),
742
+ getChainFingerprint: (connectId, chain) => this.getChainFingerprint(connectId, "", chain)
743
+ });
478
744
  // ---------------------------------------------------------------------------
479
745
  // Private helpers
480
746
  // ---------------------------------------------------------------------------
@@ -528,12 +794,23 @@ var _LedgerAdapter = class _LedgerAdapter {
528
794
  );
529
795
  return;
530
796
  }
797
+ const { appName, progress } = event.payload;
798
+ const key = `${connectId}:${appName}`;
799
+ if (this._installProgressLastKey !== key || this._installProgressLastEmittedValue >= 1) {
800
+ this._installProgressLastEmittedValue = -Infinity;
801
+ this._installProgressLastKey = key;
802
+ }
803
+ const delta = progress - this._installProgressLastEmittedValue;
804
+ if (delta < _LedgerAdapter.APP_INSTALL_PROGRESS_MIN_DELTA && progress < 1) {
805
+ return;
806
+ }
807
+ this._installProgressLastEmittedValue = progress;
531
808
  this.emitter.emit("ui-event", {
532
809
  type: EConnectorInteraction.AppInstallProgress,
533
810
  payload: {
534
811
  connectId,
535
- appName: event.payload.appName,
536
- progress: event.payload.progress
812
+ appName,
813
+ progress
537
814
  }
538
815
  });
539
816
  return;
@@ -590,30 +867,42 @@ var _LedgerAdapter = class _LedgerAdapter {
590
867
  // Device management
591
868
  // ---------------------------------------------------------------------------
592
869
  async searchDevices(options) {
593
- if (options?.resetSession) {
594
- this._doConnectAbortController?.abort();
595
- this._sessions.clear();
596
- this._connectingPromise = null;
597
- this._doConnectAbortController = null;
598
- this._btcHighIndexConfirmedThisSession = false;
599
- }
600
- await this._ensureDevicePermission();
601
- const devices = await this.connector.searchDevices();
602
- this._discoveredDevices.clear();
603
- for (const d of devices) {
604
- if (d.connectId) {
605
- this._discoveredDevices.set(d.connectId, this.connectorDeviceToDeviceInfo(d));
870
+ debugLog("[LedgerAdapter][REQ]", { method: "searchDevices", params: options });
871
+ try {
872
+ if (options?.resetSession) {
873
+ this._doConnectAbortController?.abort();
874
+ this._sessions.clear();
875
+ this._connectingPromise = null;
876
+ this._doConnectAbortController = null;
877
+ this._btcHighIndexConfirmedThisSession = false;
606
878
  }
607
- }
608
- if (this._discoveredDevices.size === 0) {
609
879
  await this._ensureDevicePermission();
880
+ const devices = await this.connector.searchDevices();
881
+ this._discoveredDevices.clear();
882
+ for (const d of devices) {
883
+ if (d.connectId) {
884
+ this._discoveredDevices.set(d.connectId, this.connectorDeviceToDeviceInfo(d));
885
+ }
886
+ }
887
+ if (this._discoveredDevices.size === 0) {
888
+ await this._ensureDevicePermission();
889
+ }
890
+ const result = Array.from(this._discoveredDevices.values());
891
+ debugLog("[LedgerAdapter][RES]", {
892
+ method: "searchDevices",
893
+ success: true,
894
+ payload: result
895
+ });
896
+ return result;
897
+ } catch (err) {
898
+ const e = err;
899
+ debugLog("[LedgerAdapter][RES]", {
900
+ method: "searchDevices",
901
+ success: false,
902
+ error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
903
+ });
904
+ throw err;
610
905
  }
611
- debugLog(
612
- `[LedgerAdapter] searchDevices() return count=${this._discoveredDevices.size} ids=[${[
613
- ...this._discoveredDevices.keys()
614
- ].join(",")}]`
615
- );
616
- return Array.from(this._discoveredDevices.values());
617
906
  }
618
907
  // USB single-session invariant: evict all sessions, best-effort (see connectDevice).
619
908
  async _evictAllSessions() {
@@ -629,14 +918,15 @@ var _LedgerAdapter = class _LedgerAdapter {
629
918
  }
630
919
  static _createDeviceBusyError(method) {
631
920
  return Object.assign(new Error(`Ledger device is busy while calling ${method}`), {
632
- code: HardwareErrorCode2.DeviceBusy
921
+ code: HardwareErrorCode3.DeviceBusy
633
922
  });
634
923
  }
635
924
  async connectDevice(connectId) {
925
+ debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
636
926
  try {
637
927
  if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
638
928
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
639
- code: HardwareErrorCode2.DeviceNotFound
929
+ code: HardwareErrorCode3.DeviceNotFound
640
930
  });
641
931
  }
642
932
  if (!isLedgerBleConnectionType(this.connector.connectionType)) {
@@ -648,28 +938,79 @@ var _LedgerAdapter = class _LedgerAdapter {
648
938
  if (session.deviceInfo) {
649
939
  this._discoveredDevices.set(connectId, session.deviceInfo);
650
940
  }
651
- return success(connectId);
941
+ const result = success(connectId);
942
+ debugLog("[LedgerAdapter][RES]", { method: "connectDevice", success: true, payload: result });
943
+ return result;
652
944
  } catch (err) {
653
- return this.errorToFailure(err);
945
+ const failureResult = this.errorToFailure(err);
946
+ debugLog("[LedgerAdapter][RES]", {
947
+ method: "connectDevice",
948
+ success: false,
949
+ payload: failureResult
950
+ });
951
+ return failureResult;
654
952
  }
655
953
  }
656
954
  async disconnectDevice(connectId) {
657
- const sessionId = this._sessions.get(connectId);
658
- if (sessionId) {
659
- await this.connector.disconnect(sessionId);
660
- this._sessions.delete(connectId);
955
+ debugLog("[LedgerAdapter][REQ]", {
956
+ method: "disconnectDevice",
957
+ connectId,
958
+ params: { connectId }
959
+ });
960
+ try {
961
+ const sessionId = this._sessions.get(connectId);
962
+ if (sessionId) {
963
+ await this.connector.disconnect(sessionId);
964
+ this._sessions.delete(connectId);
965
+ }
966
+ debugLog("[LedgerAdapter][RES]", { method: "disconnectDevice", success: true });
967
+ } catch (err) {
968
+ const e = err;
969
+ debugLog("[LedgerAdapter][RES]", {
970
+ method: "disconnectDevice",
971
+ success: false,
972
+ error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
973
+ });
974
+ throw err;
661
975
  }
662
976
  }
663
977
  async getDeviceInfo(connectId, deviceId) {
664
- await this._ensureDevicePermission(connectId, deviceId);
665
- const cached = this._discoveredDevices.get(connectId) ?? Array.from(this._discoveredDevices.values()).find((d) => d.deviceId === deviceId);
666
- if (cached) {
667
- return success(cached);
978
+ debugLog("[LedgerAdapter][REQ]", {
979
+ method: "getDeviceInfo",
980
+ connectId,
981
+ params: { connectId, deviceId }
982
+ });
983
+ try {
984
+ await this._ensureDevicePermission(connectId, deviceId);
985
+ const cached = this._discoveredDevices.get(connectId) ?? Array.from(this._discoveredDevices.values()).find((d) => d.deviceId === deviceId);
986
+ if (cached) {
987
+ const result = success(cached);
988
+ debugLog("[LedgerAdapter][RES]", {
989
+ method: "getDeviceInfo",
990
+ success: true,
991
+ payload: result
992
+ });
993
+ return result;
994
+ }
995
+ const notFound = failure2(
996
+ HardwareErrorCode3.DeviceNotFound,
997
+ "Device not found in cache. Call searchDevices() or wait for a device-connected event first."
998
+ );
999
+ debugLog("[LedgerAdapter][RES]", {
1000
+ method: "getDeviceInfo",
1001
+ success: false,
1002
+ payload: notFound
1003
+ });
1004
+ return notFound;
1005
+ } catch (err) {
1006
+ const e = err;
1007
+ debugLog("[LedgerAdapter][RES]", {
1008
+ method: "getDeviceInfo",
1009
+ success: false,
1010
+ error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
1011
+ });
1012
+ throw err;
668
1013
  }
669
- return failure(
670
- HardwareErrorCode2.DeviceNotFound,
671
- "Device not found in cache. Call searchDevices() or wait for a device-connected event first."
672
- );
673
1014
  }
674
1015
  getSupportedChains() {
675
1016
  return ["evm", "btc", "sol", "tron"];
@@ -677,7 +1018,7 @@ var _LedgerAdapter = class _LedgerAdapter {
677
1018
  // ---------------------------------------------------------------------------
678
1019
  // Chain call helper
679
1020
  // ---------------------------------------------------------------------------
680
- async callChain(connectId, deviceId, chain, method, params, commonParams, skipFingerprint = false) {
1021
+ async callChain(connectId, deviceId, chain, method, params, commonParams, skipFingerprint = false, installContext) {
681
1022
  try {
682
1023
  const result = await this.connectorCall(
683
1024
  connectId,
@@ -689,183 +1030,204 @@ var _LedgerAdapter = class _LedgerAdapter {
689
1030
  skipFingerprint
690
1031
  },
691
1032
  void 0,
692
- commonParams
1033
+ commonParams,
1034
+ installContext
693
1035
  );
694
1036
  return success(result);
695
1037
  } catch (err) {
696
1038
  return this.errorToFailure(err);
697
1039
  }
698
1040
  }
1041
+ static normalizeCallArgs(connectId, deviceId, params) {
1042
+ return {
1043
+ connectId: connectId ?? "",
1044
+ deviceId: deviceId ?? "",
1045
+ params: params ?? {}
1046
+ };
1047
+ }
1048
+ static splitCommonParams(params) {
1049
+ if (params && typeof params === "object") {
1050
+ const {
1051
+ autoInstallApp,
1052
+ passphraseState: _passphraseState,
1053
+ useEmptyPassphrase: _useEmptyPassphrase,
1054
+ ...rest
1055
+ } = params;
1056
+ return {
1057
+ commonParams: {
1058
+ autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0
1059
+ },
1060
+ rest
1061
+ };
1062
+ }
1063
+ return { commonParams: {}, rest: params ?? {} };
1064
+ }
1065
+ callChainWithMergedParams(connectId, deviceId, chain, method, params) {
1066
+ const normalized = _LedgerAdapter.normalizeCallArgs(connectId, deviceId, params);
1067
+ const { commonParams, rest } = _LedgerAdapter.splitCommonParams(normalized.params);
1068
+ return this.callChain(
1069
+ normalized.connectId,
1070
+ normalized.deviceId,
1071
+ chain,
1072
+ method,
1073
+ rest,
1074
+ commonParams
1075
+ );
1076
+ }
699
1077
  // ---------------------------------------------------------------------------
700
1078
  // EVM chain methods
701
1079
  // ---------------------------------------------------------------------------
702
- evmGetAddress(connectId, deviceId, params, commonParams) {
703
- return this.callChain(
1080
+ evmGetAddress(connectId, deviceId, params) {
1081
+ return this.callChainWithMergedParams(
704
1082
  connectId,
705
1083
  deviceId,
706
1084
  "evm",
707
1085
  "evmGetAddress",
708
- params,
709
- commonParams
1086
+ params
710
1087
  );
711
1088
  }
712
- evmSignTransaction(connectId, deviceId, params, commonParams) {
713
- return this.callChain(
1089
+ evmSignTransaction(connectId, deviceId, params) {
1090
+ return this.callChainWithMergedParams(
714
1091
  connectId,
715
1092
  deviceId,
716
1093
  "evm",
717
1094
  "evmSignTransaction",
718
- params,
719
- commonParams
1095
+ params
720
1096
  );
721
1097
  }
722
- evmSignMessage(connectId, deviceId, params, commonParams) {
723
- return this.callChain(
1098
+ evmSignMessage(connectId, deviceId, params) {
1099
+ return this.callChainWithMergedParams(
724
1100
  connectId,
725
1101
  deviceId,
726
1102
  "evm",
727
1103
  "evmSignMessage",
728
- params,
729
- commonParams
1104
+ params
730
1105
  );
731
1106
  }
732
- evmSignTypedData(connectId, deviceId, params, commonParams) {
733
- return this.callChain(
1107
+ evmSignTypedData(connectId, deviceId, params) {
1108
+ return this.callChainWithMergedParams(
734
1109
  connectId,
735
1110
  deviceId,
736
1111
  "evm",
737
1112
  "evmSignTypedData",
738
- params,
739
- commonParams
1113
+ params
740
1114
  );
741
1115
  }
742
1116
  // ---------------------------------------------------------------------------
743
1117
  // BTC chain methods
744
1118
  // ---------------------------------------------------------------------------
745
- btcGetAddress(connectId, deviceId, params, commonParams) {
746
- return this.callChain(
1119
+ btcGetAddress(connectId, deviceId, params) {
1120
+ return this.callChainWithMergedParams(
747
1121
  connectId,
748
1122
  deviceId,
749
1123
  "btc",
750
1124
  "btcGetAddress",
751
- params,
752
- commonParams
1125
+ params
753
1126
  );
754
1127
  }
755
- btcGetPublicKey(connectId, deviceId, params, commonParams) {
756
- return this.callChain(
1128
+ btcGetPublicKey(connectId, deviceId, params) {
1129
+ return this.callChainWithMergedParams(
757
1130
  connectId,
758
1131
  deviceId,
759
1132
  "btc",
760
1133
  "btcGetPublicKey",
761
- params,
762
- commonParams
1134
+ params
763
1135
  );
764
1136
  }
765
- btcSignTransaction(connectId, deviceId, params, commonParams) {
766
- return this.callChain(
1137
+ btcSignTransaction(connectId, deviceId, params) {
1138
+ return this.callChainWithMergedParams(
767
1139
  connectId,
768
1140
  deviceId,
769
1141
  "btc",
770
1142
  "btcSignTransaction",
771
- params,
772
- commonParams
1143
+ params
773
1144
  );
774
1145
  }
775
- btcSignPsbt(connectId, deviceId, params, commonParams) {
776
- return this.callChain(
1146
+ btcSignPsbt(connectId, deviceId, params) {
1147
+ return this.callChainWithMergedParams(
777
1148
  connectId,
778
1149
  deviceId,
779
1150
  "btc",
780
1151
  "btcSignPsbt",
781
- params,
782
- commonParams
1152
+ params
783
1153
  );
784
1154
  }
785
- btcSignMessage(connectId, deviceId, params, commonParams) {
786
- return this.callChain(
1155
+ btcSignMessage(connectId, deviceId, params) {
1156
+ return this.callChainWithMergedParams(
787
1157
  connectId,
788
1158
  deviceId,
789
1159
  "btc",
790
1160
  "btcSignMessage",
791
- params,
792
- commonParams
1161
+ params
793
1162
  );
794
1163
  }
795
- btcGetMasterFingerprint(connectId, deviceId, commonParams) {
796
- return this.callChain(
1164
+ btcGetMasterFingerprint(connectId, deviceId, params) {
1165
+ return this.callChainWithMergedParams(
797
1166
  connectId,
798
1167
  deviceId,
799
1168
  "btc",
800
1169
  "btcGetMasterFingerprint",
801
- {},
802
- commonParams
1170
+ params
803
1171
  );
804
1172
  }
805
1173
  // ---------------------------------------------------------------------------
806
1174
  // SOL chain methods
807
1175
  // ---------------------------------------------------------------------------
808
- solGetAddress(connectId, deviceId, params, commonParams) {
809
- return this.callChain(
1176
+ solGetAddress(connectId, deviceId, params) {
1177
+ return this.callChainWithMergedParams(
810
1178
  connectId,
811
1179
  deviceId,
812
1180
  "sol",
813
1181
  "solGetAddress",
814
- params,
815
- commonParams
1182
+ params
816
1183
  );
817
1184
  }
818
- solSignTransaction(connectId, deviceId, params, commonParams) {
819
- return this.callChain(
1185
+ solSignTransaction(connectId, deviceId, params) {
1186
+ return this.callChainWithMergedParams(
820
1187
  connectId,
821
1188
  deviceId,
822
1189
  "sol",
823
1190
  "solSignTransaction",
824
- params,
825
- commonParams
1191
+ params
826
1192
  );
827
1193
  }
828
- solSignMessage(connectId, deviceId, params, commonParams) {
829
- return this.callChain(
1194
+ solSignMessage(connectId, deviceId, params) {
1195
+ return this.callChainWithMergedParams(
830
1196
  connectId,
831
1197
  deviceId,
832
1198
  "sol",
833
1199
  "solSignMessage",
834
- params,
835
- commonParams
1200
+ params
836
1201
  );
837
1202
  }
838
1203
  // ---------------------------------------------------------------------------
839
1204
  // TRON chain methods
840
1205
  // ---------------------------------------------------------------------------
841
- tronGetAddress(connectId, deviceId, params, commonParams) {
842
- return this.callChain(
1206
+ tronGetAddress(connectId, deviceId, params) {
1207
+ return this.callChainWithMergedParams(
843
1208
  connectId,
844
1209
  deviceId,
845
1210
  "tron",
846
1211
  "tronGetAddress",
847
- params,
848
- commonParams
1212
+ params
849
1213
  );
850
1214
  }
851
- tronSignTransaction(connectId, deviceId, params, commonParams) {
852
- return this.callChain(
1215
+ tronSignTransaction(connectId, deviceId, params) {
1216
+ return this.callChainWithMergedParams(
853
1217
  connectId,
854
1218
  deviceId,
855
1219
  "tron",
856
1220
  "tronSignTransaction",
857
- params,
858
- commonParams
1221
+ params
859
1222
  );
860
1223
  }
861
- tronSignMessage(connectId, deviceId, params, commonParams) {
862
- return this.callChain(
1224
+ tronSignMessage(connectId, deviceId, params) {
1225
+ return this.callChainWithMergedParams(
863
1226
  connectId,
864
1227
  deviceId,
865
1228
  "tron",
866
1229
  "tronSignMessage",
867
- params,
868
- commonParams
1230
+ params
869
1231
  );
870
1232
  }
871
1233
  // ---------------------------------------------------------------------------
@@ -889,6 +1251,15 @@ var _LedgerAdapter = class _LedgerAdapter {
889
1251
  return this.errorToFailure(err);
890
1252
  }
891
1253
  }
1254
+ // Offline app-presence + unlock probe. No manager-api catalog (unlike listInstalledApps).
1255
+ async listInstalledNames(connectId) {
1256
+ try {
1257
+ const result = await this.connectorCall(connectId, "listInstalledNames", {});
1258
+ return success(result);
1259
+ } catch (err) {
1260
+ return this.errorToFailure(err);
1261
+ }
1262
+ }
892
1263
  async listAvailableApps(connectId) {
893
1264
  try {
894
1265
  const result = await this.connectorCall(connectId, "listAvailableApps", {});
@@ -921,7 +1292,7 @@ var _LedgerAdapter = class _LedgerAdapter {
921
1292
  }
922
1293
  cancel(connectId) {
923
1294
  const userAbortReason = Object.assign(new Error("User aborted operation"), {
924
- code: HardwareErrorCode2.UserAborted,
1295
+ code: HardwareErrorCode3.UserAborted,
925
1296
  _tag: ERROR_TAG.UserAborted
926
1297
  });
927
1298
  this._lastCancelReason = userAbortReason;
@@ -978,7 +1349,7 @@ var _LedgerAdapter = class _LedgerAdapter {
978
1349
  return { success: false, expected: deviceId, actual: fingerprint };
979
1350
  } catch (err) {
980
1351
  const mapped = mapLedgerError(err);
981
- if (mapped.code === HardwareErrorCode2.WrongApp || mapped.code === HardwareErrorCode2.DeviceLocked) {
1352
+ if (mapped.code === HardwareErrorCode3.WrongApp || mapped.code === HardwareErrorCode3.DeviceLocked) {
982
1353
  return { success: true };
983
1354
  }
984
1355
  throw err;
@@ -1058,14 +1429,14 @@ var _LedgerAdapter = class _LedgerAdapter {
1058
1429
  });
1059
1430
  throw Object.assign(new Error("User cancelled Ledger connection"), {
1060
1431
  _tag: ERROR_TAG.UserAborted,
1061
- code: HardwareErrorCode2.UserAborted,
1432
+ code: HardwareErrorCode3.UserAborted,
1062
1433
  cause: err
1063
1434
  });
1064
1435
  }
1065
1436
  if (!payload?.confirmed) {
1066
1437
  throw Object.assign(new Error("User cancelled Ledger connection"), {
1067
1438
  _tag: ERROR_TAG.UserAborted,
1068
- code: HardwareErrorCode2.UserAborted
1439
+ code: HardwareErrorCode3.UserAborted
1069
1440
  });
1070
1441
  }
1071
1442
  await new Promise((resolve) => {
@@ -1144,7 +1515,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1144
1515
  if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
1145
1516
  if (isLedgerBleConnectionType(this.connector.connectionType) && !connectId) {
1146
1517
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
1147
- code: HardwareErrorCode2.DeviceNotFound
1518
+ code: HardwareErrorCode3.DeviceNotFound
1148
1519
  });
1149
1520
  }
1150
1521
  if (connectId && this._sessions.has(connectId)) return connectId;
@@ -1154,7 +1525,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1154
1525
  new Error(
1155
1526
  "Ledger USB session invariant violated: more than one session is active. Please reconnect the device."
1156
1527
  ),
1157
- { code: HardwareErrorCode2.DeviceOneDeviceOnly }
1528
+ { code: HardwareErrorCode3.DeviceOneDeviceOnly }
1158
1529
  );
1159
1530
  }
1160
1531
  return this._sessions.keys().next().value;
@@ -1203,7 +1574,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1203
1574
  if (devices.length === 0) {
1204
1575
  for (let i = 0; i < 3 && !internalSignal.aborted; i += 1) {
1205
1576
  await new Promise((resolve) => {
1206
- setTimeout(resolve, 1e3);
1577
+ setTimeout(resolve, DEVICE_CONNECT_RETRY_DELAY_MS);
1207
1578
  });
1208
1579
  devices = await this.searchDevices();
1209
1580
  if (devices.length > 0) break;
@@ -1234,7 +1605,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1234
1605
  new Error(
1235
1606
  "Device not connected after multiple attempts. Please ensure your Ledger is awake, unlocked, and in range, then try again."
1236
1607
  ),
1237
- { code: HardwareErrorCode2.DeviceNotFound }
1608
+ { code: HardwareErrorCode3.DeviceNotFound }
1238
1609
  );
1239
1610
  }
1240
1611
  await this._waitForDeviceConnect(internalSignal);
@@ -1258,7 +1629,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1258
1629
  return this._connectDeviceOrThrow(devices[0].connectId);
1259
1630
  }
1260
1631
  const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
1261
- code: HardwareErrorCode2.DeviceNotFound
1632
+ code: HardwareErrorCode3.DeviceNotFound
1262
1633
  });
1263
1634
  if (isLedgerBleConnectionType(this.connector.connectionType)) {
1264
1635
  err._tag = ERROR_TAG.DeviceNotAdvertising;
@@ -1267,7 +1638,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1267
1638
  }
1268
1639
  if (isLedgerBleConnectionType(this.connector.connectionType)) {
1269
1640
  throw Object.assign(new Error("Ledger BLE connectId is required."), {
1270
- code: HardwareErrorCode2.DeviceNotFound
1641
+ code: HardwareErrorCode3.DeviceNotFound
1271
1642
  });
1272
1643
  }
1273
1644
  if (devices.length > 1) {
@@ -1275,7 +1646,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1275
1646
  }
1276
1647
  if (devices.length !== 1) {
1277
1648
  throw Object.assign(new Error("Ledger device not found."), {
1278
- code: HardwareErrorCode2.DeviceNotFound
1649
+ code: HardwareErrorCode3.DeviceNotFound
1279
1650
  });
1280
1651
  }
1281
1652
  return this._connectDeviceOrThrow(devices[0].connectId);
@@ -1326,26 +1697,43 @@ var _LedgerAdapter = class _LedgerAdapter {
1326
1697
  const result = signal ? await this._abortable(signal, promise) : await promise;
1327
1698
  return this._unwrapConnectorResult(result);
1328
1699
  }
1329
- async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams) {
1330
- debugLog("[LedgerAdapter] connectorCall:", method, "connectId:", connectId || "(empty)");
1700
+ async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
1701
+ debugLog("[LedgerAdapter][REQ]", { method, connectId: connectId || "(empty)", params });
1331
1702
  const queueKey = connectId || "__ledger_default__";
1332
- return this._jobQueue.enqueue(
1333
- queueKey,
1334
- async (signal) => this._runConnectorCall(
1335
- connectId,
1703
+ try {
1704
+ const result = await this._jobQueue.enqueue(
1705
+ queueKey,
1706
+ async (signal) => this._runConnectorCall(
1707
+ connectId,
1708
+ method,
1709
+ params,
1710
+ signal,
1711
+ fingerprint,
1712
+ permissionDeviceId,
1713
+ commonParams,
1714
+ installContext
1715
+ ),
1716
+ {
1717
+ label: method,
1718
+ rejectIfBusy: true,
1719
+ busyError: _LedgerAdapter._createDeviceBusyError(method)
1720
+ }
1721
+ );
1722
+ debugLog("[LedgerAdapter][RES]", { method, success: true, payload: result });
1723
+ return result;
1724
+ } catch (err) {
1725
+ const e = err;
1726
+ debugLog("[LedgerAdapter][RES]", {
1336
1727
  method,
1337
- params,
1338
- signal,
1339
- fingerprint,
1340
- permissionDeviceId,
1341
- commonParams
1342
- ),
1343
- {
1344
- label: method,
1345
- rejectIfBusy: true,
1346
- busyError: _LedgerAdapter._createDeviceBusyError(method)
1347
- }
1348
- );
1728
+ success: false,
1729
+ error: {
1730
+ message: e?.message,
1731
+ _tag: e?._tag,
1732
+ code: e?.code ?? e?.errorCode
1733
+ }
1734
+ });
1735
+ throw err;
1736
+ }
1349
1737
  }
1350
1738
  /**
1351
1739
  * Race a promise against an abort signal. On abort, rejects with
@@ -1380,7 +1768,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1380
1768
  }
1381
1769
  }
1382
1770
  /** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
1383
- async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams) {
1771
+ async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext) {
1384
1772
  _LedgerAdapter._throwIfAborted(signal);
1385
1773
  await this._ensureDevicePermission(
1386
1774
  connectId,
@@ -1394,7 +1782,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1394
1782
  if (gatedParams === null) {
1395
1783
  throw Object.assign(new Error("User cancelled BTC high-index confirmation"), {
1396
1784
  _tag: ERROR_TAG.UserAborted,
1397
- code: HardwareErrorCode2.UserAborted
1785
+ code: HardwareErrorCode3.UserAborted
1398
1786
  });
1399
1787
  }
1400
1788
  effectiveParams = gatedParams;
@@ -1423,7 +1811,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1423
1811
  );
1424
1812
  if (!fp.success) {
1425
1813
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1426
- code: HardwareErrorCode2.DeviceMismatch
1814
+ code: HardwareErrorCode3.DeviceMismatch
1427
1815
  });
1428
1816
  }
1429
1817
  }
@@ -1442,22 +1830,62 @@ var _LedgerAdapter = class _LedgerAdapter {
1442
1830
  isStuckApp: isStuckAppStateError(err)
1443
1831
  });
1444
1832
  const autoInstallApp = commonParams?.autoInstallApp ?? this._defaultAutoInstallApp;
1445
- const isAppMissing = isAppNotInstalledError(err) || err?.code === HardwareErrorCode2.AppNotInstalled;
1833
+ const isAppMissing = isAppNotInstalledError(err) || err?.code === HardwareErrorCode3.AppNotInstalled;
1446
1834
  if (autoInstallApp && isAppMissing) {
1835
+ if (installContext?.deviceOutOfMemoryError) {
1836
+ throw installContext.deviceOutOfMemoryError;
1837
+ }
1447
1838
  const appName = err?.appName ?? mapLedgerError(err).appName;
1448
1839
  if (appName) {
1840
+ if (installContext?.installAttemptedAppNames?.has(appName)) {
1841
+ throw createHwkError({
1842
+ code: HardwareErrorCode3.AppNotInstalled,
1843
+ message: `${appName} install reported success but the app is still missing on device`,
1844
+ _tag: ERROR_TAG.AppInstallVerifyFailed,
1845
+ appName
1846
+ });
1847
+ }
1449
1848
  const confirmed = await this._waitForInstallAppConfirm(appName);
1450
- if (!confirmed) throw err;
1849
+ if (!confirmed) {
1850
+ throw createHwkError({
1851
+ code: HardwareErrorCode3.UserAborted,
1852
+ message: `User declined to install ${appName}`,
1853
+ _tag: ERROR_TAG.UserAborted,
1854
+ appName
1855
+ });
1856
+ }
1451
1857
  this.emitter.emit("ui-event", {
1452
1858
  type: EConnectorInteraction.AppInstallProgress,
1453
1859
  payload: { connectId: resolvedConnectId, appName, progress: 0 }
1454
1860
  });
1455
- await this._callConnector(sessionId, "installApp", { appName }, signal);
1861
+ try {
1862
+ await this._callConnector(sessionId, "installApp", { appName }, signal);
1863
+ } catch (installErr) {
1864
+ if (mapLedgerError(installErr).code === HardwareErrorCode3.DeviceOutOfMemory) {
1865
+ if (installContext) {
1866
+ installContext.deviceOutOfMemoryError = installErr;
1867
+ }
1868
+ }
1869
+ throw installErr;
1870
+ }
1871
+ if (installContext) {
1872
+ installContext.installAttemptedAppNames = installContext.installAttemptedAppNames ?? /* @__PURE__ */ new Set();
1873
+ installContext.installAttemptedAppNames.add(appName);
1874
+ }
1456
1875
  this.emitter.emit(UI_REQUEST.CLOSE_UI_WINDOW, {
1457
1876
  type: UI_REQUEST.CLOSE_UI_WINDOW,
1458
1877
  payload: {}
1459
1878
  });
1460
- return await this._callConnector(sessionId, method, effectiveParams, signal);
1879
+ return await this._runConnectorCall(
1880
+ resolvedConnectId,
1881
+ method,
1882
+ effectiveParams,
1883
+ signal,
1884
+ fingerprint,
1885
+ permissionDeviceId,
1886
+ commonParams,
1887
+ installContext
1888
+ );
1461
1889
  }
1462
1890
  }
1463
1891
  if (isStuckAppStateError(err)) {
@@ -1532,7 +1960,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1532
1960
  );
1533
1961
  if (!fp.success) {
1534
1962
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1535
- code: HardwareErrorCode2.DeviceMismatch
1963
+ code: HardwareErrorCode3.DeviceMismatch
1536
1964
  });
1537
1965
  }
1538
1966
  }
@@ -1567,7 +1995,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1567
1995
  this.connector.reset();
1568
1996
  const codeNum = err?.code;
1569
1997
  throw Object.assign(err, {
1570
- code: codeNum ?? HardwareErrorCode2.DeviceDisconnected
1998
+ code: codeNum ?? HardwareErrorCode3.DeviceDisconnected
1571
1999
  });
1572
2000
  }
1573
2001
  throw err;
@@ -1601,7 +2029,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1601
2029
  );
1602
2030
  if (!fp.success) {
1603
2031
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1604
- code: HardwareErrorCode2.DeviceMismatch
2032
+ code: HardwareErrorCode3.DeviceMismatch
1605
2033
  });
1606
2034
  }
1607
2035
  }
@@ -1658,7 +2086,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1658
2086
  );
1659
2087
  if (!fp.success) {
1660
2088
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1661
- code: HardwareErrorCode2.DeviceMismatch
2089
+ code: HardwareErrorCode3.DeviceMismatch
1662
2090
  });
1663
2091
  }
1664
2092
  }
@@ -1699,7 +2127,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1699
2127
  );
1700
2128
  if (!fp.success) {
1701
2129
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1702
- code: HardwareErrorCode2.DeviceMismatch
2130
+ code: HardwareErrorCode3.DeviceMismatch
1703
2131
  });
1704
2132
  }
1705
2133
  }
@@ -1749,7 +2177,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1749
2177
  });
1750
2178
  throw Object.assign(new Error("Device permission request superseded"), {
1751
2179
  _tag: ERROR_TAG.UserAborted,
1752
- code: HardwareErrorCode2.UserAborted,
2180
+ code: HardwareErrorCode3.UserAborted,
1753
2181
  cause: err
1754
2182
  });
1755
2183
  }
@@ -1760,7 +2188,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1760
2188
  const { granted, reason, message } = response;
1761
2189
  if (!granted) {
1762
2190
  throw Object.assign(new Error(message ?? "Device permission denied"), {
1763
- code: HardwareErrorCode2.DevicePermissionDenied,
2191
+ code: HardwareErrorCode3.DevicePermissionDenied,
1764
2192
  reason
1765
2193
  });
1766
2194
  }
@@ -1774,7 +2202,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1774
2202
  const tag = err && typeof err === "object" ? err._tag : void 0;
1775
2203
  if (err && typeof err === "object" && "code" in err && typeof err.code === "number") {
1776
2204
  const e = err;
1777
- const params = e.code === HardwareErrorCode2.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
2205
+ const params = e.code === HardwareErrorCode3.DevicePermissionDenied && e.reason ? { permissionDeniedReason: e.reason } : e.params;
1778
2206
  return ledgerFailure(e.code, e.message ?? "Unknown error", e.appName, tag, params);
1779
2207
  }
1780
2208
  const mapped = mapLedgerError(err);
@@ -1821,12 +2249,16 @@ _LedgerAdapter.STUCK_APP_RETRY_DELAY_MS = 500;
1821
2249
  // Confirm but device never shows up / never unlocks), throw DeviceNotFound
1822
2250
  // instead of looping forever.
1823
2251
  _LedgerAdapter.MAX_DOCONNECT_CONFIRMS = 3;
2252
+ // Throttle AppInstallProgress by progress delta — DMK streams much faster
2253
+ // than UIs need. Final frame (progress >= 1) always passes.
2254
+ _LedgerAdapter.APP_INSTALL_PROGRESS_MIN_DELTA = 0.05;
1824
2255
  var LedgerAdapter = _LedgerAdapter;
1825
2256
 
1826
2257
  // src/connector/LedgerConnectorBase.ts
1827
2258
  import {
1828
2259
  EConnectorInteraction as EConnectorInteraction4,
1829
- HardwareErrorCode as HardwareErrorCode7,
2260
+ HardwareErrorCode as HardwareErrorCode8,
2261
+ isKnownNonTargetHardwareVendor,
1830
2262
  serializeConnectorError
1831
2263
  } from "@onekeyfe/hwk-adapter-core";
1832
2264
 
@@ -2383,7 +2815,7 @@ var SignerManager = class _SignerManager {
2383
2815
  };
2384
2816
 
2385
2817
  // src/connector/chains/evm.ts
2386
- import { HardwareErrorCode as HardwareErrorCode3, padHex64, stripHex } from "@onekeyfe/hwk-adapter-core";
2818
+ import { HardwareErrorCode as HardwareErrorCode4, padHex64, stripHex } from "@onekeyfe/hwk-adapter-core";
2387
2819
 
2388
2820
  // src/connector/chains/utils.ts
2389
2821
  import { EConnectorInteraction as EConnectorInteraction2 } from "@onekeyfe/hwk-adapter-core";
@@ -2425,7 +2857,7 @@ async function evmSignTransaction(ctx, sessionId, params) {
2425
2857
  new Error(
2426
2858
  "Ledger requires a pre-serialized transaction (serializedTx). Provide an RLP-encoded hex string."
2427
2859
  ),
2428
- { code: HardwareErrorCode3.InvalidParams }
2860
+ { code: HardwareErrorCode4.InvalidParams }
2429
2861
  );
2430
2862
  }
2431
2863
  const signer = await _getEthSigner(ctx, sessionId);
@@ -2466,7 +2898,7 @@ async function evmSignTypedData(ctx, sessionId, params) {
2466
2898
  new Error(
2467
2899
  'Ledger does not support hash-only EIP-712 signing. Use mode "full" with the complete typed data structure.'
2468
2900
  ),
2469
- { code: HardwareErrorCode3.MethodNotSupported }
2901
+ { code: HardwareErrorCode4.MethodNotSupported }
2470
2902
  );
2471
2903
  }
2472
2904
  const signer = await _getEthSigner(ctx, sessionId);
@@ -2501,7 +2933,7 @@ async function _getEthSigner(ctx, sessionId) {
2501
2933
  }
2502
2934
 
2503
2935
  // src/connector/chains/btc.ts
2504
- import { HardwareErrorCode as HardwareErrorCode4, stripHex as stripHex2 } from "@onekeyfe/hwk-adapter-core";
2936
+ import { HardwareErrorCode as HardwareErrorCode5, stripHex as stripHex2 } from "@onekeyfe/hwk-adapter-core";
2505
2937
  import { Psbt } from "bitcoinjs-lib";
2506
2938
 
2507
2939
  // src/signer/SignerBtc.ts
@@ -2622,7 +3054,7 @@ function _purposeToTemplate(purpose, DDT) {
2622
3054
  return DDT.TAPROOT;
2623
3055
  default:
2624
3056
  throw Object.assign(new Error(`Unsupported BTC purpose: m/${purpose ?? "<undefined>"}'`), {
2625
- code: HardwareErrorCode4.InvalidParams
3057
+ code: HardwareErrorCode5.InvalidParams
2626
3058
  });
2627
3059
  }
2628
3060
  }
@@ -2678,7 +3110,7 @@ async function btcSignTransaction(ctx, sessionId, params) {
2678
3110
  if (!params.psbt) {
2679
3111
  throw Object.assign(
2680
3112
  new Error("Ledger requires PSBT format for BTC transaction signing. Provide params.psbt."),
2681
- { code: HardwareErrorCode4.InvalidParams }
3113
+ { code: HardwareErrorCode5.InvalidParams }
2682
3114
  );
2683
3115
  }
2684
3116
  const btcSigner = await _createBtcSigner(ctx, sessionId);
@@ -2703,7 +3135,7 @@ async function btcSignTransaction(ctx, sessionId, params) {
2703
3135
  async function btcSignPsbt(ctx, sessionId, params) {
2704
3136
  if (!params.psbt) {
2705
3137
  throw Object.assign(new Error("btcSignPsbt requires params.psbt"), {
2706
- code: HardwareErrorCode4.InvalidParams
3138
+ code: HardwareErrorCode5.InvalidParams
2707
3139
  });
2708
3140
  }
2709
3141
  const btcSigner = await _createBtcSigner(ctx, sessionId);
@@ -2948,7 +3380,7 @@ async function _createSolSigner(ctx, sessionId) {
2948
3380
  }
2949
3381
 
2950
3382
  // src/connector/chains/tron.ts
2951
- import { HardwareErrorCode as HardwareErrorCode6 } from "@onekeyfe/hwk-adapter-core";
3383
+ import { HardwareErrorCode as HardwareErrorCode7 } from "@onekeyfe/hwk-adapter-core";
2952
3384
  import Trx from "@ledgerhq/hw-app-trx";
2953
3385
 
2954
3386
  // src/connector/chains/legacyChainCall.ts
@@ -2961,7 +3393,7 @@ import {
2961
3393
  OpenAppCommand,
2962
3394
  isSuccessCommandResult
2963
3395
  } from "@ledgerhq/device-management-kit";
2964
- import { HardwareErrorCode as HardwareErrorCode5 } from "@onekeyfe/hwk-adapter-core";
3396
+ import { HardwareErrorCode as HardwareErrorCode6 } from "@onekeyfe/hwk-adapter-core";
2965
3397
  var APP_NAME_MAP = {
2966
3398
  ETH: "Ethereum",
2967
3399
  BTC: "Bitcoin",
@@ -3065,15 +3497,30 @@ var AppManager = class {
3065
3497
  command: new OpenAppCommand({ appName })
3066
3498
  });
3067
3499
  if (!isSuccessCommandResult(result)) {
3068
- const { statusCode } = result;
3069
- const hasStatusCode2 = statusCode != null && statusCode !== "";
3070
- debugLog("[AppManager] openApp failed:", appName, "statusCode:", statusCode);
3500
+ const dmkErr = result.error;
3501
+ const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
3502
+ const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
3503
+ debugLog(
3504
+ "[AppManager] openApp failed:",
3505
+ appName,
3506
+ "errorCode:",
3507
+ errorCode,
3508
+ "tag:",
3509
+ dmkErr._tag
3510
+ );
3511
+ let code;
3512
+ if (errorCode === "6807" || /unknown application/i.test(message)) {
3513
+ code = HardwareErrorCode6.AppNotInstalled;
3514
+ } else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
3515
+ code = HardwareErrorCode6.UserRejected;
3516
+ }
3071
3517
  throw Object.assign(new Error(`Failed to open "${appName}"`), {
3072
3518
  _tag: ERROR_TAG.OpenAppCommand,
3073
- code: hasStatusCode2 ? void 0 : HardwareErrorCode5.AppNotInstalled,
3074
- errorCode: String(statusCode ?? ""),
3075
- statusCode,
3076
- appName
3519
+ code,
3520
+ errorCode,
3521
+ statusCode: errorCode,
3522
+ appName,
3523
+ originalError: dmkErr
3077
3524
  });
3078
3525
  }
3079
3526
  }
@@ -3252,7 +3699,7 @@ async function tronSignTransaction(ctx, sessionId, params) {
3252
3699
  if (!params.rawTxHex) {
3253
3700
  throw Object.assign(
3254
3701
  new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
3255
- { code: HardwareErrorCode6.InvalidParams }
3702
+ { code: HardwareErrorCode7.InvalidParams }
3256
3703
  );
3257
3704
  }
3258
3705
  const path = normalizePath(params.path);
@@ -3413,6 +3860,22 @@ var DeviceApps = class {
3413
3860
  );
3414
3861
  return result.filter((a) => a !== null).map(applicationToMetadata);
3415
3862
  }
3863
+ /** Installed app names via ListApps APDU. Offline (no manager-api), but still requires unlock + dashboard. */
3864
+ async listInstalledNames(options) {
3865
+ const action = this._dmk.executeDeviceAction({
3866
+ sessionId: this._sessionId,
3867
+ deviceAction: new this._ledgerKit.ListAppsDeviceAction({
3868
+ input: { unlockTimeout: options?.unlockTimeout }
3869
+ })
3870
+ });
3871
+ const result = await deviceActionToPromise(
3872
+ action,
3873
+ this.onInteraction,
3874
+ void 0,
3875
+ this.onRegisterCanceller
3876
+ );
3877
+ return result.map((entry) => entry?.appName).filter((name) => typeof name === "string" && name.length > 0);
3878
+ }
3416
3879
  // Catalog lookup via custom device action — DMK has no typed wrapper for this.
3417
3880
  async listAvailable() {
3418
3881
  const customAction = new ListAvailableAppsDeviceAction({
@@ -3476,6 +3939,22 @@ var DeviceApps = class {
3476
3939
  async install(appName, onProgress, options) {
3477
3940
  if (!appName) throw new Error("DeviceApps.install: appName is required");
3478
3941
  debugLog("[DeviceApps] install:", appName);
3942
+ const refreshAction = this._dmk.executeDeviceAction({
3943
+ sessionId: this._sessionId,
3944
+ deviceAction: new this._ledgerKit.GetDeviceMetadataDeviceAction({
3945
+ input: {
3946
+ useSecureChannel: true,
3947
+ forceUpdate: true,
3948
+ unlockTimeout: options?.unlockTimeout
3949
+ }
3950
+ })
3951
+ });
3952
+ await deviceActionToPromise(
3953
+ refreshAction,
3954
+ this.onInteraction,
3955
+ INSTALL_TIMEOUT_MS,
3956
+ this.onRegisterCanceller
3957
+ );
3479
3958
  const action = this._dmk.executeDeviceAction({
3480
3959
  sessionId: this._sessionId,
3481
3960
  deviceAction: new this._ledgerKit.InstallOrUpdateAppsDeviceAction({
@@ -3553,7 +4032,7 @@ var METHOD_PREFIX_TO_APP_NAME = {
3553
4032
  tron: "Tron"
3554
4033
  };
3555
4034
  var HARDWARE_ERROR_CODE_VALUES = new Set(
3556
- Object.values(HardwareErrorCode7).filter((value) => typeof value === "number")
4035
+ Object.values(HardwareErrorCode8).filter((value) => typeof value === "number")
3557
4036
  );
3558
4037
  var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
3559
4038
  async function defaultLedgerKitImporter(pkg) {
@@ -3676,7 +4155,7 @@ var LedgerConnectorBase = class {
3676
4155
  async searchDevices() {
3677
4156
  const dm = await this._getDeviceManager();
3678
4157
  const descriptors = await this._discoverDescriptors(dm);
3679
- const resolvedDescriptors = descriptors.map((d) => ({
4158
+ const resolvedDescriptors = descriptors.filter((d) => !isKnownNonTargetHardwareVendor(d, "ledger")).map((d) => ({
3680
4159
  descriptor: d,
3681
4160
  connectId: this._resolveConnectId(d)
3682
4161
  }));
@@ -3724,7 +4203,7 @@ var LedgerConnectorBase = class {
3724
4203
  `Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
3725
4204
  );
3726
4205
  err._tag = ERROR_TAG.BlePairingTimeout;
3727
- err.code = HardwareErrorCode7.BlePairingTimeout;
4206
+ err.code = HardwareErrorCode8.BlePairingTimeout;
3728
4207
  reject(err);
3729
4208
  }, HANG_CEILING_MS);
3730
4209
  });
@@ -3757,7 +4236,7 @@ var LedgerConnectorBase = class {
3757
4236
  "Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
3758
4237
  );
3759
4238
  err._tag = ERROR_TAG.DeviceNotAdvertising;
3760
- err.code = HardwareErrorCode7.DeviceNotFound;
4239
+ err.code = HardwareErrorCode8.DeviceNotFound;
3761
4240
  throw err;
3762
4241
  };
3763
4242
  const doConnect = async (path) => {
@@ -3821,7 +4300,7 @@ var LedgerConnectorBase = class {
3821
4300
  "Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
3822
4301
  );
3823
4302
  wrapped._tag = ERROR_TAG.BleGattBondingFailed;
3824
- wrapped.code = HardwareErrorCode7.BlePairingTimeout;
4303
+ wrapped.code = HardwareErrorCode8.BlePairingTimeout;
3825
4304
  wrapped.originalError = err;
3826
4305
  throw wrapped;
3827
4306
  }
@@ -3899,7 +4378,7 @@ var LedgerConnectorBase = class {
3899
4378
  this._unwatchSessionState(sessionId);
3900
4379
  this._signerManager?.invalidate(sessionId);
3901
4380
  this._cancellers.get(sessionId)?.({
3902
- code: HardwareErrorCode7.DeviceDisconnected,
4381
+ code: HardwareErrorCode8.DeviceDisconnected,
3903
4382
  tag: "DeviceDisconnected",
3904
4383
  message: "Device disconnected"
3905
4384
  });
@@ -3925,7 +4404,7 @@ var LedgerConnectorBase = class {
3925
4404
  success: false,
3926
4405
  error: serializeConnectorError(
3927
4406
  Object.assign(new Error("Ledger app is unresponsive"), {
3928
- code: HardwareErrorCode7.DeviceAppStuck,
4407
+ code: HardwareErrorCode8.DeviceAppStuck,
3929
4408
  _tag: ERROR_TAG.DeviceAppStuck,
3930
4409
  originalError: err
3931
4410
  })
@@ -3937,7 +4416,7 @@ var LedgerConnectorBase = class {
3937
4416
  success: false,
3938
4417
  error: serializeConnectorError(
3939
4418
  Object.assign(new Error("Device communication interrupted, please retry"), {
3940
- code: HardwareErrorCode7.TransportError,
4419
+ code: HardwareErrorCode8.TransportError,
3941
4420
  _tag: ERROR_TAG.DeviceTransportStuck,
3942
4421
  originalError: err
3943
4422
  })
@@ -4033,6 +4512,17 @@ var LedgerConnectorBase = class {
4033
4512
  ctx.clearCanceller(sessionId);
4034
4513
  }
4035
4514
  }
4515
+ case "listInstalledNames": {
4516
+ const apps = await _getDeviceApps(ctx, sessionId);
4517
+ try {
4518
+ return await apps.listInstalledNames(params);
4519
+ } catch (err) {
4520
+ ctx.invalidateSession(sessionId);
4521
+ throw ctx.wrapError(err);
4522
+ } finally {
4523
+ ctx.clearCanceller(sessionId);
4524
+ }
4525
+ }
4036
4526
  case "listAvailableApps": {
4037
4527
  const apps = await _getDeviceApps(ctx, sessionId);
4038
4528
  try {