@onekeyfe/hwk-trezor-adapter 1.2.5 → 1.2.6-alpha.0

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
@@ -5,6 +5,20 @@
5
5
 
6
6
 
7
7
 
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
8
22
 
9
23
 
10
24
 
@@ -762,10 +776,28 @@ function debugLog(...args) {
762
776
  emitLog("debug", ...args);
763
777
  }
764
778
 
779
+ // src/utils/queueKey.ts
780
+ function trezorQueueKey({
781
+ operationId,
782
+ connectId
783
+ }) {
784
+ return operationId || connectId || "";
785
+ }
786
+
765
787
  // src/adapter/TrezorAdapter.ts
766
788
  var KNOWN_HARDWARE_ERROR_CODES = new Set(
767
789
  Object.values(_hwkadaptercore.HardwareErrorCode).filter((value) => typeof value === "number")
768
790
  );
791
+ var ALL_NETWORK_COMMON_PARAM_KEYS = [
792
+ "autoInstallApp",
793
+ "passphraseState",
794
+ "useEmptyPassphrase",
795
+ "operationId",
796
+ "knownConnections",
797
+ "extra",
798
+ "allowDeviceSelection",
799
+ "supportedTransports"
800
+ ];
769
801
  var TREZOR_BTC_NETWORK_COIN_MAP = {
770
802
  btc: "Bitcoin",
771
803
  bitcoin: "Bitcoin",
@@ -782,9 +814,24 @@ var TREZOR_BTC_NETWORK_COIN_MAP = {
782
814
  dash: "Dash"
783
815
  };
784
816
  var _TrezorAdapter = class _TrezorAdapter {
785
- constructor(connector) {
817
+ constructor(connector, options) {
786
818
  this.vendor = "trezor";
819
+ // The connector has no protocol-level cancel: the adapter stops waiting and
820
+ // drops the late response, the device carries on.
821
+ this.cancelCapability = "stops-waiting";
787
822
  this._emitter = new (0, _hwkadaptercore.TypedEventEmitter)();
823
+ this._operations = new (0, _hwkadaptercore.OperationRegistry)({
824
+ vendor: "trezor",
825
+ onEnded: (operation, reason) => {
826
+ this._emitter.emit(_hwkadaptercore.SDK.OPERATION_ENDED, {
827
+ type: _hwkadaptercore.SDK.OPERATION_ENDED,
828
+ payload: { operationId: operation.operationId, reason }
829
+ });
830
+ if (reason === "timeout") {
831
+ void this._releaseOperationConnection(operation).catch(() => void 0);
832
+ }
833
+ }
834
+ });
788
835
  this._devices = /* @__PURE__ */ new Map();
789
836
  this._sessions = /* @__PURE__ */ new Map();
790
837
  this._connectingPromises = /* @__PURE__ */ new Map();
@@ -793,12 +840,21 @@ var _TrezorAdapter = class _TrezorAdapter {
793
840
  this._uiRegistry = new (0, _hwkadaptercore.UiRequestRegistry)();
794
841
  this._passphraseRequestContextByConnectId = /* @__PURE__ */ new Map();
795
842
  this._verifiedPassphraseSessionsByConnectId = /* @__PURE__ */ new Map();
843
+ /** Raw connector calls that outlive an aborted caller, keyed by session. */
844
+ this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
845
+ this._connectorIdleWaiters = /* @__PURE__ */ new Set();
846
+ this._resetPromise = null;
847
+ this._connectorTeardownTail = Promise.resolve();
848
+ this._pendingConnectorTeardowns = 0;
849
+ this._stateGeneration = 0;
850
+ this._knownDeviceConnections = /* @__PURE__ */ new Map();
796
851
  this._onDeviceConnect = (data) => {
797
852
  const device = this._connectorDeviceToDeviceInfo(data.device);
798
853
  this._devices.set(device.connectId, device);
799
854
  this._emitter.emit(_hwkadaptercore.DEVICE.CONNECT, { type: _hwkadaptercore.DEVICE.CONNECT, payload: device });
800
855
  };
801
856
  this._onDeviceDisconnect = (data) => {
857
+ this._operations.endByConnectionKey(data.connectId, "disconnect");
802
858
  this._sessions.delete(data.connectId);
803
859
  this._verifiedPassphraseSessionsByConnectId.delete(data.connectId);
804
860
  this._devices.delete(data.connectId);
@@ -818,25 +874,32 @@ var _TrezorAdapter = class _TrezorAdapter {
818
874
  payload: { device }
819
875
  });
820
876
  };
877
+ // The one outlet for every connector-raised UI request (PIN, passphrase,
878
+ // button, THP pairing), so the operation is stamped here instead of at each
879
+ // connector call site.
821
880
  this._onUiRequest = (data) => {
881
+ const operationId = this._activeOperationId();
882
+ const basePayload = data.payload && typeof data.payload === "object" ? data.payload : {};
883
+ const attribution = operationId === void 0 ? {} : { operationId };
822
884
  if (data.type === _hwkadaptercore.UI_REQUEST.REQUEST_PASSPHRASE) {
823
- const payload = data.payload && typeof data.payload === "object" ? data.payload : {};
824
- const connectId = typeof payload.connectId === "string" ? payload.connectId : void 0;
885
+ const connectId = typeof basePayload.connectId === "string" ? basePayload.connectId : void 0;
825
886
  const context = connectId ? this._passphraseRequestContextByConnectId.get(connectId) : void 0;
826
887
  this._emitter.emit(
827
888
  data.type,
828
889
  {
829
890
  ...data,
830
891
  payload: {
831
- ...payload,
892
+ ...basePayload,
832
893
  ..._optionalChain([context, 'optionalAccess', _13 => _13.passphraseState]) !== void 0 ? { passphraseState: context.passphraseState } : {},
833
- ..._optionalChain([context, 'optionalAccess', _14 => _14.useEmptyPassphrase]) !== void 0 ? { useEmptyPassphrase: context.useEmptyPassphrase } : {}
894
+ ..._optionalChain([context, 'optionalAccess', _14 => _14.useEmptyPassphrase]) !== void 0 ? { useEmptyPassphrase: context.useEmptyPassphrase } : {},
895
+ ...attribution
834
896
  }
835
897
  }
836
898
  );
837
899
  return;
838
900
  }
839
- this._emitter.emit(data.type, data);
901
+ const payload = operationId === void 0 ? data.payload : { ...basePayload, ...attribution };
902
+ this._emitter.emit(data.type, { ...data, payload });
840
903
  };
841
904
  this._onUiEvent = (data) => {
842
905
  this._emitter.emit("ui-event", data);
@@ -848,6 +911,11 @@ var _TrezorAdapter = class _TrezorAdapter {
848
911
  });
849
912
  };
850
913
  this._connector = connector;
914
+ for (const connection of _nullishCoalesce(_optionalChain([options, 'optionalAccess', _15 => _15.knownDeviceConnections]), () => ( []))) {
915
+ if (connection.deviceId) {
916
+ this._knownDeviceConnections.set(connection.deviceId, { ...connection });
917
+ }
918
+ }
851
919
  this._jobQueue = new (0, _hwkadaptercore.DeviceJobQueue)();
852
920
  this._registerConnectorEvents();
853
921
  }
@@ -872,29 +940,35 @@ var _TrezorAdapter = class _TrezorAdapter {
872
940
  const {
873
941
  passphraseState,
874
942
  autoInstallApp: _autoInstallApp,
943
+ operationId,
875
944
  useEmptyPassphrase,
945
+ knownConnections,
946
+ extra,
947
+ allowDeviceSelection,
948
+ supportedTransports,
876
949
  ...rest
877
950
  } = params;
878
951
  return {
879
952
  passphraseState: typeof passphraseState === "string" ? passphraseState : void 0,
880
953
  useEmptyPassphrase: typeof useEmptyPassphrase === "boolean" ? useEmptyPassphrase : void 0,
954
+ operationId: typeof operationId === "string" ? operationId : void 0,
955
+ connectionContext: {
956
+ knownConnections,
957
+ extra,
958
+ allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0,
959
+ supportedTransports
960
+ },
881
961
  rest
882
962
  };
883
963
  }
884
964
  return { rest: _nullishCoalesce(params, () => ( {})) };
885
965
  }
886
966
  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) {
891
- return item;
892
- }
967
+ const defined = ALL_NETWORK_COMMON_PARAM_KEYS.filter((key) => _optionalChain([commonParams, 'optionalAccess', _16 => _16[key]]) !== void 0);
968
+ if (!defined.length) return item;
893
969
  return {
894
970
  ...item,
895
- ...autoInstallApp !== void 0 ? { autoInstallApp } : {},
896
- ...passphraseState !== void 0 ? { passphraseState } : {},
897
- ...useEmptyPassphrase !== void 0 ? { useEmptyPassphrase } : {}
971
+ ...Object.fromEntries(defined.map((key) => [key, _optionalChain([commonParams, 'optionalAccess', _17 => _17[key]])]))
898
972
  };
899
973
  }
900
974
  static _normalizeLogKey(key) {
@@ -1018,13 +1092,11 @@ var _TrezorAdapter = class _TrezorAdapter {
1018
1092
  * selected is gone (the device evicted it from its limited slot pool). The
1019
1093
  * caller drops the stale cache entry and recreates from scratch.
1020
1094
  *
1021
- * NOTE: the exact eviction code still needs real-device confirmation — match
1022
- * conservatively so a genuine error isn't mistaken for an evicted session
1023
- * (which would silently re-prompt the passphrase).
1095
+ * Match only the protocol's explicit unallocated-channel code. Guessing from
1096
+ * an error message could replay a request after an unrelated business error.
1024
1097
  */
1025
1098
  static _isStaleSessionError(code) {
1026
- if (typeof code !== "string") return false;
1027
- return code === "ThpUnallocatedChannel" || /invalid.?session|unallocated.?session|session.?not.?found/i.test(code);
1099
+ return code === "ThpUnallocatedChannel";
1028
1100
  }
1029
1101
  static _errorCode(error) {
1030
1102
  return _nullishCoalesce(error.code, () => ( _TrezorAdapter._trezorFailureMessage(error).code));
@@ -1038,13 +1110,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1038
1110
  return protocolError.name === "TrezorProtocolError" && protocolError.code === "Malformed protocol format" && protocolError.message === "Malformed protocol format";
1039
1111
  }
1040
1112
  /**
1041
- * Race a promise against an abort signal. Copied from LedgerAdapter:
1042
- * IConnector.call() can't actually be cancelled at the protocol level, but
1043
- * the caller gets the abort immediately and the underlying work completes
1044
- * uselessly in the background.
1113
+ * Race a promise against an abort signal. IConnector.call() can't actually
1114
+ * be cancelled at the protocol level, so the caller gets the abort while the
1115
+ * adapter keeps the transport exclusively reserved until the raw call drains.
1045
1116
  */
1046
1117
  static _abortable(signal, promise) {
1047
1118
  if (signal.aborted) {
1119
+ void promise.catch(() => void 0);
1048
1120
  return Promise.reject(
1049
1121
  _nullishCoalesce(signal.reason, () => ( new Error("Aborted")))
1050
1122
  );
@@ -1066,17 +1138,37 @@ var _TrezorAdapter = class _TrezorAdapter {
1066
1138
  );
1067
1139
  });
1068
1140
  }
1069
- async _connectSession(connectId, signal) {
1141
+ async _connectSession(connectId, signal, stateGeneration, transportType) {
1070
1142
  const connectOnce = async () => {
1071
- const connectPromise = this._connector.connect(connectId);
1072
- if (!signal) return connectPromise;
1143
+ if (_optionalChain([signal, 'optionalAccess', _22 => _22.aborted])) {
1144
+ throw _nullishCoalesce(signal.reason, () => ( new Error("Aborted")));
1145
+ }
1146
+ this._assertConnectorIdle("connectDevice");
1147
+ const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
1148
+ let connectPromise;
1073
1149
  try {
1074
- return await _TrezorAdapter._abortable(signal, connectPromise);
1150
+ connectPromise = transportType ? this._connector.connect(connectId, { transportType }) : this._connector.connect(connectId);
1075
1151
  } catch (error) {
1076
- void connectPromise.then(
1077
- (orphan) => this._connector.disconnect(orphan.sessionId).catch(() => void 0),
1078
- () => void 0
1079
- );
1152
+ releaseOperation();
1153
+ throw error;
1154
+ }
1155
+ if (!signal) {
1156
+ try {
1157
+ return await connectPromise;
1158
+ } finally {
1159
+ releaseOperation();
1160
+ }
1161
+ }
1162
+ try {
1163
+ const session2 = await _TrezorAdapter._abortable(signal, connectPromise);
1164
+ releaseOperation();
1165
+ return session2;
1166
+ } catch (error) {
1167
+ if (signal.aborted) {
1168
+ void connectPromise.then((orphan) => this._connector.disconnect(orphan.sessionId).catch(() => void 0)).catch(() => void 0).finally(releaseOperation);
1169
+ } else {
1170
+ releaseOperation();
1171
+ }
1080
1172
  throw error;
1081
1173
  }
1082
1174
  };
@@ -1084,7 +1176,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1084
1176
  try {
1085
1177
  session = await connectOnce();
1086
1178
  } catch (error) {
1087
- if (_optionalChain([signal, 'optionalAccess', _22 => _22.aborted]) || !_TrezorAdapter._isRecoverableProtocolResidueError(error)) {
1179
+ if (_optionalChain([signal, 'optionalAccess', _23 => _23.aborted]) || !_TrezorAdapter._isRecoverableProtocolResidueError(error)) {
1088
1180
  throw error;
1089
1181
  }
1090
1182
  debugLog("[TrezorAdapter] retrying connect after draining a stale THP frame", {
@@ -1092,8 +1184,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1092
1184
  });
1093
1185
  session = await connectOnce();
1094
1186
  }
1095
- if (this._disconnectRequested.delete(connectId)) {
1096
- await this._connector.disconnect(session.sessionId).catch(() => void 0);
1187
+ if (_optionalChain([signal, 'optionalAccess', _24 => _24.aborted]) || stateGeneration !== this._stateGeneration || this._disconnectRequested.delete(connectId)) {
1188
+ const releaseCleanup = this._retainConnectorOperation(`disconnect:${session.sessionId}`);
1189
+ try {
1190
+ await this._connector.disconnect(session.sessionId).catch(() => void 0);
1191
+ } finally {
1192
+ releaseCleanup();
1193
+ }
1097
1194
  throw Object.assign(new Error("Trezor connection aborted"), {
1098
1195
  code: _hwkadaptercore.HardwareErrorCode.UserAborted
1099
1196
  });
@@ -1109,14 +1206,9 @@ var _TrezorAdapter = class _TrezorAdapter {
1109
1206
  async init(_config) {
1110
1207
  }
1111
1208
  async dispose() {
1209
+ await this._resetStateAndDisconnectSessions();
1112
1210
  this._unregisterConnectorEvents();
1113
- this._uiRegistry.reset();
1114
- this._jobQueue.clear();
1115
1211
  this._connector.reset();
1116
- this._devices.clear();
1117
- this._sessions.clear();
1118
- this._verifiedPassphraseSessionsByConnectId.clear();
1119
- this._connectingPromises.clear();
1120
1212
  this._emitter.removeAllListeners();
1121
1213
  }
1122
1214
  /**
@@ -1125,12 +1217,30 @@ var _TrezorAdapter = class _TrezorAdapter {
1125
1217
  * error flows. Aborts active jobs and releases UI waits.
1126
1218
  */
1127
1219
  resetState() {
1220
+ void this._resetStateAndDisconnectSessions();
1221
+ }
1222
+ _resetStateAndDisconnectSessions() {
1223
+ if (this._resetPromise) return this._resetPromise;
1224
+ const sessionIds = new Set(this._sessions.values());
1225
+ this._stateGeneration += 1;
1226
+ this._operations.endAll("runtime-reset");
1128
1227
  this._uiRegistry.reset();
1129
1228
  this._jobQueue.clear();
1130
1229
  this._devices.clear();
1131
1230
  this._sessions.clear();
1132
1231
  this._verifiedPassphraseSessionsByConnectId.clear();
1133
1232
  this._connectingPromises.clear();
1233
+ const resetPromise = this._runConnectorTeardown(async () => {
1234
+ for (const sessionId of sessionIds) {
1235
+ await this._connector.disconnect(sessionId).catch(() => void 0);
1236
+ }
1237
+ });
1238
+ this._resetPromise = resetPromise;
1239
+ return resetPromise.finally(() => {
1240
+ if (this._resetPromise === resetPromise) {
1241
+ this._resetPromise = null;
1242
+ }
1243
+ });
1134
1244
  }
1135
1245
  getAvailableTransports() {
1136
1246
  return this.activeTransport ? [this.activeTransport] : [];
@@ -1138,32 +1248,162 @@ var _TrezorAdapter = class _TrezorAdapter {
1138
1248
  async switchTransport(_type) {
1139
1249
  }
1140
1250
  async searchDevices(options) {
1141
- await this._ensureDevicePermission();
1251
+ await this._ensureDevicePermission(void 0, void 0, _optionalChain([options, 'optionalAccess', _25 => _25.transportType]));
1252
+ return this._searchDevices(options);
1253
+ }
1254
+ async _searchDevices(options, signal) {
1255
+ if (_optionalChain([options, 'optionalAccess', _26 => _26.transportType]) && !this._availableTransports().includes(options.transportType))
1256
+ return [];
1257
+ if (_optionalChain([options, 'optionalAccess', _27 => _27.resetSession])) {
1258
+ await this._resetStateAndDisconnectSessions();
1259
+ } else {
1260
+ await this._connectorTeardownTail;
1261
+ }
1262
+ if (_optionalChain([signal, 'optionalAccess', _28 => _28.aborted])) throw signal.reason;
1263
+ const stateGeneration = this._stateGeneration;
1142
1264
  const scanned = await this._connector.searchDevices(
1143
- _optionalChain([options, 'optionalAccess', _23 => _23.waitForAllTransports]) ? { waitForAll: true } : void 0
1265
+ _optionalChain([options, 'optionalAccess', _29 => _29.waitForAllTransports]) || _optionalChain([options, 'optionalAccess', _30 => _30.transportType]) ? {
1266
+ ...options.waitForAllTransports ? { waitForAll: true } : {},
1267
+ ...options.transportType ? { transportType: options.transportType } : {}
1268
+ } : void 0
1144
1269
  );
1270
+ if (_optionalChain([signal, 'optionalAccess', _31 => _31.aborted])) throw signal.reason;
1271
+ if (stateGeneration !== this._stateGeneration) {
1272
+ throw _hwkadaptercore.createHwkError.call(void 0, {
1273
+ code: _hwkadaptercore.HardwareErrorCode.UserAborted,
1274
+ message: "Trezor discovery belongs to an ended operation"
1275
+ });
1276
+ }
1145
1277
  const scannedIds = new Set(scanned.map((d) => d.connectId));
1146
1278
  for (const connectId of [...this._devices.keys()]) {
1147
- if (!scannedIds.has(connectId) && !this._sessions.has(connectId)) {
1279
+ if ((!_optionalChain([options, 'optionalAccess', _32 => _32.transportType]) || _optionalChain([this, 'access', _33 => _33._devices, 'access', _34 => _34.get, 'call', _35 => _35(connectId), 'optionalAccess', _36 => _36.connectionType]) === options.transportType) && !scannedIds.has(connectId) && !this._sessions.has(connectId)) {
1148
1280
  this._devices.delete(connectId);
1149
1281
  }
1150
1282
  }
1151
1283
  for (const device of scanned) {
1152
1284
  const info = this._connectorDeviceToDeviceInfo(device);
1153
- this._devices.set(info.connectId, info);
1285
+ if (!this._sessions.has(info.connectId)) this._devices.set(info.connectId, info);
1154
1286
  }
1155
- return Array.from(this._devices.values());
1287
+ return Array.from(this._devices.values()).filter(
1288
+ (device) => !_optionalChain([options, 'optionalAccess', _37 => _37.transportType]) || device.connectionType === options.transportType
1289
+ );
1156
1290
  }
1157
- async connectDevice(connectId) {
1291
+ async searchDeviceTargets(options) {
1292
+ const devices = await this.searchDevices(options);
1293
+ return devices.map((device) => ({
1294
+ searchTargetId: device.connectId,
1295
+ searchTargetReusePolicy: _hwkadaptercore.resolveSearchTargetReusePolicy.call(void 0, device),
1296
+ vendor: "trezor",
1297
+ connectionType: device.connectionType,
1298
+ kind: "physical",
1299
+ label: device.label,
1300
+ model: device.model,
1301
+ modelName: device.modelName,
1302
+ serialNumber: device.serialNumber
1303
+ }));
1304
+ }
1305
+ async listConnectionTargets(options) {
1306
+ const targets = await this.searchDeviceTargets(options);
1307
+ return targets.map(({ searchTargetId, ...target }) => ({
1308
+ ...target,
1309
+ targetId: searchTargetId
1310
+ }));
1311
+ }
1312
+ async bindBleDevice(params) {
1313
+ if (_optionalChain([params, 'access', _38 => _38.identity, 'optionalAccess', _39 => _39.vendor]) !== "trezor" || !params.identity.value) {
1314
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.InvalidParams, "Trezor device identity is required");
1315
+ }
1316
+ const { value: deviceId } = params.identity;
1158
1317
  try {
1159
- await this._ensureDevicePermission(connectId);
1160
- await this._ensureSession(connectId);
1161
- return _hwkadaptercore.success.call(void 0, connectId);
1318
+ return await this._jobQueue.enqueue(
1319
+ deviceId,
1320
+ async (signal) => {
1321
+ const selected = await this._selectBleDeviceForBinding(
1322
+ deviceId,
1323
+ signal,
1324
+ { extra: params.extra },
1325
+ "manual-rebind"
1326
+ );
1327
+ try {
1328
+ const persisted = await this._emitVerifiedBinding(
1329
+ deviceId,
1330
+ selected.connectId,
1331
+ selected.selectionRequestId,
1332
+ { extra: params.extra },
1333
+ signal
1334
+ );
1335
+ if (!persisted) {
1336
+ throw _hwkadaptercore.createHwkError.call(void 0, {
1337
+ code: _hwkadaptercore.HardwareErrorCode.UnknownError,
1338
+ message: "Bluetooth binding could not be saved",
1339
+ origin: "host"
1340
+ });
1341
+ }
1342
+ this._rememberVerifiedConnection(deviceId, selected.connectId);
1343
+ return _hwkadaptercore.success.call(void 0, selected.connectId);
1344
+ } catch (error) {
1345
+ await this._releaseProvisionalConnection(selected.connectId, signal);
1346
+ throw error;
1347
+ }
1348
+ },
1349
+ {
1350
+ label: "bindBleDevice",
1351
+ rejectIfBusy: true,
1352
+ busyError: _TrezorAdapter._createDeviceBusyError("bindBleDevice")
1353
+ }
1354
+ );
1162
1355
  } catch (error) {
1163
1356
  return this._errorToFailure(error);
1164
1357
  }
1165
1358
  }
1166
- async disconnectDevice(connectId) {
1359
+ async connectDevice(searchTargetId) {
1360
+ const scope = this._jobQueue.createCancelScope(searchTargetId);
1361
+ try {
1362
+ await _TrezorAdapter._abortable(
1363
+ scope.signal,
1364
+ this._ensureDevicePermission(
1365
+ searchTargetId,
1366
+ void 0,
1367
+ _optionalChain([this, 'access', _40 => _40._devices, 'access', _41 => _41.get, 'call', _42 => _42(searchTargetId), 'optionalAccess', _43 => _43.connectionType])
1368
+ )
1369
+ );
1370
+ await this._ensureSession(searchTargetId, scope.signal);
1371
+ if (scope.signal.aborted) throw scope.signal.reason;
1372
+ const device = this._devices.get(searchTargetId);
1373
+ if (!device) {
1374
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.DeviceNotFound, "Trezor device not found after connect");
1375
+ }
1376
+ this._operations.endByConnectionKey(searchTargetId, "explicit");
1377
+ const operation = this._operations.create({
1378
+ searchTargetId,
1379
+ connectId: searchTargetId,
1380
+ device,
1381
+ connectionType: device.connectionType,
1382
+ connectionKeys: [_nullishCoalesce(this._sessions.get(searchTargetId), () => ( ""))]
1383
+ });
1384
+ return _hwkadaptercore.success.call(void 0, operation.operationId);
1385
+ } catch (error) {
1386
+ if (scope.signal.aborted) {
1387
+ await this._waitForConnectorOperationsToDrain();
1388
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.UserAborted, "Trezor connection aborted");
1389
+ }
1390
+ return this._errorToFailure(error);
1391
+ } finally {
1392
+ scope.release();
1393
+ }
1394
+ }
1395
+ async releaseOperation(operationId) {
1396
+ const operation = this._operations.find(operationId);
1397
+ if (!operation) {
1398
+ this._operations.resolve(operationId);
1399
+ return;
1400
+ }
1401
+ const endedOperation = this._operations.end(operationId, "explicit");
1402
+ if (!endedOperation) return;
1403
+ await this._releaseOperationConnection(endedOperation);
1404
+ }
1405
+ async _releaseOperationConnection(operation) {
1406
+ const { connectId } = operation;
1167
1407
  const connecting = this._connectingPromises.has(connectId);
1168
1408
  this._connectingPromises.delete(connectId);
1169
1409
  const sessionId = this._sessions.get(connectId);
@@ -1171,90 +1411,132 @@ var _TrezorAdapter = class _TrezorAdapter {
1171
1411
  if (connecting) this._disconnectRequested.add(connectId);
1172
1412
  return;
1173
1413
  }
1174
- this._sessions.delete(connectId);
1175
- this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1176
- await this._connector.disconnect(sessionId);
1414
+ await this._disconnectSession(connectId, sessionId);
1177
1415
  }
1178
- async getDeviceInfo(connectId, deviceId) {
1416
+ async getDeviceInfo(connectIdOrOperationId, deviceId) {
1417
+ let connectId;
1418
+ try {
1419
+ connectId = _hwkadaptercore.isHardwareOperationId.call(void 0, connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
1420
+ } catch (error) {
1421
+ return this._errorToFailure(error);
1422
+ }
1179
1423
  const device = _nullishCoalesce(this._devices.get(connectId), () => ( Array.from(this._devices.values()).find((item) => item.deviceId === deviceId)));
1180
1424
  if (!device) return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.DeviceNotFound, "Trezor device not found");
1181
1425
  return _hwkadaptercore.success.call(void 0, device);
1182
1426
  }
1183
- getSupportedChains() {
1184
- return ["btc", "evm", "sol", "tron"];
1185
- }
1186
1427
  async allNetworkGetAddress(connectId, deviceId, params) {
1428
+ const target = _hwkadaptercore.resolveHardwareOperationTarget.call(void 0, connectId, params.operationId, "trezor");
1429
+ if (!target.success) return target;
1430
+ let effectiveTargetId = _nullishCoalesce(target.payload.targetId, () => ( ""));
1431
+ let releaseOperationRetention;
1432
+ try {
1433
+ releaseOperationRetention = target.payload.operationId ? this._operations.retain(target.payload.operationId) : void 0;
1434
+ } catch (error) {
1435
+ return this._errorToFailure(error);
1436
+ }
1437
+ const cancelScope = this._jobQueue.createCancelScope(
1438
+ trezorQueueKey({ operationId: target.payload.operationId, connectId: effectiveTargetId })
1439
+ );
1187
1440
  let liveDeviceId = "";
1441
+ const bundleContext = {};
1188
1442
  const topLevelFailureIndexes = /* @__PURE__ */ new Set();
1189
1443
  const isSingleNetworkBundle = params.bundle.every(
1190
- (item) => item.network === _optionalChain([params, 'access', _24 => _24.bundle, 'access', _25 => _25[0], 'optionalAccess', _26 => _26.network])
1444
+ (item) => item.network === _optionalChain([params, 'access', _44 => _44.bundle, 'access', _45 => _45[0], 'optionalAccess', _46 => _46.network])
1191
1445
  );
1192
- const getLiveDeviceId = async () => {
1446
+ const getLiveDeviceId = async (expectedDeviceId) => {
1193
1447
  if (liveDeviceId) return _hwkadaptercore.success.call(void 0, liveDeviceId);
1194
- const deviceIdResponse = await this._getTrezorDeviceId(connectId);
1448
+ const deviceIdResponse = await this._getTrezorDeviceId(
1449
+ effectiveTargetId,
1450
+ expectedDeviceId || deviceId,
1451
+ params,
1452
+ bundleContext
1453
+ );
1195
1454
  if (deviceIdResponse.success) {
1196
1455
  liveDeviceId = deviceIdResponse.payload;
1456
+ if (!target.payload.operationId) {
1457
+ effectiveTargetId = _nullishCoalesce(_optionalChain([bundleContext, 'access', _47 => _47.connection, 'optionalAccess', _48 => _48.connectId]), () => ( effectiveTargetId));
1458
+ }
1197
1459
  }
1198
1460
  return deviceIdResponse;
1199
1461
  };
1200
- return _hwkadaptercore.runAllNetworkGetAddress.call(void 0, {
1201
- connectId,
1202
- deviceId,
1203
- params,
1204
- normalizeItem: _TrezorAdapter._normalizeAllNetworkItem,
1205
- callItem: async ({ method, item, index }) => {
1206
- const expectedDeviceId = _nullishCoalesce(_TrezorAdapter._getItemDeviceId(item), () => ( deviceId));
1207
- if (expectedDeviceId) {
1208
- const fingerprint = await getLiveDeviceId();
1209
- if (!fingerprint.success) {
1462
+ try {
1463
+ return await _hwkadaptercore.runAllNetworkGetAddress.call(void 0, {
1464
+ connectId: effectiveTargetId,
1465
+ deviceId,
1466
+ params,
1467
+ normalizeItem: _TrezorAdapter._normalizeAllNetworkItem,
1468
+ callItem: async ({ method, item, index }) => {
1469
+ if (cancelScope.signal.aborted) {
1210
1470
  topLevelFailureIndexes.add(index);
1211
- return fingerprint;
1471
+ return _TrezorAdapter._buildCancelledFailure(cancelScope.signal);
1212
1472
  }
1213
- if (expectedDeviceId !== fingerprint.payload) {
1214
- topLevelFailureIndexes.add(index);
1215
- return _hwkadaptercore.failure.call(void 0,
1216
- _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
1217
- `Wrong device: expected ${expectedDeviceId}, got ${fingerprint.payload}`,
1218
- { expected: expectedDeviceId, actual: fingerprint.payload }
1219
- );
1473
+ const expectedDeviceId = _nullishCoalesce(_TrezorAdapter._getItemDeviceId(item), () => ( deviceId));
1474
+ if (expectedDeviceId) {
1475
+ const fingerprint = await getLiveDeviceId(expectedDeviceId);
1476
+ if (!fingerprint.success) {
1477
+ topLevelFailureIndexes.add(index);
1478
+ return fingerprint;
1479
+ }
1480
+ if (expectedDeviceId !== fingerprint.payload) {
1481
+ topLevelFailureIndexes.add(index);
1482
+ return _hwkadaptercore.failure.call(void 0,
1483
+ _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
1484
+ `Wrong device: expected ${expectedDeviceId}, got ${fingerprint.payload}`,
1485
+ { expected: expectedDeviceId, actual: fingerprint.payload }
1486
+ );
1487
+ }
1220
1488
  }
1221
- }
1222
- const callItem = _TrezorAdapter._withAllNetworkRequestCommonParams(item, params);
1223
- return this._callAllNetworkMethod(connectId, method, callItem);
1224
- },
1225
- attachIdentity: async ({ item, chain, payload }) => {
1226
- if (!liveDeviceId) {
1227
- const fingerprint = await getLiveDeviceId();
1228
- if (!fingerprint.success) {
1229
- return { ...item, success: false, payload: fingerprint.payload };
1489
+ const callItem = _TrezorAdapter._withAllNetworkRequestCommonParams(item, params);
1490
+ return this._callAllNetworkMethod(effectiveTargetId, method, callItem, bundleContext);
1491
+ },
1492
+ attachIdentity: async ({ item, chain, payload, index }) => {
1493
+ if (!liveDeviceId) {
1494
+ if (cancelScope.signal.aborted) {
1495
+ topLevelFailureIndexes.add(index);
1496
+ const cancelled = _TrezorAdapter._buildCancelledFailure(cancelScope.signal);
1497
+ return { ...item, success: false, payload: cancelled.payload };
1498
+ }
1499
+ const fingerprint = await getLiveDeviceId(deviceId);
1500
+ if (!fingerprint.success) {
1501
+ return { ...item, success: false, payload: fingerprint.payload };
1502
+ }
1230
1503
  }
1504
+ return {
1505
+ ...item,
1506
+ success: true,
1507
+ payload: {
1508
+ ...payload,
1509
+ deviceIdentity: {
1510
+ vendor: "trezor",
1511
+ type: "deviceId",
1512
+ value: liveDeviceId
1513
+ },
1514
+ chainFingerprint: liveDeviceId,
1515
+ chainFingerprintChain: chain
1516
+ }
1517
+ };
1518
+ },
1519
+ // One named boolean per abort reason.
1520
+ shouldAbortBundle: (response, { index }) => {
1521
+ const isSessionLevelFailure = topLevelFailureIndexes.has(index);
1522
+ const isWholeChainForbidden = isSingleNetworkBundle && _optionalChain([response, 'access', _49 => _49.payload, 'optionalAccess', _50 => _50.code]) === _hwkadaptercore.HardwareErrorCode.DevicePathForbidden;
1523
+ const isPassphrasePolicyFailure = _optionalChain([response, 'access', _51 => _51.payload, 'optionalAccess', _52 => _52.code]) === _hwkadaptercore.HardwareErrorCode.PassphraseAlwaysOnDevice;
1524
+ const { code } = _nullishCoalesce(response.payload, () => ( {}));
1525
+ return isSessionLevelFailure || isWholeChainForbidden || isPassphrasePolicyFailure || _hwkadaptercore.isUserRefusal.call(void 0, code) || _hwkadaptercore.isConnectionLost.call(void 0, code);
1231
1526
  }
1232
- return {
1233
- ...item,
1234
- success: true,
1235
- payload: {
1236
- ...payload,
1237
- deviceIdentity: {
1238
- vendor: "trezor",
1239
- type: "deviceId",
1240
- value: liveDeviceId
1241
- },
1242
- chainFingerprint: liveDeviceId,
1243
- chainFingerprintChain: chain
1244
- }
1245
- };
1246
- },
1247
- // One named boolean per abort reason.
1248
- shouldAbortBundle: (response, { index }) => {
1249
- const isSessionLevelFailure = topLevelFailureIndexes.has(index);
1250
- const isWholeChainForbidden = isSingleNetworkBundle && _optionalChain([response, 'access', _27 => _27.payload, 'optionalAccess', _28 => _28.code]) === _hwkadaptercore.HardwareErrorCode.DevicePathForbidden;
1251
- const isPassphrasePolicyFailure = _optionalChain([response, 'access', _29 => _29.payload, 'optionalAccess', _30 => _30.code]) === _hwkadaptercore.HardwareErrorCode.PassphraseAlwaysOnDevice;
1252
- return isSessionLevelFailure || isWholeChainForbidden || isPassphrasePolicyFailure;
1253
- }
1254
- });
1527
+ });
1528
+ } finally {
1529
+ cancelScope.release();
1530
+ _optionalChain([releaseOperationRetention, 'optionalCall', _53 => _53()]);
1531
+ }
1255
1532
  }
1256
- getFeatures(connectId) {
1257
- return this._callDeviceManagerMethod("getFeatures", connectId, {});
1533
+ getFeatures(connectId, operationContext) {
1534
+ return this._callDeviceManagerMethod(
1535
+ "getFeatures",
1536
+ connectId,
1537
+ {},
1538
+ operationContext
1539
+ );
1258
1540
  }
1259
1541
  /**
1260
1542
  * Device authenticity attestation. Sends a host-generated challenge, has the
@@ -1289,7 +1571,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1289
1571
  if (!internalModel) {
1290
1572
  return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.UnknownError, "Device internal_model unavailable");
1291
1573
  }
1292
- const challengeHex = _nullishCoalesce(_optionalChain([params, 'access', _31 => _31.challenge, 'optionalAccess', _32 => _32.toLowerCase, 'call', _33 => _33()]), () => ( Buffer.from(_utils.randomBytes.call(void 0, 32)).toString("hex")));
1574
+ const challengeHex = _nullishCoalesce(_optionalChain([params, 'access', _54 => _54.challenge, 'optionalAccess', _55 => _55.toLowerCase, 'call', _56 => _56()]), () => ( Buffer.from(_utils.randomBytes.call(void 0, 32)).toString("hex")));
1293
1575
  const challenge = Buffer.from(challengeHex, "hex");
1294
1576
  const proofRes = await this._callDeviceManagerMethod(
1295
1577
  "authenticateDevice",
@@ -1314,28 +1596,65 @@ var _TrezorAdapter = class _TrezorAdapter {
1314
1596
  }
1315
1597
  });
1316
1598
  }
1317
- deviceSettings(connectId, params) {
1599
+ deviceSettings(connectId, params, operationContext) {
1318
1600
  return this._callDeviceManagerMethod(
1319
1601
  "deviceSettings",
1320
1602
  connectId,
1321
- params
1603
+ params,
1604
+ operationContext
1322
1605
  );
1323
1606
  }
1324
- setBrightness(connectId, params = {}) {
1607
+ setBrightness(connectId, params = {}, operationContext) {
1325
1608
  return this._callDeviceManagerMethod(
1326
1609
  "setBrightness",
1327
1610
  connectId,
1328
- params
1611
+ params,
1612
+ operationContext
1329
1613
  );
1330
1614
  }
1331
- changePin(connectId, params = {}) {
1332
- return this._callDeviceManagerMethod("changePin", connectId, params);
1615
+ changePin(connectId, params = {}, operationContext) {
1616
+ return this._callDeviceManagerMethod(
1617
+ "changePin",
1618
+ connectId,
1619
+ params,
1620
+ operationContext
1621
+ );
1333
1622
  }
1334
- wipeDevice(connectId) {
1335
- return this._callDeviceManagerMethod("wipeDevice", connectId, {});
1623
+ wipeDevice(connectId, operationContext) {
1624
+ return this._callDeviceManagerMethod(
1625
+ "wipeDevice",
1626
+ connectId,
1627
+ {},
1628
+ operationContext
1629
+ );
1336
1630
  }
1337
- async _getTrezorDeviceId(connectId) {
1338
- const features = await this.getFeatures(connectId);
1631
+ async _getTrezorDeviceId(connectId, expectedDeviceId, commonParams, bundleContext) {
1632
+ const hasConnectionContext = _optionalChain([commonParams, 'optionalAccess', _57 => _57.knownConnections]) !== void 0 || _optionalChain([commonParams, 'optionalAccess', _58 => _58.extra]) !== void 0;
1633
+ const features = bundleContext || hasConnectionContext ? await this._callMethod(
1634
+ "getFeatures",
1635
+ connectId,
1636
+ hasConnectionContext || !connectId ? expectedDeviceId : void 0,
1637
+ hasConnectionContext ? {
1638
+ operationId: commonParams.operationId,
1639
+ knownConnections: commonParams.knownConnections,
1640
+ extra: commonParams.extra,
1641
+ allowDeviceSelection: commonParams.allowDeviceSelection,
1642
+ supportedTransports: commonParams.supportedTransports,
1643
+ passphraseState: commonParams.passphraseState,
1644
+ useEmptyPassphrase: commonParams.useEmptyPassphrase
1645
+ } : {},
1646
+ false,
1647
+ bundleContext
1648
+ ) : await this.getFeatures(
1649
+ connectId,
1650
+ !connectId && expectedDeviceId ? {
1651
+ expectedDeviceIdentity: {
1652
+ vendor: "trezor",
1653
+ type: "deviceId",
1654
+ value: expectedDeviceId
1655
+ }
1656
+ } : void 0
1657
+ );
1339
1658
  if (!features.success) return features;
1340
1659
  const liveDeviceId = features.payload.device_id;
1341
1660
  if (typeof liveDeviceId !== "string" || liveDeviceId.length === 0) {
@@ -1344,23 +1663,67 @@ var _TrezorAdapter = class _TrezorAdapter {
1344
1663
  return _hwkadaptercore.success.call(void 0, liveDeviceId);
1345
1664
  }
1346
1665
  cancel(connectId) {
1666
+ if (_hwkadaptercore.isHardwareOperationId.call(void 0, connectId)) {
1667
+ try {
1668
+ this._operations.resolve(connectId);
1669
+ } catch (e5) {
1670
+ return;
1671
+ }
1672
+ }
1347
1673
  const userAbortReason = Object.assign(new Error("User aborted operation"), {
1348
1674
  code: _hwkadaptercore.HardwareErrorCode.UserAborted
1349
1675
  });
1350
- this._uiRegistry.cancel();
1351
- this._connector.uiResponse({ type: _hwkadaptercore.UI_RESPONSE.CANCEL });
1352
- if (connectId) {
1353
- this._jobQueue.forceCancelActive(connectId, userAbortReason);
1354
- void this._connector.cancel(_nullishCoalesce(this._sessions.get(connectId), () => ( connectId)));
1676
+ const activeJobId = _optionalChain([this, 'access', _59 => _59._jobQueue, 'access', _60 => _60.getActiveJob, 'call', _61 => _61(), 'optionalAccess', _62 => _62.deviceId]);
1677
+ const targetId = _nullishCoalesce(connectId, () => ( activeJobId));
1678
+ if (targetId) {
1679
+ let resolvedConnectId = targetId;
1680
+ let operationId;
1681
+ if (_hwkadaptercore.isHardwareOperationId.call(void 0, targetId)) {
1682
+ try {
1683
+ operationId = targetId;
1684
+ resolvedConnectId = this._operations.resolve(targetId).connectId;
1685
+ } catch (e6) {
1686
+ resolvedConnectId = "";
1687
+ }
1688
+ }
1689
+ const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
1690
+ const pendingOperationId = _nullishCoalesce(operationId, () => ( _optionalChain([interactionForPhysicalId, 'optionalAccess', _63 => _63.operationId])));
1691
+ if (!connectId) {
1692
+ this._uiRegistry.cancel();
1693
+ } else if (pendingOperationId) {
1694
+ this._uiRegistry.cancel(void 0, void 0, pendingOperationId);
1695
+ }
1696
+ const queueKeys = /* @__PURE__ */ new Set([trezorQueueKey({ connectId: targetId })]);
1697
+ if (pendingOperationId) queueKeys.add(trezorQueueKey({ operationId: pendingOperationId }));
1698
+ const ownsConnectorInteraction = activeJobId !== void 0 ? queueKeys.has(activeJobId) || pendingOperationId !== void 0 && this._activeOperationId() === pendingOperationId : this._connectingPromises.size === 1 && this._connectingPromises.has(resolvedConnectId);
1699
+ if (connectId === void 0 || ownsConnectorInteraction) {
1700
+ this._connector.uiResponse({ type: _hwkadaptercore.UI_RESPONSE.CANCEL });
1701
+ }
1702
+ for (const key of queueKeys) {
1703
+ this._jobQueue.cancelActiveAndPending(key, userAbortReason);
1704
+ }
1705
+ if (resolvedConnectId) {
1706
+ void this._connector.cancel(_nullishCoalesce(this._sessions.get(resolvedConnectId), () => ( resolvedConnectId)));
1707
+ }
1355
1708
  return;
1356
1709
  }
1357
- for (const cid of this._sessions.keys()) {
1358
- this._jobQueue.forceCancelActive(cid, userAbortReason);
1359
- }
1710
+ this._uiRegistry.cancel();
1711
+ this._connector.uiResponse({ type: _hwkadaptercore.UI_RESPONSE.CANCEL });
1712
+ this._jobQueue.cancelActiveAndPending(void 0, userAbortReason);
1360
1713
  for (const sessionId of this._sessions.values()) {
1361
1714
  void this._connector.cancel(sessionId);
1362
1715
  }
1363
1716
  }
1717
+ /**
1718
+ * Operation owning the running job: its queue key when pinned, else the live
1719
+ * operation of its connectId, so a mid-call UI request can name it.
1720
+ */
1721
+ _activeOperationId() {
1722
+ const activeJobId = _optionalChain([this, 'access', _64 => _64._jobQueue, 'access', _65 => _65.getActiveJob, 'call', _66 => _66(), 'optionalAccess', _67 => _67.deviceId]);
1723
+ if (!activeJobId) return void 0;
1724
+ if (_hwkadaptercore.isHardwareOperationId.call(void 0, activeJobId)) return activeJobId;
1725
+ return _optionalChain([this, 'access', _68 => _68._operations, 'access', _69 => _69.findActiveByConnectionKey, 'call', _70 => _70(activeJobId), 'optionalAccess', _71 => _71.operationId]);
1726
+ }
1364
1727
  /**
1365
1728
  * Routes to BOTH the adapter's local UI registry (preemption decisions,
1366
1729
  * future SELECT_DEVICE / DEVICE_PERMISSION) AND the connector's registry
@@ -1369,6 +1732,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1369
1732
  */
1370
1733
  uiResponse(response) {
1371
1734
  this._uiRegistry.resolve(response.type, response.payload);
1735
+ if (response.type === _hwkadaptercore.UI_RESPONSE.RECEIVE_SELECT_DEVICE) return;
1372
1736
  this._connector.uiResponse(response);
1373
1737
  }
1374
1738
  /**
@@ -1380,8 +1744,8 @@ var _TrezorAdapter = class _TrezorAdapter {
1380
1744
  * Unlike Ledger (which hashes a per-chain address), it is chain-global, so
1381
1745
  * `chain` is accepted for API parity but ignored.
1382
1746
  */
1383
- async getChainFingerprint(connectId, _deviceId, _chain) {
1384
- return this._getTrezorDeviceId(connectId);
1747
+ async getChainFingerprint(connectId, deviceId, _chain) {
1748
+ return this._getTrezorDeviceId(connectId, deviceId);
1385
1749
  }
1386
1750
  /**
1387
1751
  * Resolve the active passphrase wallet identity (`passphraseState`).
@@ -1391,17 +1755,87 @@ var _TrezorAdapter = class _TrezorAdapter {
1391
1755
  * can't interleave with a chain call on the same device (which would desync
1392
1756
  * the THP session selection).
1393
1757
  */
1394
- getPassphraseState(connectId, passphraseState) {
1395
- return this._jobQueue.enqueue(
1758
+ getPassphraseState(connectId, passphraseState, operationContext) {
1759
+ const expectedIdentity = _optionalChain([operationContext, 'optionalAccess', _72 => _72.expectedDeviceIdentity]);
1760
+ if (expectedIdentity && (expectedIdentity.vendor !== "trezor" || expectedIdentity.type !== "deviceId")) {
1761
+ return Promise.resolve(
1762
+ _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.InvalidParams, "Trezor device identity is required")
1763
+ );
1764
+ }
1765
+ const target = _hwkadaptercore.resolveHardwareOperationTarget.call(void 0,
1396
1766
  connectId,
1767
+ _optionalChain([operationContext, 'optionalAccess', _73 => _73.operationId]),
1768
+ "trezor"
1769
+ );
1770
+ if (!target.success) return Promise.resolve(target);
1771
+ const { operationId } = target.payload;
1772
+ let resolvedConnectId = _nullishCoalesce(target.payload.targetId, () => ( ""));
1773
+ let releaseOperationRetention;
1774
+ if (operationId) {
1775
+ try {
1776
+ resolvedConnectId = this._operations.resolve(operationId).connectId;
1777
+ releaseOperationRetention = this._operations.retain(operationId);
1778
+ } catch (error) {
1779
+ return Promise.resolve(this._errorToFailure(error));
1780
+ }
1781
+ }
1782
+ return this._jobQueue.enqueue(
1783
+ trezorQueueKey({ operationId, connectId: resolvedConnectId }),
1397
1784
  async (signal) => {
1398
- const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {
1399
- passphraseState
1400
- });
1785
+ let selectionRequestId;
1786
+ let verified = false;
1401
1787
  try {
1402
- return await this._resolvePassphraseState(connectId, passphraseState, signal);
1788
+ if (!operationId && _optionalChain([expectedIdentity, 'optionalAccess', _74 => _74.value]) && (!connectId || _optionalChain([operationContext, 'optionalAccess', _75 => _75.knownConnections]) !== void 0)) {
1789
+ const resolved = await this._resolveExpectedDeviceConnectId(
1790
+ expectedIdentity.value,
1791
+ signal,
1792
+ operationContext
1793
+ );
1794
+ resolvedConnectId = resolved.connectId;
1795
+ selectionRequestId = resolved.selectionRequestId;
1796
+ }
1797
+ if (_optionalChain([expectedIdentity, 'optionalAccess', _76 => _76.value])) {
1798
+ if (!operationId) await this._ensureSession(resolvedConnectId, signal);
1799
+ if (_optionalChain([this, 'access', _77 => _77._devices, 'access', _78 => _78.get, 'call', _79 => _79(resolvedConnectId), 'optionalAccess', _80 => _80.deviceId]) !== expectedIdentity.value) {
1800
+ return _hwkadaptercore.failure.call(void 0,
1801
+ _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
1802
+ "Trezor device identity does not match"
1803
+ );
1804
+ }
1805
+ }
1806
+ const restorePassphraseRequestContext = this._setPassphraseRequestContext(
1807
+ resolvedConnectId,
1808
+ {
1809
+ passphraseState
1810
+ }
1811
+ );
1812
+ try {
1813
+ const result = await this._resolvePassphraseState(
1814
+ resolvedConnectId,
1815
+ passphraseState,
1816
+ signal,
1817
+ operationId
1818
+ );
1819
+ if (result.success && _optionalChain([expectedIdentity, 'optionalAccess', _81 => _81.value])) {
1820
+ await this._emitVerifiedBinding(
1821
+ expectedIdentity.value,
1822
+ resolvedConnectId,
1823
+ selectionRequestId,
1824
+ operationContext,
1825
+ signal
1826
+ );
1827
+ verified = true;
1828
+ this._rememberVerifiedConnection(expectedIdentity.value, resolvedConnectId);
1829
+ }
1830
+ return result;
1831
+ } finally {
1832
+ restorePassphraseRequestContext();
1833
+ }
1403
1834
  } finally {
1404
- restorePassphraseRequestContext();
1835
+ if (selectionRequestId && !verified) {
1836
+ this._emitBindingStatus(selectionRequestId, signal.aborted ? "cancelled" : "failed");
1837
+ await this._releaseProvisionalConnection(resolvedConnectId, signal);
1838
+ }
1405
1839
  }
1406
1840
  },
1407
1841
  {
@@ -1409,7 +1843,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1409
1843
  rejectIfBusy: true,
1410
1844
  busyError: _TrezorAdapter._createDeviceBusyError("getPassphraseState")
1411
1845
  }
1412
- ).catch((error) => this._errorToFailure(error));
1846
+ ).catch((error) => this._errorToFailure(error)).finally(() => _optionalChain([releaseOperationRetention, 'optionalCall', _82 => _82()]));
1413
1847
  }
1414
1848
  evmGetAddress(connectId, deviceId, params) {
1415
1849
  return this._callWalletMethod("evmGetAddress", connectId, deviceId, params);
@@ -1472,11 +1906,94 @@ var _TrezorAdapter = class _TrezorAdapter {
1472
1906
  }
1473
1907
  return result;
1474
1908
  }
1475
- static async _callConnector(connector, sessionId, method, params, signal) {
1476
- const promise = connector.call(sessionId, method, params);
1909
+ async _callConnector(sessionId, method, params, signal) {
1910
+ if (_optionalChain([signal, 'optionalAccess', _83 => _83.aborted])) {
1911
+ throw _nullishCoalesce(signal.reason, () => ( new Error("Aborted")));
1912
+ }
1913
+ this._assertConnectorIdle(method);
1914
+ const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
1915
+ let promise;
1916
+ try {
1917
+ promise = this._connector.call(sessionId, method, params).finally(() => {
1918
+ releaseOperation();
1919
+ });
1920
+ } catch (error) {
1921
+ releaseOperation();
1922
+ throw error;
1923
+ }
1477
1924
  const result = signal ? await _TrezorAdapter._abortable(signal, promise) : await promise;
1478
1925
  return _TrezorAdapter._unwrapConnectorResult(result);
1479
1926
  }
1927
+ _assertConnectorIdle(method) {
1928
+ if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
1929
+ throw _TrezorAdapter._createDeviceBusyError(method);
1930
+ }
1931
+ }
1932
+ _runConnectorTeardown(task) {
1933
+ const previous = this._connectorTeardownTail;
1934
+ let releaseTail = () => void 0;
1935
+ this._connectorTeardownTail = new Promise((resolve) => {
1936
+ releaseTail = resolve;
1937
+ });
1938
+ this._pendingConnectorTeardowns += 1;
1939
+ return (async () => {
1940
+ try {
1941
+ await previous;
1942
+ await this._waitForConnectorOperationsToDrain();
1943
+ await task();
1944
+ } finally {
1945
+ this._pendingConnectorTeardowns -= 1;
1946
+ releaseTail();
1947
+ }
1948
+ })();
1949
+ }
1950
+ async _disconnectSession(connectId, sessionId = this._sessions.get(connectId)) {
1951
+ this._sessions.delete(connectId);
1952
+ this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1953
+ if (sessionId) await this._runConnectorTeardown(() => this._connector.disconnect(sessionId));
1954
+ }
1955
+ _availableTransports() {
1956
+ return _nullishCoalesce(this._connector.availableTransports, () => ( [this._connector.connectionType]));
1957
+ }
1958
+ async _releaseProvisionalConnection(connectId, signal) {
1959
+ const sessionId = this._sessions.get(connectId);
1960
+ this._sessions.delete(connectId);
1961
+ this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1962
+ if (!sessionId) return;
1963
+ const teardown = this._runConnectorTeardown(() => this._connector.disconnect(sessionId)).catch(
1964
+ () => void 0
1965
+ );
1966
+ if (!signal.aborted) await teardown;
1967
+ }
1968
+ _waitForConnectorOperationsToDrain() {
1969
+ if (this._unsettledConnectorOperations.size === 0) {
1970
+ return Promise.resolve();
1971
+ }
1972
+ return new Promise((resolve) => {
1973
+ this._connectorIdleWaiters.add(resolve);
1974
+ });
1975
+ }
1976
+ _retainConnectorOperation(key) {
1977
+ this._unsettledConnectorOperations.set(
1978
+ key,
1979
+ (_nullishCoalesce(this._unsettledConnectorOperations.get(key), () => ( 0))) + 1
1980
+ );
1981
+ let released = false;
1982
+ return () => {
1983
+ if (released) return;
1984
+ released = true;
1985
+ const remaining = (_nullishCoalesce(this._unsettledConnectorOperations.get(key), () => ( 1))) - 1;
1986
+ if (remaining > 0) {
1987
+ this._unsettledConnectorOperations.set(key, remaining);
1988
+ } else {
1989
+ this._unsettledConnectorOperations.delete(key);
1990
+ }
1991
+ if (this._unsettledConnectorOperations.size === 0) {
1992
+ for (const resolve of this._connectorIdleWaiters) resolve();
1993
+ this._connectorIdleWaiters.clear();
1994
+ }
1995
+ };
1996
+ }
1480
1997
  static _normalizeAllNetworkItem(method, item) {
1481
1998
  if (method !== "btcGetAddress" && method !== "btcGetPublicKey") {
1482
1999
  return item;
@@ -1495,18 +2012,21 @@ var _TrezorAdapter = class _TrezorAdapter {
1495
2012
  const { deviceId } = item;
1496
2013
  return typeof deviceId === "string" && deviceId.length > 0 ? deviceId : void 0;
1497
2014
  }
1498
- _callAllNetworkMethod(connectId, method, item) {
2015
+ /** UserAborted is a top-level abort code, so this ends the bundle. */
2016
+ static _buildCancelledFailure(signal) {
2017
+ const reason = signal.reason;
2018
+ const code = typeof _optionalChain([reason, 'optionalAccess', _84 => _84.code]) === "number" ? reason.code : _hwkadaptercore.HardwareErrorCode.UserAborted;
2019
+ const message = typeof _optionalChain([reason, 'optionalAccess', _85 => _85.message]) === "string" ? reason.message : "";
2020
+ return _hwkadaptercore.failure.call(void 0, code, message || "All-network get-address cancelled");
2021
+ }
2022
+ _callAllNetworkMethod(connectId, method, item, bundleContext) {
1499
2023
  switch (method) {
1500
2024
  case "evmGetAddress":
1501
- return this.evmGetAddress(connectId, "", item);
1502
2025
  case "btcGetAddress":
1503
- return this.btcGetAddress(connectId, "", item);
1504
2026
  case "btcGetPublicKey":
1505
- return this.btcGetPublicKey(connectId, "", item);
1506
2027
  case "solGetAddress":
1507
- return this.solGetAddress(connectId, "", item);
1508
2028
  case "tronGetAddress":
1509
- return this.tronGetAddress(connectId, "", item);
2029
+ return this._callMethod(method, connectId, "", item, true, bundleContext);
1510
2030
  default:
1511
2031
  return Promise.resolve(
1512
2032
  _hwkadaptercore.failure.call(void 0,
@@ -1525,11 +2045,51 @@ var _TrezorAdapter = class _TrezorAdapter {
1525
2045
  _callWalletMethod(methodName, connectId, deviceId, params) {
1526
2046
  return this._callMethod(methodName, connectId, deviceId, params, true);
1527
2047
  }
1528
- _callDeviceManagerMethod(methodName, connectId, params) {
1529
- return this._callMethod(methodName, connectId, void 0, params, false);
2048
+ _callDeviceManagerMethod(methodName, connectId, params, operationContext) {
2049
+ const expectedIdentity = _optionalChain([operationContext, 'optionalAccess', _86 => _86.expectedDeviceIdentity]);
2050
+ if (expectedIdentity && (expectedIdentity.vendor !== "trezor" || expectedIdentity.type !== "deviceId")) {
2051
+ return Promise.resolve(
2052
+ _hwkadaptercore.failure.call(void 0,
2053
+ _hwkadaptercore.HardwareErrorCode.InvalidParams,
2054
+ "Trezor device-manager operations require a Trezor deviceId identity"
2055
+ )
2056
+ );
2057
+ }
2058
+ return this._callMethod(
2059
+ methodName,
2060
+ connectId,
2061
+ _optionalChain([expectedIdentity, 'optionalAccess', _87 => _87.value]),
2062
+ {
2063
+ ...params && typeof params === "object" ? params : {},
2064
+ knownConnections: _optionalChain([operationContext, 'optionalAccess', _88 => _88.knownConnections]),
2065
+ extra: _optionalChain([operationContext, 'optionalAccess', _89 => _89.extra]),
2066
+ allowDeviceSelection: _optionalChain([operationContext, 'optionalAccess', _90 => _90.allowDeviceSelection]),
2067
+ supportedTransports: _optionalChain([operationContext, 'optionalAccess', _91 => _91.supportedTransports]),
2068
+ ..._optionalChain([operationContext, 'optionalAccess', _92 => _92.operationId]) ? { operationId: operationContext.operationId } : {}
2069
+ },
2070
+ false
2071
+ );
1530
2072
  }
1531
- async _callMethod(methodName, connectId, deviceId, params, requiresWalletIntent) {
2073
+ async _callMethod(methodName, connectId, deviceId, params, requiresWalletIntent, bundleContext) {
1532
2074
  const call = _TrezorAdapter._normalizeCallArgs(connectId, deviceId, params);
2075
+ const commonParams = _TrezorAdapter._splitCommonParams(call.params);
2076
+ const positionalOperationId = _hwkadaptercore.isHardwareOperationId.call(void 0, call.connectId) ? call.connectId : void 0;
2077
+ if (positionalOperationId && commonParams.operationId && positionalOperationId !== commonParams.operationId) {
2078
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.InvalidParams, "Conflicting Trezor operation ids", {
2079
+ positionalOperationId,
2080
+ commonOperationId: commonParams.operationId
2081
+ });
2082
+ }
2083
+ const operationId = _nullishCoalesce(commonParams.operationId, () => ( positionalOperationId));
2084
+ let releaseOperationRetention;
2085
+ try {
2086
+ if (operationId) {
2087
+ call.connectId = this._operations.resolve(operationId).connectId;
2088
+ releaseOperationRetention = this._operations.retain(operationId);
2089
+ }
2090
+ } catch (error) {
2091
+ return this._errorToFailure(error);
2092
+ }
1533
2093
  debugLog("[TrezorAdapter][REQ]", {
1534
2094
  method: methodName,
1535
2095
  connectId: call.connectId || "(empty)",
@@ -1538,15 +2098,40 @@ var _TrezorAdapter = class _TrezorAdapter {
1538
2098
  });
1539
2099
  try {
1540
2100
  const response = await this._jobQueue.enqueue(
1541
- call.connectId,
1542
- (signal) => this._callWithRetry(
1543
- call.connectId,
1544
- methodName,
1545
- call.params,
1546
- requiresWalletIntent,
1547
- true,
1548
- signal
1549
- ),
2101
+ trezorQueueKey({ operationId, connectId: call.connectId }),
2102
+ async (signal) => {
2103
+ let effectiveConnectId = call.connectId;
2104
+ let selectionRequestId;
2105
+ try {
2106
+ if (!_optionalChain([bundleContext, 'optionalAccess', _93 => _93.connection]) && !operationId && deviceId && (!connectId || _optionalChain([commonParams, 'access', _94 => _94.connectionContext, 'optionalAccess', _95 => _95.knownConnections]) !== void 0)) {
2107
+ const resolved = await this._resolveExpectedDeviceConnectId(
2108
+ deviceId,
2109
+ signal,
2110
+ commonParams.connectionContext
2111
+ );
2112
+ effectiveConnectId = resolved.connectId;
2113
+ selectionRequestId = resolved.selectionRequestId;
2114
+ }
2115
+ } catch (error) {
2116
+ return this._errorToFailure(error);
2117
+ }
2118
+ const response2 = await this._callWithRetry(
2119
+ _nullishCoalesce(_optionalChain([bundleContext, 'optionalAccess', _96 => _96.connection, 'optionalAccess', _97 => _97.connectId]), () => ( effectiveConnectId)),
2120
+ methodName,
2121
+ call.params,
2122
+ deviceId || "",
2123
+ requiresWalletIntent,
2124
+ true,
2125
+ signal,
2126
+ operationId,
2127
+ selectionRequestId,
2128
+ bundleContext
2129
+ );
2130
+ if (!response2.success && selectionRequestId) {
2131
+ await this._releaseProvisionalConnection(effectiveConnectId, signal);
2132
+ }
2133
+ return response2;
2134
+ },
1550
2135
  {
1551
2136
  label: methodName,
1552
2137
  rejectIfBusy: true,
@@ -1567,16 +2152,53 @@ var _TrezorAdapter = class _TrezorAdapter {
1567
2152
  payload: _TrezorAdapter._sanitizeForLog(response.payload, methodName)
1568
2153
  });
1569
2154
  return response;
2155
+ } finally {
2156
+ _optionalChain([releaseOperationRetention, 'optionalCall', _98 => _98()]);
1570
2157
  }
1571
2158
  }
1572
- async _callWithRetry(connectId, methodName, params, requiresWalletIntent, allowRetry, signal) {
1573
- const { passphraseState, useEmptyPassphrase, rest } = _TrezorAdapter._splitCommonParams(params);
2159
+ async _callWithRetry(connectId, methodName, params, expectedDeviceId, requiresWalletIntent, allowRetry, signal, operationId, selectionRequestId, bundleContext) {
2160
+ const { passphraseState, useEmptyPassphrase, rest, connectionContext } = _TrezorAdapter._splitCommonParams(params);
2161
+ let pendingBindingRequestId = selectionRequestId;
2162
+ let businessCallStarted = false;
1574
2163
  const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {
1575
2164
  passphraseState,
1576
2165
  useEmptyPassphrase
1577
2166
  });
1578
2167
  try {
1579
- const sessionId = await this._ensureSession(connectId, signal);
2168
+ if (_optionalChain([bundleContext, 'optionalAccess', _99 => _99.connection])) {
2169
+ const pinned = bundleContext.connection;
2170
+ if (this._sessions.get(pinned.connectId) !== pinned.sessionId) {
2171
+ return _hwkadaptercore.failure.call(void 0,
2172
+ _hwkadaptercore.HardwareErrorCode.DeviceDisconnected,
2173
+ "Trezor all-network connection ended"
2174
+ );
2175
+ }
2176
+ }
2177
+ const sessionId = operationId ? this._sessions.get(this._operations.resolve(operationId).connectId) : await this._ensureSession(connectId, signal);
2178
+ if (!sessionId) {
2179
+ if (operationId) {
2180
+ this._operations.end(operationId, "disconnect");
2181
+ return _hwkadaptercore.failure.call(void 0,
2182
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2183
+ "Trezor operation connection is no longer active",
2184
+ { operationId, reason: "disconnect" }
2185
+ );
2186
+ }
2187
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.DeviceNotFound, "Trezor session was not found");
2188
+ }
2189
+ if (bundleContext && !bundleContext.connection) {
2190
+ bundleContext.connection = { connectId, sessionId };
2191
+ }
2192
+ if (expectedDeviceId) {
2193
+ const actualDeviceId = _optionalChain([this, 'access', _100 => _100._devices, 'access', _101 => _101.get, 'call', _102 => _102(connectId), 'optionalAccess', _103 => _103.deviceId]);
2194
+ if (!actualDeviceId || actualDeviceId !== expectedDeviceId) {
2195
+ return _hwkadaptercore.failure.call(void 0,
2196
+ _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
2197
+ `Wrong device: expected ${expectedDeviceId}, got ${actualDeviceId || "unknown"}`,
2198
+ { expected: expectedDeviceId, actual: actualDeviceId || "" }
2199
+ );
2200
+ }
2201
+ }
1580
2202
  if (requiresWalletIntent && !passphraseState && useEmptyPassphrase !== true) {
1581
2203
  return _hwkadaptercore.failure.call(void 0,
1582
2204
  _hwkadaptercore.HardwareErrorCode.InvalidParams,
@@ -1591,39 +2213,85 @@ var _TrezorAdapter = class _TrezorAdapter {
1591
2213
  } else if (useEmptyPassphrase === true) {
1592
2214
  await _TrezorAdapter._abortable(signal, this._createFreshAppSession(sessionId, signal));
1593
2215
  }
2216
+ if (expectedDeviceId) {
2217
+ await this._emitVerifiedBinding(
2218
+ expectedDeviceId,
2219
+ connectId,
2220
+ pendingBindingRequestId,
2221
+ connectionContext,
2222
+ signal
2223
+ );
2224
+ pendingBindingRequestId = void 0;
2225
+ this._rememberVerifiedConnection(expectedDeviceId, connectId);
2226
+ }
2227
+ businessCallStarted = true;
1594
2228
  const result = await _TrezorAdapter._abortable(
1595
2229
  signal,
1596
- _TrezorAdapter._callConnector(this._connector, sessionId, methodName, rest)
2230
+ this._callConnector(sessionId, methodName, rest, signal)
1597
2231
  );
1598
2232
  return _hwkadaptercore.success.call(void 0, result);
1599
2233
  } catch (error) {
1600
2234
  if (signal.aborted) {
2235
+ if (pendingBindingRequestId) {
2236
+ this._emitBindingStatus(pendingBindingRequestId, "cancelled");
2237
+ pendingBindingRequestId = void 0;
2238
+ }
1601
2239
  return this._errorToFailure(error);
1602
2240
  }
1603
2241
  const code = _TrezorAdapter._errorCode(error);
1604
- if (allowRetry && code === _hwkadaptercore.HardwareErrorCode.DeviceDisconnected) {
2242
+ const willRetryStaleSession = allowRetry && (passphraseState || useEmptyPassphrase === true) && _TrezorAdapter._isStaleSessionError(code);
2243
+ if (pendingBindingRequestId && !willRetryStaleSession) {
2244
+ this._emitBindingStatus(pendingBindingRequestId, "failed");
2245
+ pendingBindingRequestId = void 0;
2246
+ }
2247
+ const ambiguousTransportFailure = code === _hwkadaptercore.HardwareErrorCode.DeviceDisconnected || code === _hwkadaptercore.HardwareErrorCode.OperationTimeout || code === _hwkadaptercore.HardwareErrorCode.TransportError;
2248
+ if (ambiguousTransportFailure) {
1605
2249
  this._sessions.delete(connectId);
1606
2250
  this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1607
- return await this._callWithRetry(
1608
- connectId,
1609
- methodName,
1610
- params,
1611
- requiresWalletIntent,
1612
- false,
1613
- signal
1614
- );
2251
+ const mayHaveCompleted = businessCallStarted && !_hwkadaptercore.canReplayHardwareMethodAfterTransportFailure.call(void 0, methodName);
2252
+ const mayHaveCompletedMessage = `Trezor ${methodName} may have completed before the connection was lost`;
2253
+ if (operationId) {
2254
+ this._operations.end(operationId, "disconnect");
2255
+ const endedParams = { operationId, reason: "disconnect" };
2256
+ return mayHaveCompleted ? _hwkadaptercore.failure.call(void 0,
2257
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2258
+ mayHaveCompletedMessage,
2259
+ _hwkadaptercore.operationMayHaveCompletedParams.call(void 0, methodName, endedParams),
2260
+ void 0,
2261
+ { scope: "unknown" }
2262
+ ) : _hwkadaptercore.failure.call(void 0,
2263
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2264
+ "Trezor operation connection was lost",
2265
+ endedParams
2266
+ );
2267
+ }
2268
+ if (mayHaveCompleted) {
2269
+ return _hwkadaptercore.failure.call(void 0,
2270
+ code,
2271
+ mayHaveCompletedMessage,
2272
+ _hwkadaptercore.operationMayHaveCompletedParams.call(void 0, methodName),
2273
+ void 0,
2274
+ { scope: "unknown" }
2275
+ );
2276
+ }
1615
2277
  }
1616
- if (allowRetry && (passphraseState || useEmptyPassphrase === true) && _TrezorAdapter._isStaleSessionError(code)) {
2278
+ if (willRetryStaleSession) {
1617
2279
  if (passphraseState) {
1618
2280
  this._forgetVerifiedPassphraseSession(connectId, passphraseState);
1619
2281
  }
2282
+ const retryBindingRequestId = pendingBindingRequestId;
2283
+ pendingBindingRequestId = void 0;
1620
2284
  return await this._callWithRetry(
1621
2285
  connectId,
1622
2286
  methodName,
1623
2287
  params,
2288
+ expectedDeviceId,
1624
2289
  requiresWalletIntent,
1625
2290
  false,
1626
- signal
2291
+ signal,
2292
+ operationId,
2293
+ retryBindingRequestId,
2294
+ bundleContext
1627
2295
  );
1628
2296
  }
1629
2297
  debugLog("[TrezorAdapter][ERROR]", {
@@ -1634,36 +2302,244 @@ var _TrezorAdapter = class _TrezorAdapter {
1634
2302
  return this._errorToFailure(error);
1635
2303
  } finally {
1636
2304
  restorePassphraseRequestContext();
2305
+ if (pendingBindingRequestId) {
2306
+ this._emitBindingStatus(pendingBindingRequestId, signal.aborted ? "cancelled" : "failed");
2307
+ pendingBindingRequestId = void 0;
2308
+ }
1637
2309
  }
1638
2310
  }
1639
- async _ensureSession(connectId, signal) {
2311
+ async _ensureSession(connectId, signal, transportType) {
1640
2312
  const existing = this._sessions.get(connectId);
1641
2313
  if (existing) return existing;
1642
2314
  const pending = this._connectingPromises.get(connectId);
1643
2315
  if (pending) return pending;
2316
+ const stateGeneration = this._stateGeneration;
1644
2317
  const promise = (async () => {
2318
+ if (_optionalChain([signal, 'optionalAccess', _104 => _104.aborted])) {
2319
+ throw Object.assign(new Error("Trezor connection aborted"), {
2320
+ code: _hwkadaptercore.HardwareErrorCode.UserAborted
2321
+ });
2322
+ }
1645
2323
  try {
1646
- if (_optionalChain([signal, 'optionalAccess', _34 => _34.aborted])) {
1647
- throw Object.assign(new Error("Trezor connection aborted"), {
1648
- code: _hwkadaptercore.HardwareErrorCode.UserAborted
1649
- });
1650
- }
1651
- try {
1652
- return await this._connectSession(connectId, signal);
1653
- } catch (error) {
1654
- if (_TrezorAdapter._isConnectionUnavailableError(error)) {
1655
- this._sessions.delete(connectId);
1656
- this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1657
- }
1658
- throw error;
2324
+ return await this._connectSession(connectId, signal, stateGeneration, transportType);
2325
+ } catch (error) {
2326
+ if (_TrezorAdapter._isConnectionUnavailableError(error)) {
2327
+ this._sessions.delete(connectId);
2328
+ this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1659
2329
  }
1660
- } finally {
1661
- this._connectingPromises.delete(connectId);
1662
- this._disconnectRequested.delete(connectId);
2330
+ throw error;
1663
2331
  }
1664
2332
  })();
1665
2333
  this._connectingPromises.set(connectId, promise);
1666
- return promise;
2334
+ try {
2335
+ return await promise;
2336
+ } finally {
2337
+ if (this._connectingPromises.get(connectId) === promise) {
2338
+ this._connectingPromises.delete(connectId);
2339
+ }
2340
+ this._disconnectRequested.delete(connectId);
2341
+ }
2342
+ }
2343
+ async _resolveExpectedDeviceConnectId(expectedDeviceId, signal, context) {
2344
+ const connectedMatch = Array.from(this._devices.values()).find(
2345
+ (device) => device.deviceId === expectedDeviceId && this._sessions.has(device.connectId)
2346
+ );
2347
+ if (connectedMatch) return { connectId: connectedMatch.connectId };
2348
+ const availableTransports = this._availableTransports();
2349
+ if (availableTransports.includes("usb")) {
2350
+ await _TrezorAdapter._abortable(
2351
+ signal,
2352
+ this._ensureDevicePermission(void 0, expectedDeviceId, "usb")
2353
+ );
2354
+ }
2355
+ const candidates = await _TrezorAdapter._abortable(
2356
+ signal,
2357
+ this._searchDevices({ transportType: "usb" }, signal)
2358
+ );
2359
+ const mismatchedDeviceIds = [];
2360
+ const knownConnection = this._knownDeviceConnections.get(expectedDeviceId);
2361
+ const knownUsbIds = _optionalChain([context, 'optionalAccess', _105 => _105.knownConnections]) !== void 0 ? context.knownConnections.flatMap(
2362
+ (connection) => connection.transport === "usb" ? [connection.connectId] : []
2363
+ ) : [_optionalChain([knownConnection, 'optionalAccess', _106 => _106.usbConnectId])].filter((id) => Boolean(id));
2364
+ const suppliedBleIds = _optionalChain([context, 'optionalAccess', _107 => _107.knownConnections, 'optionalAccess', _108 => _108.flatMap, 'call', _109 => _109(
2365
+ (connection) => connection.transport === "ble" ? [connection.connectId] : []
2366
+ )]);
2367
+ const knownBleIds = _optionalChain([suppliedBleIds, 'optionalAccess', _110 => _110.length]) ? suppliedBleIds : [_optionalChain([knownConnection, 'optionalAccess', _111 => _111.bleConnectId])].filter((id) => Boolean(id));
2368
+ const usbCandidates = candidates.filter(
2369
+ (device) => device.connectionType === "usb" && (!knownUsbIds.length || knownUsbIds.includes(device.connectId))
2370
+ );
2371
+ for (const candidate of usbCandidates) {
2372
+ let sessionId;
2373
+ try {
2374
+ sessionId = await this._ensureSession(candidate.connectId, signal, "usb");
2375
+ const actualDeviceId = _nullishCoalesce(_optionalChain([this, 'access', _112 => _112._devices, 'access', _113 => _113.get, 'call', _114 => _114(candidate.connectId), 'optionalAccess', _115 => _115.deviceId]), () => ( ""));
2376
+ if (actualDeviceId === expectedDeviceId) return { connectId: candidate.connectId };
2377
+ mismatchedDeviceIds.push(actualDeviceId || "unknown");
2378
+ } catch (error) {
2379
+ if (signal.aborted) throw error;
2380
+ if (!_TrezorAdapter._isConnectionUnavailableError(error)) throw error;
2381
+ }
2382
+ if (sessionId) {
2383
+ await this._disconnectSession(candidate.connectId, sessionId);
2384
+ }
2385
+ }
2386
+ if (mismatchedDeviceIds.length) {
2387
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2388
+ code: _hwkadaptercore.HardwareErrorCode.DeviceSearchMismatch,
2389
+ message: "The connected Trezor is not the wallet this operation needs. Unplug it and connect the right device.",
2390
+ params: { connectedDeviceIds: mismatchedDeviceIds }
2391
+ });
2392
+ }
2393
+ if (usbCandidates.length > 0) {
2394
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2395
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2396
+ message: "Trezor devices are present but none could be opened to check its identity."
2397
+ });
2398
+ }
2399
+ const [knownBleConnectId] = knownBleIds;
2400
+ if (knownBleConnectId && availableTransports.includes("ble")) {
2401
+ await _TrezorAdapter._abortable(
2402
+ signal,
2403
+ this._ensureDevicePermission(knownBleConnectId, expectedDeviceId, "ble")
2404
+ );
2405
+ await this._ensureSession(knownBleConnectId, signal, "ble");
2406
+ const actualDeviceId = _optionalChain([this, 'access', _116 => _116._devices, 'access', _117 => _117.get, 'call', _118 => _118(knownBleConnectId), 'optionalAccess', _119 => _119.deviceId]);
2407
+ if (actualDeviceId === expectedDeviceId) return { connectId: knownBleConnectId };
2408
+ await this._disconnectSession(knownBleConnectId);
2409
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2410
+ code: _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
2411
+ message: "The bound Trezor Bluetooth device has a different identity"
2412
+ });
2413
+ }
2414
+ if (_optionalChain([context, 'optionalAccess', _120 => _120.supportedTransports]) && !context.supportedTransports.includes("ble")) {
2415
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2416
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2417
+ message: "This Trezor model has no Bluetooth transport; connect it over USB"
2418
+ });
2419
+ }
2420
+ if (_optionalChain([context, 'optionalAccess', _121 => _121.allowDeviceSelection]) === false) {
2421
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2422
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2423
+ message: "No known Trezor connection is available"
2424
+ });
2425
+ }
2426
+ return this._selectBleDeviceForBinding(expectedDeviceId, signal, context, "missing-binding");
2427
+ }
2428
+ async _selectBleDeviceForBinding(expectedDeviceId, signal, context, reason) {
2429
+ const availableTransports = this._availableTransports();
2430
+ const allowUsbFallback = reason !== "manual-rebind" && availableTransports.includes("usb");
2431
+ const knownUsbIds = _optionalChain([context, 'optionalAccess', _122 => _122.knownConnections]) ? context.knownConnections.flatMap(
2432
+ (connection) => connection.transport === "usb" ? [connection.connectId] : []
2433
+ ) : [_optionalChain([this, 'access', _123 => _123._knownDeviceConnections, 'access', _124 => _124.get, 'call', _125 => _125(expectedDeviceId), 'optionalAccess', _126 => _126.usbConnectId])];
2434
+ if (availableTransports.includes("ble")) {
2435
+ await _TrezorAdapter._abortable(
2436
+ signal,
2437
+ this._ensureDevicePermission(void 0, expectedDeviceId, "ble")
2438
+ );
2439
+ }
2440
+ const bleCandidates = (await _TrezorAdapter._abortable(
2441
+ signal,
2442
+ this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)
2443
+ )).filter((device) => device.connectionType === "ble");
2444
+ if (signal.aborted) throw signal.reason;
2445
+ const rejectedConnectIds = /* @__PURE__ */ new Set();
2446
+ const bindingSessionId = this._uiRegistry.createRequestId();
2447
+ let rejectedConnectId;
2448
+ while (availableTransports.includes("ble") && this._emitter.listenerCount(_hwkadaptercore.UI_REQUEST.REQUEST_SELECT_DEVICE)) {
2449
+ const { device: selected, requestId } = await _hwkadaptercore.requestBleDeviceSelection.call(void 0, {
2450
+ emitter: this._emitter,
2451
+ registry: this._uiRegistry,
2452
+ signal,
2453
+ allowUsbFallback,
2454
+ scan: async () => {
2455
+ if (allowUsbFallback) {
2456
+ const usbCandidates = (await this._searchDevices({ transportType: "usb" }, signal)).filter(
2457
+ (device) => device.connectionType === "usb" && !rejectedConnectIds.has(device.connectId)
2458
+ );
2459
+ const candidate = _nullishCoalesce(usbCandidates.find((device) => knownUsbIds.includes(device.connectId)), () => ( usbCandidates[0]));
2460
+ if (candidate) return [candidate];
2461
+ }
2462
+ return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter(
2463
+ (device) => device.connectionType === "ble" && !rejectedConnectIds.has(device.connectId)
2464
+ );
2465
+ },
2466
+ request: {
2467
+ devices: bleCandidates.filter((device) => !rejectedConnectIds.has(device.connectId)),
2468
+ bindingSessionId,
2469
+ rejectedConnectId,
2470
+ context: {
2471
+ kind: "bind-connection",
2472
+ transport: "ble",
2473
+ reason
2474
+ },
2475
+ extra: _optionalChain([context, 'optionalAccess', _127 => _127.extra]),
2476
+ operationId: this._activeOperationId()
2477
+ }
2478
+ });
2479
+ try {
2480
+ if (reason === "manual-rebind") {
2481
+ this._operations.endByConnectionKey(selected.connectId, "explicit");
2482
+ await this._releaseProvisionalConnection(selected.connectId, signal);
2483
+ }
2484
+ await this._ensureSession(selected.connectId, signal, selected.connectionType);
2485
+ } catch (error) {
2486
+ this._emitBindingStatus(requestId, signal.aborted ? "cancelled" : "failed");
2487
+ throw error;
2488
+ }
2489
+ if (_optionalChain([this, 'access', _128 => _128._devices, 'access', _129 => _129.get, 'call', _130 => _130(selected.connectId), 'optionalAccess', _131 => _131.deviceId]) !== expectedDeviceId) {
2490
+ await this._disconnectSession(selected.connectId);
2491
+ rejectedConnectId = selected.connectId;
2492
+ rejectedConnectIds.add(selected.connectId);
2493
+ continue;
2494
+ }
2495
+ if (selected.connectionType === "usb") {
2496
+ this._emitBindingStatus(requestId, "cancelled");
2497
+ return { connectId: selected.connectId };
2498
+ }
2499
+ return { connectId: selected.connectId, selectionRequestId: requestId };
2500
+ }
2501
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2502
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2503
+ message: "No readable Trezor device is available"
2504
+ });
2505
+ }
2506
+ _emitBindingStatus(selectionRequestId, status) {
2507
+ this._emitter.emit(_hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS, {
2508
+ type: _hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS,
2509
+ payload: { selectionRequestId, status }
2510
+ });
2511
+ }
2512
+ _rememberVerifiedConnection(deviceId, connectId) {
2513
+ const device = this._devices.get(connectId);
2514
+ if (!device || device.deviceId !== deviceId) return;
2515
+ const known = _nullishCoalesce(this._knownDeviceConnections.get(deviceId), () => ( { deviceId }));
2516
+ if (device.connectionType === "ble") {
2517
+ known.bleConnectId = connectId;
2518
+ } else if (device.connectionType === "usb" && _optionalChain([device, 'access', _132 => _132.capabilities, 'optionalAccess', _133 => _133.persistentDeviceIdentity])) {
2519
+ known.usbConnectId = connectId;
2520
+ }
2521
+ this._knownDeviceConnections.set(deviceId, known);
2522
+ }
2523
+ async _emitVerifiedBinding(deviceId, connectId, selectionRequestId, context, signal) {
2524
+ if (!selectionRequestId) return false;
2525
+ const device = this._devices.get(connectId);
2526
+ if (!device || device.deviceId !== deviceId || device.connectionType !== "ble") {
2527
+ this._emitBindingStatus(selectionRequestId, "cancelled");
2528
+ return false;
2529
+ }
2530
+ const outcome = await _hwkadaptercore.requestSaveDeviceBinding.call(void 0,
2531
+ this._emitter,
2532
+ this._uiRegistry,
2533
+ {
2534
+ selectionRequestId,
2535
+ connection: { transport: "ble", connectId },
2536
+ identity: { vendor: "trezor", type: "deviceId", value: deviceId },
2537
+ extra: _optionalChain([context, 'optionalAccess', _134 => _134.extra]),
2538
+ operationId: this._activeOperationId()
2539
+ },
2540
+ signal
2541
+ );
2542
+ return outcome.saved;
1667
2543
  }
1668
2544
  /**
1669
2545
  * Pin the device's active wallet session to `passphraseState` (the target
@@ -1685,12 +2561,21 @@ var _TrezorAdapter = class _TrezorAdapter {
1685
2561
  if (reused) return;
1686
2562
  const created = await _TrezorAdapter._abortable(
1687
2563
  signal,
1688
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpCreateSession", {
1689
- passphraseMode: "prompt"
1690
- })
2564
+ this._callConnector(
2565
+ sessionId,
2566
+ "__thpCreateSession",
2567
+ {
2568
+ passphraseMode: "prompt"
2569
+ },
2570
+ signal
2571
+ )
1691
2572
  );
1692
- if (_optionalChain([created, 'optionalAccess', _35 => _35.protocol]) === "thp" && !created.thpSessionId) return;
1693
- if (_optionalChain([created, 'optionalAccess', _36 => _36.protocol]) !== "thp" && _optionalChain([created, 'optionalAccess', _37 => _37.protocol]) !== "v1") return;
2573
+ if (_optionalChain([created, 'optionalAccess', _135 => _135.protocol]) === "thp" && !created.thpSessionId || _optionalChain([created, 'optionalAccess', _136 => _136.protocol]) !== "thp" && _optionalChain([created, 'optionalAccess', _137 => _137.protocol]) !== "v1") {
2574
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2575
+ code: _hwkadaptercore.HardwareErrorCode.TransportError,
2576
+ message: "Cannot verify the requested Trezor wallet session"
2577
+ });
2578
+ }
1694
2579
  const state = await this._deriveState(sessionId, signal);
1695
2580
  if (state !== passphraseState) {
1696
2581
  throw Object.assign(
@@ -1704,13 +2589,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1704
2589
  }
1705
2590
  _rememberVerifiedPassphraseSession(connectId, passphraseState, sessionId, created) {
1706
2591
  let session;
1707
- if (_optionalChain([created, 'optionalAccess', _38 => _38.protocol]) === "thp" && typeof created.thpSessionId === "string") {
2592
+ if (_optionalChain([created, 'optionalAccess', _138 => _138.protocol]) === "thp" && typeof created.thpSessionId === "string") {
1708
2593
  session = {
1709
2594
  sessionId,
1710
2595
  protocol: "thp",
1711
2596
  thpSessionId: created.thpSessionId
1712
2597
  };
1713
- } else if (_optionalChain([created, 'optionalAccess', _39 => _39.protocol]) === "v1") {
2598
+ } else if (_optionalChain([created, 'optionalAccess', _139 => _139.protocol]) === "v1") {
1714
2599
  session = {
1715
2600
  sessionId,
1716
2601
  protocol: "v1"
@@ -1730,7 +2615,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1730
2615
  }
1731
2616
  }
1732
2617
  async _reuseVerifiedPassphraseSession(connectId, sessionId, passphraseState, signal) {
1733
- const cached = _optionalChain([this, 'access', _40 => _40._verifiedPassphraseSessionsByConnectId, 'access', _41 => _41.get, 'call', _42 => _42(connectId), 'optionalAccess', _43 => _43.get, 'call', _44 => _44(passphraseState)]);
2618
+ const cached = _optionalChain([this, 'access', _140 => _140._verifiedPassphraseSessionsByConnectId, 'access', _141 => _141.get, 'call', _142 => _142(connectId), 'optionalAccess', _143 => _143.get, 'call', _144 => _144(passphraseState)]);
1734
2619
  if (!cached || cached.sessionId !== sessionId) {
1735
2620
  this._forgetVerifiedPassphraseSession(connectId, passphraseState);
1736
2621
  return false;
@@ -1740,9 +2625,14 @@ var _TrezorAdapter = class _TrezorAdapter {
1740
2625
  if (!cached.thpSessionId) return false;
1741
2626
  await _TrezorAdapter._abortable(
1742
2627
  signal,
1743
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpSelectSession", {
1744
- thpSessionId: cached.thpSessionId
1745
- })
2628
+ this._callConnector(
2629
+ sessionId,
2630
+ "__thpSelectSession",
2631
+ {
2632
+ thpSessionId: cached.thpSessionId
2633
+ },
2634
+ signal
2635
+ )
1746
2636
  );
1747
2637
  }
1748
2638
  const state = await this._deriveState(sessionId, signal);
@@ -1760,9 +2650,14 @@ var _TrezorAdapter = class _TrezorAdapter {
1760
2650
  async _createFreshAppSession(sessionId, signal) {
1761
2651
  await _TrezorAdapter._abortable(
1762
2652
  signal,
1763
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpCreateSession", {
1764
- passphraseMode: "empty"
1765
- })
2653
+ this._callConnector(
2654
+ sessionId,
2655
+ "__thpCreateSession",
2656
+ {
2657
+ passphraseMode: "empty"
2658
+ },
2659
+ signal
2660
+ )
1766
2661
  );
1767
2662
  }
1768
2663
  /**
@@ -1777,12 +2672,17 @@ var _TrezorAdapter = class _TrezorAdapter {
1777
2672
  async _deriveState(sessionId, signal) {
1778
2673
  const derived = await _TrezorAdapter._abortable(
1779
2674
  signal,
1780
- _TrezorAdapter._callConnector(this._connector, sessionId, "btcGetPublicKey", {
1781
- path: "m/44'/0'/0'",
1782
- showOnDevice: false
1783
- })
2675
+ this._callConnector(
2676
+ sessionId,
2677
+ "btcGetPublicKey",
2678
+ {
2679
+ path: "m/44'/0'/0'",
2680
+ showOnDevice: false
2681
+ },
2682
+ signal
2683
+ )
1784
2684
  );
1785
- const state = _optionalChain([derived, 'optionalAccess', _45 => _45.publicKey]);
2685
+ const state = _optionalChain([derived, 'optionalAccess', _145 => _145.publicKey]);
1786
2686
  if (!state) {
1787
2687
  throw Object.assign(new Error("Failed to derive passphrase state (wallet public key)"), {
1788
2688
  code: _hwkadaptercore.HardwareErrorCode.UnknownError
@@ -1790,25 +2690,36 @@ var _TrezorAdapter = class _TrezorAdapter {
1790
2690
  }
1791
2691
  return state;
1792
2692
  }
1793
- async _resolvePassphraseState(connectId, passphraseState, signal) {
2693
+ async _resolvePassphraseState(connectId, passphraseState, signal, operationId) {
1794
2694
  try {
1795
- const sessionId = await this._ensureSession(connectId, signal);
2695
+ const sessionId = operationId ? this._sessions.get(this._operations.resolve(operationId).connectId) : await this._ensureSession(connectId, signal);
2696
+ if (!sessionId) {
2697
+ return _hwkadaptercore.failure.call(void 0,
2698
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2699
+ "Trezor operation connection is no longer active"
2700
+ );
2701
+ }
1796
2702
  if (passphraseState) {
1797
2703
  await this._alignAppSession(connectId, sessionId, passphraseState, signal);
1798
2704
  return _hwkadaptercore.success.call(void 0, passphraseState);
1799
2705
  }
1800
2706
  const created = await _TrezorAdapter._abortable(
1801
2707
  signal,
1802
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpCreateSession", {
1803
- passphraseMode: "prompt"
1804
- })
2708
+ this._callConnector(
2709
+ sessionId,
2710
+ "__thpCreateSession",
2711
+ {
2712
+ passphraseMode: "prompt"
2713
+ },
2714
+ signal
2715
+ )
1805
2716
  );
1806
2717
  const state = await this._deriveState(sessionId, signal);
1807
2718
  const features = await _TrezorAdapter._abortable(
1808
2719
  signal,
1809
- _TrezorAdapter._callConnector(this._connector, sessionId, "getFeatures", { refresh: true })
2720
+ this._callConnector(sessionId, "getFeatures", { refresh: true }, signal)
1810
2721
  );
1811
- if (_optionalChain([features, 'optionalAccess', _46 => _46.passphrase_protection]) !== true) {
2722
+ if (_optionalChain([features, 'optionalAccess', _146 => _146.passphrase_protection]) !== true) {
1812
2723
  return _hwkadaptercore.success.call(void 0, null);
1813
2724
  }
1814
2725
  this._rememberVerifiedPassphraseSession(connectId, state, sessionId, created);
@@ -1856,7 +2767,16 @@ var _TrezorAdapter = class _TrezorAdapter {
1856
2767
  }
1857
2768
  const trustedNumericCode = typeof typed.code === "number" && KNOWN_HARDWARE_ERROR_CODES.has(typed.code) ? typed.code : void 0;
1858
2769
  const code = _nullishCoalesce(_nullishCoalesce(trustedNumericCode, () => ( _TrezorAdapter._mapTrezorFailureCode(error))), () => ( _hwkadaptercore.HardwareErrorCode.UnknownError));
1859
- return _hwkadaptercore.failure.call(void 0, code, _nullishCoalesce(typed.message, () => ( String(error))));
2770
+ const stampedOrigin = _optionalChain([error, 'optionalAccess', _147 => _147.origin]);
2771
+ const origin = stampedOrigin === "device" || stampedOrigin === "transport" || stampedOrigin === "host" ? stampedOrigin : _hwkadaptercore.defaultOriginForCode.call(void 0, code);
2772
+ const params = typed.params && typeof typed.params === "object" ? typed.params : void 0;
2773
+ return _hwkadaptercore.failure.call(void 0,
2774
+ code,
2775
+ _nullishCoalesce(typed.message, () => ( String(error))),
2776
+ params,
2777
+ origin,
2778
+ _hwkadaptercore.isHwkRecoveryHint.call(void 0, typed.recovery) ? typed.recovery : void 0
2779
+ );
1860
2780
  }
1861
2781
  /**
1862
2782
  * Emit `REQUEST_DEVICE_PERMISSION` and await the host's
@@ -1871,29 +2791,33 @@ var _TrezorAdapter = class _TrezorAdapter {
1871
2791
  * - no `connectId` → environment-level check (scan / picker)
1872
2792
  * - with `connectId` → device-level check (per-call gating)
1873
2793
  */
1874
- async _ensureDevicePermission(connectId, deviceId) {
2794
+ async _ensureDevicePermission(connectId, deviceId, requestedTransport) {
1875
2795
  if (!this._emitter.listenerCount(_hwkadaptercore.UI_REQUEST.REQUEST_DEVICE_PERMISSION)) {
1876
2796
  return;
1877
2797
  }
1878
- const transportType = _nullishCoalesce(this.activeTransport, () => ( "usb"));
2798
+ const transportType = _nullishCoalesce(_nullishCoalesce(requestedTransport, () => ( this.activeTransport)), () => ( "usb"));
2799
+ const operationId = this._activeOperationId();
1879
2800
  const waitPromise = this._uiRegistry.wait(
1880
2801
  _hwkadaptercore.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
1881
- { timeoutMs: 6e4 }
2802
+ { timeoutMs: 6e4, operationId }
1882
2803
  );
1883
2804
  this._emitter.emit(_hwkadaptercore.UI_REQUEST.REQUEST_DEVICE_PERMISSION, {
1884
2805
  type: _hwkadaptercore.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
1885
- payload: { transportType, connectId, deviceId }
2806
+ payload: { transportType, connectId, deviceId, operationId }
1886
2807
  });
1887
- const { granted } = await waitPromise;
2808
+ const { granted, reason } = await waitPromise;
1888
2809
  if (!granted) {
1889
2810
  throw Object.assign(new Error("Device permission denied"), {
1890
- code: _hwkadaptercore.HardwareErrorCode.DevicePermissionDenied
2811
+ code: _hwkadaptercore.HardwareErrorCode.DevicePermissionDenied,
2812
+ ...reason ? { params: { permissionDeniedReason: reason } } : {}
1891
2813
  });
1892
2814
  }
1893
2815
  }
1894
2816
  };
1895
2817
  // Keys are matched after normalizeLogKey, so snake_case/camelCase both hit.
1896
2818
  _TrezorAdapter._sensitiveLogKeys = /* @__PURE__ */ new Set([
2819
+ "extra",
2820
+ "knownconnections",
1897
2821
  "credential",
1898
2822
  "credentials",
1899
2823
  "entropy",
@@ -2012,7 +2936,7 @@ async function fetchDefinitionHex(url, fetchImpl) {
2012
2936
  }
2013
2937
  return arrayBufferToHex(await response.arrayBuffer());
2014
2938
  });
2015
- } catch (e5) {
2939
+ } catch (e7) {
2016
2940
  return void 0;
2017
2941
  }
2018
2942
  }
@@ -2041,7 +2965,7 @@ async function fetchSolanaTokenDefinition({
2041
2965
  }
2042
2966
  return Buffer.from(await response.arrayBuffer()).toString("hex");
2043
2967
  });
2044
- } catch (e6) {
2968
+ } catch (e8) {
2045
2969
  return void 0;
2046
2970
  }
2047
2971
  }