@onekeyfe/hwk-trezor-adapter 1.2.2-alpha.9 → 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
@@ -7,6 +7,18 @@
7
7
 
8
8
 
9
9
 
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
10
22
 
11
23
 
12
24
 
@@ -782,9 +794,21 @@ var TREZOR_BTC_NETWORK_COIN_MAP = {
782
794
  dash: "Dash"
783
795
  };
784
796
  var _TrezorAdapter = class _TrezorAdapter {
785
- constructor(connector) {
797
+ constructor(connector, options) {
786
798
  this.vendor = "trezor";
787
799
  this._emitter = new (0, _hwkadaptercore.TypedEventEmitter)();
800
+ this._operations = new (0, _hwkadaptercore.OperationRegistry)({
801
+ vendor: "trezor",
802
+ onEnded: (operation, reason) => {
803
+ this._emitter.emit(_hwkadaptercore.SDK.OPERATION_ENDED, {
804
+ type: _hwkadaptercore.SDK.OPERATION_ENDED,
805
+ payload: { operationId: operation.operationId, reason }
806
+ });
807
+ if (reason === "timeout") {
808
+ void this._releaseOperationConnection(operation).catch(() => void 0);
809
+ }
810
+ }
811
+ });
788
812
  this._devices = /* @__PURE__ */ new Map();
789
813
  this._sessions = /* @__PURE__ */ new Map();
790
814
  this._connectingPromises = /* @__PURE__ */ new Map();
@@ -793,12 +817,21 @@ var _TrezorAdapter = class _TrezorAdapter {
793
817
  this._uiRegistry = new (0, _hwkadaptercore.UiRequestRegistry)();
794
818
  this._passphraseRequestContextByConnectId = /* @__PURE__ */ new Map();
795
819
  this._verifiedPassphraseSessionsByConnectId = /* @__PURE__ */ new Map();
820
+ /** Raw connector calls that outlive an aborted caller, keyed by session. */
821
+ this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
822
+ this._connectorIdleWaiters = /* @__PURE__ */ new Set();
823
+ this._resetPromise = null;
824
+ this._connectorTeardownTail = Promise.resolve();
825
+ this._pendingConnectorTeardowns = 0;
826
+ this._stateGeneration = 0;
827
+ this._knownDeviceConnections = /* @__PURE__ */ new Map();
796
828
  this._onDeviceConnect = (data) => {
797
829
  const device = this._connectorDeviceToDeviceInfo(data.device);
798
830
  this._devices.set(device.connectId, device);
799
831
  this._emitter.emit(_hwkadaptercore.DEVICE.CONNECT, { type: _hwkadaptercore.DEVICE.CONNECT, payload: device });
800
832
  };
801
833
  this._onDeviceDisconnect = (data) => {
834
+ this._operations.endByConnectionKey(data.connectId, "disconnect");
802
835
  this._sessions.delete(data.connectId);
803
836
  this._verifiedPassphraseSessionsByConnectId.delete(data.connectId);
804
837
  this._devices.delete(data.connectId);
@@ -848,6 +881,11 @@ var _TrezorAdapter = class _TrezorAdapter {
848
881
  });
849
882
  };
850
883
  this._connector = connector;
884
+ for (const connection of _nullishCoalesce(_optionalChain([options, 'optionalAccess', _15 => _15.knownDeviceConnections]), () => ( []))) {
885
+ if (connection.deviceId) {
886
+ this._knownDeviceConnections.set(connection.deviceId, { ...connection });
887
+ }
888
+ }
851
889
  this._jobQueue = new (0, _hwkadaptercore.DeviceJobQueue)();
852
890
  this._registerConnectorEvents();
853
891
  }
@@ -872,29 +910,47 @@ var _TrezorAdapter = class _TrezorAdapter {
872
910
  const {
873
911
  passphraseState,
874
912
  autoInstallApp: _autoInstallApp,
913
+ operationId,
875
914
  useEmptyPassphrase,
915
+ knownConnections,
916
+ extra,
917
+ allowDeviceSelection,
876
918
  ...rest
877
919
  } = params;
878
920
  return {
879
921
  passphraseState: typeof passphraseState === "string" ? passphraseState : void 0,
880
922
  useEmptyPassphrase: typeof useEmptyPassphrase === "boolean" ? useEmptyPassphrase : void 0,
923
+ operationId: typeof operationId === "string" ? operationId : void 0,
924
+ connectionContext: {
925
+ knownConnections,
926
+ extra,
927
+ allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0
928
+ },
881
929
  rest
882
930
  };
883
931
  }
884
932
  return { rest: _nullishCoalesce(params, () => ( {})) };
885
933
  }
886
934
  static _withAllNetworkRequestCommonParams(item, commonParams) {
887
- const autoInstallApp = _optionalChain([commonParams, 'optionalAccess', _15 => _15.autoInstallApp]);
888
- const passphraseState = _optionalChain([commonParams, 'optionalAccess', _16 => _16.passphraseState]);
889
- const useEmptyPassphrase = _optionalChain([commonParams, 'optionalAccess', _17 => _17.useEmptyPassphrase]);
890
- if (autoInstallApp === void 0 && passphraseState === void 0 && useEmptyPassphrase === void 0) {
935
+ const autoInstallApp = _optionalChain([commonParams, 'optionalAccess', _16 => _16.autoInstallApp]);
936
+ const passphraseState = _optionalChain([commonParams, 'optionalAccess', _17 => _17.passphraseState]);
937
+ const useEmptyPassphrase = _optionalChain([commonParams, 'optionalAccess', _18 => _18.useEmptyPassphrase]);
938
+ const operationId = _optionalChain([commonParams, 'optionalAccess', _19 => _19.operationId]);
939
+ const knownConnections = _optionalChain([commonParams, 'optionalAccess', _20 => _20.knownConnections]);
940
+ const extra = _optionalChain([commonParams, 'optionalAccess', _21 => _21.extra]);
941
+ const allowDeviceSelection = _optionalChain([commonParams, 'optionalAccess', _22 => _22.allowDeviceSelection]);
942
+ if (autoInstallApp === void 0 && passphraseState === void 0 && useEmptyPassphrase === void 0 && operationId === void 0 && knownConnections === void 0 && extra === void 0 && allowDeviceSelection === void 0) {
891
943
  return item;
892
944
  }
893
945
  return {
894
946
  ...item,
895
947
  ...autoInstallApp !== void 0 ? { autoInstallApp } : {},
896
948
  ...passphraseState !== void 0 ? { passphraseState } : {},
897
- ...useEmptyPassphrase !== void 0 ? { useEmptyPassphrase } : {}
949
+ ...useEmptyPassphrase !== void 0 ? { useEmptyPassphrase } : {},
950
+ ...operationId !== void 0 ? { operationId } : {},
951
+ ...knownConnections !== void 0 ? { knownConnections } : {},
952
+ ...extra !== void 0 ? { extra } : {},
953
+ ...allowDeviceSelection !== void 0 ? { allowDeviceSelection } : {}
898
954
  };
899
955
  }
900
956
  static _normalizeLogKey(key) {
@@ -950,8 +1006,8 @@ var _TrezorAdapter = class _TrezorAdapter {
950
1006
  };
951
1007
  }
952
1008
  static _trezorFailureMessage(error) {
953
- const response = _optionalChain([error, 'optionalAccess', _18 => _18.response]);
954
- const message = _optionalChain([response, 'optionalAccess', _19 => _19.message]);
1009
+ const response = _optionalChain([error, 'optionalAccess', _23 => _23.response]);
1010
+ const message = _optionalChain([response, 'optionalAccess', _24 => _24.message]);
955
1011
  if (!message || typeof message !== "object") return {};
956
1012
  const failureMessage = message;
957
1013
  return {
@@ -960,7 +1016,7 @@ var _TrezorAdapter = class _TrezorAdapter {
960
1016
  };
961
1017
  }
962
1018
  static _mapTrezorFailureCode(error) {
963
- const directCode = _optionalChain([error, 'optionalAccess', _20 => _20.code]);
1019
+ const directCode = _optionalChain([error, 'optionalAccess', _25 => _25.code]);
964
1020
  if (directCode === "Device_InitializeFailed") {
965
1021
  return _hwkadaptercore.HardwareErrorCode.PinCancelled;
966
1022
  }
@@ -973,7 +1029,7 @@ var _TrezorAdapter = class _TrezorAdapter {
973
1029
  if (directCode === "Failure_UnexpectedMessage") {
974
1030
  return _hwkadaptercore.HardwareErrorCode.MethodNotSupported;
975
1031
  }
976
- const message = _optionalChain([error, 'optionalAccess', _21 => _21.message]);
1032
+ const message = _optionalChain([error, 'optionalAccess', _26 => _26.message]);
977
1033
  if (typeof message === "string" && message.includes("Failed to execute 'transfer") && message.includes("on 'USBDevice'")) {
978
1034
  return _hwkadaptercore.HardwareErrorCode.TransportError;
979
1035
  }
@@ -996,6 +1052,9 @@ var _TrezorAdapter = class _TrezorAdapter {
996
1052
  if (failure2.code === "Failure_DataError" && failure2.message === "Forbidden key path") {
997
1053
  return _hwkadaptercore.HardwareErrorCode.DevicePathForbidden;
998
1054
  }
1055
+ if (failure2.code === "Failure_DataError" && typeof failure2.message === "string" && failure2.message.includes("PASSPHRASE_ALWAYS_ON_DEVICE")) {
1056
+ return _hwkadaptercore.HardwareErrorCode.PassphraseAlwaysOnDevice;
1057
+ }
999
1058
  if (failure2.code === "Failure_FirmwareError") {
1000
1059
  return _hwkadaptercore.HardwareErrorCode.MethodNotSupported;
1001
1060
  }
@@ -1015,13 +1074,11 @@ var _TrezorAdapter = class _TrezorAdapter {
1015
1074
  * selected is gone (the device evicted it from its limited slot pool). The
1016
1075
  * caller drops the stale cache entry and recreates from scratch.
1017
1076
  *
1018
- * NOTE: the exact eviction code still needs real-device confirmation — match
1019
- * conservatively so a genuine error isn't mistaken for an evicted session
1020
- * (which would silently re-prompt the passphrase).
1077
+ * Match only the protocol's explicit unallocated-channel code. Guessing from
1078
+ * an error message could replay a request after an unrelated business error.
1021
1079
  */
1022
1080
  static _isStaleSessionError(code) {
1023
- if (typeof code !== "string") return false;
1024
- return code === "ThpUnallocatedChannel" || /invalid.?session|unallocated.?session|session.?not.?found/i.test(code);
1081
+ return code === "ThpUnallocatedChannel";
1025
1082
  }
1026
1083
  static _errorCode(error) {
1027
1084
  return _nullishCoalesce(error.code, () => ( _TrezorAdapter._trezorFailureMessage(error).code));
@@ -1035,13 +1092,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1035
1092
  return protocolError.name === "TrezorProtocolError" && protocolError.code === "Malformed protocol format" && protocolError.message === "Malformed protocol format";
1036
1093
  }
1037
1094
  /**
1038
- * Race a promise against an abort signal. Copied from LedgerAdapter:
1039
- * IConnector.call() can't actually be cancelled at the protocol level, but
1040
- * the caller gets the abort immediately and the underlying work completes
1041
- * uselessly in the background.
1095
+ * Race a promise against an abort signal. IConnector.call() can't actually
1096
+ * be cancelled at the protocol level, so the caller gets the abort while the
1097
+ * adapter keeps the transport exclusively reserved until the raw call drains.
1042
1098
  */
1043
1099
  static _abortable(signal, promise) {
1044
1100
  if (signal.aborted) {
1101
+ void promise.catch(() => void 0);
1045
1102
  return Promise.reject(
1046
1103
  _nullishCoalesce(signal.reason, () => ( new Error("Aborted")))
1047
1104
  );
@@ -1063,17 +1120,37 @@ var _TrezorAdapter = class _TrezorAdapter {
1063
1120
  );
1064
1121
  });
1065
1122
  }
1066
- async _connectSession(connectId, signal) {
1123
+ async _connectSession(connectId, signal, stateGeneration, transportType) {
1067
1124
  const connectOnce = async () => {
1068
- const connectPromise = this._connector.connect(connectId);
1069
- if (!signal) return connectPromise;
1125
+ if (_optionalChain([signal, 'optionalAccess', _27 => _27.aborted])) {
1126
+ throw _nullishCoalesce(signal.reason, () => ( new Error("Aborted")));
1127
+ }
1128
+ this._assertConnectorIdle("connectDevice");
1129
+ const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
1130
+ let connectPromise;
1070
1131
  try {
1071
- return await _TrezorAdapter._abortable(signal, connectPromise);
1132
+ connectPromise = transportType ? this._connector.connect(connectId, { transportType }) : this._connector.connect(connectId);
1072
1133
  } catch (error) {
1073
- void connectPromise.then(
1074
- (orphan) => this._connector.disconnect(orphan.sessionId).catch(() => void 0),
1075
- () => void 0
1076
- );
1134
+ releaseOperation();
1135
+ throw error;
1136
+ }
1137
+ if (!signal) {
1138
+ try {
1139
+ return await connectPromise;
1140
+ } finally {
1141
+ releaseOperation();
1142
+ }
1143
+ }
1144
+ try {
1145
+ const session2 = await _TrezorAdapter._abortable(signal, connectPromise);
1146
+ releaseOperation();
1147
+ return session2;
1148
+ } catch (error) {
1149
+ if (signal.aborted) {
1150
+ void connectPromise.then((orphan) => this._connector.disconnect(orphan.sessionId).catch(() => void 0)).catch(() => void 0).finally(releaseOperation);
1151
+ } else {
1152
+ releaseOperation();
1153
+ }
1077
1154
  throw error;
1078
1155
  }
1079
1156
  };
@@ -1081,7 +1158,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1081
1158
  try {
1082
1159
  session = await connectOnce();
1083
1160
  } catch (error) {
1084
- if (_optionalChain([signal, 'optionalAccess', _22 => _22.aborted]) || !_TrezorAdapter._isRecoverableProtocolResidueError(error)) {
1161
+ if (_optionalChain([signal, 'optionalAccess', _28 => _28.aborted]) || !_TrezorAdapter._isRecoverableProtocolResidueError(error)) {
1085
1162
  throw error;
1086
1163
  }
1087
1164
  debugLog("[TrezorAdapter] retrying connect after draining a stale THP frame", {
@@ -1089,8 +1166,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1089
1166
  });
1090
1167
  session = await connectOnce();
1091
1168
  }
1092
- if (this._disconnectRequested.delete(connectId)) {
1093
- await this._connector.disconnect(session.sessionId).catch(() => void 0);
1169
+ if (stateGeneration !== this._stateGeneration || this._disconnectRequested.delete(connectId)) {
1170
+ const releaseCleanup = this._retainConnectorOperation(`disconnect:${session.sessionId}`);
1171
+ try {
1172
+ await this._connector.disconnect(session.sessionId).catch(() => void 0);
1173
+ } finally {
1174
+ releaseCleanup();
1175
+ }
1094
1176
  throw Object.assign(new Error("Trezor connection aborted"), {
1095
1177
  code: _hwkadaptercore.HardwareErrorCode.UserAborted
1096
1178
  });
@@ -1106,14 +1188,9 @@ var _TrezorAdapter = class _TrezorAdapter {
1106
1188
  async init(_config) {
1107
1189
  }
1108
1190
  async dispose() {
1191
+ await this._resetStateAndDisconnectSessions();
1109
1192
  this._unregisterConnectorEvents();
1110
- this._uiRegistry.reset();
1111
- this._jobQueue.clear();
1112
1193
  this._connector.reset();
1113
- this._devices.clear();
1114
- this._sessions.clear();
1115
- this._verifiedPassphraseSessionsByConnectId.clear();
1116
- this._connectingPromises.clear();
1117
1194
  this._emitter.removeAllListeners();
1118
1195
  }
1119
1196
  /**
@@ -1122,12 +1199,30 @@ var _TrezorAdapter = class _TrezorAdapter {
1122
1199
  * error flows. Aborts active jobs and releases UI waits.
1123
1200
  */
1124
1201
  resetState() {
1202
+ void this._resetStateAndDisconnectSessions();
1203
+ }
1204
+ _resetStateAndDisconnectSessions() {
1205
+ if (this._resetPromise) return this._resetPromise;
1206
+ const sessionIds = new Set(this._sessions.values());
1207
+ this._stateGeneration += 1;
1208
+ this._operations.endAll("runtime-reset");
1125
1209
  this._uiRegistry.reset();
1126
1210
  this._jobQueue.clear();
1127
1211
  this._devices.clear();
1128
1212
  this._sessions.clear();
1129
1213
  this._verifiedPassphraseSessionsByConnectId.clear();
1130
1214
  this._connectingPromises.clear();
1215
+ const resetPromise = this._runConnectorTeardown(async () => {
1216
+ for (const sessionId of sessionIds) {
1217
+ await this._connector.disconnect(sessionId).catch(() => void 0);
1218
+ }
1219
+ });
1220
+ this._resetPromise = resetPromise;
1221
+ return resetPromise.finally(() => {
1222
+ if (this._resetPromise === resetPromise) {
1223
+ this._resetPromise = null;
1224
+ }
1225
+ });
1131
1226
  }
1132
1227
  getAvailableTransports() {
1133
1228
  return this.activeTransport ? [this.activeTransport] : [];
@@ -1135,32 +1230,151 @@ var _TrezorAdapter = class _TrezorAdapter {
1135
1230
  async switchTransport(_type) {
1136
1231
  }
1137
1232
  async searchDevices(options) {
1138
- await this._ensureDevicePermission();
1233
+ return this._searchDevices(options);
1234
+ }
1235
+ async _searchDevices(options, signal) {
1236
+ if (_optionalChain([options, 'optionalAccess', _29 => _29.transportType]) && !(_nullishCoalesce(this._connector.availableTransports, () => ( [this._connector.connectionType]))).includes(
1237
+ options.transportType
1238
+ ))
1239
+ return [];
1240
+ if (_optionalChain([options, 'optionalAccess', _30 => _30.resetSession])) {
1241
+ await this._resetStateAndDisconnectSessions();
1242
+ } else {
1243
+ await this._connectorTeardownTail;
1244
+ }
1245
+ if (_optionalChain([signal, 'optionalAccess', _31 => _31.aborted])) throw signal.reason;
1246
+ const stateGeneration = this._stateGeneration;
1139
1247
  const scanned = await this._connector.searchDevices(
1140
- _optionalChain([options, 'optionalAccess', _23 => _23.waitForAllTransports]) ? { waitForAll: true } : void 0
1248
+ _optionalChain([options, 'optionalAccess', _32 => _32.waitForAllTransports]) || _optionalChain([options, 'optionalAccess', _33 => _33.transportType]) ? {
1249
+ ...options.waitForAllTransports ? { waitForAll: true } : {},
1250
+ ...options.transportType ? { transportType: options.transportType } : {}
1251
+ } : void 0
1141
1252
  );
1253
+ if (_optionalChain([signal, 'optionalAccess', _34 => _34.aborted])) throw signal.reason;
1254
+ if (stateGeneration !== this._stateGeneration) {
1255
+ throw _hwkadaptercore.createHwkError.call(void 0, {
1256
+ code: _hwkadaptercore.HardwareErrorCode.UserAborted,
1257
+ message: "Trezor discovery belongs to an ended operation"
1258
+ });
1259
+ }
1142
1260
  const scannedIds = new Set(scanned.map((d) => d.connectId));
1143
1261
  for (const connectId of [...this._devices.keys()]) {
1144
- if (!scannedIds.has(connectId) && !this._sessions.has(connectId)) {
1262
+ if ((!_optionalChain([options, 'optionalAccess', _35 => _35.transportType]) || _optionalChain([this, 'access', _36 => _36._devices, 'access', _37 => _37.get, 'call', _38 => _38(connectId), 'optionalAccess', _39 => _39.connectionType]) === options.transportType) && !scannedIds.has(connectId) && !this._sessions.has(connectId)) {
1145
1263
  this._devices.delete(connectId);
1146
1264
  }
1147
1265
  }
1148
1266
  for (const device of scanned) {
1149
1267
  const info = this._connectorDeviceToDeviceInfo(device);
1150
- this._devices.set(info.connectId, info);
1268
+ if (!this._sessions.has(info.connectId)) this._devices.set(info.connectId, info);
1269
+ }
1270
+ return Array.from(this._devices.values()).filter(
1271
+ (device) => !_optionalChain([options, 'optionalAccess', _40 => _40.transportType]) || device.connectionType === options.transportType
1272
+ );
1273
+ }
1274
+ async searchDeviceTargets(options) {
1275
+ const devices = await this.searchDevices(options);
1276
+ return devices.map((device) => ({
1277
+ searchTargetId: device.connectId,
1278
+ searchTargetReusePolicy: _hwkadaptercore.resolveSearchTargetReusePolicy.call(void 0, device),
1279
+ vendor: "trezor",
1280
+ connectionType: device.connectionType,
1281
+ kind: "physical",
1282
+ label: device.label,
1283
+ model: device.model,
1284
+ modelName: device.modelName,
1285
+ serialNumber: device.serialNumber
1286
+ }));
1287
+ }
1288
+ async listConnectionTargets(options) {
1289
+ const targets = await this.searchDeviceTargets(options);
1290
+ return targets.map(({ searchTargetId, ...target }) => ({
1291
+ ...target,
1292
+ targetId: searchTargetId
1293
+ }));
1294
+ }
1295
+ async bindBleDevice(params) {
1296
+ if (params.identity.vendor !== "trezor" || !params.identity.value) {
1297
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.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 _hwkadaptercore.createHwkError.call(void 0, {
1320
+ code: _hwkadaptercore.HardwareErrorCode.UnknownError,
1321
+ message: "Bluetooth binding could not be saved",
1322
+ origin: "host"
1323
+ });
1324
+ }
1325
+ this._rememberVerifiedConnection(deviceId, selected.connectId);
1326
+ return _hwkadaptercore.success.call(void 0, 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);
1151
1340
  }
1152
- return Array.from(this._devices.values());
1153
1341
  }
1154
- async connectDevice(connectId) {
1342
+ async connectDevice(searchTargetId) {
1155
1343
  try {
1156
- await this._ensureDevicePermission(connectId);
1157
- await this._ensureSession(connectId);
1158
- return _hwkadaptercore.success.call(void 0, connectId);
1344
+ await this._ensureDevicePermission(
1345
+ searchTargetId,
1346
+ void 0,
1347
+ _optionalChain([this, 'access', _41 => _41._devices, 'access', _42 => _42.get, 'call', _43 => _43(searchTargetId), 'optionalAccess', _44 => _44.connectionType])
1348
+ );
1349
+ await this._ensureSession(searchTargetId);
1350
+ const device = this._devices.get(searchTargetId);
1351
+ if (!device) {
1352
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.DeviceNotFound, "Trezor device not found after connect");
1353
+ }
1354
+ this._operations.endByConnectionKey(searchTargetId, "explicit");
1355
+ const operation = this._operations.create({
1356
+ searchTargetId,
1357
+ connectId: searchTargetId,
1358
+ device,
1359
+ connectionKeys: [_nullishCoalesce(this._sessions.get(searchTargetId), () => ( ""))]
1360
+ });
1361
+ return _hwkadaptercore.success.call(void 0, operation.operationId);
1159
1362
  } catch (error) {
1160
1363
  return this._errorToFailure(error);
1161
1364
  }
1162
1365
  }
1163
- async disconnectDevice(connectId) {
1366
+ async releaseOperation(operationId) {
1367
+ const operation = this._operations.find(operationId);
1368
+ if (!operation) {
1369
+ this._operations.resolve(operationId);
1370
+ return;
1371
+ }
1372
+ const endedOperation = this._operations.end(operationId, "explicit");
1373
+ if (!endedOperation) return;
1374
+ await this._releaseOperationConnection(endedOperation);
1375
+ }
1376
+ async _releaseOperationConnection(operation) {
1377
+ const { connectId } = operation;
1164
1378
  const connecting = this._connectingPromises.has(connectId);
1165
1379
  this._connectingPromises.delete(connectId);
1166
1380
  const sessionId = this._sessions.get(connectId);
@@ -1170,87 +1384,119 @@ var _TrezorAdapter = class _TrezorAdapter {
1170
1384
  }
1171
1385
  this._sessions.delete(connectId);
1172
1386
  this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1173
- await this._connector.disconnect(sessionId);
1387
+ await this._runConnectorTeardown(() => this._connector.disconnect(sessionId));
1174
1388
  }
1175
- async getDeviceInfo(connectId, deviceId) {
1389
+ async getDeviceInfo(connectIdOrOperationId, deviceId) {
1390
+ let connectId;
1391
+ try {
1392
+ connectId = _hwkadaptercore.isHardwareOperationId.call(void 0, connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
1393
+ } catch (error) {
1394
+ return this._errorToFailure(error);
1395
+ }
1176
1396
  const device = _nullishCoalesce(this._devices.get(connectId), () => ( Array.from(this._devices.values()).find((item) => item.deviceId === deviceId)));
1177
1397
  if (!device) return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.DeviceNotFound, "Trezor device not found");
1178
1398
  return _hwkadaptercore.success.call(void 0, device);
1179
1399
  }
1180
- getSupportedChains() {
1181
- return ["btc", "evm", "sol", "tron"];
1182
- }
1183
1400
  async allNetworkGetAddress(connectId, deviceId, params) {
1401
+ const target = _hwkadaptercore.resolveHardwareOperationTarget.call(void 0, connectId, params.operationId, "trezor");
1402
+ if (!target.success) return target;
1403
+ let effectiveTargetId = _nullishCoalesce(target.payload.targetId, () => ( ""));
1404
+ let releaseOperationRetention;
1405
+ try {
1406
+ releaseOperationRetention = target.payload.operationId ? this._operations.retain(target.payload.operationId) : void 0;
1407
+ } catch (error) {
1408
+ return this._errorToFailure(error);
1409
+ }
1184
1410
  let liveDeviceId = "";
1411
+ const bundleContext = {};
1185
1412
  const topLevelFailureIndexes = /* @__PURE__ */ new Set();
1186
1413
  const isSingleNetworkBundle = params.bundle.every(
1187
- (item) => item.network === _optionalChain([params, 'access', _24 => _24.bundle, 'access', _25 => _25[0], 'optionalAccess', _26 => _26.network])
1414
+ (item) => item.network === _optionalChain([params, 'access', _45 => _45.bundle, 'access', _46 => _46[0], 'optionalAccess', _47 => _47.network])
1188
1415
  );
1189
- const getLiveDeviceId = async () => {
1416
+ const getLiveDeviceId = async (expectedDeviceId) => {
1190
1417
  if (liveDeviceId) return _hwkadaptercore.success.call(void 0, liveDeviceId);
1191
- const deviceIdResponse = await this._getTrezorDeviceId(connectId);
1418
+ const deviceIdResponse = await this._getTrezorDeviceId(
1419
+ effectiveTargetId,
1420
+ expectedDeviceId || deviceId,
1421
+ params,
1422
+ bundleContext
1423
+ );
1192
1424
  if (deviceIdResponse.success) {
1193
1425
  liveDeviceId = deviceIdResponse.payload;
1426
+ if (!target.payload.operationId) {
1427
+ effectiveTargetId = _nullishCoalesce(_optionalChain([bundleContext, 'access', _48 => _48.connection, 'optionalAccess', _49 => _49.connectId]), () => ( effectiveTargetId));
1428
+ }
1194
1429
  }
1195
1430
  return deviceIdResponse;
1196
1431
  };
1197
- return _hwkadaptercore.runAllNetworkGetAddress.call(void 0, {
1198
- connectId,
1199
- deviceId,
1200
- params,
1201
- normalizeItem: _TrezorAdapter._normalizeAllNetworkItem,
1202
- callItem: async ({ method, item, index }) => {
1203
- const expectedDeviceId = _nullishCoalesce(_TrezorAdapter._getItemDeviceId(item), () => ( deviceId));
1204
- if (expectedDeviceId) {
1205
- const fingerprint = await getLiveDeviceId();
1206
- if (!fingerprint.success) {
1207
- topLevelFailureIndexes.add(index);
1208
- return fingerprint;
1209
- }
1210
- if (expectedDeviceId !== fingerprint.payload) {
1211
- topLevelFailureIndexes.add(index);
1212
- return _hwkadaptercore.failure.call(void 0,
1213
- _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
1214
- `Wrong device: expected ${expectedDeviceId}, got ${fingerprint.payload}`,
1215
- { expected: expectedDeviceId, actual: fingerprint.payload }
1216
- );
1432
+ try {
1433
+ return await _hwkadaptercore.runAllNetworkGetAddress.call(void 0, {
1434
+ connectId: effectiveTargetId,
1435
+ deviceId,
1436
+ params,
1437
+ normalizeItem: _TrezorAdapter._normalizeAllNetworkItem,
1438
+ callItem: async ({ method, item, index }) => {
1439
+ const expectedDeviceId = _nullishCoalesce(_TrezorAdapter._getItemDeviceId(item), () => ( deviceId));
1440
+ if (expectedDeviceId) {
1441
+ const fingerprint = await getLiveDeviceId(expectedDeviceId);
1442
+ if (!fingerprint.success) {
1443
+ topLevelFailureIndexes.add(index);
1444
+ return fingerprint;
1445
+ }
1446
+ if (expectedDeviceId !== fingerprint.payload) {
1447
+ topLevelFailureIndexes.add(index);
1448
+ return _hwkadaptercore.failure.call(void 0,
1449
+ _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
1450
+ `Wrong device: expected ${expectedDeviceId}, got ${fingerprint.payload}`,
1451
+ { expected: expectedDeviceId, actual: fingerprint.payload }
1452
+ );
1453
+ }
1217
1454
  }
1218
- }
1219
- const callItem = _TrezorAdapter._withAllNetworkRequestCommonParams(item, params);
1220
- return this._callAllNetworkMethod(connectId, method, callItem);
1221
- },
1222
- attachIdentity: async ({ item, chain, payload }) => {
1223
- if (!liveDeviceId) {
1224
- const fingerprint = await getLiveDeviceId();
1225
- if (!fingerprint.success) {
1226
- return { ...item, success: false, payload: fingerprint.payload };
1455
+ const callItem = _TrezorAdapter._withAllNetworkRequestCommonParams(item, params);
1456
+ return this._callAllNetworkMethod(effectiveTargetId, method, callItem, bundleContext);
1457
+ },
1458
+ attachIdentity: async ({ item, chain, payload }) => {
1459
+ if (!liveDeviceId) {
1460
+ const fingerprint = await getLiveDeviceId(deviceId);
1461
+ if (!fingerprint.success) {
1462
+ return { ...item, success: false, payload: fingerprint.payload };
1463
+ }
1227
1464
  }
1465
+ return {
1466
+ ...item,
1467
+ success: true,
1468
+ payload: {
1469
+ ...payload,
1470
+ deviceIdentity: {
1471
+ vendor: "trezor",
1472
+ type: "deviceId",
1473
+ value: liveDeviceId
1474
+ },
1475
+ chainFingerprint: liveDeviceId,
1476
+ chainFingerprintChain: chain
1477
+ }
1478
+ };
1479
+ },
1480
+ // One named boolean per abort reason.
1481
+ shouldAbortBundle: (response, { index }) => {
1482
+ const isSessionLevelFailure = topLevelFailureIndexes.has(index);
1483
+ const isWholeChainForbidden = isSingleNetworkBundle && _optionalChain([response, 'access', _50 => _50.payload, 'optionalAccess', _51 => _51.code]) === _hwkadaptercore.HardwareErrorCode.DevicePathForbidden;
1484
+ const isPassphrasePolicyFailure = _optionalChain([response, 'access', _52 => _52.payload, 'optionalAccess', _53 => _53.code]) === _hwkadaptercore.HardwareErrorCode.PassphraseAlwaysOnDevice;
1485
+ const isConnectionLost = _optionalChain([response, 'access', _54 => _54.payload, 'optionalAccess', _55 => _55.code]) === _hwkadaptercore.HardwareErrorCode.DeviceDisconnected || _optionalChain([response, 'access', _56 => _56.payload, 'optionalAccess', _57 => _57.code]) === _hwkadaptercore.HardwareErrorCode.OperationTimeout || _optionalChain([response, 'access', _58 => _58.payload, 'optionalAccess', _59 => _59.code]) === _hwkadaptercore.HardwareErrorCode.TransportError || _optionalChain([response, 'access', _60 => _60.payload, 'optionalAccess', _61 => _61.code]) === _hwkadaptercore.HardwareErrorCode.OperationEnded || _optionalChain([response, 'access', _62 => _62.payload, 'optionalAccess', _63 => _63.code]) === _hwkadaptercore.HardwareErrorCode.OperationNotFound;
1486
+ return isSessionLevelFailure || isWholeChainForbidden || isPassphrasePolicyFailure || isConnectionLost;
1228
1487
  }
1229
- return {
1230
- ...item,
1231
- success: true,
1232
- payload: {
1233
- ...payload,
1234
- deviceIdentity: {
1235
- vendor: "trezor",
1236
- type: "deviceId",
1237
- value: liveDeviceId
1238
- },
1239
- chainFingerprint: liveDeviceId,
1240
- chainFingerprintChain: chain
1241
- }
1242
- };
1243
- },
1244
- // One named boolean per abort reason.
1245
- shouldAbortBundle: (response, { index }) => {
1246
- const isSessionLevelFailure = topLevelFailureIndexes.has(index);
1247
- const isWholeChainForbidden = isSingleNetworkBundle && _optionalChain([response, 'access', _27 => _27.payload, 'optionalAccess', _28 => _28.code]) === _hwkadaptercore.HardwareErrorCode.DevicePathForbidden;
1248
- return isSessionLevelFailure || isWholeChainForbidden;
1249
- }
1250
- });
1488
+ });
1489
+ } finally {
1490
+ _optionalChain([releaseOperationRetention, 'optionalCall', _64 => _64()]);
1491
+ }
1251
1492
  }
1252
- getFeatures(connectId) {
1253
- return this._callDeviceManagerMethod("getFeatures", connectId, {});
1493
+ getFeatures(connectId, operationContext) {
1494
+ return this._callDeviceManagerMethod(
1495
+ "getFeatures",
1496
+ connectId,
1497
+ {},
1498
+ operationContext
1499
+ );
1254
1500
  }
1255
1501
  /**
1256
1502
  * Device authenticity attestation. Sends a host-generated challenge, has the
@@ -1285,7 +1531,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1285
1531
  if (!internalModel) {
1286
1532
  return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.UnknownError, "Device internal_model unavailable");
1287
1533
  }
1288
- const challengeHex = _nullishCoalesce(_optionalChain([params, 'access', _29 => _29.challenge, 'optionalAccess', _30 => _30.toLowerCase, 'call', _31 => _31()]), () => ( Buffer.from(_utils.randomBytes.call(void 0, 32)).toString("hex")));
1534
+ const challengeHex = _nullishCoalesce(_optionalChain([params, 'access', _65 => _65.challenge, 'optionalAccess', _66 => _66.toLowerCase, 'call', _67 => _67()]), () => ( Buffer.from(_utils.randomBytes.call(void 0, 32)).toString("hex")));
1289
1535
  const challenge = Buffer.from(challengeHex, "hex");
1290
1536
  const proofRes = await this._callDeviceManagerMethod(
1291
1537
  "authenticateDevice",
@@ -1310,28 +1556,64 @@ var _TrezorAdapter = class _TrezorAdapter {
1310
1556
  }
1311
1557
  });
1312
1558
  }
1313
- deviceSettings(connectId, params) {
1559
+ deviceSettings(connectId, params, operationContext) {
1314
1560
  return this._callDeviceManagerMethod(
1315
1561
  "deviceSettings",
1316
1562
  connectId,
1317
- params
1563
+ params,
1564
+ operationContext
1318
1565
  );
1319
1566
  }
1320
- setBrightness(connectId, params = {}) {
1567
+ setBrightness(connectId, params = {}, operationContext) {
1321
1568
  return this._callDeviceManagerMethod(
1322
1569
  "setBrightness",
1323
1570
  connectId,
1324
- params
1571
+ params,
1572
+ operationContext
1325
1573
  );
1326
1574
  }
1327
- changePin(connectId, params = {}) {
1328
- return this._callDeviceManagerMethod("changePin", connectId, params);
1575
+ changePin(connectId, params = {}, operationContext) {
1576
+ return this._callDeviceManagerMethod(
1577
+ "changePin",
1578
+ connectId,
1579
+ params,
1580
+ operationContext
1581
+ );
1329
1582
  }
1330
- wipeDevice(connectId) {
1331
- return this._callDeviceManagerMethod("wipeDevice", connectId, {});
1583
+ wipeDevice(connectId, operationContext) {
1584
+ return this._callDeviceManagerMethod(
1585
+ "wipeDevice",
1586
+ connectId,
1587
+ {},
1588
+ operationContext
1589
+ );
1332
1590
  }
1333
- async _getTrezorDeviceId(connectId) {
1334
- const features = await this.getFeatures(connectId);
1591
+ async _getTrezorDeviceId(connectId, expectedDeviceId, commonParams, bundleContext) {
1592
+ const hasConnectionContext = _optionalChain([commonParams, 'optionalAccess', _68 => _68.knownConnections]) !== void 0 || _optionalChain([commonParams, 'optionalAccess', _69 => _69.extra]) !== void 0;
1593
+ const features = bundleContext || hasConnectionContext ? await this._callMethod(
1594
+ "getFeatures",
1595
+ connectId,
1596
+ hasConnectionContext || !connectId ? expectedDeviceId : void 0,
1597
+ hasConnectionContext ? {
1598
+ operationId: commonParams.operationId,
1599
+ knownConnections: commonParams.knownConnections,
1600
+ extra: commonParams.extra,
1601
+ allowDeviceSelection: commonParams.allowDeviceSelection,
1602
+ passphraseState: commonParams.passphraseState,
1603
+ useEmptyPassphrase: commonParams.useEmptyPassphrase
1604
+ } : {},
1605
+ false,
1606
+ bundleContext
1607
+ ) : await this.getFeatures(
1608
+ connectId,
1609
+ !connectId && expectedDeviceId ? {
1610
+ expectedDeviceIdentity: {
1611
+ vendor: "trezor",
1612
+ type: "deviceId",
1613
+ value: expectedDeviceId
1614
+ }
1615
+ } : void 0
1616
+ );
1335
1617
  if (!features.success) return features;
1336
1618
  const liveDeviceId = features.payload.device_id;
1337
1619
  if (typeof liveDeviceId !== "string" || liveDeviceId.length === 0) {
@@ -1345,14 +1627,30 @@ var _TrezorAdapter = class _TrezorAdapter {
1345
1627
  });
1346
1628
  this._uiRegistry.cancel();
1347
1629
  this._connector.uiResponse({ type: _hwkadaptercore.UI_RESPONSE.CANCEL });
1348
- if (connectId) {
1349
- this._jobQueue.forceCancelActive(connectId, userAbortReason);
1350
- void this._connector.cancel(_nullishCoalesce(this._sessions.get(connectId), () => ( connectId)));
1630
+ const activeJobId = _optionalChain([this, 'access', _70 => _70._jobQueue, 'access', _71 => _71.getActiveJob, 'call', _72 => _72(), 'optionalAccess', _73 => _73.deviceId]);
1631
+ const targetId = _nullishCoalesce(connectId, () => ( activeJobId));
1632
+ if (targetId) {
1633
+ let resolvedConnectId = targetId;
1634
+ let operationId;
1635
+ if (_hwkadaptercore.isHardwareOperationId.call(void 0, targetId)) {
1636
+ try {
1637
+ operationId = targetId;
1638
+ resolvedConnectId = this._operations.resolve(targetId).connectId;
1639
+ } catch (e5) {
1640
+ resolvedConnectId = "";
1641
+ }
1642
+ }
1643
+ const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
1644
+ this._jobQueue.cancelActive(
1645
+ _nullishCoalesce(_nullishCoalesce(operationId, () => ( (activeJobId === targetId ? targetId : _optionalChain([interactionForPhysicalId, 'optionalAccess', _74 => _74.operationId])))), () => ( targetId)),
1646
+ userAbortReason
1647
+ );
1648
+ if (resolvedConnectId) {
1649
+ void this._connector.cancel(_nullishCoalesce(this._sessions.get(resolvedConnectId), () => ( resolvedConnectId)));
1650
+ }
1351
1651
  return;
1352
1652
  }
1353
- for (const cid of this._sessions.keys()) {
1354
- this._jobQueue.forceCancelActive(cid, userAbortReason);
1355
- }
1653
+ this._jobQueue.cancelActive(void 0, userAbortReason);
1356
1654
  for (const sessionId of this._sessions.values()) {
1357
1655
  void this._connector.cancel(sessionId);
1358
1656
  }
@@ -1365,6 +1663,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1365
1663
  */
1366
1664
  uiResponse(response) {
1367
1665
  this._uiRegistry.resolve(response.type, response.payload);
1666
+ if (response.type === _hwkadaptercore.UI_RESPONSE.RECEIVE_SELECT_DEVICE) return;
1368
1667
  this._connector.uiResponse(response);
1369
1668
  }
1370
1669
  /**
@@ -1376,8 +1675,8 @@ var _TrezorAdapter = class _TrezorAdapter {
1376
1675
  * Unlike Ledger (which hashes a per-chain address), it is chain-global, so
1377
1676
  * `chain` is accepted for API parity but ignored.
1378
1677
  */
1379
- async getChainFingerprint(connectId, _deviceId, _chain) {
1380
- return this._getTrezorDeviceId(connectId);
1678
+ async getChainFingerprint(connectId, deviceId, _chain) {
1679
+ return this._getTrezorDeviceId(connectId, deviceId);
1381
1680
  }
1382
1681
  /**
1383
1682
  * Resolve the active passphrase wallet identity (`passphraseState`).
@@ -1387,17 +1686,93 @@ var _TrezorAdapter = class _TrezorAdapter {
1387
1686
  * can't interleave with a chain call on the same device (which would desync
1388
1687
  * the THP session selection).
1389
1688
  */
1390
- getPassphraseState(connectId, passphraseState) {
1391
- return this._jobQueue.enqueue(
1689
+ getPassphraseState(connectId, passphraseState, operationContext) {
1690
+ const expectedIdentity = _optionalChain([operationContext, 'optionalAccess', _75 => _75.expectedDeviceIdentity]);
1691
+ if (expectedIdentity && (expectedIdentity.vendor !== "trezor" || expectedIdentity.type !== "deviceId")) {
1692
+ return Promise.resolve(
1693
+ _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.InvalidParams, "Trezor device identity is required")
1694
+ );
1695
+ }
1696
+ const target = _hwkadaptercore.resolveHardwareOperationTarget.call(void 0,
1392
1697
  connectId,
1698
+ _optionalChain([operationContext, 'optionalAccess', _76 => _76.operationId]),
1699
+ "trezor"
1700
+ );
1701
+ if (!target.success) return Promise.resolve(target);
1702
+ const { operationId } = target.payload;
1703
+ let resolvedConnectId = _nullishCoalesce(target.payload.targetId, () => ( ""));
1704
+ let releaseOperationRetention;
1705
+ if (operationId) {
1706
+ try {
1707
+ resolvedConnectId = this._operations.resolve(operationId).connectId;
1708
+ releaseOperationRetention = this._operations.retain(operationId);
1709
+ } catch (error) {
1710
+ return Promise.resolve(this._errorToFailure(error));
1711
+ }
1712
+ }
1713
+ return this._jobQueue.enqueue(
1714
+ operationId || resolvedConnectId,
1393
1715
  async (signal) => {
1394
- const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {
1395
- passphraseState
1396
- });
1716
+ let selectionRequestId;
1717
+ let verified = false;
1397
1718
  try {
1398
- return await this._resolvePassphraseState(connectId, passphraseState, signal);
1719
+ if (!operationId && _optionalChain([expectedIdentity, 'optionalAccess', _77 => _77.value]) && (!connectId || _optionalChain([operationContext, 'optionalAccess', _78 => _78.knownConnections]) !== void 0)) {
1720
+ const resolved = await this._resolveExpectedDeviceConnectId(
1721
+ expectedIdentity.value,
1722
+ signal,
1723
+ operationContext
1724
+ );
1725
+ resolvedConnectId = resolved.connectId;
1726
+ selectionRequestId = resolved.selectionRequestId;
1727
+ }
1728
+ if (_optionalChain([expectedIdentity, 'optionalAccess', _79 => _79.value])) {
1729
+ if (!operationId) await this._ensureSession(resolvedConnectId, signal);
1730
+ if (_optionalChain([this, 'access', _80 => _80._devices, 'access', _81 => _81.get, 'call', _82 => _82(resolvedConnectId), 'optionalAccess', _83 => _83.deviceId]) !== expectedIdentity.value) {
1731
+ return _hwkadaptercore.failure.call(void 0,
1732
+ _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
1733
+ "Trezor device identity does not match"
1734
+ );
1735
+ }
1736
+ }
1737
+ const restorePassphraseRequestContext = this._setPassphraseRequestContext(
1738
+ resolvedConnectId,
1739
+ {
1740
+ passphraseState
1741
+ }
1742
+ );
1743
+ try {
1744
+ const result = await this._resolvePassphraseState(
1745
+ resolvedConnectId,
1746
+ passphraseState,
1747
+ signal,
1748
+ operationId
1749
+ );
1750
+ if (result.success && _optionalChain([expectedIdentity, 'optionalAccess', _84 => _84.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();
1764
+ }
1399
1765
  } finally {
1400
- restorePassphraseRequestContext();
1766
+ if (selectionRequestId && !verified) {
1767
+ this._emitter.emit(_hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS, {
1768
+ type: _hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS,
1769
+ payload: {
1770
+ selectionRequestId,
1771
+ status: signal.aborted ? "cancelled" : "failed"
1772
+ }
1773
+ });
1774
+ await this._releaseProvisionalConnection(resolvedConnectId, signal);
1775
+ }
1401
1776
  }
1402
1777
  },
1403
1778
  {
@@ -1405,7 +1780,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1405
1780
  rejectIfBusy: true,
1406
1781
  busyError: _TrezorAdapter._createDeviceBusyError("getPassphraseState")
1407
1782
  }
1408
- ).catch((error) => this._errorToFailure(error));
1783
+ ).catch((error) => this._errorToFailure(error)).finally(() => _optionalChain([releaseOperationRetention, 'optionalCall', _85 => _85()]));
1409
1784
  }
1410
1785
  evmGetAddress(connectId, deviceId, params) {
1411
1786
  return this._callWalletMethod("evmGetAddress", connectId, deviceId, params);
@@ -1468,11 +1843,86 @@ var _TrezorAdapter = class _TrezorAdapter {
1468
1843
  }
1469
1844
  return result;
1470
1845
  }
1471
- static async _callConnector(connector, sessionId, method, params, signal) {
1472
- const promise = connector.call(sessionId, method, params);
1846
+ async _callConnector(sessionId, method, params, signal) {
1847
+ if (_optionalChain([signal, 'optionalAccess', _86 => _86.aborted])) {
1848
+ throw _nullishCoalesce(signal.reason, () => ( new Error("Aborted")));
1849
+ }
1850
+ this._assertConnectorIdle(method);
1851
+ const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
1852
+ let promise;
1853
+ try {
1854
+ promise = this._connector.call(sessionId, method, params).finally(() => {
1855
+ releaseOperation();
1856
+ });
1857
+ } catch (error) {
1858
+ releaseOperation();
1859
+ throw error;
1860
+ }
1473
1861
  const result = signal ? await _TrezorAdapter._abortable(signal, promise) : await promise;
1474
1862
  return _TrezorAdapter._unwrapConnectorResult(result);
1475
1863
  }
1864
+ _assertConnectorIdle(method) {
1865
+ if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
1866
+ throw _TrezorAdapter._createDeviceBusyError(method);
1867
+ }
1868
+ }
1869
+ _runConnectorTeardown(task) {
1870
+ const previous = this._connectorTeardownTail;
1871
+ let releaseTail = () => void 0;
1872
+ this._connectorTeardownTail = new Promise((resolve) => {
1873
+ releaseTail = resolve;
1874
+ });
1875
+ this._pendingConnectorTeardowns += 1;
1876
+ return (async () => {
1877
+ try {
1878
+ await previous;
1879
+ await this._waitForConnectorOperationsToDrain();
1880
+ await task();
1881
+ } finally {
1882
+ this._pendingConnectorTeardowns -= 1;
1883
+ releaseTail();
1884
+ }
1885
+ })();
1886
+ }
1887
+ async _releaseProvisionalConnection(connectId, signal) {
1888
+ const sessionId = this._sessions.get(connectId);
1889
+ this._sessions.delete(connectId);
1890
+ this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1891
+ if (!sessionId) return;
1892
+ const teardown = this._runConnectorTeardown(() => this._connector.disconnect(sessionId)).catch(
1893
+ () => void 0
1894
+ );
1895
+ if (!signal.aborted) await teardown;
1896
+ }
1897
+ _waitForConnectorOperationsToDrain() {
1898
+ if (this._unsettledConnectorOperations.size === 0) {
1899
+ return Promise.resolve();
1900
+ }
1901
+ return new Promise((resolve) => {
1902
+ this._connectorIdleWaiters.add(resolve);
1903
+ });
1904
+ }
1905
+ _retainConnectorOperation(key) {
1906
+ this._unsettledConnectorOperations.set(
1907
+ key,
1908
+ (_nullishCoalesce(this._unsettledConnectorOperations.get(key), () => ( 0))) + 1
1909
+ );
1910
+ let released = false;
1911
+ return () => {
1912
+ if (released) return;
1913
+ released = true;
1914
+ const remaining = (_nullishCoalesce(this._unsettledConnectorOperations.get(key), () => ( 1))) - 1;
1915
+ if (remaining > 0) {
1916
+ this._unsettledConnectorOperations.set(key, remaining);
1917
+ } else {
1918
+ this._unsettledConnectorOperations.delete(key);
1919
+ }
1920
+ if (this._unsettledConnectorOperations.size === 0) {
1921
+ for (const resolve of this._connectorIdleWaiters) resolve();
1922
+ this._connectorIdleWaiters.clear();
1923
+ }
1924
+ };
1925
+ }
1476
1926
  static _normalizeAllNetworkItem(method, item) {
1477
1927
  if (method !== "btcGetAddress" && method !== "btcGetPublicKey") {
1478
1928
  return item;
@@ -1491,18 +1941,14 @@ var _TrezorAdapter = class _TrezorAdapter {
1491
1941
  const { deviceId } = item;
1492
1942
  return typeof deviceId === "string" && deviceId.length > 0 ? deviceId : void 0;
1493
1943
  }
1494
- _callAllNetworkMethod(connectId, method, item) {
1944
+ _callAllNetworkMethod(connectId, method, item, bundleContext) {
1495
1945
  switch (method) {
1496
1946
  case "evmGetAddress":
1497
- return this.evmGetAddress(connectId, "", item);
1498
1947
  case "btcGetAddress":
1499
- return this.btcGetAddress(connectId, "", item);
1500
1948
  case "btcGetPublicKey":
1501
- return this.btcGetPublicKey(connectId, "", item);
1502
1949
  case "solGetAddress":
1503
- return this.solGetAddress(connectId, "", item);
1504
1950
  case "tronGetAddress":
1505
- return this.tronGetAddress(connectId, "", item);
1951
+ return this._callMethod(method, connectId, "", item, true, bundleContext);
1506
1952
  default:
1507
1953
  return Promise.resolve(
1508
1954
  _hwkadaptercore.failure.call(void 0,
@@ -1521,11 +1967,50 @@ var _TrezorAdapter = class _TrezorAdapter {
1521
1967
  _callWalletMethod(methodName, connectId, deviceId, params) {
1522
1968
  return this._callMethod(methodName, connectId, deviceId, params, true);
1523
1969
  }
1524
- _callDeviceManagerMethod(methodName, connectId, params) {
1525
- return this._callMethod(methodName, connectId, void 0, params, false);
1970
+ _callDeviceManagerMethod(methodName, connectId, params, operationContext) {
1971
+ const expectedIdentity = _optionalChain([operationContext, 'optionalAccess', _87 => _87.expectedDeviceIdentity]);
1972
+ if (expectedIdentity && (expectedIdentity.vendor !== "trezor" || expectedIdentity.type !== "deviceId")) {
1973
+ return Promise.resolve(
1974
+ _hwkadaptercore.failure.call(void 0,
1975
+ _hwkadaptercore.HardwareErrorCode.InvalidParams,
1976
+ "Trezor device-manager operations require a Trezor deviceId identity"
1977
+ )
1978
+ );
1979
+ }
1980
+ return this._callMethod(
1981
+ methodName,
1982
+ connectId,
1983
+ _optionalChain([expectedIdentity, 'optionalAccess', _88 => _88.value]),
1984
+ {
1985
+ ...params && typeof params === "object" ? params : {},
1986
+ knownConnections: _optionalChain([operationContext, 'optionalAccess', _89 => _89.knownConnections]),
1987
+ extra: _optionalChain([operationContext, 'optionalAccess', _90 => _90.extra]),
1988
+ allowDeviceSelection: _optionalChain([operationContext, 'optionalAccess', _91 => _91.allowDeviceSelection]),
1989
+ ..._optionalChain([operationContext, 'optionalAccess', _92 => _92.operationId]) ? { operationId: operationContext.operationId } : {}
1990
+ },
1991
+ false
1992
+ );
1526
1993
  }
1527
- async _callMethod(methodName, connectId, deviceId, params, requiresWalletIntent) {
1994
+ async _callMethod(methodName, connectId, deviceId, params, requiresWalletIntent, bundleContext) {
1528
1995
  const call = _TrezorAdapter._normalizeCallArgs(connectId, deviceId, params);
1996
+ const commonParams = _TrezorAdapter._splitCommonParams(call.params);
1997
+ const positionalOperationId = _hwkadaptercore.isHardwareOperationId.call(void 0, call.connectId) ? call.connectId : void 0;
1998
+ if (positionalOperationId && commonParams.operationId && positionalOperationId !== commonParams.operationId) {
1999
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.InvalidParams, "Conflicting Trezor operation ids", {
2000
+ positionalOperationId,
2001
+ commonOperationId: commonParams.operationId
2002
+ });
2003
+ }
2004
+ const operationId = _nullishCoalesce(commonParams.operationId, () => ( positionalOperationId));
2005
+ let releaseOperationRetention;
2006
+ try {
2007
+ if (operationId) {
2008
+ call.connectId = this._operations.resolve(operationId).connectId;
2009
+ releaseOperationRetention = this._operations.retain(operationId);
2010
+ }
2011
+ } catch (error) {
2012
+ return this._errorToFailure(error);
2013
+ }
1529
2014
  debugLog("[TrezorAdapter][REQ]", {
1530
2015
  method: methodName,
1531
2016
  connectId: call.connectId || "(empty)",
@@ -1534,15 +2019,40 @@ var _TrezorAdapter = class _TrezorAdapter {
1534
2019
  });
1535
2020
  try {
1536
2021
  const response = await this._jobQueue.enqueue(
1537
- call.connectId,
1538
- (signal) => this._callWithRetry(
1539
- call.connectId,
1540
- methodName,
1541
- call.params,
1542
- requiresWalletIntent,
1543
- true,
1544
- signal
1545
- ),
2022
+ _nullishCoalesce(operationId, () => ( call.connectId)),
2023
+ async (signal) => {
2024
+ let effectiveConnectId = call.connectId;
2025
+ let selectionRequestId;
2026
+ try {
2027
+ if (!_optionalChain([bundleContext, 'optionalAccess', _93 => _93.connection]) && !operationId && deviceId && (!connectId || _optionalChain([commonParams, 'access', _94 => _94.connectionContext, 'optionalAccess', _95 => _95.knownConnections]) !== void 0)) {
2028
+ const resolved = await this._resolveExpectedDeviceConnectId(
2029
+ deviceId,
2030
+ signal,
2031
+ commonParams.connectionContext
2032
+ );
2033
+ effectiveConnectId = resolved.connectId;
2034
+ selectionRequestId = resolved.selectionRequestId;
2035
+ }
2036
+ } catch (error) {
2037
+ return this._errorToFailure(error);
2038
+ }
2039
+ const response2 = await this._callWithRetry(
2040
+ _nullishCoalesce(_optionalChain([bundleContext, 'optionalAccess', _96 => _96.connection, 'optionalAccess', _97 => _97.connectId]), () => ( effectiveConnectId)),
2041
+ methodName,
2042
+ call.params,
2043
+ deviceId || "",
2044
+ requiresWalletIntent,
2045
+ true,
2046
+ signal,
2047
+ operationId,
2048
+ selectionRequestId,
2049
+ bundleContext
2050
+ );
2051
+ if (!response2.success && selectionRequestId) {
2052
+ await this._releaseProvisionalConnection(effectiveConnectId, signal);
2053
+ }
2054
+ return response2;
2055
+ },
1546
2056
  {
1547
2057
  label: methodName,
1548
2058
  rejectIfBusy: true,
@@ -1563,16 +2073,53 @@ var _TrezorAdapter = class _TrezorAdapter {
1563
2073
  payload: _TrezorAdapter._sanitizeForLog(response.payload, methodName)
1564
2074
  });
1565
2075
  return response;
2076
+ } finally {
2077
+ _optionalChain([releaseOperationRetention, 'optionalCall', _98 => _98()]);
1566
2078
  }
1567
2079
  }
1568
- async _callWithRetry(connectId, methodName, params, requiresWalletIntent, allowRetry, signal) {
1569
- const { passphraseState, useEmptyPassphrase, rest } = _TrezorAdapter._splitCommonParams(params);
2080
+ async _callWithRetry(connectId, methodName, params, expectedDeviceId, requiresWalletIntent, allowRetry, signal, operationId, selectionRequestId, bundleContext) {
2081
+ const { passphraseState, useEmptyPassphrase, rest, connectionContext } = _TrezorAdapter._splitCommonParams(params);
2082
+ let pendingBindingRequestId = selectionRequestId;
2083
+ let businessCallStarted = false;
1570
2084
  const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {
1571
2085
  passphraseState,
1572
2086
  useEmptyPassphrase
1573
2087
  });
1574
2088
  try {
1575
- const sessionId = await this._ensureSession(connectId, signal);
2089
+ if (_optionalChain([bundleContext, 'optionalAccess', _99 => _99.connection])) {
2090
+ const pinned = bundleContext.connection;
2091
+ if (this._sessions.get(pinned.connectId) !== pinned.sessionId) {
2092
+ return _hwkadaptercore.failure.call(void 0,
2093
+ _hwkadaptercore.HardwareErrorCode.DeviceDisconnected,
2094
+ "Trezor all-network connection ended"
2095
+ );
2096
+ }
2097
+ }
2098
+ const sessionId = operationId ? this._sessions.get(this._operations.resolve(operationId).connectId) : await this._ensureSession(connectId, signal);
2099
+ if (!sessionId) {
2100
+ if (operationId) {
2101
+ this._operations.end(operationId, "disconnect");
2102
+ return _hwkadaptercore.failure.call(void 0,
2103
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2104
+ "Trezor operation connection is no longer active",
2105
+ { operationId, reason: "disconnect" }
2106
+ );
2107
+ }
2108
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.DeviceNotFound, "Trezor session was not found");
2109
+ }
2110
+ if (bundleContext && !bundleContext.connection) {
2111
+ bundleContext.connection = { connectId, sessionId };
2112
+ }
2113
+ if (expectedDeviceId) {
2114
+ const actualDeviceId = _optionalChain([this, 'access', _100 => _100._devices, 'access', _101 => _101.get, 'call', _102 => _102(connectId), 'optionalAccess', _103 => _103.deviceId]);
2115
+ if (!actualDeviceId || actualDeviceId !== expectedDeviceId) {
2116
+ return _hwkadaptercore.failure.call(void 0,
2117
+ _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
2118
+ `Wrong device: expected ${expectedDeviceId}, got ${actualDeviceId || "unknown"}`,
2119
+ { expected: expectedDeviceId, actual: actualDeviceId || "" }
2120
+ );
2121
+ }
2122
+ }
1576
2123
  if (requiresWalletIntent && !passphraseState && useEmptyPassphrase !== true) {
1577
2124
  return _hwkadaptercore.failure.call(void 0,
1578
2125
  _hwkadaptercore.HardwareErrorCode.InvalidParams,
@@ -1587,28 +2134,68 @@ var _TrezorAdapter = class _TrezorAdapter {
1587
2134
  } else if (useEmptyPassphrase === true) {
1588
2135
  await _TrezorAdapter._abortable(signal, this._createFreshAppSession(sessionId, signal));
1589
2136
  }
2137
+ if (expectedDeviceId) {
2138
+ await this._emitVerifiedBinding(
2139
+ expectedDeviceId,
2140
+ connectId,
2141
+ pendingBindingRequestId,
2142
+ connectionContext,
2143
+ signal
2144
+ );
2145
+ pendingBindingRequestId = void 0;
2146
+ this._rememberVerifiedConnection(expectedDeviceId, connectId);
2147
+ }
2148
+ businessCallStarted = true;
1590
2149
  const result = await _TrezorAdapter._abortable(
1591
2150
  signal,
1592
- _TrezorAdapter._callConnector(this._connector, sessionId, methodName, rest)
2151
+ this._callConnector(sessionId, methodName, rest)
1593
2152
  );
1594
2153
  return _hwkadaptercore.success.call(void 0, result);
1595
2154
  } catch (error) {
1596
2155
  if (signal.aborted) {
2156
+ if (pendingBindingRequestId)
2157
+ this._emitter.emit(_hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS, {
2158
+ type: _hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS,
2159
+ payload: { selectionRequestId: pendingBindingRequestId, status: "cancelled" }
2160
+ });
1597
2161
  return this._errorToFailure(error);
1598
2162
  }
1599
2163
  const code = _TrezorAdapter._errorCode(error);
1600
- if (allowRetry && code === _hwkadaptercore.HardwareErrorCode.DeviceDisconnected) {
2164
+ if (pendingBindingRequestId && !(allowRetry && (passphraseState || useEmptyPassphrase === true) && _TrezorAdapter._isStaleSessionError(code))) {
2165
+ this._emitter.emit(_hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS, {
2166
+ type: _hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS,
2167
+ payload: { selectionRequestId: pendingBindingRequestId, status: "failed" }
2168
+ });
2169
+ }
2170
+ const ambiguousTransportFailure = code === _hwkadaptercore.HardwareErrorCode.DeviceDisconnected || code === _hwkadaptercore.HardwareErrorCode.OperationTimeout || code === _hwkadaptercore.HardwareErrorCode.TransportError;
2171
+ if (operationId && ambiguousTransportFailure) {
1601
2172
  this._sessions.delete(connectId);
1602
2173
  this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1603
- return await this._callWithRetry(
1604
- connectId,
1605
- methodName,
1606
- params,
1607
- requiresWalletIntent,
1608
- false,
1609
- signal
2174
+ this._operations.end(operationId, "disconnect");
2175
+ return _hwkadaptercore.failure.call(void 0,
2176
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2177
+ businessCallStarted && !_hwkadaptercore.canReplayHardwareMethodAfterTransportFailure.call(void 0, methodName) ? `Trezor ${methodName} may have completed before the connection was lost` : "Trezor operation connection was lost",
2178
+ businessCallStarted && !_hwkadaptercore.canReplayHardwareMethodAfterTransportFailure.call(void 0, methodName) ? _hwkadaptercore.operationMayHaveCompletedParams.call(void 0, methodName, {
2179
+ operationId,
2180
+ reason: "disconnect"
2181
+ }) : { operationId, reason: "disconnect" },
2182
+ void 0,
2183
+ businessCallStarted && !_hwkadaptercore.canReplayHardwareMethodAfterTransportFailure.call(void 0, methodName) ? { scope: "unknown" } : void 0
1610
2184
  );
1611
2185
  }
2186
+ if (!operationId && ambiguousTransportFailure) {
2187
+ this._sessions.delete(connectId);
2188
+ this._verifiedPassphraseSessionsByConnectId.delete(connectId);
2189
+ if (businessCallStarted && !_hwkadaptercore.canReplayHardwareMethodAfterTransportFailure.call(void 0, methodName)) {
2190
+ return _hwkadaptercore.failure.call(void 0,
2191
+ code,
2192
+ `Trezor ${methodName} may have completed before the connection was lost`,
2193
+ _hwkadaptercore.operationMayHaveCompletedParams.call(void 0, methodName),
2194
+ void 0,
2195
+ { scope: "unknown" }
2196
+ );
2197
+ }
2198
+ }
1612
2199
  if (allowRetry && (passphraseState || useEmptyPassphrase === true) && _TrezorAdapter._isStaleSessionError(code)) {
1613
2200
  if (passphraseState) {
1614
2201
  this._forgetVerifiedPassphraseSession(connectId, passphraseState);
@@ -1617,9 +2204,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1617
2204
  connectId,
1618
2205
  methodName,
1619
2206
  params,
2207
+ expectedDeviceId,
1620
2208
  requiresWalletIntent,
1621
2209
  false,
1622
- signal
2210
+ signal,
2211
+ operationId,
2212
+ pendingBindingRequestId,
2213
+ bundleContext
1623
2214
  );
1624
2215
  }
1625
2216
  debugLog("[TrezorAdapter][ERROR]", {
@@ -1632,34 +2223,247 @@ var _TrezorAdapter = class _TrezorAdapter {
1632
2223
  restorePassphraseRequestContext();
1633
2224
  }
1634
2225
  }
1635
- async _ensureSession(connectId, signal) {
2226
+ async _ensureSession(connectId, signal, transportType) {
1636
2227
  const existing = this._sessions.get(connectId);
1637
2228
  if (existing) return existing;
1638
2229
  const pending = this._connectingPromises.get(connectId);
1639
2230
  if (pending) return pending;
2231
+ const stateGeneration = this._stateGeneration;
1640
2232
  const promise = (async () => {
2233
+ if (_optionalChain([signal, 'optionalAccess', _104 => _104.aborted])) {
2234
+ throw Object.assign(new Error("Trezor connection aborted"), {
2235
+ code: _hwkadaptercore.HardwareErrorCode.UserAborted
2236
+ });
2237
+ }
1641
2238
  try {
1642
- if (_optionalChain([signal, 'optionalAccess', _32 => _32.aborted])) {
1643
- throw Object.assign(new Error("Trezor connection aborted"), {
1644
- code: _hwkadaptercore.HardwareErrorCode.UserAborted
1645
- });
1646
- }
1647
- try {
1648
- return await this._connectSession(connectId, signal);
1649
- } catch (error) {
1650
- if (_TrezorAdapter._isConnectionUnavailableError(error)) {
1651
- this._sessions.delete(connectId);
1652
- this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1653
- }
1654
- throw error;
2239
+ return await this._connectSession(connectId, signal, stateGeneration, transportType);
2240
+ } catch (error) {
2241
+ if (_TrezorAdapter._isConnectionUnavailableError(error)) {
2242
+ this._sessions.delete(connectId);
2243
+ this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1655
2244
  }
1656
- } finally {
1657
- this._connectingPromises.delete(connectId);
1658
- this._disconnectRequested.delete(connectId);
2245
+ throw error;
1659
2246
  }
1660
2247
  })();
1661
2248
  this._connectingPromises.set(connectId, promise);
1662
- return promise;
2249
+ try {
2250
+ return await promise;
2251
+ } finally {
2252
+ if (this._connectingPromises.get(connectId) === promise) {
2253
+ this._connectingPromises.delete(connectId);
2254
+ }
2255
+ this._disconnectRequested.delete(connectId);
2256
+ }
2257
+ }
2258
+ async _resolveExpectedDeviceConnectId(expectedDeviceId, signal, context) {
2259
+ const connectedMatch = Array.from(this._devices.values()).find(
2260
+ (device) => device.deviceId === expectedDeviceId && this._sessions.has(device.connectId)
2261
+ );
2262
+ if (connectedMatch) return { connectId: connectedMatch.connectId };
2263
+ const availableTransports = _nullishCoalesce(this._connector.availableTransports, () => ( [
2264
+ this._connector.connectionType
2265
+ ]));
2266
+ if (availableTransports.includes("usb")) {
2267
+ await _TrezorAdapter._abortable(
2268
+ signal,
2269
+ this._ensureDevicePermission(void 0, expectedDeviceId, "usb")
2270
+ );
2271
+ }
2272
+ const candidates = await _TrezorAdapter._abortable(
2273
+ signal,
2274
+ this._searchDevices({ transportType: "usb" }, signal)
2275
+ );
2276
+ const mismatchedDeviceIds = [];
2277
+ const knownConnection = this._knownDeviceConnections.get(expectedDeviceId);
2278
+ const knownUsbIds = _optionalChain([context, 'optionalAccess', _105 => _105.knownConnections]) !== void 0 ? context.knownConnections.flatMap(
2279
+ (connection) => connection.transport === "usb" ? [connection.connectId] : []
2280
+ ) : [_optionalChain([knownConnection, 'optionalAccess', _106 => _106.usbConnectId])].filter((id) => Boolean(id));
2281
+ const suppliedBleIds = _optionalChain([context, 'optionalAccess', _107 => _107.knownConnections, 'optionalAccess', _108 => _108.flatMap, 'call', _109 => _109(
2282
+ (connection) => connection.transport === "ble" ? [connection.connectId] : []
2283
+ )]);
2284
+ const knownBleIds = _optionalChain([suppliedBleIds, 'optionalAccess', _110 => _110.length]) ? suppliedBleIds : [_optionalChain([knownConnection, 'optionalAccess', _111 => _111.bleConnectId])].filter((id) => Boolean(id));
2285
+ const usbCandidates = candidates.filter(
2286
+ (device) => device.connectionType === "usb" && (!knownUsbIds.length || knownUsbIds.includes(device.connectId))
2287
+ );
2288
+ for (const candidate of usbCandidates) {
2289
+ let sessionId;
2290
+ try {
2291
+ sessionId = await this._ensureSession(candidate.connectId, signal, "usb");
2292
+ const actualDeviceId = _nullishCoalesce(_optionalChain([this, 'access', _112 => _112._devices, 'access', _113 => _113.get, 'call', _114 => _114(candidate.connectId), 'optionalAccess', _115 => _115.deviceId]), () => ( ""));
2293
+ if (actualDeviceId === expectedDeviceId) return { connectId: candidate.connectId };
2294
+ mismatchedDeviceIds.push(actualDeviceId || "unknown");
2295
+ } catch (error) {
2296
+ if (signal.aborted) throw error;
2297
+ if (!_TrezorAdapter._isConnectionUnavailableError(error)) throw error;
2298
+ }
2299
+ if (sessionId) {
2300
+ const mismatchedSessionId = sessionId;
2301
+ this._sessions.delete(candidate.connectId);
2302
+ this._verifiedPassphraseSessionsByConnectId.delete(candidate.connectId);
2303
+ await this._runConnectorTeardown(() => this._connector.disconnect(mismatchedSessionId));
2304
+ }
2305
+ }
2306
+ if (mismatchedDeviceIds.length) {
2307
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2308
+ code: _hwkadaptercore.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
+ }
2313
+ if (usbCandidates.length > 0) {
2314
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2315
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2316
+ message: "Trezor devices are present but none could be opened to check its identity."
2317
+ });
2318
+ }
2319
+ const [knownBleConnectId] = knownBleIds;
2320
+ if (knownBleConnectId && availableTransports.includes("ble")) {
2321
+ await _TrezorAdapter._abortable(
2322
+ signal,
2323
+ this._ensureDevicePermission(knownBleConnectId, expectedDeviceId, "ble")
2324
+ );
2325
+ await this._ensureSession(knownBleConnectId, signal, "ble");
2326
+ const actualDeviceId = _optionalChain([this, 'access', _116 => _116._devices, 'access', _117 => _117.get, 'call', _118 => _118(knownBleConnectId), 'optionalAccess', _119 => _119.deviceId]);
2327
+ if (actualDeviceId === expectedDeviceId) return { connectId: knownBleConnectId };
2328
+ const sessionId = this._sessions.get(knownBleConnectId);
2329
+ this._sessions.delete(knownBleConnectId);
2330
+ this._verifiedPassphraseSessionsByConnectId.delete(knownBleConnectId);
2331
+ if (sessionId) {
2332
+ await this._runConnectorTeardown(() => this._connector.disconnect(sessionId));
2333
+ }
2334
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2335
+ code: _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
2336
+ message: "The bound Trezor Bluetooth device has a different identity"
2337
+ });
2338
+ }
2339
+ if (_optionalChain([context, 'optionalAccess', _120 => _120.allowDeviceSelection]) === false) {
2340
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2341
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2342
+ message: "No known Trezor connection is available"
2343
+ });
2344
+ }
2345
+ return this._selectBleDeviceForBinding(expectedDeviceId, signal, context, "missing-binding");
2346
+ }
2347
+ async _selectBleDeviceForBinding(expectedDeviceId, signal, context, reason) {
2348
+ const availableTransports = _nullishCoalesce(this._connector.availableTransports, () => ( [
2349
+ this._connector.connectionType
2350
+ ]));
2351
+ const allowUsbFallback = reason !== "manual-rebind" && availableTransports.includes("usb");
2352
+ const knownUsbIds = _optionalChain([context, 'optionalAccess', _121 => _121.knownConnections]) ? context.knownConnections.flatMap(
2353
+ (connection) => connection.transport === "usb" ? [connection.connectId] : []
2354
+ ) : [_optionalChain([this, 'access', _122 => _122._knownDeviceConnections, 'access', _123 => _123.get, 'call', _124 => _124(expectedDeviceId), 'optionalAccess', _125 => _125.usbConnectId])];
2355
+ if (availableTransports.includes("ble")) {
2356
+ await _TrezorAdapter._abortable(
2357
+ signal,
2358
+ this._ensureDevicePermission(void 0, expectedDeviceId, "ble")
2359
+ );
2360
+ }
2361
+ const bleCandidates = (await _TrezorAdapter._abortable(
2362
+ signal,
2363
+ this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)
2364
+ )).filter((device) => device.connectionType === "ble");
2365
+ if (signal.aborted) throw signal.reason;
2366
+ const rejectedConnectIds = /* @__PURE__ */ new Set();
2367
+ const bindingSessionId = this._uiRegistry.createRequestId();
2368
+ let rejectedConnectId;
2369
+ while (availableTransports.includes("ble") && this._emitter.listenerCount(_hwkadaptercore.UI_REQUEST.REQUEST_SELECT_DEVICE)) {
2370
+ const { device: selected, requestId } = await _hwkadaptercore.requestBleDeviceSelection.call(void 0, {
2371
+ emitter: this._emitter,
2372
+ registry: this._uiRegistry,
2373
+ signal,
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 = _nullishCoalesce(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
+ },
2387
+ request: {
2388
+ devices: bleCandidates.filter((device) => !rejectedConnectIds.has(device.connectId)),
2389
+ bindingSessionId,
2390
+ rejectedConnectId,
2391
+ context: {
2392
+ kind: "bind-connection",
2393
+ transport: "ble",
2394
+ reason
2395
+ },
2396
+ extra: _optionalChain([context, 'optionalAccess', _126 => _126.extra])
2397
+ }
2398
+ });
2399
+ try {
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);
2405
+ } catch (error) {
2406
+ this._emitter.emit(_hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS, {
2407
+ type: _hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS,
2408
+ payload: {
2409
+ selectionRequestId: requestId,
2410
+ status: signal.aborted ? "cancelled" : "failed"
2411
+ }
2412
+ });
2413
+ throw error;
2414
+ }
2415
+ if (_optionalChain([this, 'access', _127 => _127._devices, 'access', _128 => _128.get, 'call', _129 => _129(selected.connectId), 'optionalAccess', _130 => _130.deviceId]) !== expectedDeviceId) {
2416
+ const sessionId = this._sessions.get(selected.connectId);
2417
+ this._sessions.delete(selected.connectId);
2418
+ this._verifiedPassphraseSessionsByConnectId.delete(selected.connectId);
2419
+ if (sessionId) {
2420
+ await this._runConnectorTeardown(() => this._connector.disconnect(sessionId));
2421
+ }
2422
+ rejectedConnectId = selected.connectId;
2423
+ rejectedConnectIds.add(selected.connectId);
2424
+ continue;
2425
+ }
2426
+ if (selected.connectionType === "usb") {
2427
+ this._emitter.emit(_hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS, {
2428
+ type: _hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS,
2429
+ payload: { selectionRequestId: requestId, status: "cancelled" }
2430
+ });
2431
+ return { connectId: selected.connectId };
2432
+ }
2433
+ return { connectId: selected.connectId, selectionRequestId: requestId };
2434
+ }
2435
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2436
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2437
+ message: "No readable Trezor device is available"
2438
+ });
2439
+ }
2440
+ _rememberVerifiedConnection(deviceId, connectId) {
2441
+ const device = this._devices.get(connectId);
2442
+ if (!device || device.deviceId !== deviceId) return;
2443
+ const known = _nullishCoalesce(this._knownDeviceConnections.get(deviceId), () => ( { deviceId }));
2444
+ if (device.connectionType === "ble") {
2445
+ known.bleConnectId = connectId;
2446
+ } else if (device.connectionType === "usb" && _optionalChain([device, 'access', _131 => _131.capabilities, 'optionalAccess', _132 => _132.persistentDeviceIdentity])) {
2447
+ known.usbConnectId = connectId;
2448
+ }
2449
+ this._knownDeviceConnections.set(deviceId, known);
2450
+ }
2451
+ async _emitVerifiedBinding(deviceId, connectId, selectionRequestId, context, signal) {
2452
+ if (!selectionRequestId) return false;
2453
+ const device = this._devices.get(connectId);
2454
+ if (!device || device.deviceId !== deviceId || device.connectionType !== "ble") return false;
2455
+ const outcome = await _hwkadaptercore.requestSaveDeviceBinding.call(void 0,
2456
+ this._emitter,
2457
+ this._uiRegistry,
2458
+ {
2459
+ selectionRequestId,
2460
+ connection: { transport: "ble", connectId },
2461
+ identity: { vendor: "trezor", type: "deviceId", value: deviceId },
2462
+ extra: _optionalChain([context, 'optionalAccess', _133 => _133.extra])
2463
+ },
2464
+ signal
2465
+ );
2466
+ return outcome.saved;
1663
2467
  }
1664
2468
  /**
1665
2469
  * Pin the device's active wallet session to `passphraseState` (the target
@@ -1681,12 +2485,16 @@ var _TrezorAdapter = class _TrezorAdapter {
1681
2485
  if (reused) return;
1682
2486
  const created = await _TrezorAdapter._abortable(
1683
2487
  signal,
1684
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpCreateSession", {
2488
+ this._callConnector(sessionId, "__thpCreateSession", {
1685
2489
  passphraseMode: "prompt"
1686
2490
  })
1687
2491
  );
1688
- if (_optionalChain([created, 'optionalAccess', _33 => _33.protocol]) === "thp" && !created.thpSessionId) return;
1689
- if (_optionalChain([created, 'optionalAccess', _34 => _34.protocol]) !== "thp" && _optionalChain([created, 'optionalAccess', _35 => _35.protocol]) !== "v1") return;
2492
+ if (_optionalChain([created, 'optionalAccess', _134 => _134.protocol]) === "thp" && !created.thpSessionId || _optionalChain([created, 'optionalAccess', _135 => _135.protocol]) !== "thp" && _optionalChain([created, 'optionalAccess', _136 => _136.protocol]) !== "v1") {
2493
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2494
+ code: _hwkadaptercore.HardwareErrorCode.TransportError,
2495
+ message: "Cannot verify the requested Trezor wallet session"
2496
+ });
2497
+ }
1690
2498
  const state = await this._deriveState(sessionId, signal);
1691
2499
  if (state !== passphraseState) {
1692
2500
  throw Object.assign(
@@ -1700,13 +2508,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1700
2508
  }
1701
2509
  _rememberVerifiedPassphraseSession(connectId, passphraseState, sessionId, created) {
1702
2510
  let session;
1703
- if (_optionalChain([created, 'optionalAccess', _36 => _36.protocol]) === "thp" && typeof created.thpSessionId === "string") {
2511
+ if (_optionalChain([created, 'optionalAccess', _137 => _137.protocol]) === "thp" && typeof created.thpSessionId === "string") {
1704
2512
  session = {
1705
2513
  sessionId,
1706
2514
  protocol: "thp",
1707
2515
  thpSessionId: created.thpSessionId
1708
2516
  };
1709
- } else if (_optionalChain([created, 'optionalAccess', _37 => _37.protocol]) === "v1") {
2517
+ } else if (_optionalChain([created, 'optionalAccess', _138 => _138.protocol]) === "v1") {
1710
2518
  session = {
1711
2519
  sessionId,
1712
2520
  protocol: "v1"
@@ -1726,7 +2534,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1726
2534
  }
1727
2535
  }
1728
2536
  async _reuseVerifiedPassphraseSession(connectId, sessionId, passphraseState, signal) {
1729
- const cached = _optionalChain([this, 'access', _38 => _38._verifiedPassphraseSessionsByConnectId, 'access', _39 => _39.get, 'call', _40 => _40(connectId), 'optionalAccess', _41 => _41.get, 'call', _42 => _42(passphraseState)]);
2537
+ const cached = _optionalChain([this, 'access', _139 => _139._verifiedPassphraseSessionsByConnectId, 'access', _140 => _140.get, 'call', _141 => _141(connectId), 'optionalAccess', _142 => _142.get, 'call', _143 => _143(passphraseState)]);
1730
2538
  if (!cached || cached.sessionId !== sessionId) {
1731
2539
  this._forgetVerifiedPassphraseSession(connectId, passphraseState);
1732
2540
  return false;
@@ -1736,7 +2544,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1736
2544
  if (!cached.thpSessionId) return false;
1737
2545
  await _TrezorAdapter._abortable(
1738
2546
  signal,
1739
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpSelectSession", {
2547
+ this._callConnector(sessionId, "__thpSelectSession", {
1740
2548
  thpSessionId: cached.thpSessionId
1741
2549
  })
1742
2550
  );
@@ -1756,7 +2564,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1756
2564
  async _createFreshAppSession(sessionId, signal) {
1757
2565
  await _TrezorAdapter._abortable(
1758
2566
  signal,
1759
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpCreateSession", {
2567
+ this._callConnector(sessionId, "__thpCreateSession", {
1760
2568
  passphraseMode: "empty"
1761
2569
  })
1762
2570
  );
@@ -1773,12 +2581,12 @@ var _TrezorAdapter = class _TrezorAdapter {
1773
2581
  async _deriveState(sessionId, signal) {
1774
2582
  const derived = await _TrezorAdapter._abortable(
1775
2583
  signal,
1776
- _TrezorAdapter._callConnector(this._connector, sessionId, "btcGetPublicKey", {
2584
+ this._callConnector(sessionId, "btcGetPublicKey", {
1777
2585
  path: "m/44'/0'/0'",
1778
2586
  showOnDevice: false
1779
2587
  })
1780
2588
  );
1781
- const state = _optionalChain([derived, 'optionalAccess', _43 => _43.publicKey]);
2589
+ const state = _optionalChain([derived, 'optionalAccess', _144 => _144.publicKey]);
1782
2590
  if (!state) {
1783
2591
  throw Object.assign(new Error("Failed to derive passphrase state (wallet public key)"), {
1784
2592
  code: _hwkadaptercore.HardwareErrorCode.UnknownError
@@ -1786,25 +2594,31 @@ var _TrezorAdapter = class _TrezorAdapter {
1786
2594
  }
1787
2595
  return state;
1788
2596
  }
1789
- async _resolvePassphraseState(connectId, passphraseState, signal) {
2597
+ async _resolvePassphraseState(connectId, passphraseState, signal, operationId) {
1790
2598
  try {
1791
- const sessionId = await this._ensureSession(connectId, signal);
2599
+ const sessionId = operationId ? this._sessions.get(this._operations.resolve(operationId).connectId) : await this._ensureSession(connectId, signal);
2600
+ if (!sessionId) {
2601
+ return _hwkadaptercore.failure.call(void 0,
2602
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2603
+ "Trezor operation connection is no longer active"
2604
+ );
2605
+ }
1792
2606
  if (passphraseState) {
1793
2607
  await this._alignAppSession(connectId, sessionId, passphraseState, signal);
1794
2608
  return _hwkadaptercore.success.call(void 0, passphraseState);
1795
2609
  }
1796
2610
  const created = await _TrezorAdapter._abortable(
1797
2611
  signal,
1798
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpCreateSession", {
2612
+ this._callConnector(sessionId, "__thpCreateSession", {
1799
2613
  passphraseMode: "prompt"
1800
2614
  })
1801
2615
  );
1802
2616
  const state = await this._deriveState(sessionId, signal);
1803
2617
  const features = await _TrezorAdapter._abortable(
1804
2618
  signal,
1805
- _TrezorAdapter._callConnector(this._connector, sessionId, "getFeatures", { refresh: true })
2619
+ this._callConnector(sessionId, "getFeatures", { refresh: true })
1806
2620
  );
1807
- if (_optionalChain([features, 'optionalAccess', _44 => _44.passphrase_protection]) !== true) {
2621
+ if (_optionalChain([features, 'optionalAccess', _145 => _145.passphrase_protection]) !== true) {
1808
2622
  return _hwkadaptercore.success.call(void 0, null);
1809
2623
  }
1810
2624
  this._rememberVerifiedPassphraseSession(connectId, state, sessionId, created);
@@ -1852,7 +2666,16 @@ var _TrezorAdapter = class _TrezorAdapter {
1852
2666
  }
1853
2667
  const trustedNumericCode = typeof typed.code === "number" && KNOWN_HARDWARE_ERROR_CODES.has(typed.code) ? typed.code : void 0;
1854
2668
  const code = _nullishCoalesce(_nullishCoalesce(trustedNumericCode, () => ( _TrezorAdapter._mapTrezorFailureCode(error))), () => ( _hwkadaptercore.HardwareErrorCode.UnknownError));
1855
- return _hwkadaptercore.failure.call(void 0, code, _nullishCoalesce(typed.message, () => ( String(error))));
2669
+ const stampedOrigin = _optionalChain([error, 'optionalAccess', _146 => _146.origin]);
2670
+ const origin = stampedOrigin === "device" || stampedOrigin === "transport" || stampedOrigin === "host" ? stampedOrigin : _hwkadaptercore.defaultOriginForCode.call(void 0, code);
2671
+ const params = typed.params && typeof typed.params === "object" ? typed.params : void 0;
2672
+ return _hwkadaptercore.failure.call(void 0,
2673
+ code,
2674
+ _nullishCoalesce(typed.message, () => ( String(error))),
2675
+ params,
2676
+ origin,
2677
+ _hwkadaptercore.isHwkRecoveryHint.call(void 0, typed.recovery) ? typed.recovery : void 0
2678
+ );
1856
2679
  }
1857
2680
  /**
1858
2681
  * Emit `REQUEST_DEVICE_PERMISSION` and await the host's
@@ -1867,11 +2690,11 @@ var _TrezorAdapter = class _TrezorAdapter {
1867
2690
  * - no `connectId` → environment-level check (scan / picker)
1868
2691
  * - with `connectId` → device-level check (per-call gating)
1869
2692
  */
1870
- async _ensureDevicePermission(connectId, deviceId) {
2693
+ async _ensureDevicePermission(connectId, deviceId, requestedTransport) {
1871
2694
  if (!this._emitter.listenerCount(_hwkadaptercore.UI_REQUEST.REQUEST_DEVICE_PERMISSION)) {
1872
2695
  return;
1873
2696
  }
1874
- const transportType = _nullishCoalesce(this.activeTransport, () => ( "usb"));
2697
+ const transportType = _nullishCoalesce(_nullishCoalesce(requestedTransport, () => ( this.activeTransport)), () => ( "usb"));
1875
2698
  const waitPromise = this._uiRegistry.wait(
1876
2699
  _hwkadaptercore.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
1877
2700
  { timeoutMs: 6e4 }
@@ -1890,6 +2713,8 @@ var _TrezorAdapter = class _TrezorAdapter {
1890
2713
  };
1891
2714
  // Keys are matched after normalizeLogKey, so snake_case/camelCase both hit.
1892
2715
  _TrezorAdapter._sensitiveLogKeys = /* @__PURE__ */ new Set([
2716
+ "extra",
2717
+ "knownconnections",
1893
2718
  "credential",
1894
2719
  "credentials",
1895
2720
  "entropy",
@@ -2008,7 +2833,7 @@ async function fetchDefinitionHex(url, fetchImpl) {
2008
2833
  }
2009
2834
  return arrayBufferToHex(await response.arrayBuffer());
2010
2835
  });
2011
- } catch (e5) {
2836
+ } catch (e6) {
2012
2837
  return void 0;
2013
2838
  }
2014
2839
  }
@@ -2037,7 +2862,7 @@ async function fetchSolanaTokenDefinition({
2037
2862
  }
2038
2863
  return Buffer.from(await response.arrayBuffer()).toString("hex");
2039
2864
  });
2040
- } catch (e6) {
2865
+ } catch (e7) {
2041
2866
  return void 0;
2042
2867
  }
2043
2868
  }