@onekeyfe/hwk-trezor-adapter 1.2.3-alpha.1 → 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.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  DEVICE,
4
4
  DeviceJobQueue,
5
5
  HardwareErrorCode,
6
- InteractionRegistry,
6
+ OperationRegistry,
7
7
  SDK,
8
8
  TypedEventEmitter,
9
9
  UI_REQUEST,
@@ -13,7 +13,7 @@ import {
13
13
  createHwkError,
14
14
  defaultOriginForCode,
15
15
  failure,
16
- isHardwareInteractionId,
16
+ isHardwareOperationId,
17
17
  isHwkRecoveryHint,
18
18
  operationMayHaveCompletedParams,
19
19
  rehydrateConnectorError,
@@ -797,15 +797,15 @@ var _TrezorAdapter = class _TrezorAdapter {
797
797
  constructor(connector, options) {
798
798
  this.vendor = "trezor";
799
799
  this._emitter = new TypedEventEmitter();
800
- this._interactions = new InteractionRegistry({
800
+ this._operations = new OperationRegistry({
801
801
  vendor: "trezor",
802
- onEnded: (interaction, reason) => {
803
- this._emitter.emit(SDK.INTERACTION_ENDED, {
804
- type: SDK.INTERACTION_ENDED,
805
- payload: { interactionId: interaction.interactionId, reason }
802
+ onEnded: (operation, reason) => {
803
+ this._emitter.emit(SDK.OPERATION_ENDED, {
804
+ type: SDK.OPERATION_ENDED,
805
+ payload: { operationId: operation.operationId, reason }
806
806
  });
807
807
  if (reason === "timeout") {
808
- void this._releaseInteractionConnection(interaction).catch(() => void 0);
808
+ void this._releaseOperationConnection(operation).catch(() => void 0);
809
809
  }
810
810
  }
811
811
  });
@@ -831,7 +831,7 @@ var _TrezorAdapter = class _TrezorAdapter {
831
831
  this._emitter.emit(DEVICE.CONNECT, { type: DEVICE.CONNECT, payload: device });
832
832
  };
833
833
  this._onDeviceDisconnect = (data) => {
834
- this._interactions.endByConnectionKey(data.connectId, "disconnect");
834
+ this._operations.endByConnectionKey(data.connectId, "disconnect");
835
835
  this._sessions.delete(data.connectId);
836
836
  this._verifiedPassphraseSessionsByConnectId.delete(data.connectId);
837
837
  this._devices.delete(data.connectId);
@@ -910,7 +910,7 @@ var _TrezorAdapter = class _TrezorAdapter {
910
910
  const {
911
911
  passphraseState,
912
912
  autoInstallApp: _autoInstallApp,
913
- interactionId,
913
+ operationId,
914
914
  useEmptyPassphrase,
915
915
  knownConnections,
916
916
  extra,
@@ -920,7 +920,7 @@ var _TrezorAdapter = class _TrezorAdapter {
920
920
  return {
921
921
  passphraseState: typeof passphraseState === "string" ? passphraseState : void 0,
922
922
  useEmptyPassphrase: typeof useEmptyPassphrase === "boolean" ? useEmptyPassphrase : void 0,
923
- interactionId: typeof interactionId === "string" ? interactionId : void 0,
923
+ operationId: typeof operationId === "string" ? operationId : void 0,
924
924
  connectionContext: {
925
925
  knownConnections,
926
926
  extra,
@@ -935,11 +935,11 @@ var _TrezorAdapter = class _TrezorAdapter {
935
935
  const autoInstallApp = commonParams?.autoInstallApp;
936
936
  const passphraseState = commonParams?.passphraseState;
937
937
  const useEmptyPassphrase = commonParams?.useEmptyPassphrase;
938
- const interactionId = commonParams?.interactionId;
938
+ const operationId = commonParams?.operationId;
939
939
  const knownConnections = commonParams?.knownConnections;
940
940
  const extra = commonParams?.extra;
941
941
  const allowDeviceSelection = commonParams?.allowDeviceSelection;
942
- if (autoInstallApp === void 0 && passphraseState === void 0 && useEmptyPassphrase === void 0 && interactionId === void 0 && knownConnections === void 0 && extra === void 0 && allowDeviceSelection === void 0) {
942
+ if (autoInstallApp === void 0 && passphraseState === void 0 && useEmptyPassphrase === void 0 && operationId === void 0 && knownConnections === void 0 && extra === void 0 && allowDeviceSelection === void 0) {
943
943
  return item;
944
944
  }
945
945
  return {
@@ -947,7 +947,7 @@ var _TrezorAdapter = class _TrezorAdapter {
947
947
  ...autoInstallApp !== void 0 ? { autoInstallApp } : {},
948
948
  ...passphraseState !== void 0 ? { passphraseState } : {},
949
949
  ...useEmptyPassphrase !== void 0 ? { useEmptyPassphrase } : {},
950
- ...interactionId !== void 0 ? { interactionId } : {},
950
+ ...operationId !== void 0 ? { operationId } : {},
951
951
  ...knownConnections !== void 0 ? { knownConnections } : {},
952
952
  ...extra !== void 0 ? { extra } : {},
953
953
  ...allowDeviceSelection !== void 0 ? { allowDeviceSelection } : {}
@@ -1205,7 +1205,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1205
1205
  if (this._resetPromise) return this._resetPromise;
1206
1206
  const sessionIds = new Set(this._sessions.values());
1207
1207
  this._stateGeneration += 1;
1208
- this._interactions.endAll("runtime-reset");
1208
+ this._operations.endAll("runtime-reset");
1209
1209
  this._uiRegistry.reset();
1210
1210
  this._jobQueue.clear();
1211
1211
  this._devices.clear();
@@ -1292,6 +1292,53 @@ var _TrezorAdapter = class _TrezorAdapter {
1292
1292
  targetId: searchTargetId
1293
1293
  }));
1294
1294
  }
1295
+ async bindBleDevice(params) {
1296
+ if (params.identity.vendor !== "trezor" || !params.identity.value) {
1297
+ return failure(HardwareErrorCode.InvalidParams, "Trezor device identity is required");
1298
+ }
1299
+ const { value: deviceId } = params.identity;
1300
+ try {
1301
+ return await this._jobQueue.enqueue(
1302
+ deviceId,
1303
+ async (signal) => {
1304
+ const selected = await this._selectBleDeviceForBinding(
1305
+ deviceId,
1306
+ signal,
1307
+ { extra: params.extra },
1308
+ "manual-rebind"
1309
+ );
1310
+ try {
1311
+ const persisted = await this._emitVerifiedBinding(
1312
+ deviceId,
1313
+ selected.connectId,
1314
+ selected.selectionRequestId,
1315
+ { extra: params.extra },
1316
+ signal
1317
+ );
1318
+ if (!persisted) {
1319
+ throw createHwkError({
1320
+ code: HardwareErrorCode.UnknownError,
1321
+ message: "Bluetooth binding could not be saved",
1322
+ origin: "host"
1323
+ });
1324
+ }
1325
+ this._rememberVerifiedConnection(deviceId, selected.connectId);
1326
+ return success(selected.connectId);
1327
+ } catch (error) {
1328
+ await this._releaseProvisionalConnection(selected.connectId, signal);
1329
+ throw error;
1330
+ }
1331
+ },
1332
+ {
1333
+ label: "bindBleDevice",
1334
+ rejectIfBusy: true,
1335
+ busyError: _TrezorAdapter._createDeviceBusyError("bindBleDevice")
1336
+ }
1337
+ );
1338
+ } catch (error) {
1339
+ return this._errorToFailure(error);
1340
+ }
1341
+ }
1295
1342
  async connectDevice(searchTargetId) {
1296
1343
  try {
1297
1344
  await this._ensureDevicePermission(
@@ -1304,30 +1351,30 @@ var _TrezorAdapter = class _TrezorAdapter {
1304
1351
  if (!device) {
1305
1352
  return failure(HardwareErrorCode.DeviceNotFound, "Trezor device not found after connect");
1306
1353
  }
1307
- this._interactions.endByConnectionKey(searchTargetId, "explicit");
1308
- const interaction = this._interactions.create({
1354
+ this._operations.endByConnectionKey(searchTargetId, "explicit");
1355
+ const operation = this._operations.create({
1309
1356
  searchTargetId,
1310
1357
  connectId: searchTargetId,
1311
1358
  device,
1312
1359
  connectionKeys: [this._sessions.get(searchTargetId) ?? ""]
1313
1360
  });
1314
- return success(interaction.interactionId);
1361
+ return success(operation.operationId);
1315
1362
  } catch (error) {
1316
1363
  return this._errorToFailure(error);
1317
1364
  }
1318
1365
  }
1319
- async releaseInteraction(interactionId) {
1320
- const interaction = this._interactions.find(interactionId);
1321
- if (!interaction) {
1322
- this._interactions.resolve(interactionId);
1366
+ async releaseOperation(operationId) {
1367
+ const operation = this._operations.find(operationId);
1368
+ if (!operation) {
1369
+ this._operations.resolve(operationId);
1323
1370
  return;
1324
1371
  }
1325
- const endedInteraction = this._interactions.end(interactionId, "explicit");
1326
- if (!endedInteraction) return;
1327
- await this._releaseInteractionConnection(endedInteraction);
1372
+ const endedOperation = this._operations.end(operationId, "explicit");
1373
+ if (!endedOperation) return;
1374
+ await this._releaseOperationConnection(endedOperation);
1328
1375
  }
1329
- async _releaseInteractionConnection(interaction) {
1330
- const { connectId } = interaction;
1376
+ async _releaseOperationConnection(operation) {
1377
+ const { connectId } = operation;
1331
1378
  const connecting = this._connectingPromises.has(connectId);
1332
1379
  this._connectingPromises.delete(connectId);
1333
1380
  const sessionId = this._sessions.get(connectId);
@@ -1339,10 +1386,10 @@ var _TrezorAdapter = class _TrezorAdapter {
1339
1386
  this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1340
1387
  await this._runConnectorTeardown(() => this._connector.disconnect(sessionId));
1341
1388
  }
1342
- async getDeviceInfo(connectIdOrInteractionId, deviceId) {
1389
+ async getDeviceInfo(connectIdOrOperationId, deviceId) {
1343
1390
  let connectId;
1344
1391
  try {
1345
- connectId = isHardwareInteractionId(connectIdOrInteractionId) ? this._interactions.resolve(connectIdOrInteractionId).connectId : connectIdOrInteractionId;
1392
+ connectId = isHardwareOperationId(connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
1346
1393
  } catch (error) {
1347
1394
  return this._errorToFailure(error);
1348
1395
  }
@@ -1350,16 +1397,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1350
1397
  if (!device) return failure(HardwareErrorCode.DeviceNotFound, "Trezor device not found");
1351
1398
  return success(device);
1352
1399
  }
1353
- getSupportedChains() {
1354
- return ["btc", "evm", "sol", "tron"];
1355
- }
1356
1400
  async allNetworkGetAddress(connectId, deviceId, params) {
1357
- const target = resolveHardwareOperationTarget(connectId, params.interactionId, "trezor");
1401
+ const target = resolveHardwareOperationTarget(connectId, params.operationId, "trezor");
1358
1402
  if (!target.success) return target;
1359
1403
  let effectiveTargetId = target.payload.targetId ?? "";
1360
- let releaseInteractionRetention;
1404
+ let releaseOperationRetention;
1361
1405
  try {
1362
- releaseInteractionRetention = target.payload.interactionId ? this._interactions.retain(target.payload.interactionId) : void 0;
1406
+ releaseOperationRetention = target.payload.operationId ? this._operations.retain(target.payload.operationId) : void 0;
1363
1407
  } catch (error) {
1364
1408
  return this._errorToFailure(error);
1365
1409
  }
@@ -1379,7 +1423,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1379
1423
  );
1380
1424
  if (deviceIdResponse.success) {
1381
1425
  liveDeviceId = deviceIdResponse.payload;
1382
- if (!target.payload.interactionId) {
1426
+ if (!target.payload.operationId) {
1383
1427
  effectiveTargetId = bundleContext.connection?.connectId ?? effectiveTargetId;
1384
1428
  }
1385
1429
  }
@@ -1438,12 +1482,12 @@ var _TrezorAdapter = class _TrezorAdapter {
1438
1482
  const isSessionLevelFailure = topLevelFailureIndexes.has(index);
1439
1483
  const isWholeChainForbidden = isSingleNetworkBundle && response.payload?.code === HardwareErrorCode.DevicePathForbidden;
1440
1484
  const isPassphrasePolicyFailure = response.payload?.code === HardwareErrorCode.PassphraseAlwaysOnDevice;
1441
- const isConnectionLost = response.payload?.code === HardwareErrorCode.DeviceDisconnected || response.payload?.code === HardwareErrorCode.OperationTimeout || response.payload?.code === HardwareErrorCode.TransportError || response.payload?.code === HardwareErrorCode.InteractionEnded || response.payload?.code === HardwareErrorCode.InteractionNotFound;
1485
+ const isConnectionLost = response.payload?.code === HardwareErrorCode.DeviceDisconnected || response.payload?.code === HardwareErrorCode.OperationTimeout || response.payload?.code === HardwareErrorCode.TransportError || response.payload?.code === HardwareErrorCode.OperationEnded || response.payload?.code === HardwareErrorCode.OperationNotFound;
1442
1486
  return isSessionLevelFailure || isWholeChainForbidden || isPassphrasePolicyFailure || isConnectionLost;
1443
1487
  }
1444
1488
  });
1445
1489
  } finally {
1446
- releaseInteractionRetention?.();
1490
+ releaseOperationRetention?.();
1447
1491
  }
1448
1492
  }
1449
1493
  getFeatures(connectId, operationContext) {
@@ -1551,7 +1595,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1551
1595
  connectId,
1552
1596
  hasConnectionContext || !connectId ? expectedDeviceId : void 0,
1553
1597
  hasConnectionContext ? {
1554
- interactionId: commonParams.interactionId,
1598
+ operationId: commonParams.operationId,
1555
1599
  knownConnections: commonParams.knownConnections,
1556
1600
  extra: commonParams.extra,
1557
1601
  allowDeviceSelection: commonParams.allowDeviceSelection,
@@ -1587,18 +1631,18 @@ var _TrezorAdapter = class _TrezorAdapter {
1587
1631
  const targetId = connectId ?? activeJobId;
1588
1632
  if (targetId) {
1589
1633
  let resolvedConnectId = targetId;
1590
- let interactionId;
1591
- if (isHardwareInteractionId(targetId)) {
1634
+ let operationId;
1635
+ if (isHardwareOperationId(targetId)) {
1592
1636
  try {
1593
- interactionId = targetId;
1594
- resolvedConnectId = this._interactions.resolve(targetId).connectId;
1637
+ operationId = targetId;
1638
+ resolvedConnectId = this._operations.resolve(targetId).connectId;
1595
1639
  } catch {
1596
1640
  resolvedConnectId = "";
1597
1641
  }
1598
1642
  }
1599
- const interactionForPhysicalId = !interactionId && connectId ? this._interactions.findActiveByConnectionKey(connectId) : void 0;
1600
- this._jobQueue.forceCancelActive(
1601
- interactionId ?? (activeJobId === targetId ? targetId : interactionForPhysicalId?.interactionId) ?? targetId,
1643
+ const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
1644
+ this._jobQueue.cancelActive(
1645
+ operationId ?? (activeJobId === targetId ? targetId : interactionForPhysicalId?.operationId) ?? targetId,
1602
1646
  userAbortReason
1603
1647
  );
1604
1648
  if (resolvedConnectId) {
@@ -1606,7 +1650,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1606
1650
  }
1607
1651
  return;
1608
1652
  }
1609
- this._jobQueue.forceCancelActive(void 0, userAbortReason);
1653
+ this._jobQueue.cancelActive(void 0, userAbortReason);
1610
1654
  for (const sessionId of this._sessions.values()) {
1611
1655
  void this._connector.cancel(sessionId);
1612
1656
  }
@@ -1651,72 +1695,82 @@ var _TrezorAdapter = class _TrezorAdapter {
1651
1695
  }
1652
1696
  const target = resolveHardwareOperationTarget(
1653
1697
  connectId,
1654
- operationContext?.interactionId,
1698
+ operationContext?.operationId,
1655
1699
  "trezor"
1656
1700
  );
1657
1701
  if (!target.success) return Promise.resolve(target);
1658
- const { interactionId } = target.payload;
1702
+ const { operationId } = target.payload;
1659
1703
  let resolvedConnectId = target.payload.targetId ?? "";
1660
- let releaseInteractionRetention;
1661
- if (interactionId) {
1704
+ let releaseOperationRetention;
1705
+ if (operationId) {
1662
1706
  try {
1663
- resolvedConnectId = this._interactions.resolve(interactionId).connectId;
1664
- releaseInteractionRetention = this._interactions.retain(interactionId);
1707
+ resolvedConnectId = this._operations.resolve(operationId).connectId;
1708
+ releaseOperationRetention = this._operations.retain(operationId);
1665
1709
  } catch (error) {
1666
1710
  return Promise.resolve(this._errorToFailure(error));
1667
1711
  }
1668
1712
  }
1669
1713
  return this._jobQueue.enqueue(
1670
- interactionId || resolvedConnectId,
1714
+ operationId || resolvedConnectId,
1671
1715
  async (signal) => {
1672
1716
  let selectionRequestId;
1673
- if (!interactionId && expectedIdentity?.value && (!connectId || operationContext?.knownConnections !== void 0)) {
1674
- const resolved = await this._resolveExpectedDeviceConnectId(
1675
- expectedIdentity.value,
1676
- signal,
1677
- operationContext
1678
- );
1679
- resolvedConnectId = resolved.connectId;
1680
- selectionRequestId = resolved.selectionRequestId;
1681
- }
1682
- if (expectedIdentity?.value) {
1683
- if (!interactionId) await this._ensureSession(resolvedConnectId, signal);
1684
- if (this._devices.get(resolvedConnectId)?.deviceId !== expectedIdentity.value) {
1685
- return failure(
1686
- HardwareErrorCode.DeviceMismatch,
1687
- "Trezor device identity does not match"
1717
+ let verified = false;
1718
+ try {
1719
+ if (!operationId && expectedIdentity?.value && (!connectId || operationContext?.knownConnections !== void 0)) {
1720
+ const resolved = await this._resolveExpectedDeviceConnectId(
1721
+ expectedIdentity.value,
1722
+ signal,
1723
+ operationContext
1688
1724
  );
1725
+ resolvedConnectId = resolved.connectId;
1726
+ selectionRequestId = resolved.selectionRequestId;
1689
1727
  }
1690
- }
1691
- const restorePassphraseRequestContext = this._setPassphraseRequestContext(
1692
- resolvedConnectId,
1693
- {
1694
- passphraseState
1728
+ if (expectedIdentity?.value) {
1729
+ if (!operationId) await this._ensureSession(resolvedConnectId, signal);
1730
+ if (this._devices.get(resolvedConnectId)?.deviceId !== expectedIdentity.value) {
1731
+ return failure(
1732
+ HardwareErrorCode.DeviceMismatch,
1733
+ "Trezor device identity does not match"
1734
+ );
1735
+ }
1695
1736
  }
1696
- );
1697
- let verified = false;
1698
- try {
1699
- const result = await this._resolvePassphraseState(
1737
+ const restorePassphraseRequestContext = this._setPassphraseRequestContext(
1700
1738
  resolvedConnectId,
1701
- passphraseState,
1702
- signal,
1703
- interactionId
1739
+ {
1740
+ passphraseState
1741
+ }
1704
1742
  );
1705
- if (result.success && expectedIdentity?.value) {
1706
- await this._emitVerifiedBinding(
1707
- expectedIdentity.value,
1743
+ try {
1744
+ const result = await this._resolvePassphraseState(
1708
1745
  resolvedConnectId,
1709
- selectionRequestId,
1710
- operationContext,
1711
- signal
1746
+ passphraseState,
1747
+ signal,
1748
+ operationId
1712
1749
  );
1713
- verified = true;
1714
- this._rememberVerifiedConnection(expectedIdentity.value, resolvedConnectId);
1750
+ if (result.success && expectedIdentity?.value) {
1751
+ await this._emitVerifiedBinding(
1752
+ expectedIdentity.value,
1753
+ resolvedConnectId,
1754
+ selectionRequestId,
1755
+ operationContext,
1756
+ signal
1757
+ );
1758
+ verified = true;
1759
+ this._rememberVerifiedConnection(expectedIdentity.value, resolvedConnectId);
1760
+ }
1761
+ return result;
1762
+ } finally {
1763
+ restorePassphraseRequestContext();
1715
1764
  }
1716
- return result;
1717
1765
  } finally {
1718
- restorePassphraseRequestContext();
1719
1766
  if (selectionRequestId && !verified) {
1767
+ this._emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
1768
+ type: UI_REQUEST.DEVICE_BINDING_STATUS,
1769
+ payload: {
1770
+ selectionRequestId,
1771
+ status: signal.aborted ? "cancelled" : "failed"
1772
+ }
1773
+ });
1720
1774
  await this._releaseProvisionalConnection(resolvedConnectId, signal);
1721
1775
  }
1722
1776
  }
@@ -1726,7 +1780,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1726
1780
  rejectIfBusy: true,
1727
1781
  busyError: _TrezorAdapter._createDeviceBusyError("getPassphraseState")
1728
1782
  }
1729
- ).catch((error) => this._errorToFailure(error)).finally(() => releaseInteractionRetention?.());
1783
+ ).catch((error) => this._errorToFailure(error)).finally(() => releaseOperationRetention?.());
1730
1784
  }
1731
1785
  evmGetAddress(connectId, deviceId, params) {
1732
1786
  return this._callWalletMethod("evmGetAddress", connectId, deviceId, params);
@@ -1932,7 +1986,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1932
1986
  knownConnections: operationContext?.knownConnections,
1933
1987
  extra: operationContext?.extra,
1934
1988
  allowDeviceSelection: operationContext?.allowDeviceSelection,
1935
- ...operationContext?.interactionId ? { interactionId: operationContext.interactionId } : {}
1989
+ ...operationContext?.operationId ? { operationId: operationContext.operationId } : {}
1936
1990
  },
1937
1991
  false
1938
1992
  );
@@ -1940,19 +1994,19 @@ var _TrezorAdapter = class _TrezorAdapter {
1940
1994
  async _callMethod(methodName, connectId, deviceId, params, requiresWalletIntent, bundleContext) {
1941
1995
  const call = _TrezorAdapter._normalizeCallArgs(connectId, deviceId, params);
1942
1996
  const commonParams = _TrezorAdapter._splitCommonParams(call.params);
1943
- const positionalInteractionId = isHardwareInteractionId(call.connectId) ? call.connectId : void 0;
1944
- if (positionalInteractionId && commonParams.interactionId && positionalInteractionId !== commonParams.interactionId) {
1945
- return failure(HardwareErrorCode.InvalidParams, "Conflicting Trezor interaction ids", {
1946
- positionalInteractionId,
1947
- commonInteractionId: commonParams.interactionId
1997
+ const positionalOperationId = isHardwareOperationId(call.connectId) ? call.connectId : void 0;
1998
+ if (positionalOperationId && commonParams.operationId && positionalOperationId !== commonParams.operationId) {
1999
+ return failure(HardwareErrorCode.InvalidParams, "Conflicting Trezor operation ids", {
2000
+ positionalOperationId,
2001
+ commonOperationId: commonParams.operationId
1948
2002
  });
1949
2003
  }
1950
- const interactionId = commonParams.interactionId ?? positionalInteractionId;
1951
- let releaseInteractionRetention;
2004
+ const operationId = commonParams.operationId ?? positionalOperationId;
2005
+ let releaseOperationRetention;
1952
2006
  try {
1953
- if (interactionId) {
1954
- call.connectId = this._interactions.resolve(interactionId).connectId;
1955
- releaseInteractionRetention = this._interactions.retain(interactionId);
2007
+ if (operationId) {
2008
+ call.connectId = this._operations.resolve(operationId).connectId;
2009
+ releaseOperationRetention = this._operations.retain(operationId);
1956
2010
  }
1957
2011
  } catch (error) {
1958
2012
  return this._errorToFailure(error);
@@ -1965,12 +2019,12 @@ var _TrezorAdapter = class _TrezorAdapter {
1965
2019
  });
1966
2020
  try {
1967
2021
  const response = await this._jobQueue.enqueue(
1968
- interactionId ?? call.connectId,
2022
+ operationId ?? call.connectId,
1969
2023
  async (signal) => {
1970
2024
  let effectiveConnectId = call.connectId;
1971
2025
  let selectionRequestId;
1972
2026
  try {
1973
- if (!bundleContext?.connection && !interactionId && deviceId && (!connectId || commonParams.connectionContext?.knownConnections !== void 0)) {
2027
+ if (!bundleContext?.connection && !operationId && deviceId && (!connectId || commonParams.connectionContext?.knownConnections !== void 0)) {
1974
2028
  const resolved = await this._resolveExpectedDeviceConnectId(
1975
2029
  deviceId,
1976
2030
  signal,
@@ -1990,7 +2044,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1990
2044
  requiresWalletIntent,
1991
2045
  true,
1992
2046
  signal,
1993
- interactionId,
2047
+ operationId,
1994
2048
  selectionRequestId,
1995
2049
  bundleContext
1996
2050
  );
@@ -2020,11 +2074,12 @@ var _TrezorAdapter = class _TrezorAdapter {
2020
2074
  });
2021
2075
  return response;
2022
2076
  } finally {
2023
- releaseInteractionRetention?.();
2077
+ releaseOperationRetention?.();
2024
2078
  }
2025
2079
  }
2026
- async _callWithRetry(connectId, methodName, params, expectedDeviceId, requiresWalletIntent, allowRetry, signal, interactionId, selectionRequestId, bundleContext) {
2080
+ async _callWithRetry(connectId, methodName, params, expectedDeviceId, requiresWalletIntent, allowRetry, signal, operationId, selectionRequestId, bundleContext) {
2027
2081
  const { passphraseState, useEmptyPassphrase, rest, connectionContext } = _TrezorAdapter._splitCommonParams(params);
2082
+ let pendingBindingRequestId = selectionRequestId;
2028
2083
  let businessCallStarted = false;
2029
2084
  const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {
2030
2085
  passphraseState,
@@ -2040,14 +2095,14 @@ var _TrezorAdapter = class _TrezorAdapter {
2040
2095
  );
2041
2096
  }
2042
2097
  }
2043
- const sessionId = interactionId ? this._sessions.get(this._interactions.resolve(interactionId).connectId) : await this._ensureSession(connectId, signal);
2098
+ const sessionId = operationId ? this._sessions.get(this._operations.resolve(operationId).connectId) : await this._ensureSession(connectId, signal);
2044
2099
  if (!sessionId) {
2045
- if (interactionId) {
2046
- this._interactions.end(interactionId, "disconnect");
2100
+ if (operationId) {
2101
+ this._operations.end(operationId, "disconnect");
2047
2102
  return failure(
2048
- HardwareErrorCode.InteractionEnded,
2049
- "Trezor interaction connection is no longer active",
2050
- { interactionId, reason: "disconnect" }
2103
+ HardwareErrorCode.OperationEnded,
2104
+ "Trezor operation connection is no longer active",
2105
+ { operationId, reason: "disconnect" }
2051
2106
  );
2052
2107
  }
2053
2108
  return failure(HardwareErrorCode.DeviceNotFound, "Trezor session was not found");
@@ -2079,67 +2134,56 @@ var _TrezorAdapter = class _TrezorAdapter {
2079
2134
  } else if (useEmptyPassphrase === true) {
2080
2135
  await _TrezorAdapter._abortable(signal, this._createFreshAppSession(sessionId, signal));
2081
2136
  }
2082
- const saveBeforeCall = this._emitter.listenerCount(UI_REQUEST.REQUEST_SAVE_DEVICE_BINDING) > 0;
2083
- if (expectedDeviceId && saveBeforeCall) {
2137
+ if (expectedDeviceId) {
2084
2138
  await this._emitVerifiedBinding(
2085
2139
  expectedDeviceId,
2086
2140
  connectId,
2087
- selectionRequestId,
2141
+ pendingBindingRequestId,
2088
2142
  connectionContext,
2089
2143
  signal
2090
2144
  );
2145
+ pendingBindingRequestId = void 0;
2146
+ this._rememberVerifiedConnection(expectedDeviceId, connectId);
2091
2147
  }
2092
2148
  businessCallStarted = true;
2093
2149
  const result = await _TrezorAdapter._abortable(
2094
2150
  signal,
2095
2151
  this._callConnector(sessionId, methodName, rest)
2096
2152
  );
2097
- if (expectedDeviceId) {
2098
- this._rememberVerifiedConnection(expectedDeviceId, connectId);
2099
- if (!saveBeforeCall) {
2100
- await this._emitVerifiedBinding(
2101
- expectedDeviceId,
2102
- connectId,
2103
- selectionRequestId,
2104
- connectionContext,
2105
- signal
2106
- );
2107
- }
2108
- }
2109
2153
  return success(result);
2110
2154
  } catch (error) {
2111
2155
  if (signal.aborted) {
2112
- if (selectionRequestId)
2156
+ if (pendingBindingRequestId)
2113
2157
  this._emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
2114
2158
  type: UI_REQUEST.DEVICE_BINDING_STATUS,
2115
- payload: { selectionRequestId, status: "cancelled" }
2159
+ payload: { selectionRequestId: pendingBindingRequestId, status: "cancelled" }
2116
2160
  });
2117
2161
  return this._errorToFailure(error);
2118
2162
  }
2119
2163
  const code = _TrezorAdapter._errorCode(error);
2120
- if (selectionRequestId && !(allowRetry && (passphraseState || useEmptyPassphrase === true) && _TrezorAdapter._isStaleSessionError(code))) {
2164
+ if (pendingBindingRequestId && !(allowRetry && (passphraseState || useEmptyPassphrase === true) && _TrezorAdapter._isStaleSessionError(code))) {
2121
2165
  this._emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
2122
2166
  type: UI_REQUEST.DEVICE_BINDING_STATUS,
2123
- payload: { selectionRequestId, status: "failed" }
2167
+ payload: { selectionRequestId: pendingBindingRequestId, status: "failed" }
2124
2168
  });
2125
2169
  }
2126
2170
  const ambiguousTransportFailure = code === HardwareErrorCode.DeviceDisconnected || code === HardwareErrorCode.OperationTimeout || code === HardwareErrorCode.TransportError;
2127
- if (interactionId && ambiguousTransportFailure) {
2171
+ if (operationId && ambiguousTransportFailure) {
2128
2172
  this._sessions.delete(connectId);
2129
2173
  this._verifiedPassphraseSessionsByConnectId.delete(connectId);
2130
- this._interactions.end(interactionId, "disconnect");
2174
+ this._operations.end(operationId, "disconnect");
2131
2175
  return failure(
2132
- HardwareErrorCode.InteractionEnded,
2133
- businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(methodName) ? `Trezor ${methodName} may have completed before the connection was lost` : "Trezor interaction connection was lost",
2176
+ HardwareErrorCode.OperationEnded,
2177
+ businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(methodName) ? `Trezor ${methodName} may have completed before the connection was lost` : "Trezor operation connection was lost",
2134
2178
  businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(methodName) ? operationMayHaveCompletedParams(methodName, {
2135
- interactionId,
2179
+ operationId,
2136
2180
  reason: "disconnect"
2137
- }) : { interactionId, reason: "disconnect" },
2181
+ }) : { operationId, reason: "disconnect" },
2138
2182
  void 0,
2139
2183
  businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(methodName) ? { scope: "unknown" } : void 0
2140
2184
  );
2141
2185
  }
2142
- if (!interactionId && ambiguousTransportFailure) {
2186
+ if (!operationId && ambiguousTransportFailure) {
2143
2187
  this._sessions.delete(connectId);
2144
2188
  this._verifiedPassphraseSessionsByConnectId.delete(connectId);
2145
2189
  if (businessCallStarted && !canReplayHardwareMethodAfterTransportFailure(methodName)) {
@@ -2164,8 +2208,8 @@ var _TrezorAdapter = class _TrezorAdapter {
2164
2208
  requiresWalletIntent,
2165
2209
  false,
2166
2210
  signal,
2167
- interactionId,
2168
- selectionRequestId,
2211
+ operationId,
2212
+ pendingBindingRequestId,
2169
2213
  bundleContext
2170
2214
  );
2171
2215
  }
@@ -2234,16 +2278,12 @@ var _TrezorAdapter = class _TrezorAdapter {
2234
2278
  const knownUsbIds = context?.knownConnections !== void 0 ? context.knownConnections.flatMap(
2235
2279
  (connection) => connection.transport === "usb" ? [connection.connectId] : []
2236
2280
  ) : [knownConnection?.usbConnectId].filter((id) => Boolean(id));
2237
- const knownBleIds = context?.knownConnections !== void 0 ? context.knownConnections.flatMap(
2281
+ const suppliedBleIds = context?.knownConnections?.flatMap(
2238
2282
  (connection) => connection.transport === "ble" ? [connection.connectId] : []
2239
- ) : [knownConnection?.bleConnectId].filter((id) => Boolean(id));
2240
- const matchingUsbCandidates = candidates.filter(
2241
- (device) => knownUsbIds.includes(device.connectId)
2242
2283
  );
2284
+ const knownBleIds = suppliedBleIds?.length ? suppliedBleIds : [knownConnection?.bleConnectId].filter((id) => Boolean(id));
2243
2285
  const usbCandidates = candidates.filter(
2244
- (device) => device.connectionType === "usb" && (!matchingUsbCandidates.length || knownUsbIds.includes(device.connectId))
2245
- ).sort(
2246
- (a, b) => Number(knownUsbIds.includes(b.connectId)) - Number(knownUsbIds.includes(a.connectId))
2286
+ (device) => device.connectionType === "usb" && (!knownUsbIds.length || knownUsbIds.includes(device.connectId))
2247
2287
  );
2248
2288
  for (const candidate of usbCandidates) {
2249
2289
  let sessionId;
@@ -2263,10 +2303,17 @@ var _TrezorAdapter = class _TrezorAdapter {
2263
2303
  await this._runConnectorTeardown(() => this._connector.disconnect(mismatchedSessionId));
2264
2304
  }
2265
2305
  }
2306
+ if (mismatchedDeviceIds.length) {
2307
+ throw createHwkError({
2308
+ code: HardwareErrorCode.DeviceSearchMismatch,
2309
+ message: "The connected Trezor is not the wallet this operation needs. Unplug it and connect the right device.",
2310
+ params: { connectedDeviceIds: mismatchedDeviceIds }
2311
+ });
2312
+ }
2266
2313
  if (usbCandidates.length > 0) {
2267
2314
  throw createHwkError({
2268
- code: mismatchedDeviceIds.length ? HardwareErrorCode.DeviceMismatch : HardwareErrorCode.DeviceNotFound,
2269
- message: "No discovered USB Trezor could be verified; Bluetooth binding requires empty USB discovery"
2315
+ code: HardwareErrorCode.DeviceNotFound,
2316
+ message: "Trezor devices are present but none could be opened to check its identity."
2270
2317
  });
2271
2318
  }
2272
2319
  const [knownBleConnectId] = knownBleIds;
@@ -2295,6 +2342,16 @@ var _TrezorAdapter = class _TrezorAdapter {
2295
2342
  message: "No known Trezor connection is available"
2296
2343
  });
2297
2344
  }
2345
+ return this._selectBleDeviceForBinding(expectedDeviceId, signal, context, "missing-binding");
2346
+ }
2347
+ async _selectBleDeviceForBinding(expectedDeviceId, signal, context, reason) {
2348
+ const availableTransports = this._connector.availableTransports ?? [
2349
+ this._connector.connectionType
2350
+ ];
2351
+ const allowUsbFallback = reason !== "manual-rebind" && availableTransports.includes("usb");
2352
+ const knownUsbIds = context?.knownConnections ? context.knownConnections.flatMap(
2353
+ (connection) => connection.transport === "usb" ? [connection.connectId] : []
2354
+ ) : [this._knownDeviceConnections.get(expectedDeviceId)?.usbConnectId];
2298
2355
  if (availableTransports.includes("ble")) {
2299
2356
  await _TrezorAdapter._abortable(
2300
2357
  signal,
@@ -2304,7 +2361,7 @@ var _TrezorAdapter = class _TrezorAdapter {
2304
2361
  const bleCandidates = (await _TrezorAdapter._abortable(
2305
2362
  signal,
2306
2363
  this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)
2307
- )).filter((device) => device.connectionType === "ble" && !knownBleIds.includes(device.connectId));
2364
+ )).filter((device) => device.connectionType === "ble");
2308
2365
  if (signal.aborted) throw signal.reason;
2309
2366
  const rejectedConnectIds = /* @__PURE__ */ new Set();
2310
2367
  const bindingSessionId = this._uiRegistry.createRequestId();
@@ -2314,9 +2371,19 @@ var _TrezorAdapter = class _TrezorAdapter {
2314
2371
  emitter: this._emitter,
2315
2372
  registry: this._uiRegistry,
2316
2373
  signal,
2317
- scan: async () => (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter(
2318
- (device) => device.connectionType === "ble" && !knownBleIds.includes(device.connectId) && !rejectedConnectIds.has(device.connectId)
2319
- ),
2374
+ allowUsbFallback,
2375
+ scan: async () => {
2376
+ if (allowUsbFallback) {
2377
+ const usbCandidates = (await this._searchDevices({ transportType: "usb" }, signal)).filter(
2378
+ (device) => device.connectionType === "usb" && !rejectedConnectIds.has(device.connectId)
2379
+ );
2380
+ const candidate = usbCandidates.find((device) => knownUsbIds.includes(device.connectId)) ?? usbCandidates[0];
2381
+ if (candidate) return [candidate];
2382
+ }
2383
+ return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter(
2384
+ (device) => device.connectionType === "ble" && !rejectedConnectIds.has(device.connectId)
2385
+ );
2386
+ },
2320
2387
  request: {
2321
2388
  devices: bleCandidates.filter((device) => !rejectedConnectIds.has(device.connectId)),
2322
2389
  bindingSessionId,
@@ -2324,13 +2391,17 @@ var _TrezorAdapter = class _TrezorAdapter {
2324
2391
  context: {
2325
2392
  kind: "bind-connection",
2326
2393
  transport: "ble",
2327
- reason: knownBleIds.length ? "known-connection-unavailable" : "missing-binding"
2394
+ reason
2328
2395
  },
2329
2396
  extra: context?.extra
2330
2397
  }
2331
2398
  });
2332
2399
  try {
2333
- await this._ensureSession(selected.connectId, signal, "ble");
2400
+ if (reason === "manual-rebind") {
2401
+ this._operations.endByConnectionKey(selected.connectId, "explicit");
2402
+ await this._releaseProvisionalConnection(selected.connectId, signal);
2403
+ }
2404
+ await this._ensureSession(selected.connectId, signal, selected.connectionType);
2334
2405
  } catch (error) {
2335
2406
  this._emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
2336
2407
  type: UI_REQUEST.DEVICE_BINDING_STATUS,
@@ -2352,18 +2423,15 @@ var _TrezorAdapter = class _TrezorAdapter {
2352
2423
  rejectedConnectIds.add(selected.connectId);
2353
2424
  continue;
2354
2425
  }
2426
+ if (selected.connectionType === "usb") {
2427
+ this._emitter.emit(UI_REQUEST.DEVICE_BINDING_STATUS, {
2428
+ type: UI_REQUEST.DEVICE_BINDING_STATUS,
2429
+ payload: { selectionRequestId: requestId, status: "cancelled" }
2430
+ });
2431
+ return { connectId: selected.connectId };
2432
+ }
2355
2433
  return { connectId: selected.connectId, selectionRequestId: requestId };
2356
2434
  }
2357
- if (mismatchedDeviceIds.length > 0) {
2358
- throw createHwkError({
2359
- code: HardwareErrorCode.DeviceMismatch,
2360
- message: `No connected Trezor matches device id ${expectedDeviceId}`,
2361
- params: {
2362
- expected: expectedDeviceId,
2363
- actual: mismatchedDeviceIds.join(",")
2364
- }
2365
- });
2366
- }
2367
2435
  throw createHwkError({
2368
2436
  code: HardwareErrorCode.DeviceNotFound,
2369
2437
  message: "No readable Trezor device is available"
@@ -2381,10 +2449,10 @@ var _TrezorAdapter = class _TrezorAdapter {
2381
2449
  this._knownDeviceConnections.set(deviceId, known);
2382
2450
  }
2383
2451
  async _emitVerifiedBinding(deviceId, connectId, selectionRequestId, context, signal) {
2384
- if (!selectionRequestId) return;
2452
+ if (!selectionRequestId) return false;
2385
2453
  const device = this._devices.get(connectId);
2386
- if (!device || device.deviceId !== deviceId || device.connectionType !== "ble") return;
2387
- const acknowledged = await requestSaveDeviceBinding(
2454
+ if (!device || device.deviceId !== deviceId || device.connectionType !== "ble") return false;
2455
+ const outcome = await requestSaveDeviceBinding(
2388
2456
  this._emitter,
2389
2457
  this._uiRegistry,
2390
2458
  {
@@ -2395,17 +2463,7 @@ var _TrezorAdapter = class _TrezorAdapter {
2395
2463
  },
2396
2464
  signal
2397
2465
  );
2398
- if (acknowledged) return;
2399
- this._emitter.emit(DEVICE.TREZOR_CONNECTION_VERIFIED, {
2400
- type: DEVICE.TREZOR_CONNECTION_VERIFIED,
2401
- payload: {
2402
- deviceId,
2403
- connectId,
2404
- connectionType: "ble",
2405
- selectionRequestId,
2406
- extra: context?.extra
2407
- }
2408
- });
2466
+ return outcome.saved;
2409
2467
  }
2410
2468
  /**
2411
2469
  * Pin the device's active wallet session to `passphraseState` (the target
@@ -2536,13 +2594,13 @@ var _TrezorAdapter = class _TrezorAdapter {
2536
2594
  }
2537
2595
  return state;
2538
2596
  }
2539
- async _resolvePassphraseState(connectId, passphraseState, signal, interactionId) {
2597
+ async _resolvePassphraseState(connectId, passphraseState, signal, operationId) {
2540
2598
  try {
2541
- const sessionId = interactionId ? this._sessions.get(this._interactions.resolve(interactionId).connectId) : await this._ensureSession(connectId, signal);
2599
+ const sessionId = operationId ? this._sessions.get(this._operations.resolve(operationId).connectId) : await this._ensureSession(connectId, signal);
2542
2600
  if (!sessionId) {
2543
2601
  return failure(
2544
- HardwareErrorCode.InteractionEnded,
2545
- "Trezor interaction connection is no longer active"
2602
+ HardwareErrorCode.OperationEnded,
2603
+ "Trezor operation connection is no longer active"
2546
2604
  );
2547
2605
  }
2548
2606
  if (passphraseState) {
@@ -2610,10 +2668,11 @@ var _TrezorAdapter = class _TrezorAdapter {
2610
2668
  const code = trustedNumericCode ?? _TrezorAdapter._mapTrezorFailureCode(error) ?? HardwareErrorCode.UnknownError;
2611
2669
  const stampedOrigin = error?.origin;
2612
2670
  const origin = stampedOrigin === "device" || stampedOrigin === "transport" || stampedOrigin === "host" ? stampedOrigin : defaultOriginForCode(code);
2671
+ const params = typed.params && typeof typed.params === "object" ? typed.params : void 0;
2613
2672
  return failure(
2614
2673
  code,
2615
2674
  typed.message ?? String(error),
2616
- void 0,
2675
+ params,
2617
2676
  origin,
2618
2677
  isHwkRecoveryHint(typed.recovery) ? typed.recovery : void 0
2619
2678
  );