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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  DeviceJobQueue,
7
7
  EConnectorInteraction,
8
8
  HardwareErrorCode as HardwareErrorCode3,
9
- InteractionRegistry,
9
+ OperationRegistry,
10
10
  SDK,
11
11
  TypedEventEmitter,
12
12
  UI_REQUEST,
@@ -16,7 +16,7 @@ import {
16
16
  createHwkError,
17
17
  deriveDeviceFingerprint,
18
18
  failure as failure2,
19
- isHardwareInteractionId,
19
+ isHardwareOperationId,
20
20
  isHwkRecoveryHint,
21
21
  operationMayHaveCompletedParams,
22
22
  rehydrateConnectorError,
@@ -530,7 +530,7 @@ var LEDGER_UNSUPPORTED_ALLNETWORK_NETWORKS = /* @__PURE__ */ new Set(["doge", "d
530
530
  function createAllNetworkGetAddress({
531
531
  callChain,
532
532
  getChainFingerprint,
533
- retainInteraction,
533
+ retainOperation,
534
534
  errorToFailure
535
535
  }) {
536
536
  return async function allNetworkGetAddress(connectId, _deviceId, params) {
@@ -539,19 +539,19 @@ function createAllNetworkGetAddress({
539
539
  connectId,
540
540
  itemCount: params.bundle.length
541
541
  });
542
- const target = resolveHardwareOperationTarget(connectId, params.interactionId, "ledger");
542
+ const target = resolveHardwareOperationTarget(connectId, params.operationId, "ledger");
543
543
  if (!target.success) return target;
544
544
  const effectiveTargetId = target.payload.targetId ?? "";
545
- let releaseInteractionRetention;
545
+ let releaseOperationRetention;
546
546
  try {
547
- releaseInteractionRetention = target.payload.interactionId ? retainInteraction(target.payload.interactionId) : void 0;
547
+ releaseOperationRetention = target.payload.operationId ? retainOperation(target.payload.operationId) : void 0;
548
548
  } catch (error) {
549
549
  return errorToFailure(error);
550
550
  }
551
551
  const installContext = {};
552
552
  const commonParams = {
553
553
  autoInstallApp: params.autoInstallApp,
554
- interactionId: target.payload.interactionId,
554
+ operationId: target.payload.operationId,
555
555
  knownConnections: params.knownConnections,
556
556
  extra: params.extra,
557
557
  allowDeviceSelection: params.allowDeviceSelection
@@ -602,7 +602,7 @@ function createAllNetworkGetAddress({
602
602
  });
603
603
  return result;
604
604
  } finally {
605
- releaseInteractionRetention?.();
605
+ releaseOperationRetention?.();
606
606
  }
607
607
  };
608
608
  }
@@ -611,7 +611,7 @@ function isTopLevelAllNetworkFailure(response) {
611
611
  return false;
612
612
  }
613
613
  const code = response.payload?.code;
614
- return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.DeviceDisconnected || code === HardwareErrorCode2.OperationTimeout || code === HardwareErrorCode2.TransportError || code === HardwareErrorCode2.InteractionEnded || code === HardwareErrorCode2.InteractionNotFound || code === HardwareErrorCode2.UserAborted || code === HardwareErrorCode2.UserRejected;
614
+ return code === HardwareErrorCode2.DeviceMismatch || code === HardwareErrorCode2.DeviceDisconnected || code === HardwareErrorCode2.OperationTimeout || code === HardwareErrorCode2.TransportError || code === HardwareErrorCode2.OperationEnded || code === HardwareErrorCode2.OperationNotFound || code === HardwareErrorCode2.UserAborted || code === HardwareErrorCode2.UserRejected;
615
615
  }
616
616
  function getItemDeviceId(item) {
617
617
  const { deviceId } = item;
@@ -774,26 +774,26 @@ var _LedgerAdapter = class _LedgerAdapter {
774
774
  constructor(connector, options) {
775
775
  this.vendor = "ledger";
776
776
  this.emitter = new TypedEventEmitter();
777
- this._interactions = new InteractionRegistry({
777
+ this._operations = new OperationRegistry({
778
778
  vendor: "ledger",
779
- onEnded: (interaction, reason) => {
780
- const binding = this._pendingInteractionBindings.get(interaction.interactionId);
779
+ onEnded: (operation, reason) => {
780
+ const binding = this._pendingOperationBindings.get(operation.operationId);
781
781
  if (binding?.selectedConnection?.requestId === this._bindingSelectionRequestId) {
782
782
  this._finishBleBinding("cancelled");
783
783
  }
784
- this._pendingInteractionBindings.delete(interaction.interactionId);
785
- this.emitter.emit(SDK.INTERACTION_ENDED, {
786
- type: SDK.INTERACTION_ENDED,
787
- payload: { interactionId: interaction.interactionId, reason }
784
+ this._pendingOperationBindings.delete(operation.operationId);
785
+ this.emitter.emit(SDK.OPERATION_ENDED, {
786
+ type: SDK.OPERATION_ENDED,
787
+ payload: { operationId: operation.operationId, reason }
788
788
  });
789
789
  if (reason === "timeout") {
790
- void this._releaseInteractionConnection(interaction);
790
+ void this._releaseOperationConnection(operation);
791
791
  }
792
792
  }
793
793
  });
794
794
  this._discoveredDevices = /* @__PURE__ */ new Map();
795
795
  this._sessions = /* @__PURE__ */ new Map();
796
- this._pendingInteractionBindings = /* @__PURE__ */ new Map();
796
+ this._pendingOperationBindings = /* @__PURE__ */ new Map();
797
797
  this._verifiedBleReconnectTargets = /* @__PURE__ */ new Map();
798
798
  this._uiRegistry = new UiRequestRegistry();
799
799
  // BTC App rejects account index >= 100 unless display=true. Cached per
@@ -814,8 +814,8 @@ var _LedgerAdapter = class _LedgerAdapter {
814
814
  this._stateGeneration = 0;
815
815
  this._connectorTeardownTail = Promise.resolve();
816
816
  this._pendingConnectorTeardowns = 0;
817
- this._activeInteractionJobs = /* @__PURE__ */ new Set();
818
- this._pendingInteractionDisconnects = /* @__PURE__ */ new Set();
817
+ this._activeOperationJobs = /* @__PURE__ */ new Set();
818
+ this._pendingOperationDisconnects = /* @__PURE__ */ new Set();
819
819
  this._installProgressLastEmittedValue = -Infinity;
820
820
  this.allNetworkGetAddress = createAllNetworkGetAddress({
821
821
  callChain: this.callChain.bind(this),
@@ -830,7 +830,7 @@ var _LedgerAdapter = class _LedgerAdapter {
830
830
  return this.errorToFailure(error);
831
831
  }
832
832
  },
833
- retainInteraction: (interactionId) => this._interactions.retain(interactionId),
833
+ retainOperation: (operationId) => this._operations.retain(operationId),
834
834
  errorToFailure: (error) => this.errorToFailure(error)
835
835
  });
836
836
  // ---------------------------------------------------------------------------
@@ -858,11 +858,11 @@ var _LedgerAdapter = class _LedgerAdapter {
858
858
  });
859
859
  };
860
860
  this.deviceDisconnectHandler = (data) => {
861
- const activeInteraction = this._interactions.findActiveByConnectionKey(data.connectId);
862
- if (activeInteraction && this._activeInteractionJobs.has(activeInteraction.interactionId)) {
863
- this._pendingInteractionDisconnects.add(activeInteraction.interactionId);
861
+ const activeOperation = this._operations.findActiveByConnectionKey(data.connectId);
862
+ if (activeOperation && this._activeOperationJobs.has(activeOperation.operationId)) {
863
+ this._pendingOperationDisconnects.add(activeOperation.operationId);
864
864
  } else {
865
- this._interactions.endByConnectionKey(data.connectId, "disconnect");
865
+ this._operations.endByConnectionKey(data.connectId, "disconnect");
866
866
  }
867
867
  this._discoveredDevices.delete(data.connectId);
868
868
  this._sessions.delete(data.connectId);
@@ -965,7 +965,7 @@ var _LedgerAdapter = class _LedgerAdapter {
965
965
  const sessionIds = new Set(this._sessions.values());
966
966
  this._stateGeneration += 1;
967
967
  this._finishBleBinding("cancelled");
968
- this._interactions.endAll("runtime-reset");
968
+ this._operations.endAll("runtime-reset");
969
969
  this._doConnectAbortController?.abort();
970
970
  this._discoveredDevices.clear();
971
971
  this._sessions.clear();
@@ -1074,8 +1074,8 @@ var _LedgerAdapter = class _LedgerAdapter {
1074
1074
  }));
1075
1075
  }
1076
1076
  // USB single-session invariant: evict all sessions, best-effort (see connectDevice).
1077
- async _evictAllSessions(preserveInteractionId) {
1078
- this._interactions.endAll("explicit", preserveInteractionId);
1077
+ async _evictAllSessions(preserveOperationId) {
1078
+ this._operations.endAll("explicit", preserveOperationId);
1079
1079
  if (this._sessions.size === 0) return;
1080
1080
  const stale = [...this._sessions.values()];
1081
1081
  this._sessions.clear();
@@ -1094,11 +1094,129 @@ var _LedgerAdapter = class _LedgerAdapter {
1094
1094
  });
1095
1095
  }
1096
1096
  async connectDevice(searchTargetId) {
1097
- const connected = await this._connectTarget(searchTargetId);
1098
- if (!connected.success) return connected;
1099
- return this._createInteraction(searchTargetId, connected.payload);
1097
+ try {
1098
+ return await this._jobQueue.enqueue(
1099
+ searchTargetId || "__ledger_connect__",
1100
+ async () => {
1101
+ const connected = await this._connectTarget(searchTargetId);
1102
+ if (!connected.success) return connected;
1103
+ return this._createOperation(searchTargetId, connected.payload);
1104
+ },
1105
+ {
1106
+ label: "connectDevice",
1107
+ rejectIfBusy: true,
1108
+ busyError: _LedgerAdapter._createDeviceBusyError("connectDevice")
1109
+ }
1110
+ );
1111
+ } catch (error) {
1112
+ return this.errorToFailure(error);
1113
+ }
1114
+ }
1115
+ async bindBleDevice(params) {
1116
+ if (params.identity.vendor !== "ledger" || !params.identity.value) {
1117
+ return failure2(HardwareErrorCode3.InvalidParams, "Ledger wallet identity is required");
1118
+ }
1119
+ const { chain, value: expectedFingerprint } = params.identity;
1120
+ try {
1121
+ return await this._jobQueue.enqueue(
1122
+ expectedFingerprint,
1123
+ async (signal) => {
1124
+ if (!this.getAvailableTransports().includes("ble")) {
1125
+ throw createHwkError({
1126
+ code: HardwareErrorCode3.TransportNotAvailable,
1127
+ message: "Ledger Bluetooth transport is not available"
1128
+ });
1129
+ }
1130
+ this._activeConnectionType = "ble";
1131
+ await this._ensureDevicePermission(void 0, void 0, signal);
1132
+ const attempt = {
1133
+ extra: params.extra,
1134
+ bindingReason: "manual-rebind"
1135
+ };
1136
+ try {
1137
+ for (; ; ) {
1138
+ const connectId = await this._connectFirstOrSelect(
1139
+ [],
1140
+ void 0,
1141
+ true,
1142
+ void 0,
1143
+ attempt,
1144
+ signal
1145
+ );
1146
+ const sessionId = this._sessions.get(connectId);
1147
+ if (!sessionId) {
1148
+ throw createHwkError({
1149
+ code: HardwareErrorCode3.DeviceDisconnected,
1150
+ message: "Selected Ledger connection ended"
1151
+ });
1152
+ }
1153
+ let saved = false;
1154
+ try {
1155
+ const installContext = {
1156
+ connection: { connectId, sessionId }
1157
+ };
1158
+ const fingerprint = await this._computeChainFingerprint(
1159
+ chain,
1160
+ (method, callParams) => this._runConnectorCall(
1161
+ connectId,
1162
+ method,
1163
+ callParams,
1164
+ signal,
1165
+ void 0,
1166
+ void 0,
1167
+ { autoInstallApp: true },
1168
+ installContext
1169
+ )
1170
+ );
1171
+ if (fingerprint !== expectedFingerprint) {
1172
+ attempt.rejectedConnectIds ?? (attempt.rejectedConnectIds = /* @__PURE__ */ new Set());
1173
+ attempt.rejectedConnectIds.add(connectId);
1174
+ attempt.rejectedConnectId = connectId;
1175
+ } else {
1176
+ const persisted = await this._publishVerifiedBleBinding(
1177
+ connectId,
1178
+ chain,
1179
+ fingerprint,
1180
+ attempt,
1181
+ void 0,
1182
+ signal
1183
+ );
1184
+ if (!persisted) {
1185
+ throw createHwkError({
1186
+ code: HardwareErrorCode3.UnknownError,
1187
+ message: "Bluetooth binding could not be saved",
1188
+ origin: "host"
1189
+ });
1190
+ }
1191
+ saved = true;
1192
+ return success(connectId);
1193
+ }
1194
+ } finally {
1195
+ if (!saved && this._sessions.get(connectId) === sessionId) {
1196
+ this._sessions.delete(connectId);
1197
+ const teardown = this._runConnectorTeardown(
1198
+ () => this.connector.disconnect(sessionId)
1199
+ ).catch(() => void 0);
1200
+ if (!signal.aborted) await teardown;
1201
+ }
1202
+ }
1203
+ }
1204
+ } catch (error) {
1205
+ this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
1206
+ throw error;
1207
+ }
1208
+ },
1209
+ {
1210
+ label: "bindBleDevice",
1211
+ rejectIfBusy: true,
1212
+ busyError: _LedgerAdapter._createDeviceBusyError("bindBleDevice")
1213
+ }
1214
+ );
1215
+ } catch (error) {
1216
+ return this.errorToFailure(error);
1217
+ }
1100
1218
  }
1101
- async acquireInteraction(connectId, context) {
1219
+ async acquireOperation(connectId, context) {
1102
1220
  try {
1103
1221
  return await this._jobQueue.enqueue(
1104
1222
  connectId || "__ledger_acquire__",
@@ -1111,8 +1229,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1111
1229
  await this._ensureDevicePermission(target, void 0, signal);
1112
1230
  const attempt = {
1113
1231
  ...context,
1114
- extra: context.extra ? { ...context.extra } : void 0,
1115
- previousConnectId: target
1232
+ extra: context.extra ? { ...context.extra } : void 0
1116
1233
  };
1117
1234
  try {
1118
1235
  const resolvedConnectId = await this.ensureConnected(
@@ -1123,9 +1240,9 @@ var _LedgerAdapter = class _LedgerAdapter {
1123
1240
  attempt
1124
1241
  );
1125
1242
  _LedgerAdapter._throwIfAborted(signal);
1126
- const result = this._createInteraction(connectId, resolvedConnectId);
1243
+ const result = this._createOperation(connectId, resolvedConnectId);
1127
1244
  if (result.success && attempt.selectedConnection) {
1128
- this._pendingInteractionBindings.set(result.payload, attempt);
1245
+ this._pendingOperationBindings.set(result.payload, attempt);
1129
1246
  }
1130
1247
  return result;
1131
1248
  } catch (error) {
@@ -1134,19 +1251,19 @@ var _LedgerAdapter = class _LedgerAdapter {
1134
1251
  }
1135
1252
  },
1136
1253
  {
1137
- label: "acquireInteraction",
1254
+ label: "acquireOperation",
1138
1255
  rejectIfBusy: true,
1139
- busyError: _LedgerAdapter._createDeviceBusyError("acquireInteraction")
1256
+ busyError: _LedgerAdapter._createDeviceBusyError("acquireOperation")
1140
1257
  }
1141
1258
  );
1142
1259
  } catch (error) {
1143
1260
  return this.errorToFailure(error);
1144
1261
  }
1145
1262
  }
1146
- _createInteraction(searchTargetId, resolvedConnectId) {
1147
- this._interactions.endByConnectionKey(resolvedConnectId, "explicit");
1263
+ _createOperation(searchTargetId, resolvedConnectId) {
1264
+ this._operations.endByConnectionKey(resolvedConnectId, "explicit");
1148
1265
  const sessionId = this._sessions.get(resolvedConnectId);
1149
- if (sessionId) this._interactions.endByConnectionKey(sessionId, "explicit");
1266
+ if (sessionId) this._operations.endByConnectionKey(sessionId, "explicit");
1150
1267
  const device = this._discoveredDevices.get(resolvedConnectId) ?? {
1151
1268
  vendor: "ledger",
1152
1269
  model: "unknown",
@@ -1155,15 +1272,15 @@ var _LedgerAdapter = class _LedgerAdapter {
1155
1272
  connectId: resolvedConnectId,
1156
1273
  connectionType: this.connector.connectionType
1157
1274
  };
1158
- const interaction = this._interactions.create({
1275
+ const operation = this._operations.create({
1159
1276
  searchTargetId,
1160
1277
  connectId: resolvedConnectId,
1161
1278
  device,
1162
1279
  connectionKeys: [this._sessions.get(resolvedConnectId) ?? ""]
1163
1280
  });
1164
- return success(interaction.interactionId);
1281
+ return success(operation.operationId);
1165
1282
  }
1166
- async _connectTarget(connectId, preserveInteractionId, signal) {
1283
+ async _connectTarget(connectId, preserveOperationId, signal) {
1167
1284
  debugLog("[LedgerAdapter][REQ]", { method: "connectDevice", connectId, params: { connectId } });
1168
1285
  try {
1169
1286
  this._assertConnectorReady("connectDevice");
@@ -1179,21 +1296,17 @@ var _LedgerAdapter = class _LedgerAdapter {
1179
1296
  await this._ensureDevicePermission(connectId, void 0, signal);
1180
1297
  if (signal) _LedgerAdapter._throwIfAborted(signal);
1181
1298
  if (this.connector.availableTransports && this.connector.availableTransports.length > 1) {
1182
- await this._evictAllSessions(preserveInteractionId);
1299
+ await this._evictAllSessions(preserveOperationId);
1183
1300
  } else if (this._isBleConnection()) {
1184
1301
  const previousSessionId = this._sessions.get(connectId);
1185
- this._interactions.endByConnectionKey(connectId, "explicit", preserveInteractionId);
1302
+ this._operations.endByConnectionKey(connectId, "explicit", preserveOperationId);
1186
1303
  if (previousSessionId) {
1187
- this._interactions.endByConnectionKey(
1188
- previousSessionId,
1189
- "explicit",
1190
- preserveInteractionId
1191
- );
1304
+ this._operations.endByConnectionKey(previousSessionId, "explicit", preserveOperationId);
1192
1305
  this._sessions.delete(connectId);
1193
1306
  await this.connector.disconnect(previousSessionId).catch(() => void 0);
1194
1307
  }
1195
1308
  } else {
1196
- await this._evictAllSessions(preserveInteractionId);
1309
+ await this._evictAllSessions(preserveOperationId);
1197
1310
  }
1198
1311
  if (signal) _LedgerAdapter._throwIfAborted(signal);
1199
1312
  const stateGeneration = this._stateGeneration;
@@ -1230,37 +1343,37 @@ var _LedgerAdapter = class _LedgerAdapter {
1230
1343
  return failureResult;
1231
1344
  }
1232
1345
  }
1233
- async releaseInteraction(interactionId) {
1234
- const interaction = this._interactions.find(interactionId);
1235
- if (!interaction) {
1236
- this._interactions.resolve(interactionId);
1346
+ async releaseOperation(operationId) {
1347
+ const operation = this._operations.find(operationId);
1348
+ if (!operation) {
1349
+ this._operations.resolve(operationId);
1237
1350
  return;
1238
1351
  }
1239
- const endedInteraction = this._interactions.end(interactionId, "explicit");
1240
- if (!endedInteraction) return;
1241
- const { connectId } = interaction;
1352
+ const endedOperation = this._operations.end(operationId, "explicit");
1353
+ if (!endedOperation) return;
1354
+ const { connectId } = operation;
1242
1355
  debugLog("[LedgerAdapter][REQ]", {
1243
- method: "releaseInteraction",
1356
+ method: "releaseOperation",
1244
1357
  connectId,
1245
1358
  params: { connectId }
1246
1359
  });
1247
1360
  try {
1248
- await this._releaseInteractionConnection(endedInteraction);
1249
- debugLog("[LedgerAdapter][RES]", { method: "releaseInteraction", success: true });
1361
+ await this._releaseOperationConnection(endedOperation);
1362
+ debugLog("[LedgerAdapter][RES]", { method: "releaseOperation", success: true });
1250
1363
  } catch (err) {
1251
1364
  const e = err;
1252
1365
  debugLog("[LedgerAdapter][RES]", {
1253
- method: "releaseInteraction",
1366
+ method: "releaseOperation",
1254
1367
  success: false,
1255
1368
  error: { message: e?.message, _tag: e?._tag, code: e?.code ?? e?.errorCode }
1256
1369
  });
1257
1370
  throw err;
1258
1371
  }
1259
1372
  }
1260
- async _releaseInteractionConnection(interaction) {
1373
+ async _releaseOperationConnection(operation) {
1261
1374
  const sessionIds = /* @__PURE__ */ new Set();
1262
1375
  for (const [connectId, sessionId] of this._sessions) {
1263
- if (connectId === interaction.connectId || interaction.connectionKeys.includes(connectId) || interaction.connectionKeys.includes(sessionId)) {
1376
+ if (connectId === operation.connectId || operation.connectionKeys.includes(connectId) || operation.connectionKeys.includes(sessionId)) {
1264
1377
  this._sessions.delete(connectId);
1265
1378
  sessionIds.add(sessionId);
1266
1379
  }
@@ -1271,16 +1384,16 @@ var _LedgerAdapter = class _LedgerAdapter {
1271
1384
  );
1272
1385
  }
1273
1386
  }
1274
- async _releaseLostInteractionConnection(interactionId) {
1275
- const endedInteraction = this._interactions.end(interactionId, "disconnect");
1276
- if (!endedInteraction) return;
1277
- this._discoveredDevices.delete(endedInteraction.connectId);
1278
- await this._releaseInteractionConnection(endedInteraction);
1387
+ async _releaseLostOperationConnection(operationId) {
1388
+ const endedOperation = this._operations.end(operationId, "disconnect");
1389
+ if (!endedOperation) return;
1390
+ this._discoveredDevices.delete(endedOperation.connectId);
1391
+ await this._releaseOperationConnection(endedOperation);
1279
1392
  }
1280
- async getDeviceInfo(connectIdOrInteractionId, deviceId) {
1393
+ async getDeviceInfo(connectIdOrOperationId, deviceId) {
1281
1394
  let connectId;
1282
1395
  try {
1283
- connectId = isHardwareInteractionId(connectIdOrInteractionId) ? this._interactions.resolve(connectIdOrInteractionId).connectId : connectIdOrInteractionId;
1396
+ connectId = isHardwareOperationId(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
1284
1397
  } catch (error) {
1285
1398
  return this.errorToFailure(error);
1286
1399
  }
@@ -1321,9 +1434,6 @@ var _LedgerAdapter = class _LedgerAdapter {
1321
1434
  throw err;
1322
1435
  }
1323
1436
  }
1324
- getSupportedChains() {
1325
- return ["evm", "btc", "sol", "tron", "zcash"];
1326
- }
1327
1437
  // ---------------------------------------------------------------------------
1328
1438
  // Chain call helper
1329
1439
  // ---------------------------------------------------------------------------
@@ -1358,7 +1468,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1358
1468
  if (params && typeof params === "object") {
1359
1469
  const {
1360
1470
  autoInstallApp,
1361
- interactionId,
1471
+ operationId,
1362
1472
  passphraseState: _passphraseState,
1363
1473
  useEmptyPassphrase: _useEmptyPassphrase,
1364
1474
  knownConnections,
@@ -1369,7 +1479,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1369
1479
  return {
1370
1480
  commonParams: {
1371
1481
  autoInstallApp: typeof autoInstallApp === "boolean" ? autoInstallApp : void 0,
1372
- interactionId: typeof interactionId === "string" ? interactionId : void 0,
1482
+ operationId: typeof operationId === "string" ? operationId : void 0,
1373
1483
  knownConnections,
1374
1484
  extra,
1375
1485
  allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0
@@ -1698,6 +1808,20 @@ var _LedgerAdapter = class _LedgerAdapter {
1698
1808
  this.emitter.off(event, listener);
1699
1809
  }
1700
1810
  cancel(connectId) {
1811
+ const namedOperationIsLive = (id) => {
1812
+ try {
1813
+ this._operations.resolve(id);
1814
+ return true;
1815
+ } catch {
1816
+ return false;
1817
+ }
1818
+ };
1819
+ if (isHardwareOperationId(connectId) && !namedOperationIsLive(connectId)) {
1820
+ debugLog("[LedgerAdapter] cancel target already ended; nothing to cancel", {
1821
+ connectId
1822
+ });
1823
+ return;
1824
+ }
1701
1825
  const userAbortReason = Object.assign(new Error("User aborted operation"), {
1702
1826
  code: HardwareErrorCode3.UserAborted,
1703
1827
  _tag: ERROR_TAG.UserAborted
@@ -1708,29 +1832,29 @@ var _LedgerAdapter = class _LedgerAdapter {
1708
1832
  this._lastCancelReason = void 0;
1709
1833
  }
1710
1834
  }, 2e3);
1711
- this._uiRegistry.cancel();
1712
- this._finishBleBinding("cancelled");
1713
1835
  const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
1714
- let interactionId;
1715
- if (isHardwareInteractionId(connectId)) {
1716
- interactionId = connectId;
1717
- } else if (!connectId && isHardwareInteractionId(activeJobId)) {
1718
- interactionId = activeJobId;
1836
+ let operationId;
1837
+ if (isHardwareOperationId(connectId)) {
1838
+ operationId = connectId;
1839
+ } else if (!connectId && isHardwareOperationId(activeJobId)) {
1840
+ operationId = activeJobId;
1719
1841
  }
1720
1842
  let resolvedConnectId = connectId;
1721
- if (interactionId) {
1843
+ if (operationId) {
1722
1844
  try {
1723
- resolvedConnectId = this._interactions.resolve(interactionId).connectId;
1845
+ resolvedConnectId = this._operations.resolve(operationId).connectId;
1724
1846
  } catch {
1725
1847
  resolvedConnectId = void 0;
1726
1848
  }
1727
1849
  }
1728
- const interactionForPhysicalId = !interactionId && connectId ? this._interactions.findActiveByConnectionKey(connectId) : void 0;
1729
- const queueKey = interactionId ?? (activeJobId === connectId ? connectId : interactionForPhysicalId?.interactionId) ?? connectId;
1730
- const pendingInteractionId = interactionId ?? interactionForPhysicalId?.interactionId;
1731
- if (!connectId) this._pendingInteractionBindings.clear();
1732
- else if (pendingInteractionId) {
1733
- this._pendingInteractionBindings.delete(pendingInteractionId);
1850
+ this._uiRegistry.cancel();
1851
+ this._finishBleBinding("cancelled");
1852
+ const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
1853
+ const queueKey = operationId ?? (activeJobId === connectId ? connectId : interactionForPhysicalId?.operationId) ?? connectId;
1854
+ const pendingOperationId = operationId ?? interactionForPhysicalId?.operationId;
1855
+ if (!connectId) this._pendingOperationBindings.clear();
1856
+ else if (pendingOperationId) {
1857
+ this._pendingOperationBindings.delete(pendingOperationId);
1734
1858
  }
1735
1859
  if (queueKey) {
1736
1860
  this._jobQueue.cancelActiveAndPending(queueKey, userAbortReason);
@@ -1764,10 +1888,10 @@ var _LedgerAdapter = class _LedgerAdapter {
1764
1888
  formatDeviceMismatchError(deviceId, fingerprint)
1765
1889
  );
1766
1890
  }
1767
- if (isHardwareInteractionId(connectId)) {
1768
- const interaction = this._interactions.resolve(connectId);
1891
+ if (isHardwareOperationId(connectId)) {
1892
+ const operation = this._operations.resolve(connectId);
1769
1893
  await this._publishVerifiedBleBinding(
1770
- interaction.connectId,
1894
+ operation.connectId,
1771
1895
  chain,
1772
1896
  fingerprint,
1773
1897
  void 0,
@@ -1782,39 +1906,53 @@ var _LedgerAdapter = class _LedgerAdapter {
1782
1906
  }
1783
1907
  }
1784
1908
  /** Discovery may select a BLE target before a later call verifies its wallet. */
1785
- async _publishVerifiedBleBinding(connectId, chain, fingerprint, attempt, interactionId, previousConnectId = "", signal) {
1786
- const binding = interactionId ? this._pendingInteractionBindings.get(interactionId) : attempt;
1787
- if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== connectId) return;
1788
- const acknowledged = await requestSaveDeviceBinding(
1789
- this.emitter,
1790
- this._uiRegistry,
1791
- {
1792
- selectionRequestId: binding.selectedConnection.requestId,
1793
- connection: { transport: "ble", connectId },
1794
- identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
1795
- extra: binding.extra
1796
- },
1797
- signal
1798
- );
1799
- if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
1800
- this._bindingSelectionRequestId = void 0;
1801
- }
1802
- if (interactionId) {
1803
- this._interactions.resolve(interactionId);
1804
- this._pendingInteractionBindings.delete(interactionId);
1909
+ async _publishVerifiedBleBinding(connectId, chain, fingerprint, attempt, operationId, signal) {
1910
+ const binding = operationId ? this._pendingOperationBindings.get(operationId) : attempt;
1911
+ if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== connectId) {
1912
+ return false;
1805
1913
  }
1806
- if (acknowledged) return;
1807
- this.emitter.emit(DEVICE.LEDGER_CONNECTION_VERIFIED, {
1808
- type: DEVICE.LEDGER_CONNECTION_VERIFIED,
1809
- payload: {
1810
- previousConnectId: binding.previousConnectId ?? previousConnectId,
1811
- connectId,
1812
- chain,
1813
- fingerprint,
1814
- extra: binding.extra,
1815
- selectionRequestId: binding.selectedConnection.requestId
1914
+ try {
1915
+ const outcome = await requestSaveDeviceBinding(
1916
+ this.emitter,
1917
+ this._uiRegistry,
1918
+ {
1919
+ selectionRequestId: binding.selectedConnection.requestId,
1920
+ connection: { transport: "ble", connectId },
1921
+ identity: { vendor: "ledger", type: "chainFingerprint", chain, value: fingerprint },
1922
+ extra: binding.extra
1923
+ },
1924
+ signal
1925
+ );
1926
+ if (operationId) this._operations.resolve(operationId);
1927
+ if (!outcome.saved) {
1928
+ debugLog("[LedgerAdapter] BLE binding not persisted by host", {
1929
+ connectId,
1930
+ reason: outcome.reason
1931
+ });
1816
1932
  }
1817
- });
1933
+ return outcome.saved;
1934
+ } catch (error) {
1935
+ if (operationId) {
1936
+ this._pendingOperationBindings.delete(operationId);
1937
+ this._operations.end(operationId, "explicit");
1938
+ }
1939
+ const sessionId = this._sessions.get(connectId);
1940
+ this._sessions.delete(connectId);
1941
+ if (sessionId) {
1942
+ const teardown = this._runConnectorTeardown(
1943
+ () => this.connector.disconnect(sessionId)
1944
+ ).catch(() => void 0);
1945
+ if (!signal?.aborted) await teardown;
1946
+ }
1947
+ throw error;
1948
+ } finally {
1949
+ if (this._bindingSelectionRequestId === binding.selectedConnection.requestId) {
1950
+ this._bindingSelectionRequestId = void 0;
1951
+ }
1952
+ if (operationId) {
1953
+ this._pendingOperationBindings.delete(operationId);
1954
+ }
1955
+ }
1818
1956
  }
1819
1957
  /** Verify on the acquired session without re-entering the job queue. */
1820
1958
  async _verifyDeviceFingerprintWithSession(sessionId, deviceId, chain) {
@@ -1986,7 +2124,7 @@ var _LedgerAdapter = class _LedgerAdapter {
1986
2124
  // Layer 1 entry. Caller signal only races the outer awaiter; the shared
1987
2125
  // `_doConnect` runs under its own internal controller so caller A's cancel
1988
2126
  // doesn't kill caller B's await.
1989
- async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false, preserveInteractionId, context) {
2127
+ async ensureConnected(connectId, signal, allowUsbEphemeralFallback = false, preserveOperationId, context) {
1990
2128
  if (signal.aborted) _LedgerAdapter._throwIfAborted(signal);
1991
2129
  this._assertConnectorReady("connectDevice");
1992
2130
  if (this._isBleConnection() && !connectId && !allowUsbEphemeralFallback) {
@@ -2016,7 +2154,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2016
2154
  innerSignal,
2017
2155
  connectId,
2018
2156
  allowUsbEphemeralFallback,
2019
- preserveInteractionId,
2157
+ preserveOperationId,
2020
2158
  context
2021
2159
  );
2022
2160
  } finally {
@@ -2032,7 +2170,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2032
2170
  // Layer 1 main loop — the ONLY place in SDK that emits unlock dialog.
2033
2171
  // Bounded by MAX_DOCONNECT_CONFIRMS — after N Confirms with no progress,
2034
2172
  // throw DeviceNotFound so the user is kicked out of the loop.
2035
- async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false, preserveInteractionId, context) {
2173
+ async _doConnect(internalSignal, targetConnectId, allowUsbEphemeralFallback = false, preserveOperationId, context) {
2036
2174
  _LedgerAdapter._throwIfAborted(internalSignal);
2037
2175
  if (this.connector.availableTransports?.includes("usb") && this.connector.availableTransports.includes("ble")) {
2038
2176
  this._activeConnectionType = "usb";
@@ -2047,7 +2185,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2047
2185
  usbDevices,
2048
2186
  usbTarget,
2049
2187
  allowUsbEphemeralFallback,
2050
- preserveInteractionId,
2188
+ preserveOperationId,
2051
2189
  context,
2052
2190
  internalSignal
2053
2191
  );
@@ -2057,24 +2195,26 @@ var _LedgerAdapter = class _LedgerAdapter {
2057
2195
  (connection) => connection.transport === "ble"
2058
2196
  );
2059
2197
  if (knownBle?.transport === "ble") {
2060
- return this._connectDeviceOrThrow(
2061
- knownBle.connectId,
2062
- preserveInteractionId,
2063
- internalSignal
2064
- );
2198
+ return this._connectDeviceOrThrow(knownBle.connectId, preserveOperationId, internalSignal);
2199
+ }
2200
+ if (targetConnectId && context?.knownConnections === void 0) {
2201
+ throw createHwkError({
2202
+ code: HardwareErrorCode3.DeviceNotFound,
2203
+ message: "Ledger connection metadata is required before starting Bluetooth binding"
2204
+ });
2065
2205
  }
2066
2206
  const bleDevices = await this._searchDevices({ transportType: "ble" }, internalSignal);
2067
2207
  return this._connectFirstOrSelect(
2068
2208
  bleDevices,
2069
2209
  void 0,
2070
2210
  allowUsbEphemeralFallback,
2071
- preserveInteractionId,
2211
+ preserveOperationId,
2072
2212
  context,
2073
2213
  internalSignal
2074
2214
  );
2075
2215
  }
2076
2216
  if (this._isBleConnection() && targetConnectId) {
2077
- return this._connectDeviceOrThrow(targetConnectId, preserveInteractionId, internalSignal);
2217
+ return this._connectDeviceOrThrow(targetConnectId, preserveOperationId, internalSignal);
2078
2218
  }
2079
2219
  let confirms = 0;
2080
2220
  while (!internalSignal.aborted) {
@@ -2101,7 +2241,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2101
2241
  devices,
2102
2242
  targetConnectId,
2103
2243
  allowUsbEphemeralFallback,
2104
- preserveInteractionId,
2244
+ preserveOperationId,
2105
2245
  context,
2106
2246
  internalSignal
2107
2247
  );
@@ -2132,20 +2272,20 @@ var _LedgerAdapter = class _LedgerAdapter {
2132
2272
  _LedgerAdapter._throwIfAborted(internalSignal);
2133
2273
  throw new Error("_doConnect aborted");
2134
2274
  }
2135
- async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback = false, preserveInteractionId, context, signal) {
2136
- if (signal) _LedgerAdapter._throwIfAborted(signal);
2275
+ async _connectFirstOrSelect(devices, targetConnectId, allowUsbEphemeralFallback, preserveOperationId, context, signal) {
2276
+ _LedgerAdapter._throwIfAborted(signal);
2137
2277
  if (targetConnectId) {
2138
2278
  const target = devices.find(
2139
2279
  (d) => d.connectId === targetConnectId || d.deviceId === targetConnectId
2140
2280
  );
2141
2281
  if (target) {
2142
- return this._connectDeviceOrThrow(target.connectId, preserveInteractionId, signal);
2282
+ return this._connectDeviceOrThrow(target.connectId, preserveOperationId, signal);
2143
2283
  }
2144
2284
  if (!this._isBleConnection() && devices.length === 1 && allowUsbEphemeralFallback) {
2145
2285
  debugLog(
2146
2286
  `[LedgerAdapter] target ${targetConnectId} not in fresh enumeration; accepting sole USB device ${devices[0].connectId} for fingerprint-verified recovery`
2147
2287
  );
2148
- return this._connectDeviceOrThrow(devices[0].connectId, preserveInteractionId, signal);
2288
+ return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
2149
2289
  }
2150
2290
  if (!this._isBleConnection() || !allowUsbEphemeralFallback) {
2151
2291
  const err = Object.assign(new Error(`Target Ledger unavailable: ${targetConnectId}`), {
@@ -2166,11 +2306,26 @@ var _LedgerAdapter = class _LedgerAdapter {
2166
2306
  if (requiresBleSelection && context?.allowDeviceSelection !== false) {
2167
2307
  const bindingSessionId = context?.bindingSessionId ?? this._uiRegistry.createRequestId();
2168
2308
  if (context) context.bindingSessionId = bindingSessionId;
2309
+ const allowUsbFallback = context?.bindingReason !== "manual-rebind" && Boolean(this.connector.availableTransports?.includes("usb"));
2310
+ const knownUsb = context?.knownConnections?.find(
2311
+ (connection) => connection.transport === "usb"
2312
+ );
2313
+ const usbConnectId = knownUsb?.transport === "usb" ? knownUsb.connectId : targetConnectId;
2169
2314
  const { device, requestId } = await requestBleDeviceSelection({
2170
2315
  emitter: this.emitter,
2171
2316
  registry: this._uiRegistry,
2172
- signal: signal ?? new AbortController().signal,
2173
- scan: async () => (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId)),
2317
+ signal,
2318
+ allowUsbFallback,
2319
+ scan: async () => {
2320
+ if (allowUsbFallback) {
2321
+ const usbDevices = await this._searchDevices({ transportType: "usb" }, signal);
2322
+ const candidate = usbDevices.find(
2323
+ (device2) => device2.connectionType === "usb" && device2.connectId === usbConnectId
2324
+ ) ?? (usbDevices.length === 1 && usbDevices[0].connectionType === "usb" ? usbDevices[0] : void 0);
2325
+ if (candidate) return [candidate];
2326
+ }
2327
+ return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId));
2328
+ },
2174
2329
  request: {
2175
2330
  devices: devices.filter((device2) => !context?.rejectedConnectIds?.has(device2.connectId)),
2176
2331
  bindingSessionId,
@@ -2178,14 +2333,25 @@ var _LedgerAdapter = class _LedgerAdapter {
2178
2333
  context: {
2179
2334
  kind: "bind-connection",
2180
2335
  transport: "ble",
2181
- reason: targetConnectId ? "known-connection-unavailable" : "missing-binding"
2336
+ reason: context?.bindingReason ?? (targetConnectId ? "known-connection-unavailable" : "missing-binding")
2182
2337
  },
2183
2338
  extra: context?.extra
2184
2339
  }
2185
2340
  });
2341
+ if (device.connectionType === "usb") {
2342
+ this._activeConnectionType = "usb";
2343
+ if (context) context.selectedConnection = void 0;
2344
+ if (preserveOperationId) this._pendingOperationBindings.delete(preserveOperationId);
2345
+ this._bindingSelectionRequestId = void 0;
2346
+ this.emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
2347
+ type: UI_REQUEST.DEVICE_BINDING_STATUS,
2348
+ payload: { selectionRequestId: requestId, status: "cancelled" }
2349
+ });
2350
+ return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
2351
+ }
2186
2352
  if (context) context.selectedConnection = { connectId: device.connectId, requestId };
2187
2353
  this._bindingSelectionRequestId = requestId;
2188
- return this._connectDeviceOrThrow(device.connectId, preserveInteractionId, signal);
2354
+ return this._connectDeviceOrThrow(device.connectId, preserveOperationId, signal);
2189
2355
  }
2190
2356
  if (devices.length > 0 && (devices.length > 1 || requiresBleSelection)) {
2191
2357
  if (context?.allowDeviceSelection === false || !this.emitter.listenerCount(UI_REQUEST.REQUEST_SELECT_DEVICE)) {
@@ -2222,17 +2388,17 @@ var _LedgerAdapter = class _LedgerAdapter {
2222
2388
  }
2223
2389
  if (context && requiresBleSelection)
2224
2390
  context.selectedConnection = { connectId: selected.connectId, requestId };
2225
- return this._connectDeviceOrThrow(selected.connectId, preserveInteractionId, signal);
2391
+ return this._connectDeviceOrThrow(selected.connectId, preserveOperationId, signal);
2226
2392
  }
2227
2393
  if (devices.length !== 1) {
2228
2394
  throw Object.assign(new Error("Ledger device not found."), {
2229
2395
  code: HardwareErrorCode3.DeviceNotFound
2230
2396
  });
2231
2397
  }
2232
- return this._connectDeviceOrThrow(devices[0].connectId, preserveInteractionId, signal);
2398
+ return this._connectDeviceOrThrow(devices[0].connectId, preserveOperationId, signal);
2233
2399
  }
2234
- async _connectDeviceOrThrow(chosenConnectId, preserveInteractionId, signal) {
2235
- const result = await this._connectTarget(chosenConnectId, preserveInteractionId, signal);
2400
+ async _connectDeviceOrThrow(chosenConnectId, preserveOperationId, signal) {
2401
+ const result = await this._connectTarget(chosenConnectId, preserveOperationId, signal);
2236
2402
  if (!result.success) {
2237
2403
  const payload = result.payload;
2238
2404
  const rethrow = Object.assign(new Error(payload.error), {
@@ -2274,6 +2440,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2274
2440
  */
2275
2441
  async _callConnector(sessionId, method, params, signal) {
2276
2442
  this._assertConnectorReady(method);
2443
+ if (signal?.aborted) throw this._abortReason(signal);
2277
2444
  const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
2278
2445
  let promise;
2279
2446
  try {
@@ -2338,32 +2505,32 @@ var _LedgerAdapter = class _LedgerAdapter {
2338
2505
  };
2339
2506
  }
2340
2507
  async connectorCall(connectId, method, params, fingerprint, permissionDeviceId, commonParams, installContext) {
2341
- const positionalInteractionId = isHardwareInteractionId(connectId) ? connectId : void 0;
2342
- if (positionalInteractionId && commonParams?.interactionId && positionalInteractionId !== commonParams.interactionId) {
2508
+ const positionalOperationId = isHardwareOperationId(connectId) ? connectId : void 0;
2509
+ if (positionalOperationId && commonParams?.operationId && positionalOperationId !== commonParams.operationId) {
2343
2510
  throw createHwkError({
2344
2511
  code: HardwareErrorCode3.InvalidParams,
2345
- message: "Conflicting Ledger interaction ids",
2512
+ message: "Conflicting Ledger operation ids",
2346
2513
  params: {
2347
- positionalInteractionId,
2348
- commonInteractionId: commonParams.interactionId
2514
+ positionalOperationId,
2515
+ commonOperationId: commonParams.operationId
2349
2516
  }
2350
2517
  });
2351
2518
  }
2352
- const interactionId = commonParams?.interactionId ?? positionalInteractionId;
2353
- const interaction = interactionId ? this._interactions.resolve(interactionId) : void 0;
2354
- const releaseInteractionRetention = interactionId ? this._interactions.retain(interactionId) : void 0;
2355
- const effectiveConnectId = interaction?.connectId ?? connectId;
2519
+ const operationId = commonParams?.operationId ?? positionalOperationId;
2520
+ const operation = operationId ? this._operations.resolve(operationId) : void 0;
2521
+ const releaseOperationRetention = operationId ? this._operations.retain(operationId) : void 0;
2522
+ const effectiveConnectId = operation?.connectId ?? connectId;
2356
2523
  debugLog("[LedgerAdapter][REQ]", {
2357
2524
  method,
2358
2525
  connectId: effectiveConnectId || "(empty)",
2359
2526
  params
2360
2527
  });
2361
- const queueKey = (interactionId ?? effectiveConnectId) || "__ledger_default__";
2528
+ const queueKey = (operationId ?? effectiveConnectId) || "__ledger_default__";
2362
2529
  try {
2363
2530
  const result = await this._jobQueue.enqueue(
2364
2531
  queueKey,
2365
2532
  async (signal) => {
2366
- if (interactionId) this._activeInteractionJobs.add(interactionId);
2533
+ if (operationId) this._activeOperationJobs.add(operationId);
2367
2534
  try {
2368
2535
  return await this._runConnectorCall(
2369
2536
  effectiveConnectId,
@@ -2374,16 +2541,16 @@ var _LedgerAdapter = class _LedgerAdapter {
2374
2541
  permissionDeviceId,
2375
2542
  commonParams,
2376
2543
  installContext ?? {},
2377
- interactionId
2544
+ operationId
2378
2545
  );
2379
2546
  } catch (error) {
2380
2547
  this._finishBleBinding(signal.aborted ? "cancelled" : "failed");
2381
2548
  throw error;
2382
2549
  } finally {
2383
- if (interactionId) {
2384
- this._activeInteractionJobs.delete(interactionId);
2385
- if (this._pendingInteractionDisconnects.delete(interactionId)) {
2386
- await this._releaseLostInteractionConnection(interactionId);
2550
+ if (operationId) {
2551
+ this._activeOperationJobs.delete(operationId);
2552
+ if (this._pendingOperationDisconnects.delete(operationId)) {
2553
+ await this._releaseLostOperationConnection(operationId);
2387
2554
  }
2388
2555
  }
2389
2556
  }
@@ -2409,15 +2576,19 @@ var _LedgerAdapter = class _LedgerAdapter {
2409
2576
  });
2410
2577
  throw err;
2411
2578
  } finally {
2412
- releaseInteractionRetention?.();
2579
+ releaseOperationRetention?.();
2413
2580
  }
2414
2581
  }
2415
2582
  /**
2416
2583
  * Race a promise against an abort signal. On abort, rejects with
2417
2584
  * signal.reason → instance _lastCancelReason → generic Error('Aborted').
2418
2585
  */
2586
+ /** Hermes/RN polyfills don't always populate signal.reason; fall back. */
2587
+ _abortReason(signal) {
2588
+ return signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
2589
+ }
2419
2590
  _abortable(signal, promise) {
2420
- const getAbortReason = () => signal.reason ?? this._lastCancelReason ?? new Error("Aborted");
2591
+ const getAbortReason = () => this._abortReason(signal);
2421
2592
  if (signal.aborted) {
2422
2593
  void promise.catch(() => void 0);
2423
2594
  return Promise.reject(getAbortReason());
@@ -2446,9 +2617,9 @@ var _LedgerAdapter = class _LedgerAdapter {
2446
2617
  }
2447
2618
  }
2448
2619
  /** Actual work done under the job queue — connection, fingerprint, call, and recovery. */
2449
- async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext, interactionId, lockedRetryBudget = _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET) {
2620
+ async _runConnectorCall(connectId, method, params, signal, fingerprint, permissionDeviceId, commonParams, installContext, operationId, lockedRetryBudget = _LedgerAdapter.MAX_BUSINESS_RETRY_BUDGET) {
2450
2621
  _LedgerAdapter._throwIfAborted(signal);
2451
- if (!interactionId) {
2622
+ if (!operationId) {
2452
2623
  await this._ensureDevicePermission(
2453
2624
  connectId,
2454
2625
  permissionDeviceId ?? fingerprint?.deviceId,
@@ -2484,7 +2655,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2484
2655
  message: "Ledger all-network connection ended"
2485
2656
  });
2486
2657
  }
2487
- let resolvedConnectId = interactionId ? this._interactions.resolve(interactionId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
2658
+ let resolvedConnectId = operationId ? this._operations.resolve(operationId).connectId : bundleConnection?.connectId ?? await this.ensureConnected(
2488
2659
  preferredConnectId,
2489
2660
  signal,
2490
2661
  allowUsbEphemeralFallback,
@@ -2496,12 +2667,12 @@ var _LedgerAdapter = class _LedgerAdapter {
2496
2667
  installContext.connection = { connectId: resolvedConnectId, sessionId };
2497
2668
  }
2498
2669
  if (!sessionId) {
2499
- if (interactionId) {
2500
- this._interactions.end(interactionId, "disconnect");
2670
+ if (operationId) {
2671
+ this._operations.end(operationId, "disconnect");
2501
2672
  throw createHwkError({
2502
- code: HardwareErrorCode3.InteractionEnded,
2503
- message: "Ledger interaction connection is no longer active",
2504
- params: { interactionId, reason: "disconnect" }
2673
+ code: HardwareErrorCode3.OperationEnded,
2674
+ message: "Ledger operation connection is no longer active",
2675
+ params: { operationId, reason: "disconnect" }
2505
2676
  });
2506
2677
  }
2507
2678
  throw Object.assign(new Error("Auto-connect succeeded but no session found"), {
@@ -2520,7 +2691,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2520
2691
  )
2521
2692
  );
2522
2693
  if (fp.success) break;
2523
- const binding = interactionId ? this._pendingInteractionBindings.get(interactionId) : connectionAttempt;
2694
+ const binding = operationId ? this._pendingOperationBindings.get(operationId) : connectionAttempt;
2524
2695
  if (!this._isBleConnection() || binding?.selectedConnection?.connectId !== resolvedConnectId) {
2525
2696
  throw Object.assign(new Error(formatDeviceMismatchError(fp.expected, fp.actual)), {
2526
2697
  code: HardwareErrorCode3.DeviceMismatch
@@ -2531,13 +2702,13 @@ var _LedgerAdapter = class _LedgerAdapter {
2531
2702
  binding.rejectedConnectId = resolvedConnectId;
2532
2703
  this._sessions.delete(resolvedConnectId);
2533
2704
  await this.connector.disconnect(sessionId);
2534
- if (interactionId) this._pendingInteractionDisconnects.delete(interactionId);
2705
+ if (operationId) this._pendingOperationDisconnects.delete(operationId);
2535
2706
  _LedgerAdapter._throwIfAborted(signal);
2536
2707
  resolvedConnectId = await this._connectFirstOrSelect(
2537
2708
  [],
2538
2709
  void 0,
2539
2710
  true,
2540
- interactionId,
2711
+ operationId,
2541
2712
  binding,
2542
2713
  signal
2543
2714
  );
@@ -2549,8 +2720,8 @@ var _LedgerAdapter = class _LedgerAdapter {
2549
2720
  message: "Selected Ledger connection ended"
2550
2721
  });
2551
2722
  sessionId = selectedSession;
2552
- if (interactionId)
2553
- this._interactions.rebind(interactionId, {
2723
+ if (operationId)
2724
+ this._operations.rebind(operationId, {
2554
2725
  connectId: resolvedConnectId,
2555
2726
  device: selectedDevice,
2556
2727
  connectionKeys: [sessionId]
@@ -2558,22 +2729,21 @@ var _LedgerAdapter = class _LedgerAdapter {
2558
2729
  if (installContext)
2559
2730
  installContext.connection = { connectId: resolvedConnectId, sessionId };
2560
2731
  }
2561
- if (!interactionId && connectionAttempt.selectedConnection?.connectId === resolvedConnectId && this._isBleConnection()) {
2562
- this._verifiedBleReconnectTargets.set(connectId, {
2563
- connectId: resolvedConnectId,
2564
- chain: fingerprint.chain,
2565
- fingerprint: fingerprint.deviceId
2566
- });
2567
- }
2568
2732
  await this._publishVerifiedBleBinding(
2569
2733
  resolvedConnectId,
2570
2734
  fingerprint.chain,
2571
2735
  fingerprint.deviceId,
2572
2736
  connectionAttempt,
2573
- interactionId,
2574
- connectId,
2737
+ operationId,
2575
2738
  signal
2576
2739
  );
2740
+ if (!operationId && connectionAttempt.selectedConnection?.connectId === resolvedConnectId && this._isBleConnection()) {
2741
+ this._verifiedBleReconnectTargets.set(connectId, {
2742
+ connectId: resolvedConnectId,
2743
+ chain: fingerprint.chain,
2744
+ fingerprint: fingerprint.deviceId
2745
+ });
2746
+ }
2577
2747
  }
2578
2748
  businessCallStarted = true;
2579
2749
  return await this._callConnector(sessionId, method, effectiveParams, signal);
@@ -2581,17 +2751,17 @@ var _LedgerAdapter = class _LedgerAdapter {
2581
2751
  if (signal.aborted) throw err;
2582
2752
  if (isDeviceDisconnectedError(err) || isDeviceNotAdvertisingError(err) || isTimeoutError(err) || isConnectionLevelError(err)) {
2583
2753
  this._discoveredDevices.delete(resolvedConnectId);
2584
- if (interactionId) await this._releaseLostInteractionConnection(interactionId);
2754
+ if (operationId) await this._releaseLostOperationConnection(operationId);
2585
2755
  else {
2586
2756
  this._sessions.delete(resolvedConnectId);
2587
2757
  await this.connector.disconnect(sessionId).catch(() => void 0);
2588
2758
  }
2589
2759
  const ambiguous = businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(method);
2590
- const interactionParams = interactionId ? { interactionId, reason: "disconnect" } : void 0;
2760
+ const interactionParams = operationId ? { operationId, reason: "disconnect" } : void 0;
2591
2761
  throw createHwkError({
2592
- code: interactionId ? HardwareErrorCode3.InteractionEnded : mapLedgerError(err).code,
2762
+ code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(err).code,
2593
2763
  message: "Ledger operation connection was lost; start a new operation",
2594
- params: ambiguous ? operationMayHaveCompletedParams(method, { interactionId }) : interactionParams,
2764
+ params: ambiguous ? operationMayHaveCompletedParams(method, { operationId }) : interactionParams,
2595
2765
  recovery: ambiguous ? { scope: "unknown" } : void 0
2596
2766
  });
2597
2767
  }
@@ -2610,7 +2780,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2610
2780
  await this._waitForDeviceConnect(signal);
2611
2781
  if (this._sessions.get(resolvedConnectId) !== sessionId) {
2612
2782
  throw createHwkError({
2613
- code: interactionId ? HardwareErrorCode3.InteractionEnded : HardwareErrorCode3.DeviceDisconnected,
2783
+ code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
2614
2784
  message: "Ledger connection ended while waiting for unlock"
2615
2785
  });
2616
2786
  }
@@ -2623,7 +2793,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2623
2793
  permissionDeviceId,
2624
2794
  commonParams,
2625
2795
  installContext,
2626
- interactionId,
2796
+ operationId,
2627
2797
  lockedRetryBudget - 1
2628
2798
  );
2629
2799
  }
@@ -2631,7 +2801,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2631
2801
  await this._sleepAbortable(_LedgerAdapter.STUCK_APP_RETRY_DELAY_MS, signal);
2632
2802
  if (this._sessions.get(resolvedConnectId) !== sessionId) {
2633
2803
  throw createHwkError({
2634
- code: interactionId ? HardwareErrorCode3.InteractionEnded : HardwareErrorCode3.DeviceDisconnected,
2804
+ code: operationId ? HardwareErrorCode3.OperationEnded : HardwareErrorCode3.DeviceDisconnected,
2635
2805
  message: "Ledger connection ended during the app transition"
2636
2806
  });
2637
2807
  }
@@ -2640,26 +2810,26 @@ var _LedgerAdapter = class _LedgerAdapter {
2640
2810
  } catch (retryErr) {
2641
2811
  if (isStuckAppStateError(retryErr)) throw err;
2642
2812
  if (isDeviceDisconnectedError(retryErr) || isDeviceNotAdvertisingError(retryErr) || isTimeoutError(retryErr) || isConnectionLevelError(retryErr)) {
2643
- if (interactionId) await this._releaseLostInteractionConnection(interactionId);
2813
+ if (operationId) await this._releaseLostOperationConnection(operationId);
2644
2814
  else {
2645
2815
  this._sessions.delete(resolvedConnectId);
2646
2816
  this._discoveredDevices.delete(resolvedConnectId);
2647
2817
  await this.connector.disconnect(sessionId).catch(() => void 0);
2648
2818
  }
2649
2819
  throw createHwkError({
2650
- code: interactionId ? HardwareErrorCode3.InteractionEnded : mapLedgerError(retryErr).code,
2820
+ code: operationId ? HardwareErrorCode3.OperationEnded : mapLedgerError(retryErr).code,
2651
2821
  message: `Ledger ${method} may have completed before the connection was lost`,
2652
2822
  params: !canReplayHardwareMethodAfterTransportFailure(method) ? operationMayHaveCompletedParams(method, {
2653
- interactionId,
2823
+ operationId,
2654
2824
  reason: "disconnect"
2655
- }) : { interactionId, reason: "disconnect" },
2825
+ }) : { operationId, reason: "disconnect" },
2656
2826
  recovery: !canReplayHardwareMethodAfterTransportFailure(method) ? { scope: "unknown" } : void 0
2657
2827
  });
2658
2828
  }
2659
2829
  throw retryErr;
2660
2830
  }
2661
2831
  }
2662
- if (!interactionId && err?.code === HardwareErrorCode3.DeviceMismatch) {
2832
+ if (!operationId && err?.code === HardwareErrorCode3.DeviceMismatch) {
2663
2833
  this._sessions.delete(resolvedConnectId);
2664
2834
  this._discoveredDevices.delete(resolvedConnectId);
2665
2835
  await this.connector.disconnect(sessionId).catch(() => void 0);
@@ -2721,7 +2891,7 @@ var _LedgerAdapter = class _LedgerAdapter {
2721
2891
  permissionDeviceId,
2722
2892
  commonParams,
2723
2893
  installContext,
2724
- interactionId
2894
+ operationId
2725
2895
  );
2726
2896
  }
2727
2897
  }
@@ -2875,8 +3045,8 @@ var LedgerAdapter = _LedgerAdapter;
2875
3045
 
2876
3046
  // src/connector/LedgerConnectorBase.ts
2877
3047
  import {
2878
- EConnectorInteraction as EConnectorInteraction4,
2879
- HardwareErrorCode as HardwareErrorCode8,
3048
+ EConnectorInteraction as EConnectorInteraction3,
3049
+ HardwareErrorCode as HardwareErrorCode7,
2880
3050
  isKnownNonTargetHardwareVendor,
2881
3051
  serializeConnectorError
2882
3052
  } from "@onekeyfe/hwk-adapter-core";
@@ -4109,361 +4279,111 @@ async function _createZcashSigner(ctx, sessionId) {
4109
4279
  }
4110
4280
 
4111
4281
  // src/connector/chains/tron.ts
4112
- import { HardwareErrorCode as HardwareErrorCode7 } from "@onekeyfe/hwk-adapter-core";
4113
- import Trx from "@ledgerhq/hw-app-trx";
4114
-
4115
- // src/connector/chains/legacyChainCall.ts
4116
- import { EConnectorInteraction as EConnectorInteraction3 } from "@onekeyfe/hwk-adapter-core";
4282
+ import { HardwareErrorCode as HardwareErrorCode6, bytesToHex as bytesToHex3, hexToBytes as hexToBytes4 } from "@onekeyfe/hwk-adapter-core";
4117
4283
 
4118
- // src/app/AppManager.ts
4119
- import {
4120
- CloseAppCommand,
4121
- GetAppAndVersionCommand,
4122
- OpenAppCommand,
4123
- isSuccessCommandResult
4124
- } from "@ledgerhq/device-management-kit";
4125
- import { HardwareErrorCode as HardwareErrorCode6 } from "@onekeyfe/hwk-adapter-core";
4126
- var APP_NAME_MAP = {
4127
- ETH: "Ethereum",
4128
- BTC: "Bitcoin",
4129
- SOL: "Solana",
4130
- TRX: "Tron",
4131
- ZEC: "Zcash",
4132
- XRP: "XRP",
4133
- ADA: "Cardano",
4134
- DOT: "Polkadot",
4135
- ATOM: "Cosmos"
4136
- };
4137
- var DASHBOARD_APP_NAME = "BOLOS";
4138
- var AppManager = class {
4139
- constructor(dmk, options) {
4140
- this._dmk = dmk;
4141
- this._waitMs = options?.waitMs ?? 1e3;
4142
- this._maxRetries = options?.maxRetries ?? 10;
4284
+ // src/signer/SignerTron.ts
4285
+ var SignerTron = class {
4286
+ // eslint-disable-next-line no-useless-constructor, no-empty-function
4287
+ constructor(_sdk) {
4288
+ this._sdk = _sdk;
4143
4289
  }
4144
- /**
4145
- * Return the Ledger app name for a given chain ticker,
4146
- * or undefined if the chain is not supported.
4147
- */
4148
- static getAppName(chain) {
4149
- return APP_NAME_MAP[chain];
4290
+ /** Base58 Tron address plus the uncompressed public key at `derivationPath`. */
4291
+ async getAddress(derivationPath, options) {
4292
+ const action = this._sdk.getAddress(derivationPath, options);
4293
+ const result = await deviceActionToPromise(
4294
+ action,
4295
+ this.onInteraction,
4296
+ void 0,
4297
+ this.onRegisterCanceller
4298
+ );
4299
+ return { address: result.address, publicKey: result.publicKey };
4150
4300
  }
4151
- /**
4152
- * Ensure the target app is open on the device identified by `sessionId`.
4153
- *
4154
- * Flow:
4155
- * 1. Check the currently running app.
4156
- * 2. If it is already the target, return immediately.
4157
- * 3. If a different app is running (not dashboard), close it first.
4158
- * 4. Open the target app.
4159
- * 5. Poll until the device confirms the target app is running.
4160
- */
4161
- /**
4162
- * @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
4163
- * device is about to display "Open <app>" on screen and wait for the
4164
- * user's button press. UI consumers should show their "open app" prompt
4165
- * in response. NOT called when the target app is already open (no user
4166
- * interaction needed in that case).
4167
- *
4168
- * Important: OpenAppCommand is blocking. It does not resolve until the user
4169
- * has physically confirmed on the device, so anything that runs AFTER
4170
- * `await this._openApp(...)` lands AFTER the prompt is already gone.
4171
- * Hence the callback must fire BEFORE that await.
4172
- */
4173
- async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
4174
- const currentApp = await this._getCurrentApp(sessionId);
4175
- if (currentApp === targetAppName) {
4176
- return;
4177
- }
4178
- if (!this._isDashboard(currentApp)) {
4179
- await this._closeCurrentApp(sessionId);
4180
- await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
4181
- }
4182
- onConfirmOnDevice?.();
4183
- await this._openApp(sessionId, targetAppName);
4184
- await this._waitForApp(sessionId, targetAppName);
4301
+ /** Sign a protobuf-encoded raw transaction. */
4302
+ async signTransaction(derivationPath, transaction, options) {
4303
+ const action = this._sdk.signTransaction(derivationPath, transaction, options);
4304
+ return deviceActionToPromise(
4305
+ action,
4306
+ this.onInteraction,
4307
+ void 0,
4308
+ this.onRegisterCanceller
4309
+ );
4185
4310
  }
4186
- // ---------------------------------------------------------------------------
4187
- // Private helpers
4188
- // ---------------------------------------------------------------------------
4189
- async _getCurrentApp(sessionId) {
4190
- const result = await this._dmk.sendCommand({
4191
- sessionId,
4192
- command: new GetAppAndVersionCommand()
4193
- });
4194
- if (isSuccessCommandResult(result)) {
4195
- debugLog("[AppManager] currentApp:", result.data.name);
4196
- return result.data.name;
4197
- }
4198
- const errResult = result;
4199
- const dmkErr = errResult.error ?? {};
4200
- const original = dmkErr.originalError;
4201
- debugLog(
4202
- "[AppManager] _getCurrentApp failed sessionId=",
4203
- sessionId,
4204
- "tag=",
4205
- dmkErr._tag,
4206
- "errorCode=",
4207
- dmkErr.errorCode,
4208
- "message=",
4209
- dmkErr.message,
4210
- "originalErrorMessage=",
4211
- original?.message ?? String(original ?? "")
4311
+ /** Sign a personal message (TIP-191). */
4312
+ async signPersonalMessage(derivationPath, message, options) {
4313
+ const action = this._sdk.signPersonalMessage(derivationPath, message, options);
4314
+ return deviceActionToPromise(
4315
+ action,
4316
+ this.onInteraction,
4317
+ void 0,
4318
+ this.onRegisterCanceller
4212
4319
  );
4320
+ }
4321
+ };
4322
+
4323
+ // src/connector/chains/tron.ts
4324
+ async function tronGetAddress(ctx, sessionId, params) {
4325
+ const tronSigner = await _createTronSigner(ctx, sessionId);
4326
+ const path = normalizePath(params.path);
4327
+ try {
4328
+ const result = await tronSigner.getAddress(path, {
4329
+ checkOnDevice: params.showOnDevice ?? false
4330
+ });
4331
+ return { address: result.address, publicKey: result.publicKey, path: params.path };
4332
+ } catch (err) {
4333
+ ctx.invalidateSession(sessionId);
4334
+ throw ctx.wrapError(err);
4335
+ } finally {
4336
+ ctx.clearCanceller(sessionId);
4337
+ }
4338
+ }
4339
+ async function tronSignTransaction(ctx, sessionId, params) {
4340
+ if (!params.rawTxHex) {
4213
4341
  throw Object.assign(
4214
- new Error(
4215
- dmkErr.message ?? "Failed to get current app from device"
4216
- ),
4217
- {
4218
- _tag: dmkErr._tag,
4219
- errorCode: dmkErr.errorCode,
4220
- originalError: original
4221
- }
4342
+ new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
4343
+ { code: HardwareErrorCode6.InvalidParams }
4222
4344
  );
4223
4345
  }
4224
- async _openApp(sessionId, appName) {
4225
- const result = await this._dmk.sendCommand({
4226
- sessionId,
4227
- command: new OpenAppCommand({ appName })
4228
- });
4229
- if (!isSuccessCommandResult(result)) {
4230
- const dmkErr = result.error;
4231
- const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
4232
- const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
4233
- debugLog(
4234
- "[AppManager] openApp failed:",
4235
- appName,
4236
- "errorCode:",
4237
- errorCode,
4238
- "tag:",
4239
- dmkErr._tag
4240
- );
4241
- let code;
4242
- if (errorCode === "6807" || /unknown application/i.test(message)) {
4243
- code = HardwareErrorCode6.AppNotInstalled;
4244
- } else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
4245
- code = HardwareErrorCode6.UserRejected;
4246
- }
4247
- throw Object.assign(new Error(`Failed to open "${appName}"`), {
4248
- _tag: ERROR_TAG.OpenAppCommand,
4249
- code,
4250
- errorCode,
4251
- statusCode: errorCode,
4252
- appName,
4253
- originalError: dmkErr
4254
- });
4255
- }
4256
- }
4257
- async _closeCurrentApp(sessionId) {
4258
- debugLog("[AppManager] closeCurrentApp");
4259
- await this._dmk.sendCommand({
4260
- sessionId,
4261
- command: new CloseAppCommand()
4262
- });
4263
- }
4264
- /**
4265
- * Poll the device until the expected app is reported as running,
4266
- * or throw after `_maxRetries` attempts.
4267
- */
4268
- async _waitForApp(sessionId, expectedAppName) {
4269
- let lastSeen = "";
4270
- for (let i = 0; i < this._maxRetries; i++) {
4271
- await this._wait();
4272
- const current = await this._getCurrentApp(sessionId);
4273
- lastSeen = current;
4274
- if (current === expectedAppName) {
4275
- return;
4276
- }
4277
- }
4278
- debugLog(
4279
- "[AppManager] waitForApp exhausted: expected=",
4280
- expectedAppName,
4281
- "lastSeen=",
4282
- lastSeen
4283
- );
4284
- throw new Error(
4285
- `Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
4286
- );
4287
- }
4288
- _isDashboard(appName) {
4289
- return appName === DASHBOARD_APP_NAME;
4290
- }
4291
- _wait() {
4292
- return new Promise((resolve) => setTimeout(resolve, this._waitMs));
4293
- }
4294
- };
4295
-
4296
- // src/connector/chains/legacyChainCall.ts
4297
- function isLegacyWrongAppError(err, _appName) {
4298
- return isWrongAppError(err);
4299
- }
4300
- async function withLegacyChainCall(ctx, sessionId, options, action) {
4301
- const { appName, needsConfirmation } = options;
4302
- let openAppPromptShown = false;
4303
- const onAppOpenPrompt = () => {
4304
- openAppPromptShown = true;
4305
- ctx.emit("ui-event", {
4306
- type: EConnectorInteraction3.ConfirmOpenApp,
4307
- payload: { sessionId }
4308
- });
4309
- };
4310
- const closeOpenAppUiIfShown = () => {
4311
- if (openAppPromptShown) {
4312
- ctx.emit("ui-event", {
4313
- type: EConnectorInteraction3.InteractionComplete,
4314
- payload: { sessionId }
4315
- });
4316
- openAppPromptShown = false;
4317
- }
4318
- };
4319
- try {
4320
- await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
4321
- } catch (err) {
4322
- debugLog(
4323
- "[LegacyChainCall] pre-flight ensureAppOpen failed:",
4324
- appName,
4325
- err?.message
4326
- );
4327
- closeOpenAppUiIfShown();
4328
- throw ctx.wrapError(err, { defaultAppName: appName });
4329
- }
4330
- const runOnce = async () => {
4331
- let confirmEmitted = false;
4332
- if (needsConfirmation) {
4333
- ctx.emit("ui-event", {
4334
- type: EConnectorInteraction3.ConfirmOnDevice,
4335
- payload: { sessionId }
4336
- });
4337
- confirmEmitted = true;
4338
- }
4339
- try {
4340
- return await action(sessionId);
4341
- } finally {
4342
- if (confirmEmitted || openAppPromptShown) {
4343
- ctx.emit("ui-event", {
4344
- type: EConnectorInteraction3.InteractionComplete,
4345
- payload: { sessionId }
4346
- });
4347
- openAppPromptShown = false;
4348
- }
4349
- }
4350
- };
4346
+ const tronSigner = await _createTronSigner(ctx, sessionId);
4347
+ const path = normalizePath(params.path);
4351
4348
  try {
4352
- return await runOnce();
4349
+ const signature = await tronSigner.signTransaction(path, hexToBytes4(params.rawTxHex));
4350
+ return { signature: bytesToHex3(signature) };
4353
4351
  } catch (err) {
4354
- if (!isLegacyWrongAppError(err, appName)) {
4355
- debugLog("[LegacyChainCall] non-wrong-app failure:", appName, err?.message);
4356
- ctx.invalidateSession(sessionId);
4357
- throw ctx.wrapError(err, { defaultAppName: appName });
4358
- }
4359
- debugLog("[LegacyChainCall] wrong-app detected, retrying:", appName);
4360
- try {
4361
- await _ensureAppOpen(ctx, sessionId, appName, onAppOpenPrompt);
4362
- } catch (switchErr) {
4363
- debugLog(
4364
- "[LegacyChainCall] retry ensureAppOpen failed:",
4365
- appName,
4366
- switchErr?.message
4367
- );
4368
- closeOpenAppUiIfShown();
4369
- throw ctx.wrapError(switchErr, { defaultAppName: appName });
4370
- }
4371
- ctx.clearAllSigners();
4372
- const result = await runOnce();
4373
- debugLog("[LegacyChainCall] retry succeeded:", appName);
4374
- return result;
4375
- }
4376
- }
4377
- async function _ensureAppOpen(ctx, sessionId, appName, onPrompt) {
4378
- const dmk = await ctx.getOrCreateDmk();
4379
- const appManager = new AppManager(dmk);
4380
- await appManager.ensureAppOpen(sessionId, appName, onPrompt);
4381
- }
4382
-
4383
- // src/transport/DmkTransport.ts
4384
- import Transport from "@ledgerhq/hw-transport";
4385
- var DmkTransport = class extends Transport {
4386
- constructor(dmk, sessionId) {
4387
- super();
4388
- this._dmk = dmk;
4389
- this._sessionId = sessionId;
4390
- }
4391
- async exchange(apdu) {
4392
- const response = await this._dmk.sendApdu({
4393
- sessionId: this._sessionId,
4394
- apdu: new Uint8Array(apdu)
4395
- });
4396
- const { data, statusCode } = response;
4397
- const result = Buffer.alloc(data.length + 2);
4398
- if (data.length > 0) {
4399
- result.set(data, 0);
4400
- }
4401
- result.set(statusCode, data.length);
4402
- return result;
4403
- }
4404
- async close() {
4405
- }
4406
- };
4407
-
4408
- // src/connector/chains/tron.ts
4409
- async function tronGetAddress(ctx, sessionId, params) {
4410
- const path = normalizePath(params.path);
4411
- const showOnDevice = params.showOnDevice ?? false;
4412
- return withLegacyChainCall(
4413
- ctx,
4414
- sessionId,
4415
- {
4416
- appName: "Tron",
4417
- // Only show "confirm on device" UI when the device is actually going
4418
- // to display the address for the user to verify.
4419
- needsConfirmation: showOnDevice
4420
- },
4421
- async (sid) => {
4422
- const trx = await _createTrx(ctx, sid);
4423
- const result = await trx.getAddress(path, showOnDevice);
4424
- return { address: result.address, publicKey: result.publicKey, path: params.path };
4425
- }
4426
- );
4427
- }
4428
- async function tronSignTransaction(ctx, sessionId, params) {
4429
- if (!params.rawTxHex) {
4430
- throw Object.assign(
4431
- new Error("TRON signing requires a protobuf-encoded raw transaction hex (rawTxHex)."),
4432
- { code: HardwareErrorCode7.InvalidParams }
4433
- );
4352
+ ctx.invalidateSession(sessionId);
4353
+ throw ctx.wrapError(err);
4354
+ } finally {
4355
+ ctx.clearCanceller(sessionId);
4434
4356
  }
4435
- const path = normalizePath(params.path);
4436
- return withLegacyChainCall(
4437
- ctx,
4438
- sessionId,
4439
- { appName: "Tron", needsConfirmation: true },
4440
- async (sid) => {
4441
- const trx = await _createTrx(ctx, sid);
4442
- const signature = await trx.signTransaction(
4443
- path,
4444
- params.rawTxHex,
4445
- params.tokenSignatures ?? []
4446
- );
4447
- return { signature };
4448
- }
4449
- );
4450
4357
  }
4451
4358
  async function tronSignMessage(ctx, sessionId, params) {
4359
+ const tronSigner = await _createTronSigner(ctx, sessionId);
4452
4360
  const path = normalizePath(params.path);
4453
- return withLegacyChainCall(
4454
- ctx,
4455
- sessionId,
4456
- { appName: "Tron", needsConfirmation: true },
4457
- async (sid) => {
4458
- const trx = await _createTrx(ctx, sid);
4459
- const signature = await trx.signPersonalMessage(path, params.messageHex);
4460
- return { signature };
4461
- }
4462
- );
4361
+ try {
4362
+ const signature = await tronSigner.signPersonalMessage(path, hexToBytes4(params.messageHex));
4363
+ return { signature: bytesToHex3(signature) };
4364
+ } catch (err) {
4365
+ ctx.invalidateSession(sessionId);
4366
+ throw ctx.wrapError(err);
4367
+ } finally {
4368
+ ctx.clearCanceller(sessionId);
4369
+ }
4463
4370
  }
4464
- async function _createTrx(ctx, sessionId) {
4371
+ async function _createTronSigner(ctx, sessionId) {
4465
4372
  const dmk = await ctx.getOrCreateDmk();
4466
- return new Trx(new DmkTransport(dmk, sessionId));
4373
+ const { SignerTrxBuilder } = await ctx.importLedgerKit("@ledgerhq/device-signer-kit-tron");
4374
+ const sdkSigner = new SignerTrxBuilder({ dmk, sessionId }).build();
4375
+ const signer = new SignerTron(sdkSigner);
4376
+ signer.onInteraction = (interaction) => {
4377
+ debugLog("[LedgerConnector] tron.onInteraction:", interaction);
4378
+ ctx.emit("ui-event", {
4379
+ type: collapseSignerInteraction(interaction),
4380
+ payload: { sessionId }
4381
+ });
4382
+ };
4383
+ signer.onRegisterCanceller = (cancel) => {
4384
+ ctx.registerCanceller(sessionId, cancel);
4385
+ };
4386
+ return signer;
4467
4387
  }
4468
4388
 
4469
4389
  // src/device-apps/customActions.ts
@@ -4642,7 +4562,7 @@ var DeviceApps = class {
4642
4562
  seTargetId: v.seTargetId,
4643
4563
  mcuTargetId: v.mcuTargetId,
4644
4564
  seVersion: v.seVersion,
4645
- seFlagsHex: bytesToHex3(v.seFlags),
4565
+ seFlagsHex: bytesToHex4(v.seFlags),
4646
4566
  mcuSephVersion: v.mcuSephVersion,
4647
4567
  mcuBootloaderVersion: v.mcuBootloaderVersion,
4648
4568
  hwVersion: v.hwVersion
@@ -4716,7 +4636,7 @@ var DeviceApps = class {
4716
4636
  }
4717
4637
  }
4718
4638
  };
4719
- function bytesToHex3(bytes) {
4639
+ function bytesToHex4(bytes) {
4720
4640
  if (!bytes) return "";
4721
4641
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
4722
4642
  }
@@ -4763,7 +4683,7 @@ var METHOD_PREFIX_TO_APP_NAME = {
4763
4683
  zcash: "Zcash"
4764
4684
  };
4765
4685
  var HARDWARE_ERROR_CODE_VALUES = new Set(
4766
- Object.values(HardwareErrorCode8).filter((value) => typeof value === "number")
4686
+ Object.values(HardwareErrorCode7).filter((value) => typeof value === "number")
4767
4687
  );
4768
4688
  var BLE_CONNECT_SCAN_TIMEOUT_MS = 1500;
4769
4689
  var LEDGER_RELAY_ALLOWED_ROOT_DOMAINS = ["onekeytest.com", "onekey.com"];
@@ -4800,6 +4720,8 @@ async function defaultLedgerKitImporter(pkg) {
4800
4720
  return import("@ledgerhq/device-signer-kit-bitcoin");
4801
4721
  case "@ledgerhq/device-signer-kit-solana":
4802
4722
  return import("@ledgerhq/device-signer-kit-solana");
4723
+ case "@ledgerhq/device-signer-kit-tron":
4724
+ return import("@ledgerhq/device-signer-kit-tron");
4803
4725
  case "@ledgerhq/device-signer-kit-zcash":
4804
4726
  return import("@ledgerhq/device-signer-kit-zcash");
4805
4727
  case "@ledgerhq/context-module":
@@ -4960,7 +4882,7 @@ var LedgerConnectorBase = class {
4960
4882
  `Ledger BLE connect did not return within ${HANG_CEILING_MS / 6e4}min \u2014 DMK hang fallback.`
4961
4883
  );
4962
4884
  err._tag = ERROR_TAG.BlePairingTimeout;
4963
- err.code = HardwareErrorCode8.BlePairingTimeout;
4885
+ err.code = HardwareErrorCode7.BlePairingTimeout;
4964
4886
  reject(err);
4965
4887
  }, HANG_CEILING_MS);
4966
4888
  });
@@ -4993,7 +4915,7 @@ var LedgerConnectorBase = class {
4993
4915
  "Ledger device is not currently advertising. Wake up and unlock the device, keep it nearby, then try again."
4994
4916
  );
4995
4917
  err._tag = ERROR_TAG.DeviceNotAdvertising;
4996
- err.code = HardwareErrorCode8.DeviceNotFound;
4918
+ err.code = HardwareErrorCode7.DeviceNotFound;
4997
4919
  throw err;
4998
4920
  };
4999
4921
  const doConnect = async (path) => {
@@ -5057,7 +4979,7 @@ var LedgerConnectorBase = class {
5057
4979
  "Ledger Bluetooth pairing failed. Make sure the device is unlocked and nearby, then try again."
5058
4980
  );
5059
4981
  wrapped._tag = ERROR_TAG.BleGattBondingFailed;
5060
- wrapped.code = HardwareErrorCode8.BlePairingTimeout;
4982
+ wrapped.code = HardwareErrorCode7.BlePairingTimeout;
5061
4983
  wrapped.originalError = err;
5062
4984
  throw wrapped;
5063
4985
  }
@@ -5135,7 +5057,7 @@ var LedgerConnectorBase = class {
5135
5057
  this._unwatchSessionState(sessionId);
5136
5058
  this._signerManager?.invalidate(sessionId);
5137
5059
  this._cancellers.get(sessionId)?.({
5138
- code: HardwareErrorCode8.DeviceDisconnected,
5060
+ code: HardwareErrorCode7.DeviceDisconnected,
5139
5061
  tag: "DeviceDisconnected",
5140
5062
  message: "Device disconnected"
5141
5063
  });
@@ -5161,7 +5083,7 @@ var LedgerConnectorBase = class {
5161
5083
  success: false,
5162
5084
  error: serializeConnectorError(
5163
5085
  Object.assign(new Error("Ledger app is unresponsive"), {
5164
- code: HardwareErrorCode8.DeviceAppStuck,
5086
+ code: HardwareErrorCode7.DeviceAppStuck,
5165
5087
  _tag: ERROR_TAG.DeviceAppStuck,
5166
5088
  originalError: err
5167
5089
  })
@@ -5173,7 +5095,7 @@ var LedgerConnectorBase = class {
5173
5095
  success: false,
5174
5096
  error: serializeConnectorError(
5175
5097
  Object.assign(new Error("Device communication interrupted, please retry"), {
5176
- code: HardwareErrorCode8.TransportError,
5098
+ code: HardwareErrorCode7.TransportError,
5177
5099
  _tag: ERROR_TAG.DeviceTransportStuck,
5178
5100
  originalError: err
5179
5101
  })
@@ -5248,7 +5170,7 @@ var LedgerConnectorBase = class {
5248
5170
  try {
5249
5171
  return await apps.install(p.appName, ({ progress }) => {
5250
5172
  ctx.emit("ui-event", {
5251
- type: EConnectorInteraction4.AppInstallProgress,
5173
+ type: EConnectorInteraction3.AppInstallProgress,
5252
5174
  payload: {
5253
5175
  sessionId,
5254
5176
  appName: p.appName,
@@ -5587,6 +5509,209 @@ var LedgerConnectorBase = class {
5587
5509
  return error;
5588
5510
  }
5589
5511
  };
5512
+
5513
+ // src/transport/DmkTransport.ts
5514
+ import Transport from "@ledgerhq/hw-transport";
5515
+ var DmkTransport = class extends Transport {
5516
+ constructor(dmk, sessionId) {
5517
+ super();
5518
+ this._dmk = dmk;
5519
+ this._sessionId = sessionId;
5520
+ }
5521
+ async exchange(apdu) {
5522
+ const response = await this._dmk.sendApdu({
5523
+ sessionId: this._sessionId,
5524
+ apdu: new Uint8Array(apdu)
5525
+ });
5526
+ const { data, statusCode } = response;
5527
+ const result = Buffer.alloc(data.length + 2);
5528
+ if (data.length > 0) {
5529
+ result.set(data, 0);
5530
+ }
5531
+ result.set(statusCode, data.length);
5532
+ return result;
5533
+ }
5534
+ async close() {
5535
+ }
5536
+ };
5537
+
5538
+ // src/app/AppManager.ts
5539
+ import {
5540
+ CloseAppCommand,
5541
+ GetAppAndVersionCommand,
5542
+ OpenAppCommand,
5543
+ isSuccessCommandResult
5544
+ } from "@ledgerhq/device-management-kit";
5545
+ import { HardwareErrorCode as HardwareErrorCode8 } from "@onekeyfe/hwk-adapter-core";
5546
+ var APP_NAME_MAP = {
5547
+ ETH: "Ethereum",
5548
+ BTC: "Bitcoin",
5549
+ SOL: "Solana",
5550
+ TRX: "Tron",
5551
+ ZEC: "Zcash",
5552
+ XRP: "XRP",
5553
+ ADA: "Cardano",
5554
+ DOT: "Polkadot",
5555
+ ATOM: "Cosmos"
5556
+ };
5557
+ var DASHBOARD_APP_NAME = "BOLOS";
5558
+ var AppManager = class {
5559
+ constructor(dmk, options) {
5560
+ this._dmk = dmk;
5561
+ this._waitMs = options?.waitMs ?? 1e3;
5562
+ this._maxRetries = options?.maxRetries ?? 10;
5563
+ }
5564
+ /**
5565
+ * Return the Ledger app name for a given chain ticker,
5566
+ * or undefined if the chain is not supported.
5567
+ */
5568
+ static getAppName(chain) {
5569
+ return APP_NAME_MAP[chain];
5570
+ }
5571
+ /**
5572
+ * Ensure the target app is open on the device identified by `sessionId`.
5573
+ *
5574
+ * Flow:
5575
+ * 1. Check the currently running app.
5576
+ * 2. If it is already the target, return immediately.
5577
+ * 3. If a different app is running (not dashboard), close it first.
5578
+ * 4. Open the target app.
5579
+ * 5. Poll until the device confirms the target app is running.
5580
+ */
5581
+ /**
5582
+ * @param onConfirmOnDevice Called BEFORE OpenAppCommand is issued — the
5583
+ * device is about to display "Open <app>" on screen and wait for the
5584
+ * user's button press. UI consumers should show their "open app" prompt
5585
+ * in response. NOT called when the target app is already open (no user
5586
+ * interaction needed in that case).
5587
+ *
5588
+ * Important: OpenAppCommand is blocking. It does not resolve until the user
5589
+ * has physically confirmed on the device, so anything that runs AFTER
5590
+ * `await this._openApp(...)` lands AFTER the prompt is already gone.
5591
+ * Hence the callback must fire BEFORE that await.
5592
+ */
5593
+ async ensureAppOpen(sessionId, targetAppName, onConfirmOnDevice) {
5594
+ const currentApp = await this._getCurrentApp(sessionId);
5595
+ if (currentApp === targetAppName) {
5596
+ return;
5597
+ }
5598
+ if (!this._isDashboard(currentApp)) {
5599
+ await this._closeCurrentApp(sessionId);
5600
+ await this._waitForApp(sessionId, DASHBOARD_APP_NAME);
5601
+ }
5602
+ onConfirmOnDevice?.();
5603
+ await this._openApp(sessionId, targetAppName);
5604
+ await this._waitForApp(sessionId, targetAppName);
5605
+ }
5606
+ // ---------------------------------------------------------------------------
5607
+ // Private helpers
5608
+ // ---------------------------------------------------------------------------
5609
+ async _getCurrentApp(sessionId) {
5610
+ const result = await this._dmk.sendCommand({
5611
+ sessionId,
5612
+ command: new GetAppAndVersionCommand()
5613
+ });
5614
+ if (isSuccessCommandResult(result)) {
5615
+ debugLog("[AppManager] currentApp:", result.data.name);
5616
+ return result.data.name;
5617
+ }
5618
+ const errResult = result;
5619
+ const dmkErr = errResult.error ?? {};
5620
+ const original = dmkErr.originalError;
5621
+ debugLog(
5622
+ "[AppManager] _getCurrentApp failed sessionId=",
5623
+ sessionId,
5624
+ "tag=",
5625
+ dmkErr._tag,
5626
+ "errorCode=",
5627
+ dmkErr.errorCode,
5628
+ "message=",
5629
+ dmkErr.message,
5630
+ "originalErrorMessage=",
5631
+ original?.message ?? String(original ?? "")
5632
+ );
5633
+ throw Object.assign(
5634
+ new Error(
5635
+ dmkErr.message ?? "Failed to get current app from device"
5636
+ ),
5637
+ {
5638
+ _tag: dmkErr._tag,
5639
+ errorCode: dmkErr.errorCode,
5640
+ originalError: original
5641
+ }
5642
+ );
5643
+ }
5644
+ async _openApp(sessionId, appName) {
5645
+ const result = await this._dmk.sendCommand({
5646
+ sessionId,
5647
+ command: new OpenAppCommand({ appName })
5648
+ });
5649
+ if (!isSuccessCommandResult(result)) {
5650
+ const dmkErr = result.error;
5651
+ const errorCode = "errorCode" in dmkErr && dmkErr.errorCode != null ? String(dmkErr.errorCode) : "";
5652
+ const message = "message" in dmkErr && typeof dmkErr.message === "string" ? dmkErr.message : "";
5653
+ debugLog(
5654
+ "[AppManager] openApp failed:",
5655
+ appName,
5656
+ "errorCode:",
5657
+ errorCode,
5658
+ "tag:",
5659
+ dmkErr._tag
5660
+ );
5661
+ let code;
5662
+ if (errorCode === "6807" || /unknown application/i.test(message)) {
5663
+ code = HardwareErrorCode8.AppNotInstalled;
5664
+ } else if (errorCode === "5501" || dmkErr._tag === "ActionRefusedError") {
5665
+ code = HardwareErrorCode8.UserRejected;
5666
+ }
5667
+ throw Object.assign(new Error(`Failed to open "${appName}"`), {
5668
+ _tag: ERROR_TAG.OpenAppCommand,
5669
+ code,
5670
+ errorCode,
5671
+ statusCode: errorCode,
5672
+ appName,
5673
+ originalError: dmkErr
5674
+ });
5675
+ }
5676
+ }
5677
+ async _closeCurrentApp(sessionId) {
5678
+ debugLog("[AppManager] closeCurrentApp");
5679
+ await this._dmk.sendCommand({
5680
+ sessionId,
5681
+ command: new CloseAppCommand()
5682
+ });
5683
+ }
5684
+ /**
5685
+ * Poll the device until the expected app is reported as running,
5686
+ * or throw after `_maxRetries` attempts.
5687
+ */
5688
+ async _waitForApp(sessionId, expectedAppName) {
5689
+ let lastSeen = "";
5690
+ for (let i = 0; i < this._maxRetries; i++) {
5691
+ await this._wait();
5692
+ const current = await this._getCurrentApp(sessionId);
5693
+ lastSeen = current;
5694
+ if (current === expectedAppName) {
5695
+ return;
5696
+ }
5697
+ }
5698
+ debugLog(
5699
+ "[AppManager] waitForApp exhausted: expected=",
5700
+ expectedAppName,
5701
+ "lastSeen=",
5702
+ lastSeen
5703
+ );
5704
+ throw new Error(
5705
+ `Ledger: failed to open "${expectedAppName}" after ${this._maxRetries} retries (last seen: ${lastSeen})`
5706
+ );
5707
+ }
5708
+ _isDashboard(appName) {
5709
+ return appName === DASHBOARD_APP_NAME;
5710
+ }
5711
+ _wait() {
5712
+ return new Promise((resolve) => setTimeout(resolve, this._waitMs));
5713
+ }
5714
+ };
5590
5715
  export {
5591
5716
  AppManager,
5592
5717
  DmkTransport,