@onekeyfe/hd-core 1.2.2-alpha.100 → 1.2.2-alpha.101

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.
Files changed (94) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +20 -4
  2. package/__tests__/DeviceCommands.test.ts +13 -0
  3. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
  4. package/__tests__/check-firmware-release-protocol-v2.test.ts +0 -2
  5. package/__tests__/device-lifecycle-events.test.ts +102 -0
  6. package/__tests__/deviceUploadNft.test.ts +68 -0
  7. package/__tests__/evmSignTypedData.test.ts +42 -0
  8. package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +825 -12
  9. package/__tests__/logBlockEvent.test.ts +45 -122
  10. package/__tests__/open-wallet-session.test.ts +781 -69
  11. package/__tests__/pro2HostAssetPackage.test.ts +165 -0
  12. package/__tests__/protocol-v2-resources.test.ts +7 -4
  13. package/__tests__/protocol-v2-unlock-policy.test.ts +35 -1
  14. package/__tests__/protocol-v2.test.ts +937 -74
  15. package/__tests__/protocolV2FileWrite.test.ts +40 -0
  16. package/__tests__/sol-sign-offchain-message.test.ts +70 -0
  17. package/dist/api/FirmwareUpdateV4.d.ts +5 -0
  18. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  19. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  20. package/dist/api/UploadPortfolio.d.ts +1 -0
  21. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  22. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  23. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  24. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
  25. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  26. package/dist/api/firmware/getBinary.d.ts +2 -2
  27. package/dist/api/helpers/protocolV2FileWrite.d.ts +1 -0
  28. package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
  29. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  30. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  31. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  32. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  33. package/dist/api/solana/SolSignOffchainMessage.d.ts.map +1 -1
  34. package/dist/core/RequestQueue.d.ts +2 -0
  35. package/dist/core/RequestQueue.d.ts.map +1 -1
  36. package/dist/core/index.d.ts +4 -1
  37. package/dist/core/index.d.ts.map +1 -1
  38. package/dist/core/uiPromiseRegistry.d.ts +1 -1
  39. package/dist/device/Device.d.ts +1 -1
  40. package/dist/device/Device.d.ts.map +1 -1
  41. package/dist/device/DeviceCommands.d.ts +4 -4
  42. package/dist/device/DeviceConnector.d.ts +1 -1
  43. package/dist/device/DeviceConnector.d.ts.map +1 -1
  44. package/dist/events/logBlockEvent.d.ts.map +1 -1
  45. package/dist/events/ui-request.d.ts +3 -0
  46. package/dist/events/ui-request.d.ts.map +1 -1
  47. package/dist/index.d.ts +39 -16
  48. package/dist/index.js +2572 -1554
  49. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  50. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +2 -0
  51. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
  52. package/dist/protocols/protocol-v2/walletSession.d.ts +3 -0
  53. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  54. package/dist/types/api/protocolV2.d.ts +3 -4
  55. package/dist/types/api/protocolV2.d.ts.map +1 -1
  56. package/dist/types/api/solSignOffchainMessage.d.ts +2 -0
  57. package/dist/types/api/solSignOffchainMessage.d.ts.map +1 -1
  58. package/dist/types/settings.d.ts +2 -2
  59. package/dist/types/settings.d.ts.map +1 -1
  60. package/dist/utils/deviceFeaturesUtils.d.ts +3 -0
  61. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  62. package/dist/utils/patch.d.ts +1 -1
  63. package/dist/utils/patch.d.ts.map +1 -1
  64. package/dist/utils/pro2HostAssetPackage.d.ts +8 -0
  65. package/dist/utils/pro2HostAssetPackage.d.ts.map +1 -0
  66. package/package.json +4 -4
  67. package/src/api/FirmwareUpdateV4.ts +474 -83
  68. package/src/api/OpenWalletSession.ts +39 -12
  69. package/src/api/UploadPortfolio.ts +5 -4
  70. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +5 -2
  71. package/src/api/evm/EVMSignTypedData.ts +1 -0
  72. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +9 -3
  73. package/src/api/helpers/protocolV2FileWrite.ts +11 -5
  74. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +7 -1
  75. package/src/api/protocol-v2/DeviceUploadNft.ts +31 -8
  76. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +33 -8
  77. package/src/api/protocol-v2/helpers.ts +8 -0
  78. package/src/api/solana/SolSignOffchainMessage.ts +57 -4
  79. package/src/core/RequestQueue.ts +16 -1
  80. package/src/core/index.ts +179 -27
  81. package/src/data/messages/messages-protocol-v2.json +1562 -1345
  82. package/src/data/messages/messages.json +8 -5
  83. package/src/device/Device.ts +3 -1
  84. package/src/device/DeviceConnector.ts +7 -3
  85. package/src/events/logBlockEvent.ts +6 -75
  86. package/src/events/ui-request.ts +3 -0
  87. package/src/protocols/protocol-v2/resources.ts +9 -1
  88. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +8 -1
  89. package/src/protocols/protocol-v2/walletSession.ts +223 -56
  90. package/src/types/api/protocolV2.ts +3 -4
  91. package/src/types/api/solSignOffchainMessage.ts +3 -0
  92. package/src/types/settings.ts +4 -2
  93. package/src/utils/deviceFeaturesUtils.ts +4 -0
  94. package/src/utils/pro2HostAssetPackage.ts +400 -0
package/src/core/index.ts CHANGED
@@ -2,7 +2,11 @@ import semver from 'semver';
2
2
  import EventEmitter from 'events';
3
3
  import {
4
4
  DeviceSessionPinType,
5
+ type LowlevelTransportSharedPlugin,
6
+ type OneKeyDeviceInfo,
7
+ type ProtocolType,
5
8
  TRANSPORT_EVENT,
9
+ type TransportDeviceDisconnectEvent,
6
10
  isProtocolV2LinkDisabledError,
7
11
  } from '@onekeyfe/hd-transport';
8
12
  import {
@@ -18,6 +22,7 @@ import {
18
22
  createNeedUpgradeFirmwareHardwareError,
19
23
  createNewFirmwareForceUpdateHardwareError,
20
24
  createNewFirmwareUnReleaseHardwareError,
25
+ isBleStaleBondHardwareError,
21
26
  } from '@onekeyfe/hd-shared';
22
27
 
23
28
  import { LoggerNames, enableLog, getLogger, setLoggerPostMessage, wait } from '../utils';
@@ -74,11 +79,6 @@ import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } fro
74
79
  import type { DeviceEvents, InitOptions, RunOptions } from '../device/Device';
75
80
  import type { SdkTracingContext } from '../utils/tracing';
76
81
  import type { Deferred } from '@onekeyfe/hd-shared';
77
- import type {
78
- LowlevelTransportSharedPlugin,
79
- OneKeyDeviceInfo,
80
- TransportDeviceDisconnectEvent,
81
- } from '@onekeyfe/hd-transport';
82
82
  import type { BaseMethod } from '../api/BaseMethod';
83
83
 
84
84
  const Log = getLogger(LoggerNames.Core);
@@ -92,7 +92,7 @@ const preWarmDoneAt = new Map<string, number>();
92
92
 
93
93
  export type CoreContext = ReturnType<Core['getCoreContext']>;
94
94
 
95
- function hasDeriveCardano(method: BaseMethod): boolean {
95
+ function resolveDeriveCardano(method: BaseMethod): boolean | undefined {
96
96
  if (
97
97
  method.name.startsWith('allNetworkGetAddress') &&
98
98
  method.payload &&
@@ -102,15 +102,22 @@ function hasDeriveCardano(method: BaseMethod): boolean {
102
102
  ) {
103
103
  return true;
104
104
  }
105
-
106
- return method.name.startsWith('cardano') || method.payload?.deriveCardano;
105
+ if (method.name.startsWith('cardano')) {
106
+ return true;
107
+ }
108
+ // V1 Initialize only sends derive_cardano on an explicit true.
109
+ // V2 AskPassphrase maps true to [Standard, Cardano] and anything else to [Standard].
110
+ if (method.payload?.deriveCardano === true) {
111
+ return true;
112
+ }
113
+ return undefined;
107
114
  }
108
115
 
109
116
  const parseInitOptions = (method?: BaseMethod): InitOptions => ({
110
117
  initSession: method?.payload.initSession,
111
118
  passphraseState: method?.payload.useEmptyPassphrase ? undefined : method?.payload.passphraseState,
112
119
  deviceId: method?.payload.deviceId,
113
- deriveCardano: method && hasDeriveCardano(method),
120
+ deriveCardano: method ? resolveDeriveCardano(method) : undefined,
114
121
  connectProtocol: method?.payload.connectProtocol,
115
122
  forceProtocolDetection: method?.payload.forceProtocolDetection,
116
123
  protocolV2DeviceInfoTimeoutMs: method?.payload.protocolV2DeviceInfoTimeoutMs,
@@ -415,6 +422,13 @@ const onCallDevice = async (
415
422
  if (method.payload?.onlyConnectBleDevice) {
416
423
  preWarmCallbackTask?.resolve();
417
424
  Log.debug('Call API - only connect ble device: ', device?.mainId);
425
+ // This early return bypasses the normal-path bookkeeping at the end of the
426
+ // call. Without it the task leaks and haunts every later queue snapshot
427
+ // and cancel sweep (field log: a completed task lingered for 6 minutes),
428
+ // and the request stays in the active maps, so repeated preconnects pile
429
+ // up phantom work in diagnostics.
430
+ completeMethodRequestContext(method);
431
+ requestQueue.releaseTask(method.responseID);
418
432
  return createResponseMessage(method.responseID, true, null);
419
433
  }
420
434
 
@@ -644,7 +658,8 @@ const onCallDevice = async (
644
658
  method.payload?.passphraseState,
645
659
  method.payload?.useEmptyPassphrase,
646
660
  method.payload?.skipPassphraseCheck,
647
- hasDeriveCardano(method)
661
+ resolveDeriveCardano(method),
662
+ method.protocolV2UnlockContext?.preflightMainPinSelected
648
663
  );
649
664
 
650
665
  // Double check, handles the special case of Touch/Pro
@@ -931,7 +946,16 @@ export function isRetryableBleConnectionError(method: BaseMethod, error: unknown
931
946
  if (method.device?.wasInterruptedByUser()) {
932
947
  return false;
933
948
  }
934
- const typedError = error as { errorCode?: unknown };
949
+ const typedError = error as {
950
+ errorCode?: unknown;
951
+ params?: { acquireDeadlineExceeded?: unknown };
952
+ };
953
+ if (
954
+ typedError?.errorCode === HardwareErrorCode.BleTimeoutError &&
955
+ typedError.params?.acquireDeadlineExceeded === true
956
+ ) {
957
+ return false;
958
+ }
935
959
  return (
936
960
  typedError?.errorCode === HardwareErrorCode.BleTimeoutError ||
937
961
  typedError?.errorCode === HardwareErrorCode.BleConnectedError ||
@@ -950,11 +974,75 @@ export function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unkn
950
974
  );
951
975
  }
952
976
 
977
+ export function isTerminalBleStaleBondError(error: unknown) {
978
+ return isBleStaleBondHardwareError(error);
979
+ }
980
+
981
+ export function isDeviceIdentityMismatchError(error: unknown) {
982
+ return (
983
+ (error as { errorCode?: unknown })?.errorCode === HardwareErrorCode.DeviceCheckDeviceIdError
984
+ );
985
+ }
986
+
953
987
  /**
954
988
  * If the Bluetooth connection times out, retry up to 6 times
955
989
  * @param retryCount - Current retry count (default 0)
956
990
  */
957
- async function connectDeviceForBle(method: BaseMethod, device: Device, retryCount = 0) {
991
+ // device.acquire awaits a transport reply with no deadline of its own; a
992
+ // transport that never settles (field case: Electron main lost an IPC reply,
993
+ // "reply was never sent" after 5 minutes) hangs the call forever and cancel()
994
+ // only takes effect at poll checkpoints. Race acquire against a deadline and
995
+ // the caller's abort signal so the hang is bounded and cancel is immediate.
996
+ const BLE_ACQUIRE_DEADLINE_MS = 60 * 1000;
997
+
998
+ function raceBleAcquire<T>(acquirePromise: Promise<T>, abortSignal?: AbortSignal): Promise<T> {
999
+ return new Promise<T>((resolve, reject) => {
1000
+ let settled = false;
1001
+ const settle = (fn: () => void) => {
1002
+ if (settled) return;
1003
+ settled = true;
1004
+ clearTimeout(deadline);
1005
+ abortSignal?.removeEventListener('abort', onAbort);
1006
+ fn();
1007
+ };
1008
+ const onAbort = () =>
1009
+ settle(() => reject(ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled)));
1010
+ const deadline = setTimeout(
1011
+ () =>
1012
+ settle(() =>
1013
+ reject(
1014
+ ERRORS.TypedError(
1015
+ HardwareErrorCode.BleTimeoutError,
1016
+ `BLE acquire exceeded ${BLE_ACQUIRE_DEADLINE_MS}ms deadline`,
1017
+ { acquireDeadlineExceeded: true }
1018
+ )
1019
+ )
1020
+ ),
1021
+ BLE_ACQUIRE_DEADLINE_MS
1022
+ );
1023
+ // Attach before any early return so a late settlement of acquirePromise
1024
+ // is always consumed — an abort or deadline must never leave the acquire
1025
+ // rejection unhandled.
1026
+ acquirePromise.then(
1027
+ value => settle(() => resolve(value)),
1028
+ error => settle(() => reject(error))
1029
+ );
1030
+ if (abortSignal) {
1031
+ if (abortSignal.aborted) {
1032
+ onAbort();
1033
+ return;
1034
+ }
1035
+ abortSignal.addEventListener('abort', onAbort);
1036
+ }
1037
+ });
1038
+ }
1039
+
1040
+ async function connectDeviceForBle(
1041
+ method: BaseMethod,
1042
+ device: Device,
1043
+ abortSignal?: AbortSignal,
1044
+ retryCount = 0
1045
+ ) {
958
1046
  try {
959
1047
  if (device.wasInterruptedByUser()) {
960
1048
  throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
@@ -968,9 +1056,44 @@ async function connectDeviceForBle(method: BaseMethod, device: Device, retryCoun
968
1056
  !device.commands ||
969
1057
  device.commands.disposed;
970
1058
  if (shouldAcquire) {
971
- await device.acquire(method.payload.connectProtocol, {
972
- forceProtocolDetection: method.payload.forceProtocolDetection,
973
- });
1059
+ const connectProtocol = resolveBleConnectProtocol(method);
1060
+ // The deadline/abort guards are scoped to the desktop electron
1061
+ // transport: its IPC acquire is the only path with a proven
1062
+ // never-settling failure mode, while react-native/lowlevel acquire may
1063
+ // legitimately block on a user-driven system bonding prompt for longer
1064
+ // than any sane deadline. Other envs keep the plain acquire unchanged.
1065
+ const useAcquireGuards = DataManager.getSettings('env') === 'desktop-web-ble';
1066
+ // A cancel landing during the retry backoff must not start a new acquire.
1067
+ if (useAcquireGuards && abortSignal?.aborted) {
1068
+ throw ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled);
1069
+ }
1070
+ if (!useAcquireGuards) {
1071
+ await device.acquire(connectProtocol, {
1072
+ forceProtocolDetection: method.payload.forceProtocolDetection,
1073
+ });
1074
+ } else {
1075
+ try {
1076
+ await raceBleAcquire(
1077
+ device.acquire(connectProtocol, {
1078
+ forceProtocolDetection: method.payload.forceProtocolDetection,
1079
+ }),
1080
+ abortSignal
1081
+ );
1082
+ } catch (err) {
1083
+ // A deadline hit means the transport is wedged mid-acquire; drop the
1084
+ // link before the retry so it cold-connects instead of stacking a
1085
+ // second connect onto the half-open one.
1086
+ if (
1087
+ err.errorCode === HardwareErrorCode.BleTimeoutError &&
1088
+ device.mainId &&
1089
+ device.deviceConnector
1090
+ ) {
1091
+ await device.deviceConnector.disconnect(device.mainId).catch(() => undefined);
1092
+ device.markTransportDisconnected();
1093
+ }
1094
+ throw err;
1095
+ }
1096
+ }
974
1097
  }
975
1098
  if (method.payload?.onlyConnectBleDevice) {
976
1099
  if (shouldAcquire) {
@@ -1010,13 +1133,21 @@ async function connectDeviceForBle(method: BaseMethod, device: Device, retryCoun
1010
1133
  const nextRetry = retryCount + 1;
1011
1134
  Log.debug(`Bluetooth connection will retry, retry count: ${nextRetry}`);
1012
1135
  await wait(3000);
1013
- await connectDeviceForBle(method, device, nextRetry);
1136
+ await connectDeviceForBle(method, device, abortSignal, nextRetry);
1014
1137
  } else {
1015
1138
  throw err;
1016
1139
  }
1017
1140
  }
1018
1141
  }
1019
1142
 
1143
+ export function resolveBleConnectProtocol(method: BaseMethod): ProtocolType | undefined {
1144
+ if (method.payload.connectProtocol === 'V1' || method.payload.connectProtocol === 'V2') {
1145
+ return method.payload.connectProtocol;
1146
+ }
1147
+ const supportedProtocols = method.getSupportedProtocols();
1148
+ return supportedProtocols.length === 1 && supportedProtocols[0] === 'V2' ? 'V2' : undefined;
1149
+ }
1150
+
1020
1151
  type IPollFn<T> = (time?: number) => T;
1021
1152
  // eslint-disable-next-line @typescript-eslint/require-await
1022
1153
  const ensureConnected = async (
@@ -1120,7 +1251,7 @@ const ensureConnected = async (
1120
1251
  if (tryCount === 1) {
1121
1252
  device.beginConnectionAttempt();
1122
1253
  }
1123
- await connectDeviceForBle(method, device);
1254
+ await connectDeviceForBle(method, device, abortSignal);
1124
1255
  }
1125
1256
  resolve(device);
1126
1257
  return;
@@ -1164,7 +1295,9 @@ const ensureConnected = async (
1164
1295
  HardwareErrorCode.BridgeNeedsPermission,
1165
1296
  HardwareErrorCode.DeviceInterruptedFromUser,
1166
1297
  HardwareErrorCode.CallQueueActionCancelled,
1167
- ].includes(error.errorCode)
1298
+ ].includes(error.errorCode) ||
1299
+ isTerminalBleStaleBondError(error) ||
1300
+ isDeviceIdentityMismatchError(error)
1168
1301
  ) {
1169
1302
  reject(error);
1170
1303
  return;
@@ -1214,12 +1347,17 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1214
1347
  // cancel callback tasks
1215
1348
  requestQueue.cancelCallbackTasks(connectId);
1216
1349
 
1217
- const requestIds = requestQueue.getRequestTasksId();
1350
+ const requestIds = requestQueue.getRequestTasksIdByConnectId(connectId);
1218
1351
  Log.debug(
1219
1352
  `Cancel Api connect requestQueues: length:${requestIds.length} requestIds:${requestIds.join(
1220
1353
  ','
1221
1354
  )}`
1222
1355
  );
1356
+ // Abort before rejecting: rejectRequest releases the task and would make
1357
+ // its AbortController unreachable to an in-flight method loop.
1358
+ // Match both the requested connectId and a device selected internally by the
1359
+ // method, such as Desktop WebUSB firmwareUpdateV4.
1360
+ requestQueue.abortRequestsByConnectId(connectId);
1223
1361
  const canceledDevices: Device[] = [];
1224
1362
  const interruptDevice = (device: Device | undefined, deviceConnectId: string) => {
1225
1363
  if (!device || canceledDevices.includes(device)) {
@@ -1235,7 +1373,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1235
1373
  // During ensureConnected the method has a connectId but device is
1236
1374
  // assigned only after the poll succeeds. Interrupt the cached BLE
1237
1375
  // Device so an in-flight acquire/initialize cannot finish.
1238
- interruptDevice(task.method?.device, task.method.connectId ?? connectId);
1376
+ interruptDevice(task.method?.device, connectId);
1239
1377
  interruptDevice(deviceCacheMap.get(connectId), connectId);
1240
1378
  requestQueue.rejectRequest(
1241
1379
  requestId,
@@ -1244,13 +1382,15 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1244
1382
  }
1245
1383
  }
1246
1384
  interruptDevice(deviceCacheMap.get(connectId), connectId);
1247
- requestQueue.abortRequestsByConnectId(connectId);
1248
1385
  pollingManager.stop(connectId);
1249
1386
  } catch (e) {
1250
1387
  Log.error('Cancel API Error: ', e);
1251
1388
  }
1252
1389
  } else {
1253
1390
  const env = DataManager.getSettings('env');
1391
+ // Abort every method before rejecting its queue task. Non-BLE methods also
1392
+ // use the signal to stop recovery loops after the public promise is rejected.
1393
+ requestQueue.abortAllRequests();
1254
1394
  if (DataManager.isBleConnect(env)) {
1255
1395
  Log.debug('Cancel Api all _deviceList: ');
1256
1396
  const canceledDevices: Device[] = [];
@@ -1277,7 +1417,6 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1277
1417
  }
1278
1418
  }
1279
1419
  deviceCacheMap.forEach(interruptDevice);
1280
- requestQueue.abortAllRequests();
1281
1420
  pollingManager.stopAll();
1282
1421
  } else {
1283
1422
  _deviceList?.allDevices().forEach(device => {
@@ -1296,8 +1435,10 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1296
1435
  }
1297
1436
  }
1298
1437
 
1299
- cleanup();
1300
- closePopup();
1438
+ cleanup(connectId);
1439
+ if (!connectId || _uiPromises.length === 0) {
1440
+ closePopup();
1441
+ }
1301
1442
  };
1302
1443
 
1303
1444
  const checkPassphraseEnableState = (method: BaseMethod, features?: Features) => {
@@ -1328,12 +1469,23 @@ const checkPassphraseEnableState = (method: BaseMethod, features?: Features) =>
1328
1469
  const shouldCheckPassphraseState = (method: BaseMethod, device: Device) => {
1329
1470
  if (!method.useDevicePassphraseState) return false;
1330
1471
 
1472
+ if (device.isProtocolV2() && method.payload?.useEmptyPassphrase === true) {
1473
+ return true;
1474
+ }
1475
+
1331
1476
  return device.hasUsePassphrase();
1332
1477
  };
1333
1478
 
1334
- const cleanup = () => {
1335
- const pendingUiPromises = _uiPromises;
1336
- _uiPromises = [];
1479
+ const cleanup = (connectId?: string) => {
1480
+ const pendingUiPromises = connectId
1481
+ ? _uiPromises.filter(
1482
+ uiPromise =>
1483
+ uiPromise.data?.mainId === connectId || uiPromise.data?.getConnectId() === connectId
1484
+ )
1485
+ : _uiPromises;
1486
+ _uiPromises = connectId
1487
+ ? _uiPromises.filter(uiPromise => !pendingUiPromises.includes(uiPromise))
1488
+ : [];
1337
1489
  rejectUiPromises(
1338
1490
  pendingUiPromises,
1339
1491
  ERRORS.TypedError(HardwareErrorCode.ActionCancelled, 'UI request was cancelled')