@onekeyfe/hwk-trezor-adapter 1.2.8-alpha.11 → 1.2.8-alpha.13

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