@onekeyfe/hwk-keystone-adapter 1.2.3-alpha.4 → 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,
@@ -1404,7 +1468,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1404
1468
  return failure(HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
1405
1469
  }
1406
1470
  try {
1407
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1471
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1408
1472
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
1409
1473
  _KeystoneAdapter._throwIfAborted(signal);
1410
1474
  const requestUr = this.urEngine.buildBtcPsbtRequest(stripHex(params.psbt));
@@ -1443,14 +1507,15 @@ var KeystoneAdapter = class _KeystoneAdapter {
1443
1507
  return failure(HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
1444
1508
  }
1445
1509
  try {
1446
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1510
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1447
1511
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
1448
1512
  _KeystoneAdapter._throwIfAborted(signal);
1449
1513
  const requestId = uuidv4();
1450
1514
  const requestUr = this.urEngine.buildBtcMessageSignRequest({
1451
1515
  requestId,
1452
1516
  messageHex,
1453
- accounts: [{ path, xfp: record.masterFingerprint }]
1517
+ accounts: [{ path, xfp: record.masterFingerprint }],
1518
+ origin: this._origin
1454
1519
  });
1455
1520
  const responseUr = await this._resolveUr(
1456
1521
  record,
@@ -1475,7 +1540,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1475
1540
  const connectId = operationTarget.payload.targetId;
1476
1541
  const deviceId = deviceIdArg ?? void 0;
1477
1542
  try {
1478
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1543
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1479
1544
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
1480
1545
  return success({ masterFingerprint: record.masterFingerprint });
1481
1546
  });
@@ -1497,7 +1562,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1497
1562
  return failure(HardwareErrorCode.InvalidParams, "solGetAddress requires params.path");
1498
1563
  const path = normalizePath(params.path);
1499
1564
  try {
1500
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1565
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1501
1566
  const { account } = await this._fetchAccount(connectId, deviceId, "sol", path, signal, {
1502
1567
  book
1503
1568
  });
@@ -1525,7 +1590,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1525
1590
  }
1526
1591
  const path = normalizePath(params.path);
1527
1592
  try {
1528
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1593
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1529
1594
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "sol", signal);
1530
1595
  _KeystoneAdapter._throwIfAborted(signal);
1531
1596
  const requestId = uuidv4();
@@ -1534,7 +1599,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1534
1599
  unsignedPayloadHex: stripHex(params.serializedTx),
1535
1600
  dataType: "transaction",
1536
1601
  path,
1537
- xfp: record.masterFingerprint
1602
+ xfp: record.masterFingerprint,
1603
+ origin: this._origin
1538
1604
  });
1539
1605
  const responseUr = await this._resolveUr(
1540
1606
  record,
@@ -1568,7 +1634,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1568
1634
  }
1569
1635
  const path = normalizePath(params.path);
1570
1636
  try {
1571
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1637
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1572
1638
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "sol", signal);
1573
1639
  _KeystoneAdapter._throwIfAborted(signal);
1574
1640
  const requestId = uuidv4();
@@ -1577,7 +1643,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1577
1643
  unsignedPayloadHex: stripHex(params.message),
1578
1644
  dataType: "message",
1579
1645
  path,
1580
- xfp: record.masterFingerprint
1646
+ xfp: record.masterFingerprint,
1647
+ origin: this._origin
1581
1648
  });
1582
1649
  const responseUr = await this._resolveUr(
1583
1650
  record,
@@ -1621,7 +1688,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1621
1688
  );
1622
1689
  }
1623
1690
  try {
1624
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1691
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1625
1692
  const { account } = await this._fetchAccount(
1626
1693
  connectId,
1627
1694
  deviceId,
@@ -1667,7 +1734,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1667
1734
  const rawTxHex = stripHex(params.rawTxHex);
1668
1735
  const path = normalizePath(params.path);
1669
1736
  try {
1670
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1737
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1671
1738
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "tron", signal);
1672
1739
  _KeystoneAdapter._throwIfAborted(signal);
1673
1740
  const requestId = uuidv4();
@@ -1675,7 +1742,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1675
1742
  requestId,
1676
1743
  rawTxHex,
1677
1744
  path,
1678
- xfp: record.masterFingerprint
1745
+ xfp: record.masterFingerprint,
1746
+ origin: this._origin
1679
1747
  });
1680
1748
  const responseUr = await this._resolveUr(
1681
1749
  record,
@@ -1716,7 +1784,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1716
1784
  const messageHex = stripHex(params.messageHex);
1717
1785
  const path = normalizePath(params.path);
1718
1786
  try {
1719
- return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1787
+ return await this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1720
1788
  const { record } = await this._ensureWalletKnown(connectId, deviceId, "tron", signal);
1721
1789
  _KeystoneAdapter._throwIfAborted(signal);
1722
1790
  const requestId = uuidv4();
@@ -1725,7 +1793,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1725
1793
  rawTxHex: messageHex,
1726
1794
  path,
1727
1795
  xfp: record.masterFingerprint,
1728
- signType: 2 /* PersonalMessage */
1796
+ signType: 2 /* PersonalMessage */,
1797
+ origin: this._origin
1729
1798
  });
1730
1799
  const responseUr = await this._resolveUr(
1731
1800
  record,
@@ -1748,8 +1817,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1748
1817
  // Internals
1749
1818
  // ---------------------------------------------------------------------------
1750
1819
  _walletIdFromIdentifier(identifier) {
1751
- const value = identifier.startsWith(KEYSTONE_WALLET_CONNECT_ID_PREFIX) ? identifier.slice(KEYSTONE_WALLET_CONNECT_ID_PREFIX.length) : identifier;
1752
- return /^[0-9a-f]{64}$/i.test(value) ? value.toLowerCase() : void 0;
1820
+ return keystoneWalletIdFromIdentifier(identifier);
1753
1821
  }
1754
1822
  _allNetworkSyncSchema(item) {
1755
1823
  if (!isAllNetworkMethodName(String(item.methodName)) || !item.path) return void 0;
@@ -1778,7 +1846,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1778
1846
  * one-path-at-a-time export flow because the device limits that channel.
1779
1847
  */
1780
1848
  async _prefetchAllNetworkAccounts(connectId, deviceId, params) {
1781
- return this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1849
+ return this._jobQueue.enqueue(keystoneQueueKey(connectId, deviceId), async (signal) => {
1782
1850
  const target = this._resolveTarget(connectId, deviceId);
1783
1851
  const { record: targetRecord } = target;
1784
1852
  let record = targetRecord;
@@ -1791,7 +1859,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1791
1859
  if (!requestedByKey.size) {
1792
1860
  return { book };
1793
1861
  }
1794
- const operation = isHardwareOperationId(params.operationId) ? this._operations.find(params.operationId) : void 0;
1862
+ const operation = isHardwareOperationId2(params.operationId) ? this._operations.find(params.operationId) : void 0;
1795
1863
  const operationRoute = operation ? this._operationRoutes.get(operation.connectId) : void 0;
1796
1864
  const routedToQr = operationRoute?.operationId === operation?.operationId && operationRoute?.connectionType === "qr";
1797
1865
  if (this._forcedTransport !== "qr" && !routedToQr && !record?.usbSessionId && this._usbConnector && target.expectedWalletId) {
@@ -1857,7 +1925,8 @@ var KeystoneAdapter = class _KeystoneAdapter {
1857
1925
  });
1858
1926
  const responseUr = await this._requestQrDisplayAndAwaitResponse(
1859
1927
  record ? toDeviceInfo(record) : placeholderDeviceInfo(),
1860
- { ...requestUr, animated: false }
1928
+ { ...requestUr, animated: false },
1929
+ signal
1861
1930
  );
1862
1931
  _KeystoneAdapter._throwIfAborted(signal);
1863
1932
  const parsed = this.urEngine.parseAccountResponse(responseUr);
@@ -1893,7 +1962,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
1893
1962
  */
1894
1963
  _resolveTarget(connectId, deviceId) {
1895
1964
  let resolvedConnectId = connectId;
1896
- if (isHardwareOperationId(connectId)) {
1965
+ if (isHardwareOperationId2(connectId)) {
1897
1966
  resolvedConnectId = this._operations.resolve(connectId).connectId;
1898
1967
  }
1899
1968
  const identifiers = [deviceId, resolvedConnectId].filter(
@@ -2143,7 +2212,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
2143
2212
  * closed rather than silently falling back to QR.
2144
2213
  */
2145
2214
  async _resolveUr(record, requestUr, animated, operationId, signal, operationName) {
2146
- const releaseOperationRetention = isHardwareOperationId(operationId) ? this._operations.retain(operationId) : void 0;
2215
+ const releaseOperationRetention = isHardwareOperationId2(operationId) ? this._operations.retain(operationId) : void 0;
2147
2216
  try {
2148
2217
  return await this._resolveUrWithRoute(
2149
2218
  record,
@@ -2159,7 +2228,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
2159
2228
  }
2160
2229
  async _resolveUrWithRoute(record, requestUr, animated, operationId, signal, operationName) {
2161
2230
  let interactionRoute;
2162
- if (isHardwareOperationId(operationId)) {
2231
+ if (isHardwareOperationId2(operationId)) {
2163
2232
  const operation = this._operations.resolve(operationId);
2164
2233
  const route = this._operationRoutes.get(operation.connectId);
2165
2234
  if (!route || route.operationId !== operationId) {
@@ -2208,10 +2277,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
2208
2277
  const usbErrorCode = usbError.code;
2209
2278
  if (!interactionRoute && !this._forcedTransport && usbErrorCode === HardwareErrorCode.PayloadTooLarge && usbErrorOrigin !== "device") {
2210
2279
  const displayDevice2 = toDeviceInfo(record);
2211
- return this._requestQrDisplayAndAwaitResponse(displayDevice2, {
2212
- ...requestUr,
2213
- animated
2214
- });
2280
+ return this._requestQrDisplayAndAwaitResponse(
2281
+ displayDevice2,
2282
+ { ...requestUr, animated },
2283
+ signal
2284
+ );
2215
2285
  }
2216
2286
  const deviceAnswered = usbErrorOrigin !== void 0 ? usbErrorOrigin === "device" : usbErrorCode === HardwareErrorCode.UserRejected || usbErrorCode === HardwareErrorCode.UserAborted || usbErrorCode === HardwareErrorCode.DeviceLocked || usbErrorCode === HardwareErrorCode.DeviceMismatch;
2217
2287
  if (!deviceAnswered) {
@@ -2249,10 +2319,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
2249
2319
  }
2250
2320
  const displayDevice = record ? toDeviceInfo(record) : placeholderDeviceInfo();
2251
2321
  try {
2252
- return await this._requestQrDisplayAndAwaitResponse(displayDevice, {
2253
- ...requestUr,
2254
- animated
2255
- });
2322
+ return await this._requestQrDisplayAndAwaitResponse(
2323
+ displayDevice,
2324
+ { ...requestUr, animated },
2325
+ signal
2326
+ );
2256
2327
  } catch (error) {
2257
2328
  const { code, _tag: tag } = error;
2258
2329
  if (operationName && (code === HardwareErrorCode.OperationTimeout || tag === UI_REQUEST_TIMEOUT_TAG)) {
@@ -2387,28 +2458,76 @@ var KeystoneAdapter = class _KeystoneAdapter {
2387
2458
  const record = this._upsertDeviceRecord(parsed);
2388
2459
  return { record };
2389
2460
  }
2390
- 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();
2391
2505
  const waitPromise = this._uiRegistry.wait(
2392
2506
  UI_REQUEST.REQUEST_QR_DISPLAY,
2393
- { timeoutMs: this._qrTimeoutMs }
2507
+ { timeoutMs: this._qrTimeoutMs, operationId }
2394
2508
  );
2395
2509
  this.emitter.emit(UI_REQUEST.REQUEST_QR_DISPLAY, {
2396
2510
  type: UI_REQUEST.REQUEST_QR_DISPLAY,
2397
- payload: { device, data }
2511
+ payload: { device, data, operationId }
2398
2512
  });
2399
- const response = await waitPromise;
2513
+ const response = await this._awaitQrResponse(
2514
+ UI_REQUEST.REQUEST_QR_DISPLAY,
2515
+ waitPromise,
2516
+ signal
2517
+ );
2400
2518
  return { urType: response.urType, urData: response.urData };
2401
2519
  }
2402
- async _requestQrScanAndAwaitResponse(device) {
2520
+ async _requestQrScanAndAwaitResponse(device, signal) {
2521
+ const operationId = this._activeOperationId();
2403
2522
  const waitPromise = this._uiRegistry.wait(
2404
2523
  UI_REQUEST.REQUEST_QR_SCAN,
2405
- { timeoutMs: this._qrTimeoutMs }
2524
+ { timeoutMs: this._qrTimeoutMs, operationId }
2406
2525
  );
2407
2526
  this.emitter.emit(UI_REQUEST.REQUEST_QR_SCAN, {
2408
2527
  type: UI_REQUEST.REQUEST_QR_SCAN,
2409
- payload: { device }
2528
+ payload: { device, operationId }
2410
2529
  });
2411
- const response = await waitPromise;
2530
+ const response = await this._awaitQrResponse(UI_REQUEST.REQUEST_QR_SCAN, waitPromise, signal);
2412
2531
  return { urType: response.urType, urData: response.urData };
2413
2532
  }
2414
2533
  async _callUsbConnector(sessionId, method, params, signal) {
@@ -2424,6 +2543,12 @@ var KeystoneAdapter = class _KeystoneAdapter {
2424
2543
  message: `Keystone USB is busy while calling ${method}`
2425
2544
  });
2426
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
+ }
2427
2552
  const releaseOperation = this._retainUsbOperation(`call:${sessionId}`);
2428
2553
  let rawCall;
2429
2554
  try {