@onekeyfe/hwk-keystone-adapter 1.2.3-alpha.3 → 1.2.3-alpha.8

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
@@ -27,6 +27,7 @@ var TronSignRequest = class _TronSignRequest extends RegistryItem {
27
27
  this.getSignData = () => this.signData;
28
28
  this.getSignType = () => this.signType;
29
29
  this.getDerivationPath = () => this.derivationPath.getPath();
30
+ this.getOrigin = () => this.origin;
30
31
  this.toDataItem = () => {
31
32
  const map = {};
32
33
  if (this.requestId) {
@@ -461,8 +462,10 @@ import {
461
462
  getAllNetworkMethodChain,
462
463
  hasHardwareRuntimeIdPrefix,
463
464
  isAllNetworkMethodName,
464
- isHardwareOperationId,
465
+ isConnectionLost,
466
+ isHardwareOperationId as isHardwareOperationId2,
465
467
  isHwkRecoveryHint,
468
+ isUserRefusal,
466
469
  operationMayHaveCompletedParams,
467
470
  parseBip32MasterFingerprint as parseBip32MasterFingerprint2,
468
471
  rehydrateConnectorError,
@@ -581,8 +584,29 @@ function placeholderDeviceInfo() {
581
584
  };
582
585
  }
583
586
 
587
+ // src/utils/queueKey.ts
588
+ import { isHardwareOperationId } from "@onekeyfe/hwk-adapter-core";
589
+ var KEYSTONE_COLD_START_JOB_LABEL = "keystone-cold-start";
590
+ function keystoneQueueKey(connectId, deviceId) {
591
+ return isHardwareOperationId(connectId) ? connectId : deviceId || connectId || KEYSTONE_COLD_START_JOB_LABEL;
592
+ }
593
+ function keystoneWalletIdFromIdentifier(identifier) {
594
+ const value = identifier.startsWith(KEYSTONE_WALLET_CONNECT_ID_PREFIX) ? identifier.slice(KEYSTONE_WALLET_CONNECT_ID_PREFIX.length) : identifier;
595
+ return /^[0-9a-f]{64}$/i.test(value) ? value.toLowerCase() : void 0;
596
+ }
597
+ function keystoneCancelQueueKeys(identifiers) {
598
+ const keys = /* @__PURE__ */ new Set();
599
+ for (const identifier of identifiers) {
600
+ if (identifier) {
601
+ keys.add(identifier);
602
+ const walletId = keystoneWalletIdFromIdentifier(identifier);
603
+ if (walletId) keys.add(walletId);
604
+ }
605
+ }
606
+ return keys;
607
+ }
608
+
584
609
  // src/adapter/KeystoneAdapter.ts
585
- var COLD_START_JOB_LABEL = "keystone-cold-start";
586
610
  var KEYSTONE_USB_REATTACH_PROBE_ATTEMPTS = 4;
587
611
  var KEYSTONE_USB_REATTACH_PROBE_INTERVAL_MS = 500;
588
612
  function resolveHardwareOperationTarget(positionalTargetId, operationId) {
@@ -594,8 +618,11 @@ function waitForKeystoneUsbReattachProbe() {
594
618
  setTimeout(resolve, KEYSTONE_USB_REATTACH_PROBE_INTERVAL_MS);
595
619
  });
596
620
  }
597
- function getKeystoneJobId(connectId, deviceId) {
598
- return isHardwareOperationId(connectId) ? connectId : deviceId || connectId || COLD_START_JOB_LABEL;
621
+ function buildCancelledFailure(signal) {
622
+ const reason = signal.reason;
623
+ const code = typeof reason?.code === "number" ? reason.code : HardwareErrorCode.UserAborted;
624
+ const message = typeof reason?.message === "string" ? reason.message : "";
625
+ return failure(code, message || "All-network get-address cancelled");
599
626
  }
600
627
  var BIP44_COIN_TYPE_TO_CHAIN = {
601
628
  60: "evm",
@@ -610,6 +637,9 @@ function inferHwkChainFromPath(path) {
610
637
  var KeystoneAdapter = class _KeystoneAdapter {
611
638
  constructor(options) {
612
639
  this.vendor = "keystone";
640
+ // No protocol-level cancel exists over Keystone USB, and QR has no transport
641
+ // to interrupt at all. Cancelling ends the wait, not the device's prompt.
642
+ this.cancelCapability = "stops-waiting";
613
643
  this.emitter = new TypedEventEmitter();
614
644
  this._operationRoutes = /* @__PURE__ */ new Map();
615
645
  this._operations = new OperationRegistry({
@@ -663,6 +693,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
663
693
  if (!operationTarget.success) return operationTarget;
664
694
  const effectiveConnectId = operationTarget.payload.targetId ?? "";
665
695
  const { operationId } = operationTarget.payload;
696
+ let cancelScope;
666
697
  try {
667
698
  const prefetched = await this._prefetchAllNetworkAccounts(
668
699
  effectiveConnectId,
@@ -671,11 +702,20 @@ var KeystoneAdapter = class _KeystoneAdapter {
671
702
  );
672
703
  const { book } = prefetched;
673
704
  const itemDeviceId = deviceId || prefetched.walletId || "";
705
+ const scope = this._jobQueue.createCancelScope(
706
+ keystoneQueueKey(effectiveConnectId, itemDeviceId)
707
+ );
708
+ cancelScope = scope;
709
+ const cancelledIndexes = /* @__PURE__ */ new Set();
674
710
  return await runAllNetworkGetAddress({
675
711
  connectId: effectiveConnectId,
676
712
  deviceId: itemDeviceId,
677
713
  params,
678
- callItem: async ({ method, item }) => {
714
+ callItem: async ({ method, item, index }) => {
715
+ if (scope.signal.aborted) {
716
+ cancelledIndexes.add(index);
717
+ return buildCancelledFailure(scope.signal);
718
+ }
679
719
  const commonArgs = {
680
720
  path: item.path,
681
721
  showOnDevice: item.showOnDevice,
@@ -714,10 +754,19 @@ var KeystoneAdapter = class _KeystoneAdapter {
714
754
  } : void 0
715
755
  }
716
756
  });
757
+ },
758
+ // The gap check plus the shared top-level table: a refusal or a lost
759
+ // session ends the batch, every other per-item failure keeps its
760
+ // existing "carry on with the next chain" behaviour.
761
+ shouldAbortBundle: (response, { index }) => {
762
+ const { code } = response.payload ?? {};
763
+ return cancelledIndexes.has(index) || isUserRefusal(code) || isConnectionLost(code);
717
764
  }
718
765
  });
719
766
  } catch (err) {
720
767
  return this._errorToFailure(err);
768
+ } finally {
769
+ cancelScope?.release();
721
770
  }
722
771
  };
723
772
  this._origin = options?.origin ?? "OneKey";
@@ -915,6 +964,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
915
964
  searchTargetId,
916
965
  connectId: connected.payload.connectId,
917
966
  device: connected.payload,
967
+ connectionType: selectedConnectionType,
918
968
  connectionKeys: selectedConnectionType === "usb" && record?.usbSessionId ? [record.usbSessionId] : []
919
969
  });
920
970
  this._operationRoutes.set(connected.payload.connectId, {
@@ -967,7 +1017,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
967
1017
  }
968
1018
  getDeviceInfo(connectIdOrOperationId, deviceId) {
969
1019
  try {
970
- const connectId = isHardwareOperationId(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
1020
+ const connectId = isHardwareOperationId2(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
971
1021
  const { record } = this._resolveTarget(connectId, deviceId);
972
1022
  if (!record) {
973
1023
  return Promise.resolve(
@@ -983,25 +1033,35 @@ var KeystoneAdapter = class _KeystoneAdapter {
983
1033
  }
984
1034
  }
985
1035
  cancel(connectId) {
1036
+ let namedOperationConnectId;
1037
+ if (isHardwareOperationId2(connectId)) {
1038
+ try {
1039
+ namedOperationConnectId = this._operations.resolve(connectId).connectId;
1040
+ } catch {
1041
+ return;
1042
+ }
1043
+ }
986
1044
  const reason = createHwkError({
987
1045
  code: HardwareErrorCode.UserAborted,
988
1046
  message: "User aborted operation"
989
1047
  });
990
- const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
991
1048
  if (connectId) {
992
- if (!activeJobId || activeJobId !== connectId && activeJobId !== this._walletIdFromIdentifier(connectId)) {
993
- this._jobQueue.cancelActiveAndPending(connectId, reason);
994
- const walletId = this._walletIdFromIdentifier(connectId);
995
- if (walletId && walletId !== connectId) {
996
- this._jobQueue.cancelActiveAndPending(walletId, reason);
997
- }
998
- return;
1049
+ const queueKeys = keystoneCancelQueueKeys([
1050
+ connectId,
1051
+ this._operationRoutes.get(connectId)?.operationId,
1052
+ namedOperationConnectId
1053
+ ]);
1054
+ for (const key of queueKeys) {
1055
+ this._jobQueue.cancelActiveAndPending(key, reason);
1056
+ }
1057
+ const scopedOperationId = isHardwareOperationId2(connectId) ? connectId : this._operationRouteForIdentifier(connectId)?.operationId;
1058
+ if (scopedOperationId) {
1059
+ this._uiRegistry.cancel(void 0, void 0, scopedOperationId);
999
1060
  }
1000
- this._jobQueue.cancelActiveAndPending(activeJobId, reason);
1001
1061
  } else {
1002
1062
  this._jobQueue.cancelActiveAndPending(void 0, reason);
1063
+ this._uiRegistry.cancel();
1003
1064
  }
1004
- this._uiRegistry.cancel();
1005
1065
  }
1006
1066
  getChainFingerprint(connectId, deviceId, chain) {
1007
1067
  try {
@@ -1036,20 +1096,21 @@ var KeystoneAdapter = class _KeystoneAdapter {
1036
1096
  // ---------------------------------------------------------------------------
1037
1097
  async importFromQr(options = {}) {
1038
1098
  try {
1039
- return await this._jobQueue.enqueue(COLD_START_JOB_LABEL, async (signal) => {
1099
+ return await this._jobQueue.enqueue(KEYSTONE_COLD_START_JOB_LABEL, async (signal) => {
1040
1100
  const displayDevice = placeholderDeviceInfo();
1041
1101
  let responseUr;
1042
1102
  if (options.mode === "scan") {
1043
- responseUr = await this._requestQrScanAndAwaitResponse(displayDevice);
1103
+ responseUr = await this._requestQrScanAndAwaitResponse(displayDevice, signal);
1044
1104
  } else {
1045
1105
  const requestUr = this.urEngine.buildKeyDerivationRequest({
1046
1106
  schemas: [{ path: KEYSTONE_WALLET_ID_PATH, curve: "secp256k1" }],
1047
1107
  origin: this._origin
1048
1108
  });
1049
- responseUr = await this._requestQrDisplayAndAwaitResponse(displayDevice, {
1050
- ...requestUr,
1051
- animated: false
1052
- });
1109
+ responseUr = await this._requestQrDisplayAndAwaitResponse(
1110
+ displayDevice,
1111
+ { ...requestUr, animated: false },
1112
+ signal
1113
+ );
1053
1114
  }
1054
1115
  _KeystoneAdapter._throwIfAborted(signal);
1055
1116
  const parsed = this.urEngine.parseAccountResponse(responseUr);
@@ -1080,7 +1141,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1080
1141
  );
1081
1142
  }
1082
1143
  try {
1083
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1144
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1084
1145
  const { account } = await this._fetchAccount(
1085
1146
  connectId,
1086
1147
  deviceId,
@@ -1126,7 +1187,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1126
1187
  const rawTxHex = stripHex(params.serializedTx);
1127
1188
  const path = normalizePath(params.path);
1128
1189
  try {
1129
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1190
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1130
1191
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "evm", signal);
1131
1192
  _KeystoneAdapter._throwIfAborted(signal);
1132
1193
  const requestId = uuidv4();
@@ -1137,7 +1198,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1137
1198
  dataType,
1138
1199
  path,
1139
1200
  xfp: record.masterFingerprint,
1140
- chainId: params.chainId
1201
+ chainId: params.chainId,
1202
+ origin: this._origin
1141
1203
  });
1142
1204
  const responseUr = await this._resolveUr(
1143
1205
  record,
@@ -1176,7 +1238,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1176
1238
  const messageHex = params.hex ? stripHex(params.message) : Buffer.from(params.message, "utf8").toString("hex");
1177
1239
  const path = normalizePath(params.path);
1178
1240
  try {
1179
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1241
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1180
1242
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "evm", signal);
1181
1243
  _KeystoneAdapter._throwIfAborted(signal);
1182
1244
  const requestId = uuidv4();
@@ -1186,7 +1248,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1186
1248
  dataType: "personalMessage",
1187
1249
  path,
1188
1250
  xfp: record.masterFingerprint,
1189
- chainId: params.chainId
1251
+ chainId: params.chainId,
1252
+ origin: this._origin
1190
1253
  });
1191
1254
  const responseUr = await this._resolveUr(
1192
1255
  record,
@@ -1228,7 +1291,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1228
1291
  ).toString("hex");
1229
1292
  const path = normalizePath(params.path);
1230
1293
  try {
1231
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1294
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1232
1295
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "evm", signal);
1233
1296
  _KeystoneAdapter._throwIfAborted(signal);
1234
1297
  const requestId = uuidv4();
@@ -1238,7 +1301,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1238
1301
  dataType: "typedData",
1239
1302
  path,
1240
1303
  xfp: record.masterFingerprint,
1241
- chainId: params.chainId
1304
+ chainId: params.chainId,
1305
+ origin: this._origin
1242
1306
  });
1243
1307
  const responseUr = await this._resolveUr(
1244
1308
  record,
@@ -1297,7 +1361,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1297
1361
  return failure(HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
1298
1362
  }
1299
1363
  try {
1300
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1364
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1301
1365
  const { account } = await this._fetchAccount(
1302
1366
  connectId,
1303
1367
  deviceId,
@@ -1353,7 +1417,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1353
1417
  return failure(HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
1354
1418
  }
1355
1419
  try {
1356
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1420
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1357
1421
  const { account } = await this._fetchAccount(
1358
1422
  connectId,
1359
1423
  deviceId,
@@ -1398,11 +1462,13 @@ var KeystoneAdapter = class _KeystoneAdapter {
1398
1462
  if (!params) return failure(HardwareErrorCode.InvalidParams, "btcSignPsbt requires params");
1399
1463
  if (!params.psbt)
1400
1464
  return failure(HardwareErrorCode.InvalidParams, "btcSignPsbt requires params.psbt");
1401
- if (params.path && !isKeystoneSignableBtcAccountPath(splitAccountPath(params.path).accountPath)) {
1465
+ if (!params.path)
1466
+ return failure(HardwareErrorCode.InvalidParams, "btcSignPsbt requires params.path");
1467
+ if (!isKeystoneSignableBtcAccountPath(splitAccountPath(params.path).accountPath)) {
1402
1468
  return failure(HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
1403
1469
  }
1404
1470
  try {
1405
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1471
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1406
1472
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
1407
1473
  _KeystoneAdapter._throwIfAborted(signal);
1408
1474
  const requestUr = this.urEngine.buildBtcPsbtRequest(stripHex(params.psbt));
@@ -1441,14 +1507,15 @@ var KeystoneAdapter = class _KeystoneAdapter {
1441
1507
  return failure(HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
1442
1508
  }
1443
1509
  try {
1444
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1510
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1445
1511
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
1446
1512
  _KeystoneAdapter._throwIfAborted(signal);
1447
1513
  const requestId = uuidv4();
1448
1514
  const requestUr = this.urEngine.buildBtcMessageSignRequest({
1449
1515
  requestId,
1450
1516
  messageHex,
1451
- accounts: [{ path, xfp: record.masterFingerprint }]
1517
+ accounts: [{ path, xfp: record.masterFingerprint }],
1518
+ origin: this._origin
1452
1519
  });
1453
1520
  const responseUr = await this._resolveUr(
1454
1521
  record,
@@ -1473,7 +1540,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1473
1540
  const connectId = operationTarget.payload.targetId;
1474
1541
  const deviceId = deviceIdArg ?? void 0;
1475
1542
  try {
1476
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1543
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1477
1544
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
1478
1545
  return success({ masterFingerprint: record.masterFingerprint });
1479
1546
  });
@@ -1495,7 +1562,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1495
1562
  return failure(HardwareErrorCode.InvalidParams, "solGetAddress requires params.path");
1496
1563
  const path = normalizePath(params.path);
1497
1564
  try {
1498
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1565
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1499
1566
  const { account } = await this._fetchAccount(connectId, deviceId, "sol", path, signal, {
1500
1567
  book
1501
1568
  });
@@ -1523,7 +1590,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1523
1590
  }
1524
1591
  const path = normalizePath(params.path);
1525
1592
  try {
1526
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1593
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1527
1594
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "sol", signal);
1528
1595
  _KeystoneAdapter._throwIfAborted(signal);
1529
1596
  const requestId = uuidv4();
@@ -1532,7 +1599,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1532
1599
  unsignedPayloadHex: stripHex(params.serializedTx),
1533
1600
  dataType: "transaction",
1534
1601
  path,
1535
- xfp: record.masterFingerprint
1602
+ xfp: record.masterFingerprint,
1603
+ origin: this._origin
1536
1604
  });
1537
1605
  const responseUr = await this._resolveUr(
1538
1606
  record,
@@ -1566,7 +1634,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1566
1634
  }
1567
1635
  const path = normalizePath(params.path);
1568
1636
  try {
1569
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1637
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1570
1638
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "sol", signal);
1571
1639
  _KeystoneAdapter._throwIfAborted(signal);
1572
1640
  const requestId = uuidv4();
@@ -1575,7 +1643,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1575
1643
  unsignedPayloadHex: stripHex(params.message),
1576
1644
  dataType: "message",
1577
1645
  path,
1578
- xfp: record.masterFingerprint
1646
+ xfp: record.masterFingerprint,
1647
+ origin: this._origin
1579
1648
  });
1580
1649
  const responseUr = await this._resolveUr(
1581
1650
  record,
@@ -1596,10 +1665,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
1596
1665
  }
1597
1666
  // ---------------------------------------------------------------------------
1598
1667
  // TRON — routed through `TronSignRequest`/`TronSignature` (see
1599
- // urEngine/TronSignRequest.ts), a port of OneKey's own already-proven
1600
- // production TRON QR-wallet implementation — NOT keystone-sdk's own
1601
- // bundled `sdk.tron` module (different, protobuf-based protocol with
1602
- // unverified response semantics).
1668
+ // urEngine/TronSignRequest.ts), whose key layout follows keystone-sdk-rust
1669
+ // (tags 5201/5202, firmware >= 2.5.0). The OneKey Pro air-gap encoding under
1670
+ // the same tag is a different layout the firmware rejects. Not keystone-sdk's
1671
+ // bundled `sdk.tron` module either (protobuf-based, unverified response
1672
+ // semantics).
1603
1673
  // ---------------------------------------------------------------------------
1604
1674
  async tronGetAddress(connectIdArg, deviceIdArg, paramsArg, book) {
1605
1675
  const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
@@ -1618,7 +1688,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1618
1688
  );
1619
1689
  }
1620
1690
  try {
1621
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1691
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1622
1692
  const { account } = await this._fetchAccount(
1623
1693
  connectId,
1624
1694
  deviceId,
@@ -1664,7 +1734,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1664
1734
  const rawTxHex = stripHex(params.rawTxHex);
1665
1735
  const path = normalizePath(params.path);
1666
1736
  try {
1667
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1737
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1668
1738
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "tron", signal);
1669
1739
  _KeystoneAdapter._throwIfAborted(signal);
1670
1740
  const requestId = uuidv4();
@@ -1672,7 +1742,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1672
1742
  requestId,
1673
1743
  rawTxHex,
1674
1744
  path,
1675
- xfp: record.masterFingerprint
1745
+ xfp: record.masterFingerprint,
1746
+ origin: this._origin
1676
1747
  });
1677
1748
  const responseUr = await this._resolveUr(
1678
1749
  record,
@@ -1713,7 +1784,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1713
1784
  const messageHex = stripHex(params.messageHex);
1714
1785
  const path = normalizePath(params.path);
1715
1786
  try {
1716
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1787
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1717
1788
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "tron", signal);
1718
1789
  _KeystoneAdapter._throwIfAborted(signal);
1719
1790
  const requestId = uuidv4();
@@ -1722,7 +1793,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1722
1793
  rawTxHex: messageHex,
1723
1794
  path,
1724
1795
  xfp: record.masterFingerprint,
1725
- signType: 2 /* PersonalMessage */
1796
+ signType: 2 /* PersonalMessage */,
1797
+ origin: this._origin
1726
1798
  });
1727
1799
  const responseUr = await this._resolveUr(
1728
1800
  record,
@@ -1745,8 +1817,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1745
1817
  // Internals
1746
1818
  // ---------------------------------------------------------------------------
1747
1819
  _walletIdFromIdentifier(identifier) {
1748
- const value = identifier.startsWith(KEYSTONE_WALLET_CONNECT_ID_PREFIX) ? identifier.slice(KEYSTONE_WALLET_CONNECT_ID_PREFIX.length) : identifier;
1749
- return /^[0-9a-f]{64}$/i.test(value) ? value.toLowerCase() : void 0;
1820
+ return keystoneWalletIdFromIdentifier(identifier);
1750
1821
  }
1751
1822
  _allNetworkSyncSchema(item) {
1752
1823
  if (!isAllNetworkMethodName(String(item.methodName)) || !item.path) return void 0;
@@ -1775,7 +1846,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1775
1846
  * one-path-at-a-time export flow because the device limits that channel.
1776
1847
  */
1777
1848
  async _prefetchAllNetworkAccounts(connectId, deviceId, params) {
1778
- return this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1849
+ return this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1779
1850
  const target = this._resolveTarget(connectId, deviceId);
1780
1851
  const { record: targetRecord } = target;
1781
1852
  let record = targetRecord;
@@ -1788,14 +1859,17 @@ var KeystoneAdapter = class _KeystoneAdapter {
1788
1859
  if (!requestedByKey.size) {
1789
1860
  return { book };
1790
1861
  }
1791
- if (this._forcedTransport !== "qr" && !record?.usbSessionId && this._usbConnector && target.expectedWalletId) {
1862
+ const operation = isHardwareOperationId2(params.operationId) ? this._operations.find(params.operationId) : void 0;
1863
+ const operationRoute = operation ? this._operationRoutes.get(operation.connectId) : void 0;
1864
+ const routedToQr = operationRoute?.operationId === operation?.operationId && operationRoute?.connectionType === "qr";
1865
+ if (this._forcedTransport !== "qr" && !routedToQr && !record?.usbSessionId && this._usbConnector && target.expectedWalletId) {
1792
1866
  record = await this._tryUsbAttach(
1793
1867
  target.expectedWalletId,
1794
1868
  record?.masterFingerprint ?? target.expectedMasterFingerprint
1795
1869
  ) ?? record;
1796
1870
  _KeystoneAdapter._throwIfAborted(signal);
1797
1871
  }
1798
- if (this._forcedTransport !== "qr" && record?.usbSessionId) {
1872
+ if (this._forcedTransport !== "qr" && !routedToQr && record?.usbSessionId) {
1799
1873
  const bundleSessionId = record.usbSessionId;
1800
1874
  const assertBundleSession = () => {
1801
1875
  if (record?.usbSessionId !== bundleSessionId) {
@@ -1851,7 +1925,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1851
1925
  });
1852
1926
  const responseUr = await this._requestQrDisplayAndAwaitResponse(
1853
1927
  record ? toDeviceInfo(record) : placeholderDeviceInfo(),
1854
- { ...requestUr, animated: false }
1928
+ { ...requestUr, animated: false },
1929
+ signal
1855
1930
  );
1856
1931
  _KeystoneAdapter._throwIfAborted(signal);
1857
1932
  const parsed = this.urEngine.parseAccountResponse(responseUr);
@@ -1887,7 +1962,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1887
1962
  */
1888
1963
  _resolveTarget(connectId, deviceId) {
1889
1964
  let resolvedConnectId = connectId;
1890
- if (isHardwareOperationId(connectId)) {
1965
+ if (isHardwareOperationId2(connectId)) {
1891
1966
  resolvedConnectId = this._operations.resolve(connectId).connectId;
1892
1967
  }
1893
1968
  const identifiers = [deviceId, resolvedConnectId].filter(
@@ -2137,7 +2212,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
2137
2212
  * closed rather than silently falling back to QR.
2138
2213
  */
2139
2214
  async _resolveUr(record, requestUr, animated, operationId, signal, operationName) {
2140
- const releaseOperationRetention = isHardwareOperationId(operationId) ? this._operations.retain(operationId) : void 0;
2215
+ const releaseOperationRetention = isHardwareOperationId2(operationId) ? this._operations.retain(operationId) : void 0;
2141
2216
  try {
2142
2217
  return await this._resolveUrWithRoute(
2143
2218
  record,
@@ -2153,7 +2228,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
2153
2228
  }
2154
2229
  async _resolveUrWithRoute(record, requestUr, animated, operationId, signal, operationName) {
2155
2230
  let interactionRoute;
2156
- if (isHardwareOperationId(operationId)) {
2231
+ if (isHardwareOperationId2(operationId)) {
2157
2232
  const operation = this._operations.resolve(operationId);
2158
2233
  const route = this._operationRoutes.get(operation.connectId);
2159
2234
  if (!route || route.operationId !== operationId) {
@@ -2202,10 +2277,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
2202
2277
  const usbErrorCode = usbError.code;
2203
2278
  if (!interactionRoute && !this._forcedTransport && usbErrorCode === HardwareErrorCode.PayloadTooLarge && usbErrorOrigin !== "device") {
2204
2279
  const displayDevice2 = toDeviceInfo(record);
2205
- return this._requestQrDisplayAndAwaitResponse(displayDevice2, {
2206
- ...requestUr,
2207
- animated
2208
- });
2280
+ return this._requestQrDisplayAndAwaitResponse(
2281
+ displayDevice2,
2282
+ { ...requestUr, animated },
2283
+ signal
2284
+ );
2209
2285
  }
2210
2286
  const deviceAnswered = usbErrorOrigin !== void 0 ? usbErrorOrigin === "device" : usbErrorCode === HardwareErrorCode.UserRejected || usbErrorCode === HardwareErrorCode.UserAborted || usbErrorCode === HardwareErrorCode.DeviceLocked || usbErrorCode === HardwareErrorCode.DeviceMismatch;
2211
2287
  if (!deviceAnswered) {
@@ -2243,10 +2319,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
2243
2319
  }
2244
2320
  const displayDevice = record ? toDeviceInfo(record) : placeholderDeviceInfo();
2245
2321
  try {
2246
- return await this._requestQrDisplayAndAwaitResponse(displayDevice, {
2247
- ...requestUr,
2248
- animated
2249
- });
2322
+ return await this._requestQrDisplayAndAwaitResponse(
2323
+ displayDevice,
2324
+ { ...requestUr, animated },
2325
+ signal
2326
+ );
2250
2327
  } catch (error) {
2251
2328
  const { code, _tag: tag } = error;
2252
2329
  if (operationName && (code === HardwareErrorCode.OperationTimeout || tag === UI_REQUEST_TIMEOUT_TAG)) {
@@ -2381,28 +2458,76 @@ var KeystoneAdapter = class _KeystoneAdapter {
2381
2458
  const record = this._upsertDeviceRecord(parsed);
2382
2459
  return { record };
2383
2460
  }
2384
- async _requestQrDisplayAndAwaitResponse(device, data) {
2461
+ /**
2462
+ * The operation the running device job belongs to, so a UI request raised
2463
+ * mid-call can name it. A call pinned to an operation queues under its id
2464
+ * (`keystoneQueueKey`); a call without one queues under the wallet id or the
2465
+ * wallet connectId, and the live operation routed to that connection is still
2466
+ * the owner. Only a cold start comes back undefined.
2467
+ */
2468
+ _activeOperationId() {
2469
+ const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
2470
+ if (!activeJobId) return void 0;
2471
+ if (isHardwareOperationId2(activeJobId)) return activeJobId;
2472
+ return this._operationRouteForIdentifier(activeJobId)?.operationId;
2473
+ }
2474
+ /** Routes are keyed by connectId; a job key may be the bare wallet id. */
2475
+ _operationRouteForIdentifier(identifier) {
2476
+ for (const key of keystoneCancelQueueKeys([identifier])) {
2477
+ const route = this._operationRoutes.get(key);
2478
+ if (route) return route;
2479
+ }
2480
+ const walletId = keystoneWalletIdFromIdentifier(identifier);
2481
+ return walletId ? this._operationRoutes.get(walletConnectId(walletId)) : void 0;
2482
+ }
2483
+ /**
2484
+ * A QR prompt has no transport to interrupt, so an aborted job would sit here
2485
+ * until the QR timeout. Releasing the registry slot on abort is what lets a
2486
+ * cancel reach the wait at all.
2487
+ */
2488
+ async _awaitQrResponse(requestType, pending, signal) {
2489
+ if (!signal) return pending;
2490
+ let settled = false;
2491
+ const onAbort = () => {
2492
+ if (!settled) this._uiRegistry.cancel(requestType);
2493
+ };
2494
+ if (signal.aborted) onAbort();
2495
+ else signal.addEventListener("abort", onAbort, { once: true });
2496
+ try {
2497
+ return await pending;
2498
+ } finally {
2499
+ settled = true;
2500
+ signal.removeEventListener("abort", onAbort);
2501
+ }
2502
+ }
2503
+ async _requestQrDisplayAndAwaitResponse(device, data, signal) {
2504
+ const operationId = this._activeOperationId();
2385
2505
  const waitPromise = this._uiRegistry.wait(
2386
2506
  UI_REQUEST.REQUEST_QR_DISPLAY,
2387
- { timeoutMs: this._qrTimeoutMs }
2507
+ { timeoutMs: this._qrTimeoutMs, operationId }
2388
2508
  );
2389
2509
  this.emitter.emit(UI_REQUEST.REQUEST_QR_DISPLAY, {
2390
2510
  type: UI_REQUEST.REQUEST_QR_DISPLAY,
2391
- payload: { device, data }
2511
+ payload: { device, data, operationId }
2392
2512
  });
2393
- const response = await waitPromise;
2513
+ const response = await this._awaitQrResponse(
2514
+ UI_REQUEST.REQUEST_QR_DISPLAY,
2515
+ waitPromise,
2516
+ signal
2517
+ );
2394
2518
  return { urType: response.urType, urData: response.urData };
2395
2519
  }
2396
- async _requestQrScanAndAwaitResponse(device) {
2520
+ async _requestQrScanAndAwaitResponse(device, signal) {
2521
+ const operationId = this._activeOperationId();
2397
2522
  const waitPromise = this._uiRegistry.wait(
2398
2523
  UI_REQUEST.REQUEST_QR_SCAN,
2399
- { timeoutMs: this._qrTimeoutMs }
2524
+ { timeoutMs: this._qrTimeoutMs, operationId }
2400
2525
  );
2401
2526
  this.emitter.emit(UI_REQUEST.REQUEST_QR_SCAN, {
2402
2527
  type: UI_REQUEST.REQUEST_QR_SCAN,
2403
- payload: { device }
2528
+ payload: { device, operationId }
2404
2529
  });
2405
- const response = await waitPromise;
2530
+ const response = await this._awaitQrResponse(UI_REQUEST.REQUEST_QR_SCAN, waitPromise, signal);
2406
2531
  return { urType: response.urType, urData: response.urData };
2407
2532
  }
2408
2533
  async _callUsbConnector(sessionId, method, params, signal) {
@@ -2418,6 +2543,12 @@ var KeystoneAdapter = class _KeystoneAdapter {
2418
2543
  message: `Keystone USB is busy while calling ${method}`
2419
2544
  });
2420
2545
  }
2546
+ if (signal?.aborted) {
2547
+ throw signal.reason instanceof Error ? signal.reason : createHwkError({
2548
+ code: HardwareErrorCode.UserAborted,
2549
+ message: "User aborted operation"
2550
+ });
2551
+ }
2421
2552
  const releaseOperation = this._retainUsbOperation(`call:${sessionId}`);
2422
2553
  let rawCall;
2423
2554
  try {
@@ -2516,7 +2647,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
2516
2647
  _errorToFailure(err) {
2517
2648
  if (err && typeof err === "object") {
2518
2649
  const e = err;
2519
- if (typeof e.code === "number") {
2650
+ if (typeof e.code === "number" && e.code >= 1e4 && e.code <= 99999) {
2520
2651
  const { origin } = e;
2521
2652
  return failure(
2522
2653
  e.code,