@onekeyfe/hwk-keystone-adapter 1.2.2-alpha.100 → 1.2.3-alpha.2

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.js CHANGED
@@ -30,17 +30,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ KEYSTONE_WALLET_CONNECT_ID_PREFIX: () => KEYSTONE_WALLET_CONNECT_ID_PREFIX,
34
+ KEYSTONE_WALLET_ID_PATH: () => KEYSTONE_WALLET_ID_PATH,
33
35
  KeystoneAdapter: () => KeystoneAdapter,
34
36
  KeystoneUrEngine: () => KeystoneUrEngine,
35
- QR_CONNECT_ID_PREFIX: () => QR_CONNECT_ID_PREFIX,
36
37
  accountKey: () => accountKey,
37
38
  btcScriptTypeFromPath: () => btcScriptTypeFromPath,
38
39
  createDeviceRecord: () => createDeviceRecord,
40
+ deriveKeystoneWalletId: () => deriveKeystoneWalletId,
39
41
  normalizePath: () => normalizePath,
40
42
  placeholderDeviceInfo: () => placeholderDeviceInfo,
41
- qrConnectId: () => qrConnectId,
42
43
  splitAccountPath: () => splitAccountPath,
43
- toDeviceInfo: () => toDeviceInfo
44
+ toDeviceInfo: () => toDeviceInfo,
45
+ walletConnectId: () => walletConnectId
44
46
  });
45
47
  module.exports = __toCommonJS(index_exports);
46
48
 
@@ -51,6 +53,7 @@ var import_bs58check = __toESM(require("bs58check"));
51
53
  var bitcoin = __toESM(require("bitcoinjs-lib"));
52
54
  var import_hdkey = __toESM(require("hdkey"));
53
55
  var import_uuid = require("uuid");
56
+ var import_hwk_adapter_core = require("@onekeyfe/hwk-adapter-core");
54
57
 
55
58
  // src/urEngine/TronSignRequest.ts
56
59
  var import_bc_ur_registry = require("@keystonehq/bc-ur-registry");
@@ -69,13 +72,13 @@ var TronSignRequest = class _TronSignRequest extends import_bc_ur_registry.Regis
69
72
  if (this.requestId) {
70
73
  map[1 /* requestId */] = new import_bc_ur_registry.DataItem(this.requestId, import_bc_ur_registry.RegistryTypes.UUID.getTag());
71
74
  }
72
- if (this.address) map[4 /* address */] = this.address;
73
- if (this.origin) map[5 /* origin */] = this.origin;
75
+ if (this.address) map[5 /* address */] = this.address;
76
+ if (this.origin) map[6 /* origin */] = this.origin;
74
77
  map[2 /* signData */] = this.signData;
75
- map[6 /* signType */] = this.signType;
78
+ map[3 /* dataType */] = this.signType;
76
79
  const keyPath = this.derivationPath.toDataItem();
77
80
  keyPath.setTag(this.derivationPath.getRegistryType().getTag());
78
- map[3 /* derivationPath */] = keyPath;
81
+ map[4 /* derivationPath */] = keyPath;
79
82
  return new import_bc_ur_registry.DataItem(map);
80
83
  };
81
84
  this.requestId = args.requestId;
@@ -88,12 +91,12 @@ var TronSignRequest = class _TronSignRequest extends import_bc_ur_registry.Regis
88
91
  static fromDataItem(dataItem) {
89
92
  const map = dataItem.getData();
90
93
  const signData = map[2 /* signData */];
91
- const derivationPath = import_bc_ur_registry.CryptoKeypath.fromDataItem(map[3 /* derivationPath */]);
92
- const address = map[4 /* address */];
94
+ const derivationPath = import_bc_ur_registry.CryptoKeypath.fromDataItem(map[4 /* derivationPath */]);
95
+ const address = map[5 /* address */];
93
96
  const requestIdItem = map[1 /* requestId */];
94
97
  const requestId = requestIdItem?.getData();
95
- const origin = map[5 /* origin */];
96
- const signType = map[6 /* signType */];
98
+ const origin = map[6 /* origin */];
99
+ const signType = map[3 /* dataType */];
97
100
  return new _TronSignRequest({ requestId, signData, derivationPath, address, origin, signType });
98
101
  }
99
102
  static fromCBOR(cborPayload) {
@@ -167,8 +170,20 @@ function fromSdkUr(ur) {
167
170
  return { urType: ur.type, urData: ur.cbor.toString("hex") };
168
171
  }
169
172
  function splitSignature65(hex) {
173
+ if (hex.length !== 130) {
174
+ throw new Error(
175
+ `Keystone returned a ${hex.length / 2}-byte signature; expected 65 bytes (r|s|v)`
176
+ );
177
+ }
170
178
  return { r: hex.slice(0, 64), s: hex.slice(64, 128), v: hex.slice(128) };
171
179
  }
180
+ function requireBip32MasterFingerprint(value) {
181
+ const fingerprint = (0, import_hwk_adapter_core.parseBip32MasterFingerprint)(value);
182
+ if (!fingerprint) {
183
+ throw new Error("Keystone master fingerprint must be exactly 4 bytes (8 hex characters)");
184
+ }
185
+ return fingerprint;
186
+ }
172
187
  var KeystoneUrEngine = class {
173
188
  constructor(origin = "OneKey") {
174
189
  this.sdk = new import_keystone_sdk.KeystoneSDK({ origin });
@@ -178,7 +193,7 @@ var KeystoneUrEngine = class {
178
193
  parseMultiAccounts(ur) {
179
194
  const parsed = this.sdk.parseMultiAccounts(toSdkUr(ur));
180
195
  return {
181
- masterFingerprint: parsed.masterFingerprint.toLowerCase(),
196
+ masterFingerprint: requireBip32MasterFingerprint(parsed.masterFingerprint),
182
197
  device: parsed.device,
183
198
  deviceId: parsed.deviceId,
184
199
  deviceVersion: parsed.deviceVersion,
@@ -188,7 +203,7 @@ var KeystoneUrEngine = class {
188
203
  path: key.path,
189
204
  publicKey: key.publicKey,
190
205
  extendedPublicKey: key.extendedPublicKey,
191
- xfp: key.xfp,
206
+ xfp: key.xfp ? requireBip32MasterFingerprint(key.xfp) : void 0,
192
207
  name: key.name
193
208
  })
194
209
  )
@@ -201,7 +216,7 @@ var KeystoneUrEngine = class {
201
216
  path: key.path,
202
217
  publicKey: key.publicKey,
203
218
  extendedPublicKey: key.extendedPublicKey,
204
- xfp: key.xfp,
219
+ xfp: key.xfp ? requireBip32MasterFingerprint(key.xfp) : void 0,
205
220
  name: key.name
206
221
  };
207
222
  }
@@ -250,7 +265,10 @@ var KeystoneUrEngine = class {
250
265
  if (!account.xfp) {
251
266
  throw new Error("Keystone crypto-hdkey response is missing its source fingerprint");
252
267
  }
253
- return { masterFingerprint: account.xfp.toLowerCase(), accounts: [account] };
268
+ return {
269
+ masterFingerprint: requireBip32MasterFingerprint(account.xfp),
270
+ accounts: [account]
271
+ };
254
272
  }
255
273
  return this.parseMultiAccounts(ur);
256
274
  }
@@ -302,12 +320,18 @@ var KeystoneUrEngine = class {
302
320
  deriveBtcAddressFromXpub(xpub, relativeDerivePath, scriptType) {
303
321
  const node = import_hdkey.default.fromExtendedKey(xpub).derive(`m/${relativeDerivePath.replace(/^m\//i, "")}`);
304
322
  if (!node.publicKey) throw new Error("HDKey derivation did not produce a public key");
305
- const pubkey = Buffer.from(node.publicKey);
323
+ return this.deriveBtcAddressFromPublicKey(
324
+ Buffer.from(node.publicKey).toString("hex"),
325
+ scriptType
326
+ );
327
+ }
328
+ deriveBtcAddressFromPublicKey(publicKeyHex, scriptType) {
329
+ const pubkey = Buffer.from(stripHexPrefix(publicKeyHex), "hex");
306
330
  const network = bitcoin.networks.bitcoin;
307
331
  switch (scriptType) {
308
332
  case "p2pkh": {
309
333
  const { address } = bitcoin.payments.p2pkh({ pubkey, network });
310
- if (!address) throw new Error("Failed to derive a P2PKH address from this xpub");
334
+ if (!address) throw new Error("Failed to derive a P2PKH address from this public key");
311
335
  return address;
312
336
  }
313
337
  case "p2sh-p2wpkh": {
@@ -315,12 +339,13 @@ var KeystoneUrEngine = class {
315
339
  redeem: bitcoin.payments.p2wpkh({ pubkey, network }),
316
340
  network
317
341
  });
318
- if (!address) throw new Error("Failed to derive a P2SH-P2WPKH address from this xpub");
342
+ if (!address)
343
+ throw new Error("Failed to derive a P2SH-P2WPKH address from this public key");
319
344
  return address;
320
345
  }
321
346
  case "p2wpkh": {
322
347
  const { address } = bitcoin.payments.p2wpkh({ pubkey, network });
323
- if (!address) throw new Error("Failed to derive a P2WPKH address from this xpub");
348
+ if (!address) throw new Error("Failed to derive a P2WPKH address from this public key");
324
349
  return address;
325
350
  }
326
351
  case "p2tr":
@@ -379,21 +404,14 @@ var KeystoneUrEngine = class {
379
404
  }
380
405
  // --- TRON ---
381
406
  /**
382
- * `@keystonehq/keystone-sdk`'s own bundled `sdk.tron` module is
383
- * deliberately NOT used here — see `TronSignRequest.ts`'s doc comment for
384
- * why: it's a different (gzip/protobuf) protocol with response semantics
385
- * this package has no way to verify, whereas `TronSignRequest`/
386
- * `TronSignature` are a direct port of OneKey's own already-proven
387
- * production QR-wallet TRON implementation (a plain CBOR-native
388
- * sign-request/signature pair, same shape as eth/sol). The device decodes
389
- * `rawTxHex` itself — no client-side contract-type pre-parsing or
390
- * `tokenInfo` needed, unlike the public SDK's module.
407
+ * Keystone's native tron-sign-request/tron-signature pair (bare signature).
408
+ * `sdk.tron` from keystone-sdk is the older protobuf envelope; not used.
391
409
  */
392
410
  buildTronSignRequest(input) {
393
411
  const request = new TronSignRequest({
394
412
  requestId: Buffer.from((0, import_uuid.parse)(input.requestId)),
395
413
  signData: Buffer.from(stripHexPrefix(input.rawTxHex), "hex"),
396
- signType: 0 /* Transaction */,
414
+ signType: input.signType ?? 1 /* Transaction */,
397
415
  derivationPath: TronSignRequest.parsePath(input.path, input.xfp),
398
416
  origin: input.origin
399
417
  });
@@ -430,12 +448,35 @@ var KeystoneUrEngine = class {
430
448
  ]);
431
449
  return import_bs58check.default.encode(addressBytes);
432
450
  }
451
+ /**
452
+ * Split an account-level xpub back into the BIP-32 fields a host needs to
453
+ * treat it as a real extended key (`BtcPublicKey`). Everything here is
454
+ * carried inside the xpub's own serialization — depth, the PARENT key
455
+ * fingerprint (not the seed's master fingerprint), the chain code and the
456
+ * compressed public key — so this is a pure decode with no device round
457
+ * trip; the xpub itself was already device-verified when it was synced.
458
+ */
459
+ parseXpubMeta(xpub) {
460
+ const raw = Buffer.from(import_bs58check.default.decode(xpub));
461
+ if (raw.length !== 78) {
462
+ throw new Error(`Keystone xpub did not decode to a 78-byte BIP-32 key (got ${raw.length})`);
463
+ }
464
+ return {
465
+ publicKey: raw.subarray(45, 78).toString("hex"),
466
+ chainCode: raw.subarray(13, 45).toString("hex"),
467
+ depth: raw.readUInt8(4),
468
+ parentFingerprint: raw.readUInt32BE(5)
469
+ };
470
+ }
433
471
  };
434
472
 
435
473
  // src/adapter/KeystoneAdapter.ts
436
474
  var import_bs58 = __toESM(require("bs58"));
437
475
  var import_uuid2 = require("uuid");
438
- var import_hwk_adapter_core = require("@onekeyfe/hwk-adapter-core");
476
+ var import_hwk_adapter_core3 = require("@onekeyfe/hwk-adapter-core");
477
+
478
+ // src/adapter/deviceTable.ts
479
+ var import_hwk_adapter_core2 = require("@onekeyfe/hwk-adapter-core");
439
480
 
440
481
  // src/adapter/pathUtils.ts
441
482
  function normalizePath(path) {
@@ -471,20 +512,41 @@ function btcScriptTypeFromPath(path) {
471
512
  return void 0;
472
513
  }
473
514
  }
515
+ function isKeystoneSignableBtcAccountPath(accountPath) {
516
+ const segments = normalizePath(accountPath).slice(2).split("/");
517
+ if (segments.length !== 3) return false;
518
+ const [purpose, coin, account] = segments;
519
+ return ["44'", "49'", "84'", "86'"].includes(purpose) && coin === "0'" && account === "0'";
520
+ }
521
+ var KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE = "Keystone BTC is limited to mainnet account 0 (m/44'|49'|84'|86'/0'/0')";
474
522
 
475
523
  // src/adapter/deviceTable.ts
476
- var QR_CONNECT_ID_PREFIX = "keystone-qr:";
477
- function qrConnectId(masterFingerprint) {
478
- return `${QR_CONNECT_ID_PREFIX}${masterFingerprint}`;
524
+ var KEYSTONE_WALLET_CONNECT_ID_PREFIX = "keystone-wallet:";
525
+ var KEYSTONE_WALLET_ID_PATH = "m/44'/60'/0'";
526
+ function walletConnectId(walletId) {
527
+ return `${KEYSTONE_WALLET_CONNECT_ID_PREFIX}${walletId}`;
479
528
  }
480
529
  function accountKey(hwkChain, path) {
481
530
  return `${hwkChain}:${normalizePath(path)}`;
482
531
  }
483
- function createDeviceRecord(masterFingerprint) {
532
+ function deriveKeystoneWalletId(accounts) {
533
+ const identityAccount = accounts.find(
534
+ (account) => normalizePath(account.path) === KEYSTONE_WALLET_ID_PATH
535
+ );
536
+ if (!identityAccount?.extendedPublicKey) {
537
+ throw new Error(
538
+ `Keystone identity response is missing the extended public key at ${KEYSTONE_WALLET_ID_PATH}`
539
+ );
540
+ }
541
+ return (0, import_hwk_adapter_core2.deriveWalletId)(
542
+ `keystone:secp256k1:${KEYSTONE_WALLET_ID_PATH}:${identityAccount.extendedPublicKey.trim()}`
543
+ );
544
+ }
545
+ function createDeviceRecord(walletId, masterFingerprint) {
484
546
  return {
547
+ walletId,
485
548
  masterFingerprint,
486
- connectId: qrConnectId(masterFingerprint),
487
- accounts: /* @__PURE__ */ new Map(),
549
+ connectId: walletConnectId(walletId),
488
550
  importedAt: Date.now()
489
551
  };
490
552
  }
@@ -499,11 +561,15 @@ function toDeviceInfo(record) {
499
561
  model: record.model ?? "unknown",
500
562
  modelName: record.model,
501
563
  firmwareVersion: record.deviceVersion ?? "0.0.0",
502
- deviceId: record.masterFingerprint,
564
+ deviceId: record.walletId,
503
565
  connectId: record.connectId,
504
566
  connectionType: record.usbSessionId ? "usb" : "qr",
505
567
  capabilities: CAPABILITIES,
506
- raw: { availableChannels }
568
+ raw: {
569
+ availableChannels,
570
+ masterFingerprint: record.masterFingerprint,
571
+ hardwareDeviceId: record.hardwareDeviceId
572
+ }
507
573
  };
508
574
  }
509
575
  function placeholderDeviceInfo() {
@@ -520,6 +586,20 @@ function placeholderDeviceInfo() {
520
586
 
521
587
  // src/adapter/KeystoneAdapter.ts
522
588
  var COLD_START_JOB_LABEL = "keystone-cold-start";
589
+ var KEYSTONE_USB_REATTACH_PROBE_ATTEMPTS = 4;
590
+ var KEYSTONE_USB_REATTACH_PROBE_INTERVAL_MS = 500;
591
+ function resolveHardwareOperationTarget(positionalTargetId, operationId) {
592
+ const result = (0, import_hwk_adapter_core3.resolveHardwareOperationTarget)(positionalTargetId, operationId, "keystone");
593
+ return result;
594
+ }
595
+ function waitForKeystoneUsbReattachProbe() {
596
+ return new Promise((resolve) => {
597
+ setTimeout(resolve, KEYSTONE_USB_REATTACH_PROBE_INTERVAL_MS);
598
+ });
599
+ }
600
+ function getKeystoneJobId(connectId, deviceId) {
601
+ return (0, import_hwk_adapter_core3.isHardwareOperationId)(connectId) ? connectId : deviceId || connectId || COLD_START_JOB_LABEL;
602
+ }
523
603
  var BIP44_COIN_TYPE_TO_CHAIN = {
524
604
  60: "evm",
525
605
  0: "btc",
@@ -530,64 +610,111 @@ function inferHwkChainFromPath(path) {
530
610
  const match = normalizePath(path).match(/^m\/\d+'\/(\d+)'/);
531
611
  return match ? BIP44_COIN_TYPE_TO_CHAIN[Number(match[1])] : void 0;
532
612
  }
533
- var DEFAULT_IMPORT_SCHEMAS = [
534
- { hwkChain: "evm", path: import_hwk_adapter_core.CHAIN_FINGERPRINT_PATHS.evm.replace(/\/0\/0$/, "") },
535
- // The 3 BTC purposes this package can actually derive addresses for
536
- // (btcScriptTypeFromPath: 44'→P2PKH, 49'→P2SH-P2WPKH, 84'→P2WPKH) —
537
- // explicitly requested, not left to chance on whether the device
538
- // volunteers the others unprompted. Still one combined round trip: these
539
- // are 3 more entries in the same qr-hardware-call, not 3 more scans.
540
- // 86' (P2TR/taproot) deliberately excluded — `deriveBtcAddressFromXpub`
541
- // can't use it yet (needs an elliptic-curve library not wired in), and
542
- // unlike app-monorepo's own sync-all bundle (which also requests 86', but
543
- // to compute a "full xfp" — a need this adapter doesn't have, since its
544
- // mfp already comes for free from the response envelope), there's no
545
- // other use for that xpub here — so it stays out until P2TR is real.
546
- { hwkChain: "btc", path: import_hwk_adapter_core.CHAIN_FINGERPRINT_PATHS.btc },
547
- { hwkChain: "btc", path: "m/49'/0'/0'" },
548
- { hwkChain: "btc", path: "m/84'/0'/0'" },
549
- { hwkChain: "sol", path: import_hwk_adapter_core.CHAIN_FINGERPRINT_PATHS.sol },
550
- { hwkChain: "tron", path: import_hwk_adapter_core.CHAIN_FINGERPRINT_PATHS.tron.replace(/\/0\/0$/, "") }
551
- ];
552
613
  var KeystoneAdapter = class _KeystoneAdapter {
553
614
  constructor(options) {
554
615
  this.vendor = "keystone";
555
- this.emitter = new import_hwk_adapter_core.TypedEventEmitter();
556
- this._uiRegistry = new import_hwk_adapter_core.UiRequestRegistry();
616
+ this.emitter = new import_hwk_adapter_core3.TypedEventEmitter();
617
+ this._operationRoutes = /* @__PURE__ */ new Map();
618
+ this._operations = new import_hwk_adapter_core3.OperationRegistry({
619
+ vendor: "keystone",
620
+ onEnded: (operation, reason) => {
621
+ const route = this._operationRoutes.get(operation.connectId);
622
+ if (route?.operationId === operation.operationId) {
623
+ this._operationRoutes.delete(operation.connectId);
624
+ }
625
+ this.emitter.emit(import_hwk_adapter_core3.SDK.OPERATION_ENDED, {
626
+ type: import_hwk_adapter_core3.SDK.OPERATION_ENDED,
627
+ payload: { operationId: operation.operationId, reason }
628
+ });
629
+ if (reason === "timeout") {
630
+ this._releaseOperationConnection(operation).catch(() => void 0);
631
+ }
632
+ }
633
+ });
634
+ this._uiRegistry = new import_hwk_adapter_core3.UiRequestRegistry();
557
635
  this._devices = /* @__PURE__ */ new Map();
636
+ this._searchDeviceTargets = /* @__PURE__ */ new Map();
637
+ /** Serializes USB open/identity handshakes that run outside the job queue. */
638
+ this._usbConnectTail = Promise.resolve();
639
+ this._unsettledUsbOperations = /* @__PURE__ */ new Map();
640
+ this._usbIdleWaiters = /* @__PURE__ */ new Set();
641
+ this._usbTeardownTail = Promise.resolve();
642
+ this._pendingUsbTeardowns = 0;
643
+ this._handleUsbDisconnect = ({ connectId }) => {
644
+ const record = Array.from(this._devices.values()).find((item) => item.usbSessionId === connectId);
645
+ if (!record) return;
646
+ this._operations.endByConnectionKey(connectId, "disconnect");
647
+ this._operations.endByConnectionKey(record.connectId, "disconnect");
648
+ record.usbSessionId = void 0;
649
+ const info = toDeviceInfo(record);
650
+ if (record.qrSynced) {
651
+ this.emitter.emit(import_hwk_adapter_core3.DEVICE.CHANGED, { type: import_hwk_adapter_core3.DEVICE.CHANGED, payload: info });
652
+ } else {
653
+ this._devices.delete(record.walletId);
654
+ this.emitter.emit(import_hwk_adapter_core3.DEVICE.DISCONNECT, { type: import_hwk_adapter_core3.DEVICE.DISCONNECT, payload: info });
655
+ }
656
+ };
657
+ this._handleUsbUiEvent = (event) => {
658
+ this.emitter.emit("ui-event", event);
659
+ };
558
660
  // ---------------------------------------------------------------------------
559
661
  // All-network bundle — dispatches to the same per-chain methods below, so
560
662
  // there's exactly one implementation of each chain's address logic.
561
663
  // ---------------------------------------------------------------------------
562
664
  this.allNetworkGetAddress = async (connectId, deviceId, params) => {
665
+ const operationTarget = resolveHardwareOperationTarget(connectId, params.operationId);
666
+ if (!operationTarget.success) return operationTarget;
667
+ const effectiveConnectId = operationTarget.payload.targetId ?? "";
668
+ const { operationId } = operationTarget.payload;
563
669
  try {
564
- return await (0, import_hwk_adapter_core.runAllNetworkGetAddress)({
565
- connectId,
670
+ const prefetched = await this._prefetchAllNetworkAccounts(
671
+ effectiveConnectId,
566
672
  deviceId,
673
+ params
674
+ );
675
+ const { book } = prefetched;
676
+ const itemDeviceId = deviceId || prefetched.walletId || "";
677
+ return await (0, import_hwk_adapter_core3.runAllNetworkGetAddress)({
678
+ connectId: effectiveConnectId,
679
+ deviceId: itemDeviceId,
567
680
  params,
568
- callItem: async ({ chain, item }) => {
569
- const commonArgs = { path: item.path, showOnDevice: item.showOnDevice };
570
- switch (chain) {
571
- case "evm":
572
- return this.evmGetAddress(connectId, deviceId, commonArgs);
573
- case "btc":
574
- return this.btcGetAddress(connectId, deviceId, commonArgs);
575
- case "sol":
576
- return this.solGetAddress(connectId, deviceId, commonArgs);
577
- case "tron":
578
- return this.tronGetAddress(connectId, deviceId, commonArgs);
681
+ callItem: async ({ method, item }) => {
682
+ const commonArgs = {
683
+ path: item.path,
684
+ showOnDevice: item.showOnDevice,
685
+ operationId
686
+ };
687
+ switch (method) {
688
+ case "evmGetAddress":
689
+ return this.evmGetAddress(effectiveConnectId, itemDeviceId, commonArgs, book);
690
+ case "btcGetAddress":
691
+ return this.btcGetAddress(effectiveConnectId, itemDeviceId, commonArgs, book);
692
+ case "btcGetPublicKey":
693
+ return this.btcGetPublicKey(effectiveConnectId, itemDeviceId, commonArgs, book);
694
+ case "solGetAddress":
695
+ return this.solGetAddress(effectiveConnectId, itemDeviceId, commonArgs, book);
696
+ case "tronGetAddress":
697
+ return this.tronGetAddress(effectiveConnectId, itemDeviceId, commonArgs, book);
579
698
  default:
580
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.MethodNotSupported, `Unsupported chain: ${chain}`);
699
+ return (0, import_hwk_adapter_core3.failure)(
700
+ import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
701
+ `Unsupported method: ${String(method)}`
702
+ );
581
703
  }
582
704
  },
583
705
  attachIdentity: (context) => {
584
- const mfp = deviceId ? deviceId.toLowerCase() : void 0;
706
+ const { record } = this._resolveTarget(effectiveConnectId, itemDeviceId);
585
707
  return Promise.resolve({
586
708
  ...context.item,
587
709
  success: true,
588
710
  payload: {
589
711
  ...context.payload,
590
- deviceIdentity: mfp ? { vendor: "keystone", type: "masterFingerprint", value: mfp } : void 0
712
+ rootFingerprint: record ? Number.parseInt(record.masterFingerprint, 16) : void 0,
713
+ deviceIdentity: record ? {
714
+ vendor: "keystone",
715
+ type: "walletId",
716
+ value: record.walletId
717
+ } : void 0
591
718
  }
592
719
  });
593
720
  }
@@ -599,8 +726,10 @@ var KeystoneAdapter = class _KeystoneAdapter {
599
726
  this._origin = options?.origin ?? "OneKey";
600
727
  this._qrTimeoutMs = options?.qrTimeoutMs;
601
728
  this.urEngine = new KeystoneUrEngine(this._origin);
602
- this._jobQueue = new import_hwk_adapter_core.DeviceJobQueue();
729
+ this._jobQueue = new import_hwk_adapter_core3.DeviceJobQueue();
603
730
  this._usbConnector = options?.usbConnector;
731
+ this._usbConnector?.on("device-disconnect", this._handleUsbDisconnect);
732
+ this._usbConnector?.on("ui-event", this._handleUsbUiEvent);
604
733
  }
605
734
  // ---------------------------------------------------------------------------
606
735
  // Lifecycle / transport
@@ -621,56 +750,184 @@ var KeystoneAdapter = class _KeystoneAdapter {
621
750
  init(_config) {
622
751
  return Promise.resolve();
623
752
  }
624
- dispose() {
753
+ async dispose() {
754
+ this._operations.endAll("runtime-reset");
625
755
  this._uiRegistry.cancel();
626
756
  this._jobQueue.clear();
627
- this.emitter.removeAllListeners();
628
- this._devices.clear();
757
+ this._searchDeviceTargets.clear();
758
+ await this._resetUsbSessions();
759
+ this._usbConnector?.off("device-disconnect", this._handleUsbDisconnect);
760
+ this._usbConnector?.off("ui-event", this._handleUsbUiEvent);
629
761
  this._usbConnector?.reset();
630
- return Promise.resolve();
762
+ this._devices.clear();
763
+ this.emitter.removeAllListeners();
631
764
  }
632
765
  // ---------------------------------------------------------------------------
633
766
  // Device table
634
767
  // ---------------------------------------------------------------------------
635
768
  /**
636
- * QR-synced wallets are always included (this instance's own state — no
637
- * enumeration exists for QR). When a USB connector is configured, its raw
638
- * scan results are appended as-is: a USB descriptor has no mfp until
639
- * `connectDevice()` actually opens+claims it (see
769
+ * Unscoped discovery includes QR-synced wallets already known to this
770
+ * adapter. Explicit QR discovery returns a virtual connection target because
771
+ * there is no physical descriptor to enumerate. When a USB connector is
772
+ * configured, its raw scan results are appended as-is: a USB descriptor has
773
+ * no mfp until `connectDevice()` actually opens+claims it (see
640
774
  * `KeystoneUsbConnectorBase.searchDevices`), so these entries carry an
641
775
  * empty `deviceId` and exist purely so a host can list "plugged in, click
642
776
  * to connect" candidates.
643
777
  */
644
- async searchDevices(_options) {
778
+ async searchDevices(options) {
779
+ const searchGeneration = /* @__PURE__ */ Symbol("keystone-device-search");
780
+ this._activeSearchGeneration = searchGeneration;
781
+ this._searchDeviceTargets.clear();
782
+ if (options?.resetSession) {
783
+ await this._resetUsbSessions();
784
+ } else {
785
+ await this._usbTeardownTail;
786
+ }
645
787
  const known = Array.from(this._devices.values()).map(toDeviceInfo);
646
- if (!this._usbConnector) return known;
788
+ if (options?.transportType === "qr") {
789
+ const searchTargetId = (0, import_hwk_adapter_core3.createHardwareSearchTargetId)("keystone");
790
+ this._searchDeviceTargets.set(searchTargetId, "qr");
791
+ return [
792
+ {
793
+ ...placeholderDeviceInfo(),
794
+ model: "",
795
+ connectId: searchTargetId,
796
+ capabilities: { persistentDeviceIdentity: false }
797
+ }
798
+ ];
799
+ }
800
+ if (options?.transportType === "ble") return [];
801
+ if (!this._usbConnector) return options?.transportType === "usb" ? [] : known;
647
802
  let usbDevices;
648
803
  try {
649
804
  usbDevices = await this._usbConnector.searchDevices();
650
- } catch {
805
+ } catch (error) {
806
+ if (options?.transportType === "usb") throw error;
651
807
  return known;
652
808
  }
809
+ if (this._activeSearchGeneration !== searchGeneration) return [];
653
810
  const placeholders = usbDevices.map((d) => ({
654
811
  vendor: "keystone",
655
- model: d.model ?? "unknown",
812
+ // Empty, not 'unknown': enumeration genuinely cannot tell the model
813
+ // apart (every Keystone shares one vid/pid), and a literal placeholder
814
+ // string would beat the host's own default-name fallback and surface as
815
+ // a device called "unknown".
816
+ model: d.model ?? "",
656
817
  modelName: d.modelName,
818
+ // Placeholders — neither is knowable until the device is opened.
657
819
  firmwareVersion: "0.0.0",
658
820
  deviceId: d.deviceId,
659
821
  connectId: d.connectId,
660
822
  connectionType: "usb",
661
- capabilities: d.capabilities ?? { persistentDeviceIdentity: true }
823
+ // The USB product name, which IS readable at enumeration time.
824
+ label: d.name,
825
+ serialNumber: d.serialNumber,
826
+ capabilities: d.capabilities ?? { persistentDeviceIdentity: false },
827
+ raw: d.raw
662
828
  }));
663
- return [...known, ...placeholders];
829
+ for (const placeholder of placeholders) {
830
+ this._searchDeviceTargets.set(placeholder.connectId, "usb");
831
+ }
832
+ return options?.transportType === "usb" ? placeholders : [...known, ...placeholders];
833
+ }
834
+ async _resetUsbSessions() {
835
+ await this._runUsbTeardown(async () => {
836
+ await this._usbConnectTail;
837
+ this._operations.endAll("runtime-reset");
838
+ if (!this._usbConnector) return;
839
+ const sessionIds = new Set(
840
+ Array.from(this._devices.values()).map((record) => record.usbSessionId).filter((sessionId) => Boolean(sessionId))
841
+ );
842
+ for (const sessionId of sessionIds) {
843
+ try {
844
+ await this._usbConnector.disconnect(sessionId);
845
+ } catch {
846
+ }
847
+ this._handleUsbDisconnect({ connectId: sessionId });
848
+ }
849
+ });
664
850
  }
665
- async connectDevice(connectId) {
666
- const existing = this._findByConnectId(connectId);
667
- if (existing) return (0, import_hwk_adapter_core.success)(existing.connectId);
668
- if (!this._usbConnector) {
669
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.DeviceNotFound, `Unknown Keystone connectId: ${connectId}`);
851
+ async searchDeviceTargets(options) {
852
+ const devices = await this.searchDevices(options);
853
+ return devices.map((device) => ({
854
+ searchTargetId: device.connectId,
855
+ // Keystone's wallet id is stable only after the USB/QR handshake. The
856
+ // selectable handle remains owned by the current discovery snapshot.
857
+ searchTargetReusePolicy: "current-discovery",
858
+ vendor: device.vendor,
859
+ connectionType: device.connectionType,
860
+ kind: device.connectionType === "qr" ? "interactive" : "physical",
861
+ label: device.label,
862
+ model: device.model,
863
+ modelName: device.modelName,
864
+ serialNumber: device.serialNumber
865
+ }));
866
+ }
867
+ async listConnectionTargets(options) {
868
+ const targets = await this.searchDeviceTargets(options);
869
+ return targets.map(({ searchTargetId, ...target }) => ({
870
+ ...target,
871
+ targetId: searchTargetId
872
+ }));
873
+ }
874
+ async connectDevice(searchTargetId) {
875
+ try {
876
+ let connected;
877
+ let selectedConnectionType;
878
+ const discoveredConnectionType = this._searchDeviceTargets.get(searchTargetId);
879
+ if (discoveredConnectionType === "qr") {
880
+ selectedConnectionType = "qr";
881
+ connected = await this.importFromQr();
882
+ } else if (discoveredConnectionType === "usb") {
883
+ selectedConnectionType = "usb";
884
+ if (!this._usbConnector) {
885
+ return (0, import_hwk_adapter_core3.failure)(
886
+ import_hwk_adapter_core3.HardwareErrorCode.TransportNotAvailable,
887
+ "No USB connector configured for this Keystone adapter"
888
+ );
889
+ }
890
+ connected = await this._connectUsb({}, searchTargetId);
891
+ } else if ((0, import_hwk_adapter_core3.hasHardwareRuntimeIdPrefix)(searchTargetId)) {
892
+ return (0, import_hwk_adapter_core3.failure)(
893
+ import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
894
+ "Keystone search target has expired; search for devices again",
895
+ void 0,
896
+ void 0,
897
+ { scope: "search-target" }
898
+ );
899
+ } else {
900
+ const target = this._resolveTarget(searchTargetId);
901
+ if (target.record) {
902
+ selectedConnectionType = target.record.usbSessionId ? "usb" : "qr";
903
+ connected = (0, import_hwk_adapter_core3.success)(toDeviceInfo(target.record));
904
+ } else if (!this._usbConnector) {
905
+ return (0, import_hwk_adapter_core3.failure)(
906
+ import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
907
+ `Unknown Keystone device search target: ${searchTargetId}`
908
+ );
909
+ } else {
910
+ selectedConnectionType = "usb";
911
+ connected = await this._connectUsb(target);
912
+ }
913
+ }
914
+ if (!connected.success) return connected;
915
+ const record = this._devices.get(connected.payload.deviceId);
916
+ this._operations.endByConnectionKey(connected.payload.connectId, "explicit");
917
+ const operation = this._operations.create({
918
+ searchTargetId,
919
+ connectId: connected.payload.connectId,
920
+ device: connected.payload,
921
+ connectionKeys: selectedConnectionType === "usb" && record?.usbSessionId ? [record.usbSessionId] : []
922
+ });
923
+ this._operationRoutes.set(connected.payload.connectId, {
924
+ operationId: operation.operationId,
925
+ connectionType: selectedConnectionType
926
+ });
927
+ return (0, import_hwk_adapter_core3.success)(operation.operationId);
928
+ } catch (err) {
929
+ return this._errorToFailure(err);
670
930
  }
671
- const result = await this._connectUsb();
672
- if (!result.success) return result;
673
- return (0, import_hwk_adapter_core.success)(result.payload.connectId);
674
931
  }
675
932
  /**
676
933
  * QR has no persistent connection to tear down — the account cache
@@ -679,57 +936,92 @@ var KeystoneAdapter = class _KeystoneAdapter {
679
936
  * QR-only (if it was ever QR-synced) or removes it entirely (pure-USB
680
937
  * wallet that was never seen over QR) — see §4.2 of the design doc.
681
938
  */
682
- async disconnectDevice(connectId) {
683
- const record = this._findByConnectId(connectId);
684
- if (!record?.usbSessionId) return;
685
- if (this._usbConnector) {
686
- try {
687
- await this._usbConnector.disconnect(record.usbSessionId);
688
- } catch {
689
- }
690
- }
691
- record.usbSessionId = void 0;
692
- if (record.qrSynced) {
693
- const info = toDeviceInfo(record);
694
- this.emitter.emit(import_hwk_adapter_core.DEVICE.CHANGED, { type: import_hwk_adapter_core.DEVICE.CHANGED, payload: info });
695
- } else {
696
- this._devices.delete(record.masterFingerprint);
697
- const info = toDeviceInfo(record);
698
- this.emitter.emit(import_hwk_adapter_core.DEVICE.DISCONNECT, { type: import_hwk_adapter_core.DEVICE.DISCONNECT, payload: info });
939
+ async releaseOperation(operationId) {
940
+ const operation = this._operations.find(operationId);
941
+ if (!operation) {
942
+ this._operations.resolve(operationId);
943
+ return;
699
944
  }
945
+ const endedOperation = this._operations.end(operationId, "explicit");
946
+ if (!endedOperation) return;
947
+ await this._releaseOperationConnection(endedOperation);
700
948
  }
701
- getDeviceInfo(connectId, deviceId) {
702
- const record = this._findByConnectId(connectId) ?? this._devices.get(deviceId.toLowerCase());
703
- if (!record) {
704
- return Promise.resolve(
705
- (0, import_hwk_adapter_core.failure)(
706
- import_hwk_adapter_core.HardwareErrorCode.DeviceNotFound,
707
- `Unknown Keystone device: ${connectId || deviceId}`
708
- )
709
- );
949
+ async _releaseOperationConnection(operation) {
950
+ const { connectId } = operation;
951
+ let record;
952
+ try {
953
+ record = this._resolveTarget(connectId).record;
954
+ } catch {
955
+ return;
956
+ }
957
+ if (!record?.usbSessionId || !operation.connectionKeys.includes(record.usbSessionId)) {
958
+ return;
710
959
  }
711
- return Promise.resolve((0, import_hwk_adapter_core.success)(toDeviceInfo(record)));
960
+ const { usbSessionId } = record;
961
+ await this._runUsbTeardown(async () => {
962
+ if (this._usbConnector) {
963
+ try {
964
+ await this._usbConnector.disconnect(usbSessionId);
965
+ } catch {
966
+ }
967
+ }
968
+ this._handleUsbDisconnect({ connectId: usbSessionId });
969
+ });
712
970
  }
713
- getSupportedChains() {
714
- return ["evm", "btc", "sol", "tron"];
971
+ getDeviceInfo(connectIdOrOperationId, deviceId) {
972
+ try {
973
+ const connectId = (0, import_hwk_adapter_core3.isHardwareOperationId)(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
974
+ const { record } = this._resolveTarget(connectId, deviceId);
975
+ if (!record) {
976
+ return Promise.resolve(
977
+ (0, import_hwk_adapter_core3.failure)(
978
+ import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
979
+ `Unknown Keystone device: ${connectId || deviceId}`
980
+ )
981
+ );
982
+ }
983
+ return Promise.resolve((0, import_hwk_adapter_core3.success)(toDeviceInfo(record)));
984
+ } catch (err) {
985
+ return Promise.resolve(this._errorToFailure(err));
986
+ }
715
987
  }
716
988
  cancel(connectId) {
717
- const reason = (0, import_hwk_adapter_core.createHwkError)({
718
- code: import_hwk_adapter_core.HardwareErrorCode.UserAborted,
989
+ const reason = (0, import_hwk_adapter_core3.createHwkError)({
990
+ code: import_hwk_adapter_core3.HardwareErrorCode.UserAborted,
719
991
  message: "User aborted operation"
720
992
  });
993
+ const activeJobId = this._jobQueue.getActiveJob()?.deviceId;
994
+ if (connectId) {
995
+ if (!activeJobId || activeJobId !== connectId && activeJobId !== this._walletIdFromIdentifier(connectId)) {
996
+ this._jobQueue.cancelActiveAndPending(connectId, reason);
997
+ const walletId = this._walletIdFromIdentifier(connectId);
998
+ if (walletId && walletId !== connectId) {
999
+ this._jobQueue.cancelActiveAndPending(walletId, reason);
1000
+ }
1001
+ return;
1002
+ }
1003
+ this._jobQueue.cancelActiveAndPending(activeJobId, reason);
1004
+ } else {
1005
+ this._jobQueue.cancelActiveAndPending(void 0, reason);
1006
+ }
721
1007
  this._uiRegistry.cancel();
722
- this._jobQueue.cancelActiveAndPending(connectId, reason);
723
1008
  }
724
1009
  getChainFingerprint(connectId, deviceId, chain) {
725
- const mfp = deviceId ? deviceId.toLowerCase() : this._mfpFromConnectId(connectId);
726
- if (!mfp) {
727
- return Promise.resolve((0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.DeviceNotFound, "Unknown Keystone device"));
1010
+ try {
1011
+ const target = this._resolveTarget(connectId, deviceId);
1012
+ const walletId = target.record?.walletId ?? target.expectedWalletId;
1013
+ if (!walletId) {
1014
+ return Promise.resolve(
1015
+ (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound, "Unknown Keystone wallet identity")
1016
+ );
1017
+ }
1018
+ return Promise.resolve((0, import_hwk_adapter_core3.success)((0, import_hwk_adapter_core3.deriveDeviceFingerprint)(`keystone:${chain}:${walletId}`)));
1019
+ } catch (err) {
1020
+ return Promise.resolve(this._errorToFailure(err));
728
1021
  }
729
- return Promise.resolve((0, import_hwk_adapter_core.success)((0, import_hwk_adapter_core.deriveDeviceFingerprint)(`keystone:${chain}:${mfp}`)));
730
1022
  }
731
1023
  uiResponse(response) {
732
- if (response.type === import_hwk_adapter_core.UI_RESPONSE.CANCEL) {
1024
+ if (response.type === import_hwk_adapter_core3.UI_RESPONSE.CANCEL) {
733
1025
  this.cancel();
734
1026
  return;
735
1027
  }
@@ -750,17 +1042,11 @@ var KeystoneAdapter = class _KeystoneAdapter {
750
1042
  return await this._jobQueue.enqueue(COLD_START_JOB_LABEL, async (signal) => {
751
1043
  const displayDevice = placeholderDeviceInfo();
752
1044
  let responseUr;
753
- let requestedChains;
754
1045
  if (options.mode === "scan") {
755
1046
  responseUr = await this._requestQrScanAndAwaitResponse(displayDevice);
756
1047
  } else {
757
- const schemas = options.paths?.length ? options.paths : DEFAULT_IMPORT_SCHEMAS;
758
- requestedChains = schemas.map((s) => s.hwkChain);
759
1048
  const requestUr = this.urEngine.buildKeyDerivationRequest({
760
- schemas: schemas.map((s) => ({
761
- path: s.path,
762
- curve: s.hwkChain === "sol" ? "ed25519" : "secp256k1"
763
- })),
1049
+ schemas: [{ path: KEYSTONE_WALLET_ID_PATH, curve: "secp256k1" }],
764
1050
  origin: this._origin
765
1051
  });
766
1052
  responseUr = await this._requestQrDisplayAndAwaitResponse(displayDevice, {
@@ -771,14 +1057,7 @@ var KeystoneAdapter = class _KeystoneAdapter {
771
1057
  _KeystoneAdapter._throwIfAborted(signal);
772
1058
  const parsed = this.urEngine.parseAccountResponse(responseUr);
773
1059
  const record = this._upsertDeviceRecord(parsed);
774
- for (const account of parsed.accounts) {
775
- const hwkChain = requestedChains?.length === 1 ? requestedChains[0] : inferHwkChainFromPath(account.path);
776
- if (hwkChain) {
777
- const entry = { ...account, hwkChain };
778
- record.accounts.set(accountKey(hwkChain, account.path), entry);
779
- }
780
- }
781
- return (0, import_hwk_adapter_core.success)(toDeviceInfo(record));
1060
+ return (0, import_hwk_adapter_core3.success)(toDeviceInfo(record));
782
1061
  });
783
1062
  } catch (err) {
784
1063
  return this._errorToFailure(err);
@@ -787,175 +1066,196 @@ var KeystoneAdapter = class _KeystoneAdapter {
787
1066
  // ---------------------------------------------------------------------------
788
1067
  // EVM
789
1068
  // ---------------------------------------------------------------------------
790
- async evmGetAddress(connectIdArg, deviceIdArg, paramsArg) {
791
- const connectId = connectIdArg ?? void 0;
1069
+ async evmGetAddress(connectIdArg, deviceIdArg, paramsArg, book) {
1070
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1071
+ if (!operationTarget.success) return operationTarget;
1072
+ const connectId = operationTarget.payload.targetId;
792
1073
  const deviceId = deviceIdArg ?? void 0;
793
1074
  const params = paramsArg;
794
- if (!params) return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "evmGetAddress requires params");
1075
+ if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "evmGetAddress requires params");
795
1076
  if (!params.path)
796
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "evmGetAddress requires params.path");
1077
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "evmGetAddress requires params.path");
797
1078
  const { accountPath, relativeDerivePath } = splitAccountPath(params.path);
798
1079
  if (!relativeDerivePath) {
799
- return (0, import_hwk_adapter_core.failure)(
800
- import_hwk_adapter_core.HardwareErrorCode.InvalidParams,
1080
+ return (0, import_hwk_adapter_core3.failure)(
1081
+ import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
801
1082
  "evmGetAddress requires a full leaf path, e.g. m/44'/60'/0'/0/0"
802
1083
  );
803
1084
  }
804
1085
  try {
805
- return await this._jobQueue.enqueue(
806
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
807
- async (signal) => {
808
- const { account } = await this._ensureAccountSynced(
809
- connectId,
810
- deviceId,
811
- "evm",
812
- accountPath,
813
- signal
814
- );
815
- _KeystoneAdapter._throwIfAborted(signal);
816
- if (!account.extendedPublicKey) {
817
- throw (0, import_hwk_adapter_core.createHwkError)({
818
- code: import_hwk_adapter_core.HardwareErrorCode.MethodNotSupported,
819
- message: "Keystone did not return an extended public key for this account path"
820
- });
821
- }
822
- const address = this.urEngine.deriveEvmAddressFromXpub(
823
- account.extendedPublicKey,
824
- relativeDerivePath
825
- );
826
- return (0, import_hwk_adapter_core.success)({ address, path: normalizePath(params.path) });
1086
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1087
+ const { account } = await this._fetchAccount(
1088
+ connectId,
1089
+ deviceId,
1090
+ "evm",
1091
+ accountPath,
1092
+ signal,
1093
+ { book }
1094
+ );
1095
+ _KeystoneAdapter._throwIfAborted(signal);
1096
+ if (!account.extendedPublicKey) {
1097
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1098
+ code: import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
1099
+ message: "Keystone did not return an extended public key for this account path"
1100
+ });
827
1101
  }
828
- );
1102
+ const address = this.urEngine.deriveEvmAddressFromXpub(
1103
+ account.extendedPublicKey,
1104
+ relativeDerivePath
1105
+ );
1106
+ return (0, import_hwk_adapter_core3.success)({ address, path: normalizePath(params.path) });
1107
+ });
829
1108
  } catch (err) {
830
1109
  return this._errorToFailure(err);
831
1110
  }
832
1111
  }
833
1112
  async evmSignTransaction(connectIdArg, deviceIdArg, paramsArg) {
834
- const connectId = connectIdArg ?? void 0;
1113
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1114
+ if (!operationTarget.success) return operationTarget;
1115
+ const connectId = operationTarget.payload.targetId;
835
1116
  const deviceId = deviceIdArg ?? void 0;
836
1117
  const params = paramsArg;
837
1118
  if (!params)
838
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "evmSignTransaction requires params");
1119
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "evmSignTransaction requires params");
839
1120
  if (!params.path) {
840
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "evmSignTransaction requires params.path");
1121
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "evmSignTransaction requires params.path");
841
1122
  }
842
1123
  if (!("serializedTx" in params) || typeof params.serializedTx !== "string" || !params.serializedTx) {
843
- return (0, import_hwk_adapter_core.failure)(
844
- import_hwk_adapter_core.HardwareErrorCode.MethodNotSupported,
1124
+ return (0, import_hwk_adapter_core3.failure)(
1125
+ import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
845
1126
  "Keystone only signs a fully RLP-serialized transaction (params.serializedTx) \u2014 structured-field signing is not supported"
846
1127
  );
847
1128
  }
848
- const rawTxHex = (0, import_hwk_adapter_core.stripHex)(params.serializedTx);
1129
+ const rawTxHex = (0, import_hwk_adapter_core3.stripHex)(params.serializedTx);
849
1130
  const path = normalizePath(params.path);
850
1131
  try {
851
- return await this._jobQueue.enqueue(
852
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
853
- async (signal) => {
854
- const { record } = await this._ensureMfpKnown(connectId, deviceId, "evm", signal);
855
- _KeystoneAdapter._throwIfAborted(signal);
856
- const requestId = (0, import_uuid2.v4)();
857
- const dataType = parseInt(rawTxHex.slice(0, 2), 16) < 192 ? "typedTransaction" : "transaction";
858
- const requestUr = this.urEngine.buildEthSignRequest({
859
- requestId,
860
- unsignedTxHex: rawTxHex,
861
- dataType,
862
- path,
863
- xfp: record.masterFingerprint,
864
- chainId: params.chainId
865
- });
866
- const responseUr = await this._resolveUr(record, requestUr, true);
867
- _KeystoneAdapter._throwIfAborted(signal);
868
- const sig = this.urEngine.parseEthSignature(responseUr);
869
- _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
870
- return (0, import_hwk_adapter_core.success)({
871
- v: (0, import_hwk_adapter_core.ensure0x)(sig.v),
872
- r: (0, import_hwk_adapter_core.ensure0x)(sig.r),
873
- s: (0, import_hwk_adapter_core.ensure0x)(sig.s)
874
- });
875
- }
876
- );
1132
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1133
+ const { record } = await this._ensureWalletKnown(connectId, deviceId, "evm", signal);
1134
+ _KeystoneAdapter._throwIfAborted(signal);
1135
+ const requestId = (0, import_uuid2.v4)();
1136
+ const dataType = parseInt(rawTxHex.slice(0, 2), 16) < 192 ? "typedTransaction" : "transaction";
1137
+ const requestUr = this.urEngine.buildEthSignRequest({
1138
+ requestId,
1139
+ unsignedTxHex: rawTxHex,
1140
+ dataType,
1141
+ path,
1142
+ xfp: record.masterFingerprint,
1143
+ chainId: params.chainId
1144
+ });
1145
+ const responseUr = await this._resolveUr(
1146
+ record,
1147
+ requestUr,
1148
+ true,
1149
+ connectId,
1150
+ signal,
1151
+ "evmSignTransaction"
1152
+ );
1153
+ _KeystoneAdapter._throwIfAborted(signal);
1154
+ const sig = this.urEngine.parseEthSignature(responseUr);
1155
+ _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1156
+ return (0, import_hwk_adapter_core3.success)({
1157
+ v: (0, import_hwk_adapter_core3.ensure0x)(sig.v),
1158
+ r: (0, import_hwk_adapter_core3.ensure0x)(sig.r),
1159
+ s: (0, import_hwk_adapter_core3.ensure0x)(sig.s)
1160
+ });
1161
+ });
877
1162
  } catch (err) {
878
1163
  return this._errorToFailure(err);
879
1164
  }
880
1165
  }
881
1166
  async evmSignMessage(connectIdArg, deviceIdArg, paramsArg) {
882
- const connectId = connectIdArg ?? void 0;
1167
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1168
+ if (!operationTarget.success) return operationTarget;
1169
+ const connectId = operationTarget.payload.targetId;
883
1170
  const deviceId = deviceIdArg ?? void 0;
884
1171
  const params = paramsArg;
885
- if (!params) return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "evmSignMessage requires params");
1172
+ if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "evmSignMessage requires params");
886
1173
  if (!params.path || params.message === void 0) {
887
- return (0, import_hwk_adapter_core.failure)(
888
- import_hwk_adapter_core.HardwareErrorCode.InvalidParams,
1174
+ return (0, import_hwk_adapter_core3.failure)(
1175
+ import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
889
1176
  "evmSignMessage requires params.path and params.message"
890
1177
  );
891
1178
  }
892
- const messageHex = params.hex ? (0, import_hwk_adapter_core.stripHex)(params.message) : Buffer.from(params.message, "utf8").toString("hex");
1179
+ const messageHex = params.hex ? (0, import_hwk_adapter_core3.stripHex)(params.message) : Buffer.from(params.message, "utf8").toString("hex");
893
1180
  const path = normalizePath(params.path);
894
1181
  try {
895
- return await this._jobQueue.enqueue(
896
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
897
- async (signal) => {
898
- const { record } = await this._ensureMfpKnown(connectId, deviceId, "evm", signal);
899
- _KeystoneAdapter._throwIfAborted(signal);
900
- const requestId = (0, import_uuid2.v4)();
901
- const requestUr = this.urEngine.buildEthSignRequest({
902
- requestId,
903
- unsignedTxHex: messageHex,
904
- dataType: "personalMessage",
905
- path,
906
- xfp: record.masterFingerprint,
907
- chainId: params.chainId
908
- });
909
- const responseUr = await this._resolveUr(record, requestUr, true);
910
- _KeystoneAdapter._throwIfAborted(signal);
911
- const sig = this.urEngine.parseEthSignature(responseUr);
912
- _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
913
- return (0, import_hwk_adapter_core.success)({ signature: (0, import_hwk_adapter_core.ensure0x)(sig.r + sig.s + sig.v) });
914
- }
915
- );
1182
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1183
+ const { record } = await this._ensureWalletKnown(connectId, deviceId, "evm", signal);
1184
+ _KeystoneAdapter._throwIfAborted(signal);
1185
+ const requestId = (0, import_uuid2.v4)();
1186
+ const requestUr = this.urEngine.buildEthSignRequest({
1187
+ requestId,
1188
+ unsignedTxHex: messageHex,
1189
+ dataType: "personalMessage",
1190
+ path,
1191
+ xfp: record.masterFingerprint,
1192
+ chainId: params.chainId
1193
+ });
1194
+ const responseUr = await this._resolveUr(
1195
+ record,
1196
+ requestUr,
1197
+ true,
1198
+ connectId,
1199
+ signal,
1200
+ "evmSignMessage"
1201
+ );
1202
+ _KeystoneAdapter._throwIfAborted(signal);
1203
+ const sig = this.urEngine.parseEthSignature(responseUr);
1204
+ _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1205
+ return (0, import_hwk_adapter_core3.success)({ signature: (0, import_hwk_adapter_core3.ensure0x)(sig.r + sig.s + sig.v) });
1206
+ });
916
1207
  } catch (err) {
917
1208
  return this._errorToFailure(err);
918
1209
  }
919
1210
  }
920
1211
  async evmSignTypedData(connectIdArg, deviceIdArg, paramsArg) {
921
- const connectId = connectIdArg ?? void 0;
1212
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1213
+ if (!operationTarget.success) return operationTarget;
1214
+ const connectId = operationTarget.payload.targetId;
922
1215
  const deviceId = deviceIdArg ?? void 0;
923
1216
  const params = paramsArg;
924
1217
  if (!params)
925
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "evmSignTypedData requires params");
1218
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "evmSignTypedData requires params");
926
1219
  if (!params.path) {
927
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "evmSignTypedData requires params.path");
1220
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "evmSignTypedData requires params.path");
928
1221
  }
929
1222
  if (params.mode === "hash") {
930
- return (0, import_hwk_adapter_core.failure)(
931
- import_hwk_adapter_core.HardwareErrorCode.MethodNotSupported,
1223
+ return (0, import_hwk_adapter_core3.failure)(
1224
+ import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
932
1225
  "Keystone always displays the full EIP-712 payload for on-device review \u2014 pre-hashed signing is not supported"
933
1226
  );
934
1227
  }
935
- const signDataHex = Buffer.from(JSON.stringify(params.data), "utf8").toString("hex");
1228
+ const signDataHex = Buffer.from(
1229
+ params.dataJson ?? JSON.stringify(params.data),
1230
+ "utf8"
1231
+ ).toString("hex");
936
1232
  const path = normalizePath(params.path);
937
1233
  try {
938
- return await this._jobQueue.enqueue(
939
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
940
- async (signal) => {
941
- const { record } = await this._ensureMfpKnown(connectId, deviceId, "evm", signal);
942
- _KeystoneAdapter._throwIfAborted(signal);
943
- const requestId = (0, import_uuid2.v4)();
944
- const requestUr = this.urEngine.buildEthSignRequest({
945
- requestId,
946
- unsignedTxHex: signDataHex,
947
- dataType: "typedData",
948
- path,
949
- xfp: record.masterFingerprint,
950
- chainId: params.chainId
951
- });
952
- const responseUr = await this._resolveUr(record, requestUr, true);
953
- _KeystoneAdapter._throwIfAborted(signal);
954
- const sig = this.urEngine.parseEthSignature(responseUr);
955
- _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
956
- return (0, import_hwk_adapter_core.success)({ signature: (0, import_hwk_adapter_core.ensure0x)(sig.r + sig.s + sig.v) });
957
- }
958
- );
1234
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1235
+ const { record } = await this._ensureWalletKnown(connectId, deviceId, "evm", signal);
1236
+ _KeystoneAdapter._throwIfAborted(signal);
1237
+ const requestId = (0, import_uuid2.v4)();
1238
+ const requestUr = this.urEngine.buildEthSignRequest({
1239
+ requestId,
1240
+ unsignedTxHex: signDataHex,
1241
+ dataType: "typedData",
1242
+ path,
1243
+ xfp: record.masterFingerprint,
1244
+ chainId: params.chainId
1245
+ });
1246
+ const responseUr = await this._resolveUr(
1247
+ record,
1248
+ requestUr,
1249
+ true,
1250
+ connectId,
1251
+ signal,
1252
+ "evmSignTypedData"
1253
+ );
1254
+ _KeystoneAdapter._throwIfAborted(signal);
1255
+ const sig = this.urEngine.parseEthSignature(responseUr);
1256
+ _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1257
+ return (0, import_hwk_adapter_core3.success)({ signature: (0, import_hwk_adapter_core3.ensure0x)(sig.r + sig.s + sig.v) });
1258
+ });
959
1259
  } catch (err) {
960
1260
  return this._errorToFailure(err);
961
1261
  }
@@ -966,17 +1266,19 @@ var KeystoneAdapter = class _KeystoneAdapter {
966
1266
  // phase doesn't wire in yet; structured-field tx signing needs host-side
967
1267
  // PSBT construction. Both are real, bounded follow-ups, not silent gaps.
968
1268
  // ---------------------------------------------------------------------------
969
- async btcGetAddress(connectIdArg, deviceIdArg, paramsArg) {
970
- const connectId = connectIdArg ?? void 0;
1269
+ async btcGetAddress(connectIdArg, deviceIdArg, paramsArg, book) {
1270
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1271
+ if (!operationTarget.success) return operationTarget;
1272
+ const connectId = operationTarget.payload.targetId;
971
1273
  const deviceId = deviceIdArg ?? void 0;
972
1274
  const params = paramsArg;
973
- if (!params) return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "btcGetAddress requires params");
1275
+ if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcGetAddress requires params");
974
1276
  if (!params.path)
975
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "btcGetAddress requires params.path");
1277
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcGetAddress requires params.path");
976
1278
  const scriptType = btcScriptTypeFromPath(params.path);
977
1279
  if (!scriptType) {
978
- return (0, import_hwk_adapter_core.failure)(
979
- import_hwk_adapter_core.HardwareErrorCode.InvalidParams,
1280
+ return (0, import_hwk_adapter_core3.failure)(
1281
+ import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
980
1282
  "btcGetAddress requires a path whose purpose is 44'/49'/84'/86' (P2PKH/P2SH-P2WPKH/P2WPKH/P2TR)"
981
1283
  );
982
1284
  }
@@ -986,48 +1288,103 @@ var KeystoneAdapter = class _KeystoneAdapter {
986
1288
  "P2TR (purpose 86') needs an elliptic-curve library for BIP-341 tweaking, not yet wired in \u2014 44'/49'/84' work"
987
1289
  );
988
1290
  }
989
- const { accountPath, relativeDerivePath } = splitAccountPath(params.path);
1291
+ const normalizedPath = normalizePath(params.path);
1292
+ const { accountPath, relativeDerivePath } = splitAccountPath(normalizedPath);
990
1293
  if (!relativeDerivePath) {
991
- return (0, import_hwk_adapter_core.failure)(
992
- import_hwk_adapter_core.HardwareErrorCode.InvalidParams,
1294
+ return (0, import_hwk_adapter_core3.failure)(
1295
+ import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
993
1296
  "btcGetAddress requires a full leaf path, e.g. m/84'/0'/0'/0/0"
994
1297
  );
995
1298
  }
1299
+ if (!isKeystoneSignableBtcAccountPath(accountPath)) {
1300
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
1301
+ }
996
1302
  try {
997
- return await this._jobQueue.enqueue(
998
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
999
- async (signal) => {
1000
- const { account } = await this._ensureAccountSynced(
1001
- connectId,
1002
- deviceId,
1003
- "btc",
1004
- accountPath,
1005
- signal
1006
- );
1007
- _KeystoneAdapter._throwIfAborted(signal);
1008
- if (!account.extendedPublicKey) {
1009
- throw (0, import_hwk_adapter_core.createHwkError)({
1010
- code: import_hwk_adapter_core.HardwareErrorCode.MethodNotSupported,
1011
- message: "Keystone did not return an extended public key for this account path"
1012
- });
1013
- }
1014
- const address = this.urEngine.deriveBtcAddressFromXpub(
1015
- account.extendedPublicKey,
1016
- relativeDerivePath,
1017
- scriptType
1018
- );
1019
- return (0, import_hwk_adapter_core.success)({ address, path: normalizePath(params.path) });
1303
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1304
+ const { account } = await this._fetchAccount(
1305
+ connectId,
1306
+ deviceId,
1307
+ "btc",
1308
+ accountPath,
1309
+ signal,
1310
+ { book }
1311
+ );
1312
+ _KeystoneAdapter._throwIfAborted(signal);
1313
+ if (!account.extendedPublicKey) {
1314
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1315
+ code: import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
1316
+ message: "Keystone did not return an extended public key for this account path"
1317
+ });
1020
1318
  }
1021
- );
1319
+ const address = this.urEngine.deriveBtcAddressFromXpub(
1320
+ account.extendedPublicKey,
1321
+ relativeDerivePath,
1322
+ scriptType
1323
+ );
1324
+ return (0, import_hwk_adapter_core3.success)({ address, path: normalizedPath });
1325
+ });
1022
1326
  } catch (err) {
1023
1327
  return this._errorToFailure(err);
1024
1328
  }
1025
1329
  }
1026
- async btcGetPublicKey(_connectId, _deviceId, _params) {
1027
- return this._unsupported(
1028
- "btcGetPublicKey",
1029
- "not yet wired \u2014 the underlying synced xpub/chainCode data already exists in the device table"
1030
- );
1330
+ /**
1331
+ * Returns the account-level extended public key. `params.path` must be an
1332
+ * ACCOUNT path (`m/84'/0'/0'`), not a leaf — that is the level Keystone
1333
+ * actually syncs, and it is what a host needs to derive a whole account's
1334
+ * addresses offline. Unlike `btcGetAddress` this has no script-type
1335
+ * restriction: an xpub is script-type agnostic, so `86'` (taproot) works
1336
+ * here even though deriving a taproot ADDRESS still needs an EC library
1337
+ * this package doesn't wire in.
1338
+ */
1339
+ async btcGetPublicKey(connectIdArg, deviceIdArg, paramsArg, book) {
1340
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1341
+ if (!operationTarget.success) return operationTarget;
1342
+ const connectId = operationTarget.payload.targetId;
1343
+ const deviceId = deviceIdArg ?? void 0;
1344
+ const params = paramsArg;
1345
+ if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcGetPublicKey requires params");
1346
+ if (!params.path)
1347
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcGetPublicKey requires params.path");
1348
+ const { accountPath, relativeDerivePath } = splitAccountPath(params.path);
1349
+ if (relativeDerivePath) {
1350
+ return (0, import_hwk_adapter_core3.failure)(
1351
+ import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
1352
+ "btcGetPublicKey requires an account path, e.g. m/84'/0'/0' (not a leaf path)"
1353
+ );
1354
+ }
1355
+ if (!isKeystoneSignableBtcAccountPath(accountPath)) {
1356
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
1357
+ }
1358
+ try {
1359
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1360
+ const { account } = await this._fetchAccount(
1361
+ connectId,
1362
+ deviceId,
1363
+ "btc",
1364
+ accountPath,
1365
+ signal,
1366
+ { book }
1367
+ );
1368
+ _KeystoneAdapter._throwIfAborted(signal);
1369
+ if (!account.extendedPublicKey) {
1370
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1371
+ code: import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
1372
+ message: "Keystone did not return an extended public key for this account path"
1373
+ });
1374
+ }
1375
+ const meta = this.urEngine.parseXpubMeta(account.extendedPublicKey);
1376
+ return (0, import_hwk_adapter_core3.success)({
1377
+ xpub: account.extendedPublicKey,
1378
+ publicKey: meta.publicKey,
1379
+ chainCode: meta.chainCode,
1380
+ depth: meta.depth,
1381
+ fingerprint: meta.parentFingerprint,
1382
+ path: normalizePath(accountPath)
1383
+ });
1384
+ });
1385
+ } catch (err) {
1386
+ return this._errorToFailure(err);
1387
+ }
1031
1388
  }
1032
1389
  async btcSignTransaction(_connectId, _deviceId, _params) {
1033
1390
  return this._unsupported(
@@ -1036,76 +1393,93 @@ var KeystoneAdapter = class _KeystoneAdapter {
1036
1393
  );
1037
1394
  }
1038
1395
  async btcSignPsbt(connectIdArg, deviceIdArg, paramsArg) {
1039
- const connectId = connectIdArg ?? void 0;
1396
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1397
+ if (!operationTarget.success) return operationTarget;
1398
+ const connectId = operationTarget.payload.targetId;
1040
1399
  const deviceId = deviceIdArg ?? void 0;
1041
1400
  const params = paramsArg;
1042
- if (!params) return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "btcSignPsbt requires params");
1401
+ if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcSignPsbt requires params");
1043
1402
  if (!params.psbt)
1044
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "btcSignPsbt requires params.psbt");
1403
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcSignPsbt requires params.psbt");
1404
+ if (params.path && !isKeystoneSignableBtcAccountPath(splitAccountPath(params.path).accountPath)) {
1405
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
1406
+ }
1045
1407
  try {
1046
- return await this._jobQueue.enqueue(
1047
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
1048
- async (signal) => {
1049
- const { record } = await this._ensureMfpKnown(connectId, deviceId, "btc", signal);
1050
- _KeystoneAdapter._throwIfAborted(signal);
1051
- const requestUr = this.urEngine.buildBtcPsbtRequest((0, import_hwk_adapter_core.stripHex)(params.psbt));
1052
- const responseUr = await this._resolveUr(record, requestUr, true);
1053
- _KeystoneAdapter._throwIfAborted(signal);
1054
- const signedPsbt = this.urEngine.parseBtcPsbt(responseUr);
1055
- return (0, import_hwk_adapter_core.success)({ signedPsbt });
1056
- }
1057
- );
1408
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1409
+ const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
1410
+ _KeystoneAdapter._throwIfAborted(signal);
1411
+ const requestUr = this.urEngine.buildBtcPsbtRequest((0, import_hwk_adapter_core3.stripHex)(params.psbt));
1412
+ const responseUr = await this._resolveUr(
1413
+ record,
1414
+ requestUr,
1415
+ true,
1416
+ connectId,
1417
+ signal,
1418
+ "btcSignPsbt"
1419
+ );
1420
+ _KeystoneAdapter._throwIfAborted(signal);
1421
+ const signedPsbt = this.urEngine.parseBtcPsbt(responseUr);
1422
+ return (0, import_hwk_adapter_core3.success)({ signedPsbt });
1423
+ });
1058
1424
  } catch (err) {
1059
1425
  return this._errorToFailure(err);
1060
1426
  }
1061
1427
  }
1062
1428
  async btcSignMessage(connectIdArg, deviceIdArg, paramsArg) {
1063
- const connectId = connectIdArg ?? void 0;
1429
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1430
+ if (!operationTarget.success) return operationTarget;
1431
+ const connectId = operationTarget.payload.targetId;
1064
1432
  const deviceId = deviceIdArg ?? void 0;
1065
1433
  const params = paramsArg;
1066
- if (!params) return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "btcSignMessage requires params");
1434
+ if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "btcSignMessage requires params");
1067
1435
  if (!params.path || params.message === void 0) {
1068
- return (0, import_hwk_adapter_core.failure)(
1069
- import_hwk_adapter_core.HardwareErrorCode.InvalidParams,
1436
+ return (0, import_hwk_adapter_core3.failure)(
1437
+ import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
1070
1438
  "btcSignMessage requires params.path and params.message"
1071
1439
  );
1072
1440
  }
1073
- const messageHex = params.hex ? (0, import_hwk_adapter_core.stripHex)(params.message) : Buffer.from(params.message, "utf8").toString("hex");
1441
+ const messageHex = params.hex ? (0, import_hwk_adapter_core3.stripHex)(params.message) : Buffer.from(params.message, "utf8").toString("hex");
1074
1442
  const path = normalizePath(params.path);
1443
+ if (!isKeystoneSignableBtcAccountPath(splitAccountPath(path).accountPath)) {
1444
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.DevicePathForbidden, KEYSTONE_BTC_ACCOUNT_FORBIDDEN_MESSAGE);
1445
+ }
1075
1446
  try {
1076
- return await this._jobQueue.enqueue(
1077
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
1078
- async (signal) => {
1079
- const { record } = await this._ensureMfpKnown(connectId, deviceId, "btc", signal);
1080
- _KeystoneAdapter._throwIfAborted(signal);
1081
- const requestId = (0, import_uuid2.v4)();
1082
- const requestUr = this.urEngine.buildBtcMessageSignRequest({
1083
- requestId,
1084
- messageHex,
1085
- accounts: [{ path, xfp: record.masterFingerprint }]
1086
- });
1087
- const responseUr = await this._resolveUr(record, requestUr, true);
1088
- _KeystoneAdapter._throwIfAborted(signal);
1089
- const sig = this.urEngine.parseBtcSignature(responseUr);
1090
- _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1091
- return (0, import_hwk_adapter_core.success)({ signature: sig.signature });
1092
- }
1093
- );
1447
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1448
+ const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
1449
+ _KeystoneAdapter._throwIfAborted(signal);
1450
+ const requestId = (0, import_uuid2.v4)();
1451
+ const requestUr = this.urEngine.buildBtcMessageSignRequest({
1452
+ requestId,
1453
+ messageHex,
1454
+ accounts: [{ path, xfp: record.masterFingerprint }]
1455
+ });
1456
+ const responseUr = await this._resolveUr(
1457
+ record,
1458
+ requestUr,
1459
+ true,
1460
+ connectId,
1461
+ signal,
1462
+ "btcSignMessage"
1463
+ );
1464
+ _KeystoneAdapter._throwIfAborted(signal);
1465
+ const sig = this.urEngine.parseBtcSignature(responseUr);
1466
+ _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1467
+ return (0, import_hwk_adapter_core3.success)({ signature: sig.signature });
1468
+ });
1094
1469
  } catch (err) {
1095
1470
  return this._errorToFailure(err);
1096
1471
  }
1097
1472
  }
1098
- async btcGetMasterFingerprint(connectIdArg, deviceIdArg) {
1099
- const connectId = connectIdArg ?? void 0;
1473
+ async btcGetMasterFingerprint(connectIdArg, deviceIdArg, paramsArg) {
1474
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1475
+ if (!operationTarget.success) return operationTarget;
1476
+ const connectId = operationTarget.payload.targetId;
1100
1477
  const deviceId = deviceIdArg ?? void 0;
1101
1478
  try {
1102
- return await this._jobQueue.enqueue(
1103
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
1104
- async (signal) => {
1105
- const { record } = await this._ensureMfpKnown(connectId, deviceId, "btc", signal);
1106
- return (0, import_hwk_adapter_core.success)({ masterFingerprint: record.masterFingerprint });
1107
- }
1108
- );
1479
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1480
+ const { record } = await this._ensureWalletKnown(connectId, deviceId, "btc", signal);
1481
+ return (0, import_hwk_adapter_core3.success)({ masterFingerprint: record.masterFingerprint });
1482
+ });
1109
1483
  } catch (err) {
1110
1484
  return this._errorToFailure(err);
1111
1485
  }
@@ -1113,105 +1487,112 @@ var KeystoneAdapter = class _KeystoneAdapter {
1113
1487
  // ---------------------------------------------------------------------------
1114
1488
  // SOL
1115
1489
  // ---------------------------------------------------------------------------
1116
- async solGetAddress(connectIdArg, deviceIdArg, paramsArg) {
1117
- const connectId = connectIdArg ?? void 0;
1490
+ async solGetAddress(connectIdArg, deviceIdArg, paramsArg, book) {
1491
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1492
+ if (!operationTarget.success) return operationTarget;
1493
+ const connectId = operationTarget.payload.targetId;
1118
1494
  const deviceId = deviceIdArg ?? void 0;
1119
1495
  const params = paramsArg;
1120
- if (!params) return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "solGetAddress requires params");
1496
+ if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "solGetAddress requires params");
1121
1497
  if (!params.path)
1122
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "solGetAddress requires params.path");
1498
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "solGetAddress requires params.path");
1123
1499
  const path = normalizePath(params.path);
1124
1500
  try {
1125
- return await this._jobQueue.enqueue(
1126
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
1127
- async (signal) => {
1128
- const { account } = await this._ensureAccountSynced(
1129
- connectId,
1130
- deviceId,
1131
- "sol",
1132
- path,
1133
- signal
1134
- );
1135
- _KeystoneAdapter._throwIfAborted(signal);
1136
- const address = import_bs58.default.encode(Buffer.from(account.publicKey, "hex"));
1137
- return (0, import_hwk_adapter_core.success)({ address, path });
1138
- }
1139
- );
1501
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1502
+ const { account } = await this._fetchAccount(connectId, deviceId, "sol", path, signal, {
1503
+ book
1504
+ });
1505
+ _KeystoneAdapter._throwIfAborted(signal);
1506
+ const address = import_bs58.default.encode(Buffer.from(account.publicKey, "hex"));
1507
+ return (0, import_hwk_adapter_core3.success)({ address, path });
1508
+ });
1140
1509
  } catch (err) {
1141
1510
  return this._errorToFailure(err);
1142
1511
  }
1143
1512
  }
1144
1513
  async solSignTransaction(connectIdArg, deviceIdArg, paramsArg) {
1145
- const connectId = connectIdArg ?? void 0;
1514
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1515
+ if (!operationTarget.success) return operationTarget;
1516
+ const connectId = operationTarget.payload.targetId;
1146
1517
  const deviceId = deviceIdArg ?? void 0;
1147
1518
  const params = paramsArg;
1148
1519
  if (!params)
1149
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "solSignTransaction requires params");
1520
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "solSignTransaction requires params");
1150
1521
  if (!params.path || !params.serializedTx) {
1151
- return (0, import_hwk_adapter_core.failure)(
1152
- import_hwk_adapter_core.HardwareErrorCode.InvalidParams,
1522
+ return (0, import_hwk_adapter_core3.failure)(
1523
+ import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
1153
1524
  "solSignTransaction requires params.path and params.serializedTx"
1154
1525
  );
1155
1526
  }
1156
1527
  const path = normalizePath(params.path);
1157
1528
  try {
1158
- return await this._jobQueue.enqueue(
1159
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
1160
- async (signal) => {
1161
- const { record } = await this._ensureMfpKnown(connectId, deviceId, "sol", signal);
1162
- _KeystoneAdapter._throwIfAborted(signal);
1163
- const requestId = (0, import_uuid2.v4)();
1164
- const requestUr = this.urEngine.buildSolSignRequest({
1165
- requestId,
1166
- unsignedPayloadHex: (0, import_hwk_adapter_core.stripHex)(params.serializedTx),
1167
- dataType: "transaction",
1168
- path,
1169
- xfp: record.masterFingerprint
1170
- });
1171
- const responseUr = await this._resolveUr(record, requestUr, true);
1172
- _KeystoneAdapter._throwIfAborted(signal);
1173
- const sig = this.urEngine.parseSolSignature(responseUr);
1174
- _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1175
- return (0, import_hwk_adapter_core.success)({ signature: sig.signature });
1176
- }
1177
- );
1529
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1530
+ const { record } = await this._ensureWalletKnown(connectId, deviceId, "sol", signal);
1531
+ _KeystoneAdapter._throwIfAborted(signal);
1532
+ const requestId = (0, import_uuid2.v4)();
1533
+ const requestUr = this.urEngine.buildSolSignRequest({
1534
+ requestId,
1535
+ unsignedPayloadHex: (0, import_hwk_adapter_core3.stripHex)(params.serializedTx),
1536
+ dataType: "transaction",
1537
+ path,
1538
+ xfp: record.masterFingerprint
1539
+ });
1540
+ const responseUr = await this._resolveUr(
1541
+ record,
1542
+ requestUr,
1543
+ true,
1544
+ connectId,
1545
+ signal,
1546
+ "solSignTransaction"
1547
+ );
1548
+ _KeystoneAdapter._throwIfAborted(signal);
1549
+ const sig = this.urEngine.parseSolSignature(responseUr);
1550
+ _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1551
+ return (0, import_hwk_adapter_core3.success)({ signature: sig.signature });
1552
+ });
1178
1553
  } catch (err) {
1179
1554
  return this._errorToFailure(err);
1180
1555
  }
1181
1556
  }
1182
1557
  async solSignMessage(connectIdArg, deviceIdArg, paramsArg) {
1183
- const connectId = connectIdArg ?? void 0;
1558
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1559
+ if (!operationTarget.success) return operationTarget;
1560
+ const connectId = operationTarget.payload.targetId;
1184
1561
  const deviceId = deviceIdArg ?? void 0;
1185
1562
  const params = paramsArg;
1186
- if (!params) return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "solSignMessage requires params");
1563
+ if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "solSignMessage requires params");
1187
1564
  if (!params.path || !params.message) {
1188
- return (0, import_hwk_adapter_core.failure)(
1189
- import_hwk_adapter_core.HardwareErrorCode.InvalidParams,
1565
+ return (0, import_hwk_adapter_core3.failure)(
1566
+ import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
1190
1567
  "solSignMessage requires params.path and params.message"
1191
1568
  );
1192
1569
  }
1193
1570
  const path = normalizePath(params.path);
1194
1571
  try {
1195
- return await this._jobQueue.enqueue(
1196
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
1197
- async (signal) => {
1198
- const { record } = await this._ensureMfpKnown(connectId, deviceId, "sol", signal);
1199
- _KeystoneAdapter._throwIfAborted(signal);
1200
- const requestId = (0, import_uuid2.v4)();
1201
- const requestUr = this.urEngine.buildSolSignRequest({
1202
- requestId,
1203
- unsignedPayloadHex: (0, import_hwk_adapter_core.stripHex)(params.message),
1204
- dataType: "message",
1205
- path,
1206
- xfp: record.masterFingerprint
1207
- });
1208
- const responseUr = await this._resolveUr(record, requestUr, true);
1209
- _KeystoneAdapter._throwIfAborted(signal);
1210
- const sig = this.urEngine.parseSolSignature(responseUr);
1211
- _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1212
- return (0, import_hwk_adapter_core.success)({ signature: sig.signature });
1213
- }
1214
- );
1572
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1573
+ const { record } = await this._ensureWalletKnown(connectId, deviceId, "sol", signal);
1574
+ _KeystoneAdapter._throwIfAborted(signal);
1575
+ const requestId = (0, import_uuid2.v4)();
1576
+ const requestUr = this.urEngine.buildSolSignRequest({
1577
+ requestId,
1578
+ unsignedPayloadHex: (0, import_hwk_adapter_core3.stripHex)(params.message),
1579
+ dataType: "message",
1580
+ path,
1581
+ xfp: record.masterFingerprint
1582
+ });
1583
+ const responseUr = await this._resolveUr(
1584
+ record,
1585
+ requestUr,
1586
+ true,
1587
+ connectId,
1588
+ signal,
1589
+ "solSignMessage"
1590
+ );
1591
+ _KeystoneAdapter._throwIfAborted(signal);
1592
+ const sig = this.urEngine.parseSolSignature(responseUr);
1593
+ _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1594
+ return (0, import_hwk_adapter_core3.success)({ signature: sig.signature });
1595
+ });
1215
1596
  } catch (err) {
1216
1597
  return this._errorToFailure(err);
1217
1598
  }
@@ -1223,111 +1604,341 @@ var KeystoneAdapter = class _KeystoneAdapter {
1223
1604
  // bundled `sdk.tron` module (different, protobuf-based protocol with
1224
1605
  // unverified response semantics).
1225
1606
  // ---------------------------------------------------------------------------
1226
- async tronGetAddress(connectIdArg, deviceIdArg, paramsArg) {
1227
- const connectId = connectIdArg ?? void 0;
1607
+ async tronGetAddress(connectIdArg, deviceIdArg, paramsArg, book) {
1608
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1609
+ if (!operationTarget.success) return operationTarget;
1610
+ const connectId = operationTarget.payload.targetId;
1228
1611
  const deviceId = deviceIdArg ?? void 0;
1229
1612
  const params = paramsArg;
1230
- if (!params) return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "tronGetAddress requires params");
1613
+ if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "tronGetAddress requires params");
1231
1614
  if (!params.path)
1232
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "tronGetAddress requires params.path");
1615
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "tronGetAddress requires params.path");
1233
1616
  const { accountPath, relativeDerivePath } = splitAccountPath(params.path);
1234
1617
  if (!relativeDerivePath) {
1235
- return (0, import_hwk_adapter_core.failure)(
1236
- import_hwk_adapter_core.HardwareErrorCode.InvalidParams,
1618
+ return (0, import_hwk_adapter_core3.failure)(
1619
+ import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
1237
1620
  "tronGetAddress requires a full leaf path, e.g. m/44'/195'/0'/0/0"
1238
1621
  );
1239
1622
  }
1240
1623
  try {
1241
- return await this._jobQueue.enqueue(
1242
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
1243
- async (signal) => {
1244
- const { account } = await this._ensureAccountSynced(
1245
- connectId,
1246
- deviceId,
1247
- "tron",
1248
- accountPath,
1249
- signal
1250
- );
1251
- _KeystoneAdapter._throwIfAborted(signal);
1252
- if (!account.extendedPublicKey) {
1253
- throw (0, import_hwk_adapter_core.createHwkError)({
1254
- code: import_hwk_adapter_core.HardwareErrorCode.MethodNotSupported,
1255
- message: "Keystone did not return an extended public key for this account path"
1256
- });
1257
- }
1258
- const address = this.urEngine.deriveTronAddressFromXpub(
1259
- account.extendedPublicKey,
1260
- relativeDerivePath
1261
- );
1262
- return (0, import_hwk_adapter_core.success)({ address, path: normalizePath(params.path) });
1624
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1625
+ const { account } = await this._fetchAccount(
1626
+ connectId,
1627
+ deviceId,
1628
+ "tron",
1629
+ accountPath,
1630
+ signal,
1631
+ { book }
1632
+ );
1633
+ _KeystoneAdapter._throwIfAborted(signal);
1634
+ if (!account.extendedPublicKey) {
1635
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1636
+ code: import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
1637
+ message: "Keystone did not return an extended public key for this account path"
1638
+ });
1263
1639
  }
1264
- );
1640
+ const address = this.urEngine.deriveTronAddressFromXpub(
1641
+ account.extendedPublicKey,
1642
+ relativeDerivePath
1643
+ );
1644
+ return (0, import_hwk_adapter_core3.success)({ address, path: normalizePath(params.path) });
1645
+ });
1265
1646
  } catch (err) {
1266
1647
  return this._errorToFailure(err);
1267
1648
  }
1268
1649
  }
1269
1650
  async tronSignTransaction(connectIdArg, deviceIdArg, paramsArg) {
1270
- const connectId = connectIdArg ?? void 0;
1651
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1652
+ if (!operationTarget.success) return operationTarget;
1653
+ const connectId = operationTarget.payload.targetId;
1271
1654
  const deviceId = deviceIdArg ?? void 0;
1272
1655
  const params = paramsArg;
1273
1656
  if (!params)
1274
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "tronSignTransaction requires params");
1657
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "tronSignTransaction requires params");
1275
1658
  if (!params.path) {
1276
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.InvalidParams, "tronSignTransaction requires params.path");
1659
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "tronSignTransaction requires params.path");
1277
1660
  }
1278
1661
  if (!params.rawTxHex) {
1279
- return (0, import_hwk_adapter_core.failure)(
1280
- import_hwk_adapter_core.HardwareErrorCode.InvalidParams,
1662
+ return (0, import_hwk_adapter_core3.failure)(
1663
+ import_hwk_adapter_core3.HardwareErrorCode.InvalidParams,
1281
1664
  "Keystone only signs a fully protobuf-serialized TRON transaction (params.rawTxHex) \u2014 the Trezor-style structured contract fields have no equivalent here"
1282
1665
  );
1283
1666
  }
1284
- const rawTxHex = (0, import_hwk_adapter_core.stripHex)(params.rawTxHex);
1667
+ const rawTxHex = (0, import_hwk_adapter_core3.stripHex)(params.rawTxHex);
1285
1668
  const path = normalizePath(params.path);
1286
1669
  try {
1287
- return await this._jobQueue.enqueue(
1288
- deviceId ?? connectId ?? COLD_START_JOB_LABEL,
1289
- async (signal) => {
1290
- const { record } = await this._ensureMfpKnown(connectId, deviceId, "tron", signal);
1291
- _KeystoneAdapter._throwIfAborted(signal);
1292
- const requestId = (0, import_uuid2.v4)();
1293
- const requestUr = this.urEngine.buildTronSignRequest({
1294
- requestId,
1295
- rawTxHex,
1296
- path,
1297
- xfp: record.masterFingerprint
1298
- });
1299
- const responseUr = await this._resolveUr(record, requestUr, true);
1300
- _KeystoneAdapter._throwIfAborted(signal);
1301
- const sig = this.urEngine.parseTronSignature(responseUr);
1302
- _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1303
- return (0, import_hwk_adapter_core.success)({ signature: sig.signature });
1304
- }
1305
- );
1670
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1671
+ const { record } = await this._ensureWalletKnown(connectId, deviceId, "tron", signal);
1672
+ _KeystoneAdapter._throwIfAborted(signal);
1673
+ const requestId = (0, import_uuid2.v4)();
1674
+ const requestUr = this.urEngine.buildTronSignRequest({
1675
+ requestId,
1676
+ rawTxHex,
1677
+ path,
1678
+ xfp: record.masterFingerprint
1679
+ });
1680
+ const responseUr = await this._resolveUr(
1681
+ record,
1682
+ requestUr,
1683
+ true,
1684
+ connectId,
1685
+ signal,
1686
+ "tronSignTransaction"
1687
+ );
1688
+ _KeystoneAdapter._throwIfAborted(signal);
1689
+ const sig = this.urEngine.parseTronSignature(responseUr);
1690
+ _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1691
+ return (0, import_hwk_adapter_core3.success)({ signature: sig.signature });
1692
+ });
1306
1693
  } catch (err) {
1307
1694
  return this._errorToFailure(err);
1308
1695
  }
1309
1696
  }
1310
- async tronSignMessage(_connectId, _deviceId, _params) {
1311
- return this._unsupported(
1312
- "tronSignMessage",
1313
- "Keystone TRON message signing needs signType V1-vs-V2 verified against real hardware first \u2014 not wired in"
1314
- );
1697
+ async tronSignMessage(connectIdArg, deviceIdArg, paramsArg) {
1698
+ const operationTarget = resolveHardwareOperationTarget(connectIdArg, paramsArg?.operationId);
1699
+ if (!operationTarget.success) return operationTarget;
1700
+ const connectId = operationTarget.payload.targetId;
1701
+ const deviceId = deviceIdArg ?? void 0;
1702
+ const params = paramsArg;
1703
+ if (!params) return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "tronSignMessage requires params");
1704
+ if (!params.path) {
1705
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "tronSignMessage requires params.path");
1706
+ }
1707
+ if (!params.messageHex) {
1708
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.InvalidParams, "tronSignMessage requires params.messageHex");
1709
+ }
1710
+ if (params.messageType !== "V2") {
1711
+ return (0, import_hwk_adapter_core3.failure)(
1712
+ import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
1713
+ 'Keystone signs TRON messages as TIP-191 signMessageV2 only (messageType: "V2")'
1714
+ );
1715
+ }
1716
+ const messageHex = (0, import_hwk_adapter_core3.stripHex)(params.messageHex);
1717
+ const path = normalizePath(params.path);
1718
+ try {
1719
+ return await this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1720
+ const { record } = await this._ensureWalletKnown(connectId, deviceId, "tron", signal);
1721
+ _KeystoneAdapter._throwIfAborted(signal);
1722
+ const requestId = (0, import_uuid2.v4)();
1723
+ const requestUr = this.urEngine.buildTronSignRequest({
1724
+ requestId,
1725
+ rawTxHex: messageHex,
1726
+ path,
1727
+ xfp: record.masterFingerprint,
1728
+ signType: 2 /* PersonalMessage */
1729
+ });
1730
+ const responseUr = await this._resolveUr(
1731
+ record,
1732
+ requestUr,
1733
+ true,
1734
+ connectId,
1735
+ signal,
1736
+ "tronSignMessage"
1737
+ );
1738
+ _KeystoneAdapter._throwIfAborted(signal);
1739
+ const sig = this.urEngine.parseTronSignature(responseUr);
1740
+ _KeystoneAdapter._assertRequestIdMatches(requestId, sig.requestId);
1741
+ return (0, import_hwk_adapter_core3.success)({ signature: sig.signature });
1742
+ });
1743
+ } catch (err) {
1744
+ return this._errorToFailure(err);
1745
+ }
1315
1746
  }
1316
1747
  // ---------------------------------------------------------------------------
1317
1748
  // Internals
1318
1749
  // ---------------------------------------------------------------------------
1319
- _mfpFromConnectId(connectId) {
1320
- return connectId?.startsWith(QR_CONNECT_ID_PREFIX) ? connectId.slice(QR_CONNECT_ID_PREFIX.length) : void 0;
1750
+ _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;
1753
+ }
1754
+ _allNetworkSyncSchema(item) {
1755
+ if (!(0, import_hwk_adapter_core3.isAllNetworkMethodName)(String(item.methodName)) || !item.path) return void 0;
1756
+ const method = item.methodName;
1757
+ const hwkChain = (0, import_hwk_adapter_core3.getAllNetworkMethodChain)(method);
1758
+ const path = normalizePath(item.path);
1759
+ if (hwkChain === "btc" && !isKeystoneSignableBtcAccountPath(splitAccountPath(path).accountPath)) {
1760
+ return void 0;
1761
+ }
1762
+ switch (method) {
1763
+ case "evmGetAddress":
1764
+ case "btcGetAddress":
1765
+ case "tronGetAddress":
1766
+ return { hwkChain, path: splitAccountPath(path).accountPath };
1767
+ case "btcGetPublicKey":
1768
+ case "solGetAddress":
1769
+ return { hwkChain, path };
1770
+ default:
1771
+ return void 0;
1772
+ }
1773
+ }
1774
+ /**
1775
+ * QR is an interactive batch transport: one account-creation action must
1776
+ * produce one request QR and one response scan, regardless of how many
1777
+ * chains or derivation paths the host bundled. USB keeps its proven
1778
+ * one-path-at-a-time export flow because the device limits that channel.
1779
+ */
1780
+ async _prefetchAllNetworkAccounts(connectId, deviceId, params) {
1781
+ return this._jobQueue.enqueue(getKeystoneJobId(connectId, deviceId), async (signal) => {
1782
+ const target = this._resolveTarget(connectId, deviceId);
1783
+ const { record: targetRecord } = target;
1784
+ let record = targetRecord;
1785
+ const requestedByKey = /* @__PURE__ */ new Map();
1786
+ for (const item of params.bundle) {
1787
+ const schema = this._allNetworkSyncSchema(item);
1788
+ if (schema) requestedByKey.set(accountKey(schema.hwkChain, schema.path), schema);
1789
+ }
1790
+ const book = /* @__PURE__ */ new Map();
1791
+ if (!requestedByKey.size) {
1792
+ return { book };
1793
+ }
1794
+ if (this._forcedTransport !== "qr" && !record?.usbSessionId && this._usbConnector && target.expectedWalletId) {
1795
+ record = await this._tryUsbAttach(
1796
+ target.expectedWalletId,
1797
+ record?.masterFingerprint ?? target.expectedMasterFingerprint
1798
+ ) ?? record;
1799
+ _KeystoneAdapter._throwIfAborted(signal);
1800
+ }
1801
+ if (this._forcedTransport !== "qr" && record?.usbSessionId) {
1802
+ const bundleSessionId = record.usbSessionId;
1803
+ const assertBundleSession = () => {
1804
+ if (record?.usbSessionId !== bundleSessionId) {
1805
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1806
+ code: import_hwk_adapter_core3.HardwareErrorCode.TransportNotAvailable,
1807
+ message: "Keystone USB connection was lost during account export"
1808
+ });
1809
+ }
1810
+ };
1811
+ const missingSchemas2 = Array.from(requestedByKey.values());
1812
+ for (const schema of missingSchemas2) {
1813
+ if (!book.has(accountKey(schema.hwkChain, schema.path))) {
1814
+ assertBundleSession();
1815
+ const synced = await this._fetchAccount(
1816
+ connectId,
1817
+ deviceId,
1818
+ schema.hwkChain,
1819
+ schema.path,
1820
+ signal,
1821
+ { book }
1822
+ );
1823
+ record = synced.record;
1824
+ assertBundleSession();
1825
+ }
1826
+ }
1827
+ for (const schema of missingSchemas2) {
1828
+ if (!book.has(accountKey(schema.hwkChain, schema.path))) {
1829
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1830
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
1831
+ message: `Keystone did not return the requested derivation path (${schema.path})`
1832
+ });
1833
+ }
1834
+ }
1835
+ return { book, walletId: record.walletId };
1836
+ }
1837
+ if (this._forcedTransport === "usb") {
1838
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1839
+ code: import_hwk_adapter_core3.HardwareErrorCode.TransportNotAvailable,
1840
+ message: "USB channel is not connected for this Keystone wallet"
1841
+ });
1842
+ }
1843
+ const missingSchemas = Array.from(requestedByKey.values());
1844
+ const schemas = [
1845
+ { hwkChain: "evm", path: KEYSTONE_WALLET_ID_PATH },
1846
+ ...missingSchemas.filter((schema) => normalizePath(schema.path) !== KEYSTONE_WALLET_ID_PATH)
1847
+ ];
1848
+ const requestUr = this.urEngine.buildKeyDerivationRequest({
1849
+ schemas: schemas.map((schema) => ({
1850
+ path: schema.path,
1851
+ curve: schema.hwkChain === "sol" ? "ed25519" : "secp256k1"
1852
+ })),
1853
+ origin: this._origin
1854
+ });
1855
+ const responseUr = await this._requestQrDisplayAndAwaitResponse(
1856
+ record ? toDeviceInfo(record) : placeholderDeviceInfo(),
1857
+ { ...requestUr, animated: false }
1858
+ );
1859
+ _KeystoneAdapter._throwIfAborted(signal);
1860
+ const parsed = this.urEngine.parseAccountResponse(responseUr);
1861
+ this._assertParsedIdentity(parsed, target);
1862
+ record = this._upsertDeviceRecord(parsed);
1863
+ const requestedChainByPath = new Map(
1864
+ schemas.map((schema) => [normalizePath(schema.path), schema.hwkChain])
1865
+ );
1866
+ for (const account of parsed.accounts) {
1867
+ const normalizedPath = normalizePath(account.path);
1868
+ const hwkChain = requestedChainByPath.get(normalizedPath) ?? inferHwkChainFromPath(normalizedPath);
1869
+ if (hwkChain) {
1870
+ book.set(accountKey(hwkChain, normalizedPath), {
1871
+ ...account,
1872
+ hwkChain
1873
+ });
1874
+ }
1875
+ }
1876
+ for (const schema of missingSchemas) {
1877
+ if (!book.has(accountKey(schema.hwkChain, schema.path))) {
1878
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1879
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
1880
+ message: `Keystone did not return the requested derivation path (${schema.path})`
1881
+ });
1882
+ }
1883
+ }
1884
+ return { book, walletId: record.walletId };
1885
+ });
1321
1886
  }
1322
1887
  /**
1323
- * Handles both connectId shapes a caller might hand back: the QR-style
1324
- * `keystone-qr:<mfp>` prefix, and a bare mfp — which is exactly what a
1325
- * USB session's `sessionId`/`connectId` is (see `KeystoneUsbConnectorBase`
1326
- * and `_connectUsb`).
1888
+ * Resolve only the stable 64-hex wallet identity. The 8-hex BIP32 master
1889
+ * fingerprint is protocol metadata and is never accepted as a lookup key.
1327
1890
  */
1328
- _findByConnectId(connectId) {
1329
- const mfp = this._mfpFromConnectId(connectId) ?? connectId?.toLowerCase();
1330
- return mfp ? this._devices.get(mfp) : void 0;
1891
+ _resolveTarget(connectId, deviceId) {
1892
+ let resolvedConnectId = connectId;
1893
+ if ((0, import_hwk_adapter_core3.isHardwareOperationId)(connectId)) {
1894
+ resolvedConnectId = this._operations.resolve(connectId).connectId;
1895
+ }
1896
+ const identifiers = [deviceId, resolvedConnectId].filter(
1897
+ (identifier) => Boolean(identifier)
1898
+ );
1899
+ if (!identifiers.length) return {};
1900
+ const walletIds = identifiers.map((identifier) => this._walletIdFromIdentifier(identifier)).filter((walletId) => Boolean(walletId));
1901
+ if (!walletIds.length) {
1902
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1903
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceNotFound,
1904
+ message: "Keystone device lookup requires a 64-hex wallet identity"
1905
+ });
1906
+ }
1907
+ const expectedWalletId = walletIds[0];
1908
+ if (walletIds.some((walletId) => walletId !== expectedWalletId)) {
1909
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1910
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
1911
+ message: "Keystone connectId and deviceId identify different wallets"
1912
+ });
1913
+ }
1914
+ if (identifiers.some((identifier) => !this._walletIdFromIdentifier(identifier))) {
1915
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1916
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
1917
+ message: "Keystone connectId and deviceId are inconsistent"
1918
+ });
1919
+ }
1920
+ const record = this._devices.get(expectedWalletId);
1921
+ return record ? {
1922
+ record,
1923
+ expectedWalletId,
1924
+ expectedMasterFingerprint: record.masterFingerprint
1925
+ } : { expectedWalletId };
1926
+ }
1927
+ _assertParsedIdentity(parsed, expected) {
1928
+ const walletId = deriveKeystoneWalletId(parsed.accounts);
1929
+ if (expected.expectedMasterFingerprint && parsed.masterFingerprint !== expected.expectedMasterFingerprint) {
1930
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1931
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
1932
+ message: `Connected Keystone wallet (mfp ${parsed.masterFingerprint}) does not match the requested wallet fingerprint (${expected.expectedMasterFingerprint})`
1933
+ });
1934
+ }
1935
+ if (expected.expectedWalletId && walletId !== expected.expectedWalletId) {
1936
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1937
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
1938
+ message: "Connected Keystone wallet does not match the requested wallet identity"
1939
+ });
1940
+ }
1941
+ return walletId;
1331
1942
  }
1332
1943
  /**
1333
1944
  * Folds a parsed account-response UR into the device table. `viaUsb`
@@ -1336,169 +1947,450 @@ var KeystoneAdapter = class _KeystoneAdapter {
1336
1947
  * already has a live session, so this must NOT unconditionally mark
1337
1948
  * `qrSynced`, or a USB-only wallet would wrongly survive a later USB
1338
1949
  * disconnect as a "QR-synced, demote to QR-only" entry instead of being
1339
- * dropped outright (see `disconnectDevice`).
1950
+ * dropped outright (see `releaseOperation`).
1340
1951
  */
1341
1952
  _upsertDeviceRecord(parsed, options) {
1342
- const mfp = parsed.masterFingerprint;
1343
- let record = this._devices.get(mfp);
1953
+ const walletId = deriveKeystoneWalletId(parsed.accounts);
1954
+ let record = this._devices.get(walletId);
1344
1955
  const isNew = !record;
1345
1956
  if (!record) {
1346
- record = createDeviceRecord(mfp);
1347
- this._devices.set(mfp, record);
1957
+ record = createDeviceRecord(walletId, parsed.masterFingerprint);
1958
+ this._devices.set(walletId, record);
1959
+ } else if (record.masterFingerprint !== parsed.masterFingerprint) {
1960
+ throw (0, import_hwk_adapter_core3.createHwkError)({
1961
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
1962
+ message: "Keystone wallet identity returned an inconsistent master fingerprint"
1963
+ });
1348
1964
  }
1349
1965
  record.model = parsed.device ?? record.model;
1350
1966
  record.deviceVersion = parsed.deviceVersion ?? record.deviceVersion;
1967
+ record.hardwareDeviceId = parsed.deviceId ?? record.hardwareDeviceId;
1968
+ record.usbSessionId = options?.usbSessionId ?? record.usbSessionId;
1351
1969
  if (!options?.viaUsb) record.qrSynced = true;
1352
1970
  const info = toDeviceInfo(record);
1353
- const eventType = isNew ? import_hwk_adapter_core.DEVICE.CONNECT : import_hwk_adapter_core.DEVICE.CHANGED;
1971
+ const eventType = isNew ? import_hwk_adapter_core3.DEVICE.CONNECT : import_hwk_adapter_core3.DEVICE.CHANGED;
1354
1972
  this.emitter.emit(eventType, { type: eventType, payload: info });
1355
1973
  return record;
1356
1974
  }
1357
1975
  /**
1358
1976
  * Opens+claims whatever Keystone the USB connector currently has
1359
- * permission for, learns its mfp via `getAppConfig`, and merges it into
1360
- * the device table by that mfp — a QR-synced entry becomes
1977
+ * permission for, learns its protocol mfp via `getAppConfig`, then requests
1978
+ * one fixed account-level xpub and derives the stable wallet id. A
1979
+ * QR-synced entry becomes
1361
1980
  * `{qr, usb}`-capable in place (one `device-changed`, not a second
1362
1981
  * `device-connect`); a wallet never seen before becomes a new USB-only
1363
1982
  * entry. See §4.2 of the design doc.
1364
1983
  */
1365
- async _connectUsb() {
1984
+ async _connectUsb(expected, searchTargetId) {
1985
+ if (this._pendingUsbTeardowns > 0) {
1986
+ return (0, import_hwk_adapter_core3.failure)(
1987
+ import_hwk_adapter_core3.HardwareErrorCode.DeviceBusyInternal,
1988
+ "Keystone USB session is being released"
1989
+ );
1990
+ }
1991
+ const previous = this._usbConnectTail;
1992
+ let release;
1993
+ this._usbConnectTail = new Promise((resolve) => {
1994
+ release = resolve;
1995
+ });
1996
+ await previous;
1997
+ try {
1998
+ return await this._connectUsbExclusive(expected, searchTargetId);
1999
+ } finally {
2000
+ release?.();
2001
+ }
2002
+ }
2003
+ async _connectUsbExclusive(expected, searchTargetId) {
1366
2004
  if (!this._usbConnector) {
1367
- return (0, import_hwk_adapter_core.failure)(
1368
- import_hwk_adapter_core.HardwareErrorCode.TransportNotAvailable,
2005
+ return (0, import_hwk_adapter_core3.failure)(
2006
+ import_hwk_adapter_core3.HardwareErrorCode.TransportNotAvailable,
1369
2007
  "No USB connector configured for this Keystone adapter"
1370
2008
  );
1371
2009
  }
2010
+ let sessionId;
1372
2011
  try {
1373
- const session = await this._usbConnector.connect();
1374
- const mfp = session.deviceInfo.deviceId.toLowerCase();
1375
- let record = this._devices.get(mfp);
1376
- const isNew = !record;
1377
- if (!record) {
1378
- record = createDeviceRecord(mfp);
1379
- this._devices.set(mfp, record);
2012
+ let connectTarget = { type: "default" };
2013
+ if (searchTargetId) {
2014
+ connectTarget = { type: "search-target", searchTargetId };
2015
+ } else if (expected.expectedMasterFingerprint) {
2016
+ connectTarget = {
2017
+ type: "expected-device-identity",
2018
+ deviceIdentity: expected.expectedMasterFingerprint
2019
+ };
2020
+ }
2021
+ const connector = this._usbConnector;
2022
+ const session = await (connector.connectTarget ? connector.connectTarget(connectTarget) : connector.connect(searchTargetId ?? expected.expectedMasterFingerprint));
2023
+ sessionId = session.sessionId;
2024
+ const raw = session.deviceInfo.raw;
2025
+ const mfpValue = (0, import_hwk_adapter_core3.parseBip32MasterFingerprint)(raw?.masterFingerprint);
2026
+ if (!mfpValue) {
2027
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2028
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
2029
+ message: "Keystone USB did not report a master fingerprint"
2030
+ });
2031
+ }
2032
+ const masterFingerprint = mfpValue.toLowerCase();
2033
+ if (expected.expectedMasterFingerprint && masterFingerprint !== expected.expectedMasterFingerprint) {
2034
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2035
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
2036
+ message: `Connected Keystone wallet (mfp ${masterFingerprint}) does not match the requested wallet fingerprint (${expected.expectedMasterFingerprint})`
2037
+ });
2038
+ }
2039
+ const identityRequest = this.urEngine.buildKeyDerivationRequest({
2040
+ schemas: [{ path: KEYSTONE_WALLET_ID_PATH, curve: "secp256k1" }],
2041
+ origin: this._origin
2042
+ });
2043
+ const identityResult = await this._callUsbConnector(
2044
+ session.sessionId,
2045
+ "resolveUr",
2046
+ identityRequest
2047
+ );
2048
+ if (!identityResult.success) {
2049
+ throw (0, import_hwk_adapter_core3.rehydrateConnectorError)(identityResult.error);
2050
+ }
2051
+ const parsed = this.urEngine.parseAccountResponse(identityResult.payload);
2052
+ this._assertParsedIdentity(parsed, {
2053
+ ...expected,
2054
+ expectedMasterFingerprint: expected.expectedMasterFingerprint ?? masterFingerprint
2055
+ });
2056
+ const walletId = deriveKeystoneWalletId(parsed.accounts);
2057
+ const previousUsbSessionId = this._devices.get(walletId)?.usbSessionId;
2058
+ const record = this._upsertDeviceRecord(parsed, {
2059
+ viaUsb: true,
2060
+ usbSessionId: session.sessionId
2061
+ });
2062
+ if (previousUsbSessionId && previousUsbSessionId !== session.sessionId) {
2063
+ try {
2064
+ await this._usbConnector.disconnect(previousUsbSessionId);
2065
+ } catch {
2066
+ }
1380
2067
  }
2068
+ record.hadUsbSession = true;
1381
2069
  record.model = session.deviceInfo.modelName ?? session.deviceInfo.model ?? record.model;
1382
2070
  record.deviceVersion = session.deviceInfo.firmwareVersion ?? record.deviceVersion;
1383
- record.usbSessionId = session.sessionId;
1384
2071
  const info = toDeviceInfo(record);
1385
- const eventType = isNew ? import_hwk_adapter_core.DEVICE.CONNECT : import_hwk_adapter_core.DEVICE.CHANGED;
1386
- this.emitter.emit(eventType, { type: eventType, payload: info });
1387
- return (0, import_hwk_adapter_core.success)(info);
2072
+ return (0, import_hwk_adapter_core3.success)(info);
1388
2073
  } catch (err) {
2074
+ if (sessionId) {
2075
+ try {
2076
+ await this._usbConnector.disconnect(sessionId);
2077
+ } catch {
2078
+ }
2079
+ }
1389
2080
  return this._errorToFailure(err);
1390
2081
  }
1391
2082
  }
2083
+ /**
2084
+ * Probe already-authorized USB devices without opening a permission picker,
2085
+ * then attach and verify the requested wallet when one is present. This is
2086
+ * used both after an adapter restart and after a QR-only period, so plugging
2087
+ * USB back in affects the next business call without changing an in-flight
2088
+ * QR operation.
2089
+ */
2090
+ async _tryUsbAttach(expectedWalletId, expectedMasterFingerprint, options) {
2091
+ if (!expectedWalletId || this._forcedTransport === "qr" || !this._usbConnector) {
2092
+ return void 0;
2093
+ }
2094
+ const maxAttempts = options?.waitForReenumeration ? KEYSTONE_USB_REATTACH_PROBE_ATTEMPTS : 1;
2095
+ let availableDevices = [];
2096
+ let lastSearchError;
2097
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
2098
+ try {
2099
+ availableDevices = await this._usbConnector.searchDevices({ purpose: "availability" });
2100
+ lastSearchError = void 0;
2101
+ } catch (error) {
2102
+ lastSearchError = error;
2103
+ }
2104
+ if (availableDevices.length) {
2105
+ break;
2106
+ }
2107
+ if (attempt < maxAttempts) {
2108
+ await waitForKeystoneUsbReattachProbe();
2109
+ }
2110
+ }
2111
+ if (!availableDevices.length) {
2112
+ if (this._forcedTransport === "usb" && lastSearchError) {
2113
+ throw lastSearchError instanceof Error ? lastSearchError : new Error(String(lastSearchError));
2114
+ }
2115
+ return void 0;
2116
+ }
2117
+ let attached;
2118
+ let record;
2119
+ const candidates = expectedMasterFingerprint ? [void 0] : availableDevices;
2120
+ for (const device of candidates) {
2121
+ attached = await this._connectUsb(
2122
+ { expectedWalletId, expectedMasterFingerprint },
2123
+ device?.connectId
2124
+ );
2125
+ if (attached.success) {
2126
+ record = this._devices.get(expectedWalletId);
2127
+ if (record) break;
2128
+ }
2129
+ }
2130
+ return record;
2131
+ }
1392
2132
  /**
1393
2133
  * The one place that decides QR vs. USB for a UR round trip and carries it
1394
2134
  * out. `record` is the (possibly not-yet-existing, for a true cold start)
1395
- * device row for the target wallet — USB is only used when `record`
1396
- * already has a live `usbSessionId` (a session comes from an explicit
1397
- * `connectDevice()`, never conjured mid-call — see the class doc). A
2135
+ * device row for the target wallet. USB is the preferred channel: a known
2136
+ * wallet record with no live session gets ONE best-effort re-attach here,
2137
+ * and anything that goes wrong just leaves the call on QR. A
1398
2138
  * `switchTransport('qr')` pin forces QR even for a USB-attached wallet;
1399
2139
  * `switchTransport('usb')` on a wallet with no live USB session fails
1400
2140
  * closed rather than silently falling back to QR.
1401
2141
  */
1402
- async _resolveUr(record, requestUr, animated) {
1403
- const wantUsb = this._forcedTransport === "usb" || this._forcedTransport !== "qr" && Boolean(record?.usbSessionId);
2142
+ async _resolveUr(record, requestUr, animated, operationId, signal, operationName) {
2143
+ const releaseOperationRetention = (0, import_hwk_adapter_core3.isHardwareOperationId)(operationId) ? this._operations.retain(operationId) : void 0;
2144
+ try {
2145
+ return await this._resolveUrWithRoute(
2146
+ record,
2147
+ requestUr,
2148
+ animated,
2149
+ operationId,
2150
+ signal,
2151
+ operationName
2152
+ );
2153
+ } finally {
2154
+ releaseOperationRetention?.();
2155
+ }
2156
+ }
2157
+ async _resolveUrWithRoute(record, requestUr, animated, operationId, signal, operationName) {
2158
+ let interactionRoute;
2159
+ if ((0, import_hwk_adapter_core3.isHardwareOperationId)(operationId)) {
2160
+ const operation = this._operations.resolve(operationId);
2161
+ const route = this._operationRoutes.get(operation.connectId);
2162
+ if (!route || route.operationId !== operationId) {
2163
+ this._operations.end(operationId, "disconnect");
2164
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2165
+ code: import_hwk_adapter_core3.HardwareErrorCode.OperationEnded,
2166
+ message: "Keystone hardware operation is no longer connected",
2167
+ params: { operationId, reason: "disconnect" }
2168
+ });
2169
+ }
2170
+ interactionRoute = route;
2171
+ }
2172
+ if (!interactionRoute && !this._forcedTransport && record && !record.usbSessionId && this._usbConnector) {
2173
+ await this._tryUsbAttach(record.walletId, record.masterFingerprint, {
2174
+ waitForReenumeration: record.hadUsbSession
2175
+ });
2176
+ }
2177
+ const wantUsb = interactionRoute ? interactionRoute.connectionType === "usb" : this._forcedTransport === "usb" || this._forcedTransport !== "qr" && Boolean(record?.usbSessionId);
1404
2178
  if (wantUsb) {
1405
2179
  if (!record?.usbSessionId || !this._usbConnector) {
1406
- throw (0, import_hwk_adapter_core.createHwkError)({
1407
- code: import_hwk_adapter_core.HardwareErrorCode.TransportNotAvailable,
2180
+ if (interactionRoute) {
2181
+ this._operations.end(interactionRoute.operationId, "disconnect");
2182
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2183
+ code: import_hwk_adapter_core3.HardwareErrorCode.OperationEnded,
2184
+ message: "Keystone operation USB connection was lost",
2185
+ params: {
2186
+ operationId: interactionRoute.operationId,
2187
+ reason: "disconnect"
2188
+ }
2189
+ });
2190
+ }
2191
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2192
+ code: import_hwk_adapter_core3.HardwareErrorCode.TransportNotAvailable,
1408
2193
  message: "USB channel is not connected for this Keystone wallet"
1409
2194
  });
1410
2195
  }
1411
- const result = await this._usbConnector.call(record.usbSessionId, "resolveUr", requestUr);
1412
- if (!result.success) throw (0, import_hwk_adapter_core.rehydrateConnectorError)(result.error);
1413
- return result.payload;
2196
+ const result = await this._callUsbConnector(
2197
+ record.usbSessionId,
2198
+ "resolveUr",
2199
+ requestUr,
2200
+ signal
2201
+ );
2202
+ if (result.success) return result.payload;
2203
+ const usbError = (0, import_hwk_adapter_core3.rehydrateConnectorError)(result.error);
2204
+ const usbErrorOrigin = usbError.origin;
2205
+ const usbErrorCode = usbError.code;
2206
+ if (!interactionRoute && !this._forcedTransport && usbErrorCode === import_hwk_adapter_core3.HardwareErrorCode.PayloadTooLarge && usbErrorOrigin !== "device") {
2207
+ const displayDevice2 = toDeviceInfo(record);
2208
+ return this._requestQrDisplayAndAwaitResponse(displayDevice2, {
2209
+ ...requestUr,
2210
+ animated
2211
+ });
2212
+ }
2213
+ const deviceAnswered = usbErrorOrigin !== void 0 ? usbErrorOrigin === "device" : usbErrorCode === import_hwk_adapter_core3.HardwareErrorCode.UserRejected || usbErrorCode === import_hwk_adapter_core3.HardwareErrorCode.UserAborted || usbErrorCode === import_hwk_adapter_core3.HardwareErrorCode.DeviceLocked || usbErrorCode === import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch;
2214
+ if (!deviceAnswered) {
2215
+ const lostSessionId = record.usbSessionId;
2216
+ if (lostSessionId) {
2217
+ this._usbConnector?.disconnect(lostSessionId).catch(() => void 0);
2218
+ }
2219
+ record.usbSessionId = void 0;
2220
+ if (interactionRoute) {
2221
+ this._operations.end(interactionRoute.operationId, "disconnect");
2222
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2223
+ code: import_hwk_adapter_core3.HardwareErrorCode.OperationEnded,
2224
+ message: operationName ? `Keystone ${operationName} may have completed before the USB connection was lost` : "Keystone operation USB connection was lost",
2225
+ recovery: operationName ? { scope: "unknown" } : void 0,
2226
+ params: operationName ? (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(operationName, {
2227
+ operationId: interactionRoute.operationId,
2228
+ reason: "disconnect"
2229
+ }) : {
2230
+ operationId: interactionRoute.operationId,
2231
+ reason: "disconnect"
2232
+ }
2233
+ });
2234
+ }
2235
+ if (operationName) {
2236
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2237
+ code: usbErrorCode ?? import_hwk_adapter_core3.HardwareErrorCode.TransportError,
2238
+ message: `Keystone ${operationName} may have completed before the USB connection was lost`,
2239
+ origin: "transport",
2240
+ recovery: { scope: "unknown" },
2241
+ params: (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(operationName)
2242
+ });
2243
+ }
2244
+ }
2245
+ throw usbError;
1414
2246
  }
1415
2247
  const displayDevice = record ? toDeviceInfo(record) : placeholderDeviceInfo();
1416
- return this._requestQrDisplayAndAwaitResponse(displayDevice, { ...requestUr, animated });
2248
+ try {
2249
+ return await this._requestQrDisplayAndAwaitResponse(displayDevice, {
2250
+ ...requestUr,
2251
+ animated
2252
+ });
2253
+ } catch (error) {
2254
+ const { code, _tag: tag } = error;
2255
+ if (operationName && (code === import_hwk_adapter_core3.HardwareErrorCode.OperationTimeout || tag === import_hwk_adapter_core3.UI_REQUEST_TIMEOUT_TAG)) {
2256
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2257
+ code: import_hwk_adapter_core3.HardwareErrorCode.OperationTimeout,
2258
+ message: `Keystone ${operationName} may have completed before the QR response timed out`,
2259
+ recovery: { scope: "unknown" },
2260
+ params: (0, import_hwk_adapter_core3.operationMayHaveCompletedParams)(operationName)
2261
+ });
2262
+ }
2263
+ throw error;
2264
+ }
1417
2265
  }
1418
2266
  /**
1419
- * Resolve (syncing over QR if needed) the account cached for `hwkChain` at
1420
- * `syncPath`. Drives the "implicit account sync, then the real request" two
1421
- * hop flow the first time a wallet/path pair is seen; a cache hit skips
1422
- * straight to the caller's own round trip.
2267
+ * Fetch one account's key material from the device for this operation only.
2268
+ * `book` lets earlier fetches of the same operation be reused; nothing is
2269
+ * retained on the record. A failed USB export ends this operation without
2270
+ * replaying the request or switching transports.
1423
2271
  */
1424
- async _ensureAccountSynced(connectId, deviceId, hwkChain, syncPath, signal) {
1425
- const wantMfp = deviceId ? deviceId.toLowerCase() : this._mfpFromConnectId(connectId ?? "");
2272
+ async _fetchAccount(connectId, deviceId, hwkChain, syncPath, signal, options) {
2273
+ const target = this._resolveTarget(connectId, deviceId);
1426
2274
  const key = accountKey(hwkChain, syncPath);
1427
- const existingRecord = wantMfp ? this._devices.get(wantMfp) : void 0;
1428
- const cached = existingRecord?.accounts.get(key);
1429
- if (existingRecord && cached) {
1430
- return { record: existingRecord, account: cached };
2275
+ let existingRecord = target.record;
2276
+ if (!existingRecord) {
2277
+ const attached = await this._tryUsbAttach(target.expectedWalletId);
2278
+ _KeystoneAdapter._throwIfAborted(signal);
2279
+ existingRecord = attached;
2280
+ }
2281
+ const book = options?.book;
2282
+ const booked = book?.get(key);
2283
+ if (existingRecord && booked) {
2284
+ return { record: existingRecord, account: booked };
2285
+ }
2286
+ if (existingRecord && !existingRecord.usbSessionId && this._forcedTransport === "usb") {
2287
+ await this._tryUsbAttach(existingRecord.walletId, existingRecord.masterFingerprint, {
2288
+ waitForReenumeration: existingRecord.hadUsbSession
2289
+ });
2290
+ _KeystoneAdapter._throwIfAborted(signal);
2291
+ existingRecord = this._devices.get(existingRecord.walletId) ?? existingRecord;
1431
2292
  }
2293
+ const useSinglePathUsbExport = this._forcedTransport !== "qr" && Boolean(existingRecord?.usbSessionId);
2294
+ const requestedSchemaPaths = [
2295
+ { hwkChain, path: syncPath }
2296
+ ];
2297
+ const schemaPaths = useSinglePathUsbExport || requestedSchemaPaths.some((schema) => normalizePath(schema.path) === KEYSTONE_WALLET_ID_PATH) ? requestedSchemaPaths : [{ hwkChain: "evm", path: KEYSTONE_WALLET_ID_PATH }, ...requestedSchemaPaths];
1432
2298
  const requestUr = this.urEngine.buildKeyDerivationRequest({
1433
- schemas: [{ path: syncPath, curve: hwkChain === "sol" ? "ed25519" : "secp256k1" }],
2299
+ schemas: schemaPaths.map((s) => ({
2300
+ path: s.path,
2301
+ curve: s.hwkChain === "sol" ? "ed25519" : "secp256k1"
2302
+ })),
1434
2303
  origin: this._origin
1435
2304
  });
1436
- const responseUr = await this._resolveUr(existingRecord, requestUr, false);
2305
+ const resolvedViaUsb = useSinglePathUsbExport;
2306
+ const responseUr = await this._resolveUr(existingRecord, requestUr, false, connectId, signal);
1437
2307
  _KeystoneAdapter._throwIfAborted(signal);
1438
2308
  const parsed = this.urEngine.parseAccountResponse(responseUr);
1439
- if (wantMfp && parsed.masterFingerprint !== wantMfp) {
1440
- throw (0, import_hwk_adapter_core.createHwkError)({
1441
- code: import_hwk_adapter_core.HardwareErrorCode.DeviceMismatch,
1442
- message: `Scanned Keystone wallet (mfp ${parsed.masterFingerprint}) does not match the requested device (${wantMfp})`
2309
+ let record;
2310
+ if (resolvedViaUsb && existingRecord) {
2311
+ if (parsed.masterFingerprint !== existingRecord.masterFingerprint) {
2312
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2313
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
2314
+ message: "Keystone USB account export returned a different master fingerprint"
2315
+ });
2316
+ }
2317
+ record = existingRecord;
2318
+ } else {
2319
+ this._assertParsedIdentity(parsed, target);
2320
+ record = this._upsertDeviceRecord(parsed, {
2321
+ viaUsb: Boolean(existingRecord?.usbSessionId)
1443
2322
  });
1444
2323
  }
1445
- const record = this._upsertDeviceRecord(parsed, {
1446
- viaUsb: Boolean(existingRecord?.usbSessionId)
1447
- });
1448
2324
  const account = parsed.accounts.find((a) => normalizePath(a.path) === syncPath);
1449
2325
  if (!account) {
1450
- throw (0, import_hwk_adapter_core.createHwkError)({
1451
- code: import_hwk_adapter_core.HardwareErrorCode.DeviceMismatch,
2326
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2327
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
1452
2328
  message: `Keystone did not return the requested derivation path (${syncPath})`
1453
2329
  });
1454
2330
  }
1455
2331
  const entry = { ...account, hwkChain };
1456
- record.accounts.set(key, entry);
2332
+ if (book) {
2333
+ for (const returned of parsed.accounts) {
2334
+ const returnedChain = inferHwkChainFromPath(returned.path);
2335
+ if (returnedChain) {
2336
+ book.set(accountKey(returnedChain, returned.path), {
2337
+ ...returned,
2338
+ hwkChain: returnedChain
2339
+ });
2340
+ }
2341
+ }
2342
+ book.set(key, entry);
2343
+ }
1457
2344
  return { record, account: entry };
1458
2345
  }
1459
2346
  /**
1460
- * Like `_ensureAccountSynced`, but for operations (PSBT signing, master
2347
+ * Like `_fetchAccount`, but for operations (PSBT signing, master
1461
2348
  * fingerprint) that only need to know WHICH wallet is attached, not a
1462
2349
  * specific cached path. Syncs the account-level path for `chain` as a
1463
- * throwaway probe when the mfp isn't already known.
2350
+ * throwaway probe when the wallet record isn't already known.
1464
2351
  *
1465
2352
  * `CHAIN_FINGERPRINT_PATHS[chain]` is a 5-segment LEAF path for `evm`
1466
2353
  * (`m/44'/60'/0'/0/0`) — sending that verbatim as a KeyDerivation request
1467
2354
  * asks Keystone for a non-standard path. Keystone's own docs
1468
2355
  * (dev.keyst.one's multichain KeyDerivation example) show the ETH
1469
2356
  * account-level path as `m/44'/60'/0'` (3 segments), same as what
1470
- * `DEFAULT_IMPORT_SCHEMAS`/`_ensureAccountSynced` already request — so
2357
+ * `_fetchAccount` already requests — so
1471
2358
  * truncate through `splitAccountPath` here too instead of using the raw
1472
2359
  * fingerprint leaf path. `btc`/`sol` are already 3-segment account paths
1473
2360
  * and pass through unchanged.
1474
2361
  */
1475
- async _ensureMfpKnown(connectId, deviceId, chain, signal) {
1476
- const wantMfp = deviceId ? deviceId.toLowerCase() : this._mfpFromConnectId(connectId ?? "");
1477
- const existing = wantMfp ? this._devices.get(wantMfp) : void 0;
1478
- if (existing) return { record: existing };
1479
- const { accountPath } = splitAccountPath(import_hwk_adapter_core.CHAIN_FINGERPRINT_PATHS[chain]);
2362
+ async _ensureWalletKnown(connectId, deviceId, chain, signal) {
2363
+ const target = this._resolveTarget(connectId, deviceId);
2364
+ if (target.record) return { record: target.record };
2365
+ const attached = await this._tryUsbAttach(target.expectedWalletId);
2366
+ _KeystoneAdapter._throwIfAborted(signal);
2367
+ if (attached) return { record: attached };
2368
+ const { accountPath } = splitAccountPath(import_hwk_adapter_core3.CHAIN_FINGERPRINT_PATHS[chain]);
2369
+ const schemas = accountPath === KEYSTONE_WALLET_ID_PATH ? [{ path: KEYSTONE_WALLET_ID_PATH, curve: "secp256k1" }] : [
2370
+ { path: KEYSTONE_WALLET_ID_PATH, curve: "secp256k1" },
2371
+ {
2372
+ path: accountPath,
2373
+ curve: chain === "sol" ? "ed25519" : "secp256k1"
2374
+ }
2375
+ ];
1480
2376
  const requestUr = this.urEngine.buildKeyDerivationRequest({
1481
- schemas: [{ path: accountPath, curve: chain === "sol" ? "ed25519" : "secp256k1" }],
2377
+ schemas,
1482
2378
  origin: this._origin
1483
2379
  });
1484
- const responseUr = await this._resolveUr(void 0, requestUr, false);
2380
+ const responseUr = await this._resolveUr(void 0, requestUr, false, connectId, signal);
1485
2381
  _KeystoneAdapter._throwIfAborted(signal);
1486
2382
  const parsed = this.urEngine.parseAccountResponse(responseUr);
1487
- if (wantMfp && parsed.masterFingerprint !== wantMfp) {
1488
- throw (0, import_hwk_adapter_core.createHwkError)({
1489
- code: import_hwk_adapter_core.HardwareErrorCode.DeviceMismatch,
1490
- message: `Scanned Keystone wallet (mfp ${parsed.masterFingerprint}) does not match the requested device (${wantMfp})`
1491
- });
1492
- }
1493
- return { record: this._upsertDeviceRecord(parsed) };
2383
+ this._assertParsedIdentity(parsed, target);
2384
+ const record = this._upsertDeviceRecord(parsed);
2385
+ return { record };
1494
2386
  }
1495
2387
  async _requestQrDisplayAndAwaitResponse(device, data) {
1496
2388
  const waitPromise = this._uiRegistry.wait(
1497
- import_hwk_adapter_core.UI_REQUEST.REQUEST_QR_DISPLAY,
2389
+ import_hwk_adapter_core3.UI_REQUEST.REQUEST_QR_DISPLAY,
1498
2390
  { timeoutMs: this._qrTimeoutMs }
1499
2391
  );
1500
- this.emitter.emit(import_hwk_adapter_core.UI_REQUEST.REQUEST_QR_DISPLAY, {
1501
- type: import_hwk_adapter_core.UI_REQUEST.REQUEST_QR_DISPLAY,
2392
+ this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_QR_DISPLAY, {
2393
+ type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_QR_DISPLAY,
1502
2394
  payload: { device, data }
1503
2395
  });
1504
2396
  const response = await waitPromise;
@@ -1506,16 +2398,103 @@ var KeystoneAdapter = class _KeystoneAdapter {
1506
2398
  }
1507
2399
  async _requestQrScanAndAwaitResponse(device) {
1508
2400
  const waitPromise = this._uiRegistry.wait(
1509
- import_hwk_adapter_core.UI_REQUEST.REQUEST_QR_SCAN,
2401
+ import_hwk_adapter_core3.UI_REQUEST.REQUEST_QR_SCAN,
1510
2402
  { timeoutMs: this._qrTimeoutMs }
1511
2403
  );
1512
- this.emitter.emit(import_hwk_adapter_core.UI_REQUEST.REQUEST_QR_SCAN, {
1513
- type: import_hwk_adapter_core.UI_REQUEST.REQUEST_QR_SCAN,
2404
+ this.emitter.emit(import_hwk_adapter_core3.UI_REQUEST.REQUEST_QR_SCAN, {
2405
+ type: import_hwk_adapter_core3.UI_REQUEST.REQUEST_QR_SCAN,
1514
2406
  payload: { device }
1515
2407
  });
1516
2408
  const response = await waitPromise;
1517
2409
  return { urType: response.urType, urData: response.urData };
1518
2410
  }
2411
+ async _callUsbConnector(sessionId, method, params, signal) {
2412
+ if (!this._usbConnector) {
2413
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2414
+ code: import_hwk_adapter_core3.HardwareErrorCode.TransportNotAvailable,
2415
+ message: "No USB connector configured for this Keystone adapter"
2416
+ });
2417
+ }
2418
+ if (this._pendingUsbTeardowns > 0 || this._unsettledUsbOperations.size > 0) {
2419
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2420
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceBusyInternal,
2421
+ message: `Keystone USB is busy while calling ${method}`
2422
+ });
2423
+ }
2424
+ const releaseOperation = this._retainUsbOperation(`call:${sessionId}`);
2425
+ let rawCall;
2426
+ try {
2427
+ const connector = this._usbConnector;
2428
+ rawCall = connector.call(sessionId, method, params).finally(releaseOperation);
2429
+ } catch (error) {
2430
+ releaseOperation();
2431
+ throw error;
2432
+ }
2433
+ return signal ? _KeystoneAdapter._abortable(signal, rawCall) : rawCall;
2434
+ }
2435
+ _runUsbTeardown(task) {
2436
+ const previous = this._usbTeardownTail;
2437
+ let releaseTail = () => void 0;
2438
+ this._usbTeardownTail = new Promise((resolve) => {
2439
+ releaseTail = resolve;
2440
+ });
2441
+ this._pendingUsbTeardowns += 1;
2442
+ return (async () => {
2443
+ try {
2444
+ await previous;
2445
+ await this._waitForUsbOperationsToDrain();
2446
+ await task();
2447
+ } finally {
2448
+ this._pendingUsbTeardowns -= 1;
2449
+ releaseTail();
2450
+ }
2451
+ })();
2452
+ }
2453
+ _waitForUsbOperationsToDrain() {
2454
+ if (this._unsettledUsbOperations.size === 0) return Promise.resolve();
2455
+ return new Promise((resolve) => {
2456
+ this._usbIdleWaiters.add(resolve);
2457
+ });
2458
+ }
2459
+ _retainUsbOperation(key) {
2460
+ this._unsettledUsbOperations.set(key, (this._unsettledUsbOperations.get(key) ?? 0) + 1);
2461
+ let released = false;
2462
+ return () => {
2463
+ if (released) return;
2464
+ released = true;
2465
+ const remaining = (this._unsettledUsbOperations.get(key) ?? 1) - 1;
2466
+ if (remaining > 0) {
2467
+ this._unsettledUsbOperations.set(key, remaining);
2468
+ } else {
2469
+ this._unsettledUsbOperations.delete(key);
2470
+ }
2471
+ if (this._unsettledUsbOperations.size === 0) {
2472
+ for (const resolve of this._usbIdleWaiters) resolve();
2473
+ this._usbIdleWaiters.clear();
2474
+ }
2475
+ };
2476
+ }
2477
+ static _abortable(signal, promise) {
2478
+ if (signal.aborted) {
2479
+ return Promise.reject(signal.reason instanceof Error ? signal.reason : new Error("Aborted"));
2480
+ }
2481
+ return new Promise((resolve, reject) => {
2482
+ const onAbort = () => {
2483
+ reject(signal.reason instanceof Error ? signal.reason : new Error("Aborted"));
2484
+ };
2485
+ signal.addEventListener("abort", onAbort, { once: true });
2486
+ promise.then(
2487
+ (value) => {
2488
+ signal.removeEventListener("abort", onAbort);
2489
+ resolve(value);
2490
+ },
2491
+ (error) => {
2492
+ signal.removeEventListener("abort", onAbort);
2493
+ reject(error);
2494
+ }
2495
+ );
2496
+ });
2497
+ }
1519
2498
  static _throwIfAborted(signal) {
1520
2499
  if (signal.aborted) {
1521
2500
  throw signal.reason instanceof Error ? signal.reason : new Error("Operation aborted");
@@ -1523,16 +2502,16 @@ var KeystoneAdapter = class _KeystoneAdapter {
1523
2502
  }
1524
2503
  static _assertRequestIdMatches(expected, actual) {
1525
2504
  if (actual && actual.toLowerCase() !== expected.toLowerCase()) {
1526
- throw (0, import_hwk_adapter_core.createHwkError)({
1527
- code: import_hwk_adapter_core.HardwareErrorCode.DeviceMismatch,
2505
+ throw (0, import_hwk_adapter_core3.createHwkError)({
2506
+ code: import_hwk_adapter_core3.HardwareErrorCode.DeviceMismatch,
1528
2507
  message: "Keystone response requestId does not match the pending request \u2014 discarding a stale or unrelated scan"
1529
2508
  });
1530
2509
  }
1531
2510
  }
1532
2511
  _unsupported(method, reason) {
1533
2512
  return Promise.resolve(
1534
- (0, import_hwk_adapter_core.failure)(
1535
- import_hwk_adapter_core.HardwareErrorCode.MethodNotSupported,
2513
+ (0, import_hwk_adapter_core3.failure)(
2514
+ import_hwk_adapter_core3.HardwareErrorCode.MethodNotSupported,
1536
2515
  `KeystoneAdapter.${method} is not implemented yet: ${reason}`
1537
2516
  )
1538
2517
  );
@@ -1541,38 +2520,47 @@ var KeystoneAdapter = class _KeystoneAdapter {
1541
2520
  if (err && typeof err === "object") {
1542
2521
  const e = err;
1543
2522
  if (typeof e.code === "number") {
1544
- return (0, import_hwk_adapter_core.failure)(e.code, e.message ?? "Unknown error", e.params);
2523
+ const { origin } = e;
2524
+ return (0, import_hwk_adapter_core3.failure)(
2525
+ e.code,
2526
+ e.message ?? "Unknown error",
2527
+ e.params,
2528
+ origin === "device" || origin === "transport" || origin === "host" ? origin : void 0,
2529
+ (0, import_hwk_adapter_core3.isHwkRecoveryHint)(e.recovery) ? e.recovery : void 0
2530
+ );
1545
2531
  }
1546
- if (e._tag === import_hwk_adapter_core.UI_REQUEST_CANCELLED_TAG || e._tag === import_hwk_adapter_core.UI_REQUEST_PREEMPTED_TAG) {
1547
- return (0, import_hwk_adapter_core.failure)(
1548
- import_hwk_adapter_core.HardwareErrorCode.UserAborted,
1549
- e.message ?? "Keystone QR interaction was cancelled"
2532
+ if (e._tag === import_hwk_adapter_core3.UI_REQUEST_CANCELLED_TAG || e._tag === import_hwk_adapter_core3.UI_REQUEST_PREEMPTED_TAG) {
2533
+ return (0, import_hwk_adapter_core3.failure)(
2534
+ import_hwk_adapter_core3.HardwareErrorCode.UserAborted,
2535
+ e.message ?? "Keystone QR operation was cancelled"
1550
2536
  );
1551
2537
  }
1552
- if (e._tag === import_hwk_adapter_core.UI_REQUEST_TIMEOUT_TAG) {
1553
- return (0, import_hwk_adapter_core.failure)(
1554
- import_hwk_adapter_core.HardwareErrorCode.OperationTimeout,
1555
- e.message ?? "Keystone QR interaction timed out"
2538
+ if (e._tag === import_hwk_adapter_core3.UI_REQUEST_TIMEOUT_TAG) {
2539
+ return (0, import_hwk_adapter_core3.failure)(
2540
+ import_hwk_adapter_core3.HardwareErrorCode.OperationTimeout,
2541
+ e.message ?? "Keystone QR operation timed out"
1556
2542
  );
1557
2543
  }
1558
2544
  }
1559
2545
  const message = err instanceof Error ? err.message : String(err);
1560
2546
  const params = err instanceof Error && err.stack ? { stack: err.stack } : void 0;
1561
- return (0, import_hwk_adapter_core.failure)(import_hwk_adapter_core.HardwareErrorCode.UnknownError, message, params);
2547
+ return (0, import_hwk_adapter_core3.failure)(import_hwk_adapter_core3.HardwareErrorCode.UnknownError, message, params);
1562
2548
  }
1563
2549
  };
1564
2550
  // Annotate the CommonJS export names for ESM import in node:
1565
2551
  0 && (module.exports = {
2552
+ KEYSTONE_WALLET_CONNECT_ID_PREFIX,
2553
+ KEYSTONE_WALLET_ID_PATH,
1566
2554
  KeystoneAdapter,
1567
2555
  KeystoneUrEngine,
1568
- QR_CONNECT_ID_PREFIX,
1569
2556
  accountKey,
1570
2557
  btcScriptTypeFromPath,
1571
2558
  createDeviceRecord,
2559
+ deriveKeystoneWalletId,
1572
2560
  normalizePath,
1573
2561
  placeholderDeviceInfo,
1574
- qrConnectId,
1575
2562
  splitAccountPath,
1576
- toDeviceInfo
2563
+ toDeviceInfo,
2564
+ walletConnectId
1577
2565
  });
1578
2566
  //# sourceMappingURL=index.js.map