@onekeyfe/hwk-ledger-adapter 1.1.26-alpha.12 → 1.1.26-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.d.mts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { IHardwareWallet, IConnector, TransportType, UiResponseEvent, DeviceInfo, Response, ChainCapability, EvmGetAddressParams, EvmAddress, EvmSignTxParams, EvmSignedTx, EvmSignMsgParams, EvmSignature, EvmSignTypedDataParams, BtcGetAddressParams, BtcAddress, BtcGetPublicKeyParams, BtcPublicKey, BtcSignTxParams, BtcSignedTx, BtcSignPsbtParams, BtcSignedPsbt, BtcSignMsgParams, BtcSignature, SolGetAddressParams, SolAddress, SolSignTxParams, SolSignedTx, SolSignMsgParams, SolSignature, TronGetAddressParams, TronAddress, TronSignTxParams, TronSignedTx, TronSignMsgParams, TronSignature, HardwareEventMap, DeviceEventListener, ChainForFingerprint, DeviceDescriptor, DeviceChangeEvent, ConnectionType, ConnectorDevice, ConnectorSession, ConnectorEventType, ConnectorEventMap, Failure, HardwareErrorCode } from '@onekeyfe/hwk-adapter-core';
2
2
  import { DeviceActionState as DeviceActionState$1, DiscoveredDevice, ExecuteDeviceActionReturnType, DeviceManagementKit } from '@ledgerhq/device-management-kit';
3
3
  import { Address, Signature, SignerEth as SignerEth$1, TypedData } from '@ledgerhq/device-signer-kit-ethereum';
4
+ import { ContextModule } from '@ledgerhq/context-module';
4
5
  import { SignerBtc as SignerBtc$1 } from '@ledgerhq/device-signer-kit-bitcoin';
5
6
  import { SignerSolana } from '@ledgerhq/device-signer-kit-solana';
6
7
  import Transport from '@ledgerhq/hw-transport';
@@ -39,6 +40,7 @@ declare class LedgerAdapter implements IHardwareWallet {
39
40
  uiResponse(response: UiResponseEvent): void;
40
41
  searchDevices(): Promise<DeviceInfo[]>;
41
42
  private static readonly MAX_BUSINESS_RETRY_BUDGET;
43
+ private static readonly STUCK_APP_RETRY_DELAY_MS;
42
44
  private static readonly MAX_DOCONNECT_CONFIRMS;
43
45
  private _lastCancelReason;
44
46
  private static _createDeviceBusyError;
@@ -132,6 +134,16 @@ declare class LedgerAdapter implements IHardwareWallet {
132
134
  private static _throwIfAborted;
133
135
  /** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
134
136
  private _runConnectorCall;
137
+ /**
138
+ * Stuck-app recovery: pause for the device's UI transition, then retry once.
139
+ *
140
+ * Caller has already cleared the session + reset connector. We wait so Stax
141
+ * finishes its post-CloseApp animation, then go through ensureConnected +
142
+ * fingerprint check + call exactly once. Caller decides what to do on a
143
+ * second stuck-app hit.
144
+ */
145
+ private _retryAfterStuckApp;
146
+ private _sleepAbortable;
135
147
  /**
136
148
  * Clear stale session, reconnect, and retry the call.
137
149
  *
@@ -446,8 +458,14 @@ type SignerEthBuilderFn = (args: {
446
458
  dmk: DeviceManagementKit;
447
459
  sessionId: string;
448
460
  }) => {
461
+ withContextModule?(contextModule: ContextModule): {
462
+ build(): SignerEth$1;
463
+ };
449
464
  build(): SignerEth$1;
450
465
  } | Promise<{
466
+ withContextModule?(contextModule: ContextModule): {
467
+ build(): SignerEth$1;
468
+ };
451
469
  build(): SignerEth$1;
452
470
  }>;
453
471
  /**
@@ -463,6 +481,8 @@ declare class SignerManager {
463
481
  invalidate(_sessionId: string): void;
464
482
  clearAll(): void;
465
483
  private static _defaultBuilder;
484
+ private static _createContextModule;
485
+ static wrapBlindSigningReportNonBlocking<T extends ContextModule>(contextModule: T): T;
466
486
  }
467
487
 
468
488
  type BtcWallet = Parameters<SignerBtc$1['getWalletAddress']>[0];
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { IHardwareWallet, IConnector, TransportType, UiResponseEvent, DeviceInfo, Response, ChainCapability, EvmGetAddressParams, EvmAddress, EvmSignTxParams, EvmSignedTx, EvmSignMsgParams, EvmSignature, EvmSignTypedDataParams, BtcGetAddressParams, BtcAddress, BtcGetPublicKeyParams, BtcPublicKey, BtcSignTxParams, BtcSignedTx, BtcSignPsbtParams, BtcSignedPsbt, BtcSignMsgParams, BtcSignature, SolGetAddressParams, SolAddress, SolSignTxParams, SolSignedTx, SolSignMsgParams, SolSignature, TronGetAddressParams, TronAddress, TronSignTxParams, TronSignedTx, TronSignMsgParams, TronSignature, HardwareEventMap, DeviceEventListener, ChainForFingerprint, DeviceDescriptor, DeviceChangeEvent, ConnectionType, ConnectorDevice, ConnectorSession, ConnectorEventType, ConnectorEventMap, Failure, HardwareErrorCode } from '@onekeyfe/hwk-adapter-core';
2
2
  import { DeviceActionState as DeviceActionState$1, DiscoveredDevice, ExecuteDeviceActionReturnType, DeviceManagementKit } from '@ledgerhq/device-management-kit';
3
3
  import { Address, Signature, SignerEth as SignerEth$1, TypedData } from '@ledgerhq/device-signer-kit-ethereum';
4
+ import { ContextModule } from '@ledgerhq/context-module';
4
5
  import { SignerBtc as SignerBtc$1 } from '@ledgerhq/device-signer-kit-bitcoin';
5
6
  import { SignerSolana } from '@ledgerhq/device-signer-kit-solana';
6
7
  import Transport from '@ledgerhq/hw-transport';
@@ -39,6 +40,7 @@ declare class LedgerAdapter implements IHardwareWallet {
39
40
  uiResponse(response: UiResponseEvent): void;
40
41
  searchDevices(): Promise<DeviceInfo[]>;
41
42
  private static readonly MAX_BUSINESS_RETRY_BUDGET;
43
+ private static readonly STUCK_APP_RETRY_DELAY_MS;
42
44
  private static readonly MAX_DOCONNECT_CONFIRMS;
43
45
  private _lastCancelReason;
44
46
  private static _createDeviceBusyError;
@@ -132,6 +134,16 @@ declare class LedgerAdapter implements IHardwareWallet {
132
134
  private static _throwIfAborted;
133
135
  /** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
134
136
  private _runConnectorCall;
137
+ /**
138
+ * Stuck-app recovery: pause for the device's UI transition, then retry once.
139
+ *
140
+ * Caller has already cleared the session + reset connector. We wait so Stax
141
+ * finishes its post-CloseApp animation, then go through ensureConnected +
142
+ * fingerprint check + call exactly once. Caller decides what to do on a
143
+ * second stuck-app hit.
144
+ */
145
+ private _retryAfterStuckApp;
146
+ private _sleepAbortable;
135
147
  /**
136
148
  * Clear stale session, reconnect, and retry the call.
137
149
  *
@@ -446,8 +458,14 @@ type SignerEthBuilderFn = (args: {
446
458
  dmk: DeviceManagementKit;
447
459
  sessionId: string;
448
460
  }) => {
461
+ withContextModule?(contextModule: ContextModule): {
462
+ build(): SignerEth$1;
463
+ };
449
464
  build(): SignerEth$1;
450
465
  } | Promise<{
466
+ withContextModule?(contextModule: ContextModule): {
467
+ build(): SignerEth$1;
468
+ };
451
469
  build(): SignerEth$1;
452
470
  }>;
453
471
  /**
@@ -463,6 +481,8 @@ declare class SignerManager {
463
481
  invalidate(_sessionId: string): void;
464
482
  clearAll(): void;
465
483
  private static _defaultBuilder;
484
+ private static _createContextModule;
485
+ static wrapBlindSigningReportNonBlocking<T extends ContextModule>(contextModule: T): T;
466
486
  }
467
487
 
468
488
  type BtcWallet = Parameters<SignerBtc$1['getWalletAddress']>[0];
package/dist/index.js CHANGED
@@ -69,7 +69,7 @@ var LOCKED_ERROR_CODES = /* @__PURE__ */ new Set(["5515", "21781", "6982", "2701
69
69
  var USER_REJECTED_CODES = /* @__PURE__ */ new Set(["6985", "27013"]);
70
70
  var WRONG_APP_CODES = /* @__PURE__ */ new Set(["6e00", "28160", "6d00", "27904", "6a83", "27267"]);
71
71
  var APP_NOT_INSTALLED_CODES = /* @__PURE__ */ new Set(["6807", "26631"]);
72
- var STEP_BLIND_SIGN_FALLBACK = "signer.eth.steps.blindSignTransactionFallback";
72
+ var STEP_BLIND_SIGN_TRANSACTION_FALLBACK = "signer.eth.steps.blindSignTransactionFallback";
73
73
  function getEthAppErrorCode(err) {
74
74
  if (!err || typeof err !== "object") return null;
75
75
  const e = err;
@@ -134,13 +134,8 @@ function mapBtcAppError(hex) {
134
134
  return null;
135
135
  }
136
136
  }
137
- function mapEthAppError(ethCode, lastStep) {
137
+ function mapEthAppErrorCode(ethCode) {
138
138
  switch (ethCode) {
139
- case "6a80":
140
- if (lastStep === STEP_BLIND_SIGN_FALLBACK) {
141
- return import_hwk_adapter_core.HardwareErrorCode.EvmBlindSigningRequired;
142
- }
143
- return null;
144
139
  case "6984":
145
140
  return import_hwk_adapter_core.HardwareErrorCode.EvmClearSignPluginMissing;
146
141
  case "6a84":
@@ -153,6 +148,14 @@ function mapEthAppError(ethCode, lastStep) {
153
148
  return null;
154
149
  }
155
150
  }
151
+ function classifyEthAppError(err) {
152
+ const ethCode = getEthAppErrorCode(err);
153
+ if (!ethCode) return null;
154
+ if (ethCode === "6a80") {
155
+ return hasBlindSignFallbackStep(err) ? import_hwk_adapter_core.HardwareErrorCode.EvmBlindSigningRequired : null;
156
+ }
157
+ return mapEthAppErrorCode(ethCode);
158
+ }
156
159
  var ERROR_TAG = {
157
160
  // SDK-mint
158
161
  DeviceNotAdvertising: "DeviceNotAdvertisingError",
@@ -239,6 +242,22 @@ var CONNECTION_LEVEL_TAGS = /* @__PURE__ */ new Set([
239
242
  ERROR_TAG.ReconnectionFailed,
240
243
  ERROR_TAG.WebHIDDisconnect
241
244
  ]);
245
+ var DEVICE_NOT_FOUND_TAGS = /* @__PURE__ */ new Set([
246
+ ERROR_TAG.NoAccessibleDevice,
247
+ ERROR_TAG.UnknownDevice,
248
+ ERROR_TAG.DeviceNotInitialized
249
+ ]);
250
+ var DEVICE_BUSY_TAGS = /* @__PURE__ */ new Set([ERROR_TAG.OpeningConnection]);
251
+ var DEVICE_DISCONNECTED_TAGS = /* @__PURE__ */ new Set([
252
+ ERROR_TAG.DeviceNotRecognized,
253
+ ERROR_TAG.DeviceSessionNotFound,
254
+ ERROR_TAG.DeviceSessionRefresher,
255
+ ERROR_TAG.DeviceDisconnectedBeforeSendingApdu,
256
+ ERROR_TAG.DeviceDisconnectedWhileSending,
257
+ ERROR_TAG.Disconnect,
258
+ ERROR_TAG.ReconnectionFailed,
259
+ ERROR_TAG.WebHIDDisconnect
260
+ ]);
242
261
  function isConnectionLevelError(err) {
243
262
  if (!err || typeof err !== "object") return false;
244
263
  const tag = err._tag;
@@ -260,6 +279,35 @@ function hasStatusCode(err, codeSet) {
260
279
  if (e.error != null && e._tag && hasStatusCode(e.error, codeSet)) return true;
261
280
  return false;
262
281
  }
282
+ function hasBlindSignFallbackStep(err) {
283
+ if (!err || typeof err !== "object") return false;
284
+ const e = err;
285
+ if (e._lastStep === STEP_BLIND_SIGN_TRANSACTION_FALLBACK) return true;
286
+ if (Array.isArray(e._deviceActionSteps) && e._deviceActionSteps.includes(STEP_BLIND_SIGN_TRANSACTION_FALLBACK)) {
287
+ return true;
288
+ }
289
+ if (e.originalError != null && hasBlindSignFallbackStep(e.originalError)) return true;
290
+ if (e.error != null && e._tag && hasBlindSignFallbackStep(e.error)) return true;
291
+ return false;
292
+ }
293
+ function isDeviceNotFoundError(err) {
294
+ if (!err || typeof err !== "object") return false;
295
+ const tag = err._tag;
296
+ if (tag && DEVICE_NOT_FOUND_TAGS.has(tag)) return true;
297
+ const e = err;
298
+ if (e.originalError != null && isDeviceNotFoundError(e.originalError)) return true;
299
+ if (e.error != null && e._tag && isDeviceNotFoundError(e.error)) return true;
300
+ return false;
301
+ }
302
+ function isDeviceBusyError(err) {
303
+ if (!err || typeof err !== "object") return false;
304
+ const tag = err._tag;
305
+ if (tag && DEVICE_BUSY_TAGS.has(tag)) return true;
306
+ const e = err;
307
+ if (e.originalError != null && isDeviceBusyError(e.originalError)) return true;
308
+ if (e.error != null && e._tag && isDeviceBusyError(e.error)) return true;
309
+ return false;
310
+ }
263
311
  function isUserRejectedError(err) {
264
312
  if (!err || typeof err !== "object") return false;
265
313
  const e = err;
@@ -292,8 +340,8 @@ function isAppNotInstalledError(err) {
292
340
  function isDeviceDisconnectedError(err) {
293
341
  if (!err || typeof err !== "object") return false;
294
342
  const e = err;
295
- if (e._tag === ERROR_TAG.DeviceNotRecognized || e._tag === ERROR_TAG.DeviceSessionNotFound)
296
- return true;
343
+ const tag = e._tag;
344
+ if (typeof tag === "string" && DEVICE_DISCONNECTED_TAGS.has(tag)) return true;
297
345
  if (typeof e.message === "string") {
298
346
  const msg = e.message.toLowerCase();
299
347
  if (msg.includes("disconnected") || msg.includes("not found") || msg.includes("no device") || msg.includes("unplugged"))
@@ -339,8 +387,10 @@ function mapLedgerError(err, opts) {
339
387
  let code;
340
388
  if (isDeviceLockedError(err)) {
341
389
  code = import_hwk_adapter_core.HardwareErrorCode.DeviceLocked;
342
- } else if (isDeviceNotAdvertisingError(err)) {
390
+ } else if (isDeviceNotAdvertisingError(err) || isDeviceNotFoundError(err)) {
343
391
  code = import_hwk_adapter_core.HardwareErrorCode.DeviceNotFound;
392
+ } else if (isDeviceBusyError(err)) {
393
+ code = import_hwk_adapter_core.HardwareErrorCode.DeviceBusy;
344
394
  } else if (isBlePairingFailureError(err)) {
345
395
  code = import_hwk_adapter_core.HardwareErrorCode.BlePairingTimeout;
346
396
  } else if (isUserRejectedError(err)) {
@@ -354,9 +404,7 @@ function mapLedgerError(err, opts) {
354
404
  } else if (isTimeoutError(err)) {
355
405
  code = import_hwk_adapter_core.HardwareErrorCode.OperationTimeout;
356
406
  } else {
357
- const ethCode = getEthAppErrorCode(err);
358
- const lastStep = err && typeof err === "object" ? err._lastStep : void 0;
359
- const ethMapped = ethCode ? mapEthAppError(ethCode, lastStep) : null;
407
+ const ethMapped = classifyEthAppError(err);
360
408
  const apduHex = ethMapped ? null : extractApduHex(err);
361
409
  const chainMapped = apduHex ? mapSolanaAppError(apduHex) ?? mapTronAppError(apduHex) ?? mapBtcAppError(apduHex) : null;
362
410
  code = ethMapped ?? chainMapped ?? import_hwk_adapter_core.HardwareErrorCode.UnknownError;
@@ -544,6 +592,17 @@ var _LedgerAdapter = class _LedgerAdapter {
544
592
  await this._ensureDevicePermission();
545
593
  debugLog(`[LedgerAdapter] searchDevices() entry, cacheBefore=${this._discoveredDevices.size}`);
546
594
  const devices = await this.connector.searchDevices();
595
+ debugLog(
596
+ "[DMK] adapter.searchDevices raw:",
597
+ devices.map((device) => ({
598
+ id: device?.deviceId,
599
+ deviceId: device?.deviceId,
600
+ connectId: device?.connectId,
601
+ deviceName: device?.name,
602
+ "device.name": device?.name,
603
+ model: device?.model
604
+ }))
605
+ );
547
606
  this._discoveredDevices.clear();
548
607
  for (const d of devices) {
549
608
  if (d.connectId) {
@@ -1185,7 +1244,39 @@ var _LedgerAdapter = class _LedgerAdapter {
1185
1244
  this.connector.reset?.();
1186
1245
  } catch {
1187
1246
  }
1188
- throw err;
1247
+ debugLog(
1248
+ "[LedgerAdapter] stuck-app retry: method=",
1249
+ method,
1250
+ "delayMs=",
1251
+ _LedgerAdapter.STUCK_APP_RETRY_DELAY_MS,
1252
+ "_tag=",
1253
+ err?._tag
1254
+ );
1255
+ try {
1256
+ const retryResult = await this._retryAfterStuckApp(
1257
+ resolvedConnectId,
1258
+ method,
1259
+ effectiveParams,
1260
+ signal,
1261
+ err,
1262
+ fingerprint
1263
+ );
1264
+ debugLog("[LedgerAdapter] stuck-app retry succeeded: method=", method);
1265
+ return retryResult;
1266
+ } catch (retryErr) {
1267
+ if (signal.aborted) throw retryErr;
1268
+ if (isStuckAppStateError(retryErr)) {
1269
+ debugLog("[LedgerAdapter] stuck-app retry exhausted (2nd 6901): method=", method);
1270
+ throw err;
1271
+ }
1272
+ debugLog(
1273
+ "[LedgerAdapter] stuck-app retry threw non-stuck error: method=",
1274
+ method,
1275
+ "retryErrTag=",
1276
+ retryErr?._tag
1277
+ );
1278
+ throw retryErr;
1279
+ }
1189
1280
  }
1190
1281
  if (isDeviceLockedError(err) || isDeviceNotAdvertisingError(err) || isDeviceDisconnectedError(err)) {
1191
1282
  let lastErr = err;
@@ -1259,6 +1350,54 @@ var _LedgerAdapter = class _LedgerAdapter {
1259
1350
  throw err;
1260
1351
  }
1261
1352
  }
1353
+ /**
1354
+ * Stuck-app recovery: pause for the device's UI transition, then retry once.
1355
+ *
1356
+ * Caller has already cleared the session + reset connector. We wait so Stax
1357
+ * finishes its post-CloseApp animation, then go through ensureConnected +
1358
+ * fingerprint check + call exactly once. Caller decides what to do on a
1359
+ * second stuck-app hit.
1360
+ */
1361
+ async _retryAfterStuckApp(resolvedConnectId, method, params, signal, originalErr, fingerprint) {
1362
+ await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
1363
+ const retryTargetConnectId = isLedgerBleConnectionType(this.connector.connectionType) ? resolvedConnectId : void 0;
1364
+ const retryConnectId = await this.ensureConnected(retryTargetConnectId, signal);
1365
+ const retrySessionId = this._sessions.get(retryConnectId);
1366
+ if (!retrySessionId) throw originalErr;
1367
+ if (fingerprint && !fingerprint.skipFingerprint && fingerprint.deviceId) {
1368
+ const fp = await this._abortable(
1369
+ signal,
1370
+ this._verifyDeviceFingerprintWithSession(
1371
+ retrySessionId,
1372
+ fingerprint.deviceId,
1373
+ fingerprint.chain
1374
+ )
1375
+ );
1376
+ if (!fp.success) {
1377
+ throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
1378
+ code: import_hwk_adapter_core2.HardwareErrorCode.DeviceMismatch
1379
+ });
1380
+ }
1381
+ }
1382
+ return this._abortable(signal, this.connector.call(retrySessionId, method, params));
1383
+ }
1384
+ _sleepAbortable(ms, signal) {
1385
+ return new Promise((resolve, reject) => {
1386
+ if (signal.aborted) {
1387
+ reject(signal.reason ?? new Error("Aborted"));
1388
+ return;
1389
+ }
1390
+ const timer = setTimeout(() => {
1391
+ signal.removeEventListener("abort", onAbort);
1392
+ resolve();
1393
+ }, ms);
1394
+ const onAbort = () => {
1395
+ clearTimeout(timer);
1396
+ reject(signal.reason ?? new Error("Aborted"));
1397
+ };
1398
+ signal.addEventListener("abort", onAbort, { once: true });
1399
+ });
1400
+ }
1262
1401
  /**
1263
1402
  * Clear stale session, reconnect, and retry the call.
1264
1403
  *
@@ -1436,6 +1575,10 @@ var _LedgerAdapter = class _LedgerAdapter {
1436
1575
  // Layer 2 retry budget after connection-class error. Each round delegates
1437
1576
  // to Layer 1 (which owns the unlock prompt). Layer 2 never emits UI itself.
1438
1577
  _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET = 3;
1578
+ // Stuck-app (APDU 0x6901) retry pause. Ledger Stax often returns 6901 when
1579
+ // OpenAppCommand lands mid-transition right after CloseApp; a short wait
1580
+ // lets the device finish its UI animation before we retry once.
1581
+ _LedgerAdapter.STUCK_APP_RETRY_DELAY_MS = 500;
1439
1582
  // Layer 1 confirm budget. After N failed Confirm cycles (user keeps clicking
1440
1583
  // Confirm but device never shows up / never unlocks), throw DeviceNotFound
1441
1584
  // instead of looping forever.
@@ -1443,7 +1586,7 @@ _LedgerAdapter.MAX_DOCONNECT_CONFIRMS = 3;
1443
1586
  var LedgerAdapter = _LedgerAdapter;
1444
1587
 
1445
1588
  // src/connector/LedgerConnectorBase.ts
1446
- var import_hwk_adapter_core11 = require("@onekeyfe/hwk-adapter-core");
1589
+ var import_hwk_adapter_core12 = require("@onekeyfe/hwk-adapter-core");
1447
1590
 
1448
1591
  // src/device/LedgerDeviceManager.ts
1449
1592
  var LedgerDeviceManager = class {
@@ -1704,6 +1847,7 @@ function deviceActionToPromise(action, onInteraction, timeoutMs = IDLE_WATCHDOG_
1704
1847
  return new Promise((resolve, reject) => {
1705
1848
  let settled = false;
1706
1849
  let lastStep;
1850
+ const observedSteps = [];
1707
1851
  let sub;
1708
1852
  let timer = null;
1709
1853
  const cancelAction = () => {
@@ -1763,7 +1907,12 @@ function deviceActionToPromise(action, onInteraction, timeoutMs = IDLE_WATCHDOG_
1763
1907
  }
1764
1908
  armIdleWatchdog();
1765
1909
  const step = state?.intermediateValue?.step;
1766
- if (step) lastStep = step;
1910
+ if (step) {
1911
+ lastStep = step;
1912
+ if (observedSteps[observedSteps.length - 1] !== step) {
1913
+ observedSteps.push(step);
1914
+ }
1915
+ }
1767
1916
  if (state.status === import_device_management_kit.DeviceActionStatus.Completed) {
1768
1917
  settled = true;
1769
1918
  if (timer) clearTimeout(timer);
@@ -1775,7 +1924,7 @@ function deviceActionToPromise(action, onInteraction, timeoutMs = IDLE_WATCHDOG_
1775
1924
  if (timer) clearTimeout(timer);
1776
1925
  onInteraction?.("interaction-complete");
1777
1926
  sub?.unsubscribe();
1778
- rejectWithLastStep(state.error, lastStep, reject);
1927
+ rejectWithStepContext(state.error, lastStep, observedSteps, reject);
1779
1928
  } else if (state.status === import_device_management_kit.DeviceActionStatus.Pending && onInteraction) {
1780
1929
  const interaction = state.intermediateValue?.requiredUserInteraction;
1781
1930
  if (interaction && interaction !== "none") {
@@ -1792,7 +1941,7 @@ function deviceActionToPromise(action, onInteraction, timeoutMs = IDLE_WATCHDOG_
1792
1941
  settled = true;
1793
1942
  if (timer) clearTimeout(timer);
1794
1943
  sub?.unsubscribe();
1795
- rejectWithLastStep(err, lastStep, reject);
1944
+ rejectWithStepContext(err, lastStep, observedSteps, reject);
1796
1945
  }
1797
1946
  },
1798
1947
  complete: () => {
@@ -1805,15 +1954,25 @@ function deviceActionToPromise(action, onInteraction, timeoutMs = IDLE_WATCHDOG_
1805
1954
  });
1806
1955
  });
1807
1956
  }
1808
- function rejectWithLastStep(err, lastStep, reject) {
1809
- if (err && typeof err === "object" && lastStep) {
1957
+ function rejectWithStepContext(err, lastStep, observedSteps, reject) {
1958
+ if (err && typeof err === "object" && (lastStep || observedSteps.length > 0)) {
1810
1959
  try {
1811
- Object.defineProperty(err, "_lastStep", {
1812
- value: lastStep,
1813
- configurable: true,
1814
- enumerable: false,
1815
- writable: true
1816
- });
1960
+ if (lastStep) {
1961
+ Object.defineProperty(err, "_lastStep", {
1962
+ value: lastStep,
1963
+ configurable: true,
1964
+ enumerable: false,
1965
+ writable: true
1966
+ });
1967
+ }
1968
+ if (observedSteps.length > 0) {
1969
+ Object.defineProperty(err, "_deviceActionSteps", {
1970
+ value: [...observedSteps],
1971
+ configurable: true,
1972
+ enumerable: false,
1973
+ writable: true
1974
+ });
1975
+ }
1817
1976
  } catch {
1818
1977
  }
1819
1978
  }
@@ -1879,7 +2038,8 @@ var SignerManager = class _SignerManager {
1879
2038
  async getOrCreate(sessionId) {
1880
2039
  debugLog("[DMK] SignerManager.getOrCreate:", { sessionId });
1881
2040
  const builder = await this._builderFn({ dmk: this._dmk, sessionId });
1882
- return new SignerEth(builder.build());
2041
+ const builderWithContext = builder.withContextModule ? builder.withContextModule(_SignerManager._createContextModule()) : builder;
2042
+ return new SignerEth(builderWithContext.build());
1883
2043
  }
1884
2044
  // eslint-disable-next-line @typescript-eslint/no-unused-vars, class-methods-use-this
1885
2045
  invalidate(_sessionId) {
@@ -1888,10 +2048,24 @@ var SignerManager = class _SignerManager {
1888
2048
  clearAll() {
1889
2049
  }
1890
2050
  static _defaultBuilder() {
1891
- return (args) => {
1892
- const contextModule = new import_context_module.ContextModuleBuilder({}).removeDefaultLoaders().build();
1893
- return new import_device_signer_kit_ethereum.SignerEthBuilder(args).withContextModule(contextModule);
2051
+ return (args) => new import_device_signer_kit_ethereum.SignerEthBuilder(args);
2052
+ }
2053
+ static _createContextModule() {
2054
+ const contextModule = new import_context_module.ContextModuleBuilder({}).removeDefaultLoaders().build();
2055
+ return _SignerManager.wrapBlindSigningReportNonBlocking(contextModule);
2056
+ }
2057
+ static wrapBlindSigningReportNonBlocking(contextModule) {
2058
+ const report = contextModule.report.bind(contextModule);
2059
+ contextModule.report = async (params) => {
2060
+ try {
2061
+ void report(params).catch((err) => {
2062
+ debugLog("[DMK] ContextModule.report failed:", err);
2063
+ });
2064
+ } catch (err) {
2065
+ debugLog("[DMK] ContextModule.report failed:", err);
2066
+ }
1894
2067
  };
2068
+ return contextModule;
1895
2069
  }
1896
2070
  };
1897
2071
 
@@ -2461,14 +2635,15 @@ async function _createSolSigner(ctx, sessionId) {
2461
2635
  }
2462
2636
 
2463
2637
  // src/connector/chains/tron.ts
2464
- var import_hwk_adapter_core10 = require("@onekeyfe/hwk-adapter-core");
2638
+ var import_hwk_adapter_core11 = require("@onekeyfe/hwk-adapter-core");
2465
2639
  var import_hw_app_trx = __toESM(require("@ledgerhq/hw-app-trx"));
2466
2640
 
2467
2641
  // src/connector/chains/legacyChainCall.ts
2468
- var import_hwk_adapter_core9 = require("@onekeyfe/hwk-adapter-core");
2642
+ var import_hwk_adapter_core10 = require("@onekeyfe/hwk-adapter-core");
2469
2643
 
2470
2644
  // src/app/AppManager.ts
2471
2645
  var import_device_management_kit2 = require("@ledgerhq/device-management-kit");
2646
+ var import_hwk_adapter_core9 = require("@onekeyfe/hwk-adapter-core");
2472
2647
  var APP_NAME_MAP = {
2473
2648
  ETH: "Ethereum",
2474
2649
  BTC: "Bitcoin",
@@ -2573,9 +2748,11 @@ var AppManager = class {
2573
2748
  });
2574
2749
  if (!(0, import_device_management_kit2.isSuccessCommandResult)(result)) {
2575
2750
  const { statusCode } = result;
2751
+ const hasStatusCode2 = statusCode != null && statusCode !== "";
2576
2752
  debugLog("[AppManager] openApp failed:", appName, "statusCode:", statusCode);
2577
2753
  throw Object.assign(new Error(`Failed to open "${appName}"`), {
2578
2754
  _tag: ERROR_TAG.OpenAppCommand,
2755
+ code: hasStatusCode2 ? void 0 : import_hwk_adapter_core9.HardwareErrorCode.AppNotInstalled,
2579
2756
  errorCode: String(statusCode ?? ""),
2580
2757
  statusCode,
2581
2758
  appName
@@ -2631,14 +2808,14 @@ async function withLegacyChainCall(ctx, sessionId, options, action) {
2631
2808
  const onAppOpenPrompt = () => {
2632
2809
  openAppPromptShown = true;
2633
2810
  ctx.emit("ui-event", {
2634
- type: import_hwk_adapter_core9.EConnectorInteraction.ConfirmOpenApp,
2811
+ type: import_hwk_adapter_core10.EConnectorInteraction.ConfirmOpenApp,
2635
2812
  payload: { sessionId }
2636
2813
  });
2637
2814
  };
2638
2815
  const closeOpenAppUiIfShown = () => {
2639
2816
  if (openAppPromptShown) {
2640
2817
  ctx.emit("ui-event", {
2641
- type: import_hwk_adapter_core9.EConnectorInteraction.InteractionComplete,
2818
+ type: import_hwk_adapter_core10.EConnectorInteraction.InteractionComplete,
2642
2819
  payload: { sessionId }
2643
2820
  });
2644
2821
  openAppPromptShown = false;
@@ -2659,7 +2836,7 @@ async function withLegacyChainCall(ctx, sessionId, options, action) {
2659
2836
  let confirmEmitted = false;
2660
2837
  if (needsConfirmation) {
2661
2838
  ctx.emit("ui-event", {
2662
- type: import_hwk_adapter_core9.EConnectorInteraction.ConfirmOnDevice,
2839
+ type: import_hwk_adapter_core10.EConnectorInteraction.ConfirmOnDevice,
2663
2840
  payload: { sessionId }
2664
2841
  });
2665
2842
  confirmEmitted = true;
@@ -2669,7 +2846,7 @@ async function withLegacyChainCall(ctx, sessionId, options, action) {
2669
2846
  } finally {
2670
2847
  if (confirmEmitted || openAppPromptShown) {
2671
2848
  ctx.emit("ui-event", {
2672
- type: import_hwk_adapter_core9.EConnectorInteraction.InteractionComplete,
2849
+ type: import_hwk_adapter_core10.EConnectorInteraction.InteractionComplete,
2673
2850
  payload: { sessionId }
2674
2851
  });
2675
2852
  openAppPromptShown = false;
@@ -2757,7 +2934,7 @@ async function tronSignTransaction(ctx, sessionId, params) {
2757
2934
  if (!params.rawTxHex) {
2758
2935
  throw Object.assign(
2759
2936
  new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
2760
- { code: import_hwk_adapter_core10.HardwareErrorCode.InvalidParams }
2937
+ { code: import_hwk_adapter_core11.HardwareErrorCode.InvalidParams }
2761
2938
  );
2762
2939
  }
2763
2940
  const path = normalizePath(params.path);
@@ -2801,6 +2978,9 @@ var METHOD_PREFIX_TO_APP_NAME = {
2801
2978
  sol: "Solana",
2802
2979
  tron: "Tron"
2803
2980
  };
2981
+ var HARDWARE_ERROR_CODE_VALUES = new Set(
2982
+ Object.values(import_hwk_adapter_core12.HardwareErrorCode).filter((value) => typeof value === "number")
2983
+ );
2804
2984
  async function defaultLedgerKitImporter(pkg) {
2805
2985
  switch (pkg) {
2806
2986
  case "@ledgerhq/device-management-kit":
@@ -2922,6 +3102,21 @@ var LedgerConnectorBase = class {
2922
3102
  descriptor: d,
2923
3103
  connectId: this._resolveConnectId(d)
2924
3104
  }));
3105
+ debugLog(
3106
+ "[DMK] connector.searchDevices() raw descriptors:",
3107
+ resolvedDescriptors.map(({ descriptor: d, connectId }) => ({
3108
+ id: d?.path,
3109
+ path: d?.path,
3110
+ connectId,
3111
+ deviceName: d?.name,
3112
+ "device.name": d?.bleName,
3113
+ localName: d?.localName,
3114
+ bleName: d?.bleName,
3115
+ model: d?.type,
3116
+ transport: d?.transport,
3117
+ rssi: d?.rssi
3118
+ }))
3119
+ );
2925
3120
  const bleConnectIdCounts = /* @__PURE__ */ new Map();
2926
3121
  for (const item of resolvedDescriptors) {
2927
3122
  if (isLedgerBleDescriptor(this.connectionType, item.descriptor)) {
@@ -2929,7 +3124,7 @@ var LedgerConnectorBase = class {
2929
3124
  debugLog(`[DMK] connector.searchDevices() invalid BLE connectId=${item.connectId}`);
2930
3125
  throw Object.assign(
2931
3126
  new Error("Ledger BLE connectId must be the 4-character BLE identifier"),
2932
- { code: import_hwk_adapter_core11.HardwareErrorCode.DeviceNotFound }
3127
+ { code: import_hwk_adapter_core12.HardwareErrorCode.DeviceNotFound }
2933
3128
  );
2934
3129
  }
2935
3130
  bleConnectIdCounts.set(item.connectId, (bleConnectIdCounts.get(item.connectId) ?? 0) + 1);
@@ -2946,7 +3141,7 @@ var LedgerConnectorBase = class {
2946
3141
  );
2947
3142
  throw Object.assign(
2948
3143
  new Error(`Duplicate Ledger BLE connectId detected: ${duplicateBleConnectId}`),
2949
- { code: import_hwk_adapter_core11.HardwareErrorCode.DeviceNotFound }
3144
+ { code: import_hwk_adapter_core12.HardwareErrorCode.DeviceNotFound }
2950
3145
  );
2951
3146
  }
2952
3147
  const result = resolvedDescriptors.map(({ descriptor: d, connectId }) => {
@@ -2992,7 +3187,7 @@ var LedgerConnectorBase = class {
2992
3187
  `Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
2993
3188
  );
2994
3189
  err._tag = ERROR_TAG.BlePairingTimeout;
2995
- err.code = import_hwk_adapter_core11.HardwareErrorCode.BlePairingTimeout;
3190
+ err.code = import_hwk_adapter_core12.HardwareErrorCode.BlePairingTimeout;
2996
3191
  reject(err);
2997
3192
  }, HANG_CEILING_MS);
2998
3193
  });
@@ -3054,7 +3249,7 @@ var LedgerConnectorBase = class {
3054
3249
  "Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
3055
3250
  );
3056
3251
  wrapped._tag = ERROR_TAG.BleGattBondingFailed;
3057
- wrapped.code = import_hwk_adapter_core11.HardwareErrorCode.BlePairingTimeout;
3252
+ wrapped.code = import_hwk_adapter_core12.HardwareErrorCode.BlePairingTimeout;
3058
3253
  wrapped.originalError = err;
3059
3254
  throw wrapped;
3060
3255
  }
@@ -3137,7 +3332,7 @@ var LedgerConnectorBase = class {
3137
3332
  this._unwatchSessionState(sessionId);
3138
3333
  this._signerManager?.invalidate(sessionId);
3139
3334
  this._cancellers.get(sessionId)?.({
3140
- code: import_hwk_adapter_core11.HardwareErrorCode.DeviceDisconnected,
3335
+ code: import_hwk_adapter_core12.HardwareErrorCode.DeviceDisconnected,
3141
3336
  tag: "DeviceDisconnected",
3142
3337
  message: "Device disconnected"
3143
3338
  });
@@ -3154,14 +3349,14 @@ var LedgerConnectorBase = class {
3154
3349
  } catch (err) {
3155
3350
  if (isAppStuckByApdu(err)) {
3156
3351
  throw Object.assign(new Error("Ledger app is unresponsive"), {
3157
- code: import_hwk_adapter_core11.HardwareErrorCode.DeviceAppStuck,
3352
+ code: import_hwk_adapter_core12.HardwareErrorCode.DeviceAppStuck,
3158
3353
  _tag: ERROR_TAG.DeviceAppStuck,
3159
3354
  originalError: err
3160
3355
  });
3161
3356
  }
3162
3357
  if (isTransportStuck(err)) {
3163
3358
  throw Object.assign(new Error("Device communication interrupted, please retry"), {
3164
- code: import_hwk_adapter_core11.HardwareErrorCode.TransportError,
3359
+ code: import_hwk_adapter_core12.HardwareErrorCode.TransportError,
3165
3360
  _tag: ERROR_TAG.DeviceTransportStuck,
3166
3361
  originalError: err
3167
3362
  });
@@ -3399,15 +3594,21 @@ var LedgerConnectorBase = class {
3399
3594
  };
3400
3595
  }
3401
3596
  _wrapError(err, opts) {
3402
- const mapped = mapLedgerError(err, opts);
3403
3597
  const src = err && typeof err === "object" ? err : {};
3598
+ const hasSerializedCode = typeof src.code === "number" && HARDWARE_ERROR_CODE_VALUES.has(src.code);
3599
+ const mapped = hasSerializedCode ? {
3600
+ code: src.code,
3601
+ message: typeof src.message === "string" ? src.message : "Unknown Ledger error",
3602
+ appName: typeof src.appName === "string" ? src.appName : opts?.defaultAppName
3603
+ } : mapLedgerError(err, opts);
3404
3604
  const error = new Error(mapped.message);
3405
3605
  Object.assign(error, {
3406
3606
  code: mapped.code,
3407
3607
  appName: mapped.appName,
3408
3608
  _tag: src._tag,
3409
3609
  errorCode: src.errorCode,
3410
- _lastStep: src._lastStep
3610
+ _lastStep: src._lastStep,
3611
+ _deviceActionSteps: src._deviceActionSteps
3411
3612
  });
3412
3613
  Object.defineProperty(error, "originalError", {
3413
3614
  value: err,