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

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 (69) hide show
  1. package/__tests__/AllNetworkGetAddressBase.tracing.test.ts +4 -20
  2. package/__tests__/DeviceCommands.test.ts +0 -13
  3. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +2 -0
  4. package/__tests__/check-firmware-release-protocol-v2.test.ts +2 -0
  5. package/__tests__/device-lifecycle-events.test.ts +0 -19
  6. package/__tests__/deviceUploadNft.test.ts +0 -68
  7. package/__tests__/evmSignTypedData.test.ts +0 -42
  8. package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +12 -825
  9. package/__tests__/open-wallet-session.test.ts +1 -127
  10. package/__tests__/protocol-v2-resources.test.ts +4 -7
  11. package/__tests__/protocol-v2-unlock-policy.test.ts +1 -35
  12. package/__tests__/protocol-v2.test.ts +27 -710
  13. package/dist/api/FirmwareUpdateV4.d.ts +0 -4
  14. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  15. package/dist/api/OpenWalletSession.d.ts.map +1 -1
  16. package/dist/api/allnetwork/AllNetworkGetAddressBase.d.ts.map +1 -1
  17. package/dist/api/evm/EVMSignTypedData.d.ts.map +1 -1
  18. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +2 -2
  19. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  20. package/dist/api/firmware/getBinary.d.ts +2 -2
  21. package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts.map +1 -1
  22. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  23. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  24. package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
  25. package/dist/core/index.d.ts +0 -1
  26. package/dist/core/index.d.ts.map +1 -1
  27. package/dist/device/Device.d.ts +1 -1
  28. package/dist/device/Device.d.ts.map +1 -1
  29. package/dist/device/DeviceConnector.d.ts +1 -1
  30. package/dist/device/DeviceConnector.d.ts.map +1 -1
  31. package/dist/events/ui-request.d.ts +0 -3
  32. package/dist/events/ui-request.d.ts.map +1 -1
  33. package/dist/index.d.ts +5 -21
  34. package/dist/index.js +402 -1136
  35. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  36. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts +0 -2
  37. package/dist/protocols/protocol-v2/unlockPolicyRunner.d.ts.map +1 -1
  38. package/dist/protocols/protocol-v2/walletSession.d.ts +0 -3
  39. package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
  40. package/dist/types/settings.d.ts +2 -2
  41. package/dist/types/settings.d.ts.map +1 -1
  42. package/dist/utils/deviceFeaturesUtils.d.ts +0 -3
  43. package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
  44. package/dist/utils/patch.d.ts +1 -1
  45. package/dist/utils/patch.d.ts.map +1 -1
  46. package/package.json +4 -4
  47. package/src/api/FirmwareUpdateV4.ts +75 -434
  48. package/src/api/OpenWalletSession.ts +9 -39
  49. package/src/api/allnetwork/AllNetworkGetAddressBase.ts +1 -2
  50. package/src/api/evm/EVMSignTypedData.ts +0 -1
  51. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +3 -9
  52. package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +1 -7
  53. package/src/api/protocol-v2/DeviceUploadNft.ts +8 -31
  54. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +8 -27
  55. package/src/api/protocol-v2/helpers.ts +0 -8
  56. package/src/core/index.ts +10 -121
  57. package/src/data/messages/messages-protocol-v2.json +1 -111
  58. package/src/device/Device.ts +1 -3
  59. package/src/device/DeviceConnector.ts +3 -7
  60. package/src/events/ui-request.ts +0 -3
  61. package/src/protocols/protocol-v2/resources.ts +1 -9
  62. package/src/protocols/protocol-v2/unlockPolicyRunner.ts +1 -8
  63. package/src/protocols/protocol-v2/walletSession.ts +22 -65
  64. package/src/types/settings.ts +2 -4
  65. package/src/utils/deviceFeaturesUtils.ts +0 -4
  66. package/__tests__/pro2HostAssetPackage.test.ts +0 -58
  67. package/dist/utils/pro2HostAssetPackage.d.ts +0 -8
  68. package/dist/utils/pro2HostAssetPackage.d.ts.map +0 -1
  69. package/src/utils/pro2HostAssetPackage.ts +0 -354
@@ -29,12 +29,6 @@ const wasResumed = (session: unknown) =>
29
29
  'resumed' in session &&
30
30
  (session as { resumed?: unknown }).resumed === true;
31
31
 
32
- const wasWalletStatusRefreshed = (session: unknown) =>
33
- !!session &&
34
- typeof session === 'object' &&
35
- 'walletStatusRefreshed' in session &&
36
- (session as { walletStatusRefreshed?: unknown }).walletStatusRefreshed === true;
37
-
38
32
  const requireHiddenWalletResponse = (session: { passphraseState?: string }) => {
39
33
  if (!session.passphraseState) {
40
34
  throw ERRORS.TypedError(
@@ -93,20 +87,12 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
93
87
  this.unlockPolicy = 'unlock-before-run';
94
88
  this.skipForceUpdateCheck = true;
95
89
  this.params = normalizeParams(this.payload as unknown as Record<string, unknown>);
96
- this.protocolV2PreUnlockPinType =
97
- this.params.mode === OpenWalletSessionMode.SelectHidden
98
- ? DeviceSessionPinType.Any
99
- : DeviceSessionPinType.Main;
100
90
  this.payload.useEmptyPassphrase = this.params.mode === OpenWalletSessionMode.Standard;
101
91
  }
102
92
 
103
93
  async run(): Promise<OpenWalletSessionPayload> {
104
94
  const isProtocolV2 = this.device.isProtocolV2();
105
- const reusePreflightStatus =
106
- isProtocolV2 && this.protocolV2UnlockContext?.preflightStatusRefreshed === true;
107
- let state = reusePreflightStatus
108
- ? await this.device.getDeviceState()
109
- : await this.device.getDeviceState({ refreshSections: ['status'] });
95
+ let state = await this.device.getDeviceState({ refreshSections: ['status'] });
110
96
  let currentDeviceId = state.identity.deviceId;
111
97
  const hasAuthoritativeProtocolV2WalletStatus = (candidate: typeof state) =>
112
98
  candidate.status.unlocked === true &&
@@ -129,25 +115,14 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
129
115
  currentDeviceId = state.identity.deviceId;
130
116
  return state;
131
117
  };
132
- const resolveProtocolV2DeviceStateAfterSession = async (session: unknown) => {
133
- if (!wasWalletStatusRefreshed(session)) {
134
- return refreshProtocolV2DeviceState();
135
- }
136
- state = await this.device.getDeviceState();
137
- currentDeviceId = state.identity.deviceId;
138
- return state;
139
- };
140
118
  const ensureProtocolV2WalletStatus = async () => {
141
119
  if (isProtocolV2 && !hasAuthoritativeProtocolV2WalletStatus(state)) {
142
- await this.device.unlockDevice(
143
- this.protocolV2PreUnlockPinType ?? DeviceSessionPinType.Main,
144
- {
145
- source: 'unlock-coordinator',
146
- reason: 'device-locked',
147
- deviceOnly: true,
148
- method: 'openWalletSession',
149
- }
150
- );
120
+ await this.device.unlockDevice(DeviceSessionPinType.Main, {
121
+ source: 'unlock-coordinator',
122
+ reason: 'device-locked',
123
+ deviceOnly: true,
124
+ method: 'openWalletSession',
125
+ });
151
126
  requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState());
152
127
  }
153
128
  return state;
@@ -164,16 +139,13 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
164
139
  selectMainWalletBeforeRestore:
165
140
  !hasAuthoritativeProtocolV2WalletStatus(state) ||
166
141
  state.status.unlockedAttachPin === true,
167
- mainPinSelected: this.protocolV2UnlockContext?.preflightMainPinSelected,
168
142
  })
169
143
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, {
170
144
  onlyMainPin: true,
171
145
  initSession: this.payload.initSession,
172
146
  });
173
147
  const refreshedState = isProtocolV2
174
- ? requireAuthoritativeProtocolV2WalletStatus(
175
- await resolveProtocolV2DeviceStateAfterSession(session)
176
- )
148
+ ? requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState())
177
149
  : state;
178
150
  const deviceId = requireDeviceId();
179
151
  if (
@@ -261,9 +233,7 @@ export default class OpenWalletSession extends BaseMethod<OpenWalletSessionParam
261
233
  })
262
234
  : await getPassphraseStateWithRefreshDeviceInfo(this.device, { initSession: true });
263
235
  const refreshedState = isProtocolV2
264
- ? requireAuthoritativeProtocolV2WalletStatus(
265
- await resolveProtocolV2DeviceStateAfterSession(session)
266
- )
236
+ ? requireAuthoritativeProtocolV2WalletStatus(await refreshProtocolV2DeviceState())
267
237
  : state;
268
238
  const deviceId = requireDeviceId();
269
239
  if (isProtocolV2 && refreshedState.status.passphraseProtection !== true) {
@@ -400,8 +400,7 @@ export default abstract class AllNetworkGetAddressBase extends BaseMethod<
400
400
  this.payload.passphraseState,
401
401
  useEmptyPassphrase,
402
402
  this.payload.skipPassphraseCheck,
403
- deriveCardano,
404
- this.protocolV2UnlockContext?.preflightMainPinSelected
403
+ deriveCardano
405
404
  );
406
405
  if (!passphraseStateSafety) {
407
406
  throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
@@ -360,7 +360,6 @@ export default class EVMSignTypedData extends BaseMethod<EVMSignTypedDataParams>
360
360
 
361
361
  const supportBiggerData =
362
362
  DeviceModelToTypes.model_classic1s.includes(currentDeviceType) ||
363
- DeviceModelToTypes.model_pro2.includes(currentDeviceType) ||
364
363
  (DeviceModelToTypes.model_touch.includes(currentDeviceType) &&
365
364
  semver.gte(currentVersion, supportBiggerDataVersion));
366
365
 
@@ -41,15 +41,9 @@ const FIRMWARE_UPDATE_CONFIRM = 'Firmware install confirmed';
41
41
  */
42
42
  export const BOOTLOADER_POLL_INITIALIZE_TIMEOUT_MS = 5000;
43
43
 
44
- type FirmwareProgressMetadata = Pick<
44
+ type FirmwareTransferMetrics = Pick<
45
45
  FirmwareProgress['payload'],
46
- | 'transferredBytes'
47
- | 'totalBytes'
48
- | 'rateBytesPerSecond'
49
- | 'elapsedMs'
50
- | 'installTargetId'
51
- | 'installPhase'
52
- | 'installPhaseProgress'
46
+ 'transferredBytes' | 'totalBytes' | 'rateBytesPerSecond' | 'elapsedMs'
53
47
  >;
54
48
 
55
49
  const isDeviceDisconnectedError = (error: unknown) => {
@@ -109,7 +103,7 @@ export class FirmwareUpdateBaseMethod<Params> extends BaseMethod<Params> {
109
103
  postProgressMessage = (
110
104
  progress: number,
111
105
  progressType: IFirmwareUpdateProgressType,
112
- metrics?: FirmwareProgressMetadata
106
+ metrics?: FirmwareTransferMetrics
113
107
  ) => {
114
108
  this.postMessage(
115
109
  createUiMessage(UI_REQUEST.FIRMWARE_PROGRESS, {
@@ -4,13 +4,7 @@ import { invalidParameter } from '../helpers/filesystemValidation';
4
4
  import type { DeviceFirmwareUpdateStatusGetParams } from './helpers';
5
5
  import type { DeviceFirmwareUpdateRecordFields } from '@onekeyfe/hd-transport';
6
6
 
7
- const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = [
8
- 'status',
9
- 'progress_percent',
10
- 'phase_info',
11
- 'payload_version',
12
- 'path',
13
- ] as const;
7
+ const DEVICE_FIRMWARE_UPDATE_STATUS_FIELDS = ['status', 'payload_version', 'path'] as const;
14
8
 
15
9
  function normalizeStatusFields(
16
10
  fields: DeviceFirmwareUpdateStatusGetParams['fields']
@@ -20,10 +20,6 @@ import {
20
20
  getCompletePro2NftBasenames,
21
21
  } from '../../utils/pro2Nft';
22
22
  import { encodePro2Image } from '../../utils/pro2Wallpaper';
23
- import {
24
- buildPro2HostAssetPackage,
25
- supportsPro2HostAssetPackage,
26
- } from '../../utils/pro2HostAssetPackage';
27
23
  import { BaseMethod } from '../BaseMethod';
28
24
  import { decodeJpegBase64ToRgba } from '../helpers/base64Data';
29
25
  import { invalidParameter } from '../helpers/filesystemValidation';
@@ -42,11 +38,8 @@ export type DeviceUploadNftParams = {
42
38
 
43
39
  export type DeviceUploadNftResponse = {
44
40
  basename: string;
45
- /** Final image path after firmware extracts a host-asset package. */
46
41
  imagePath: string;
47
- /** Final thumbnail path after firmware extracts a host-asset package. */
48
42
  thumbnailPath: string;
49
- /** Final metadata path after firmware extracts a host-asset package. */
50
43
  metadataPath: string;
51
44
  totalSize: number;
52
45
  nftUpdated: true;
@@ -145,7 +138,7 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
145
138
  this.useDevicePassphraseState = false;
146
139
  }
147
140
 
148
- private async assertCapabilities(useHostAssetPackage: boolean) {
141
+ private async assertCapabilities() {
149
142
  const protocolInfo = await this.device.ensureProtocolV2RuntimeContext();
150
143
  const hasFileWrite = supportsProtocolV2Message(
151
144
  protocolInfo,
@@ -157,7 +150,7 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
157
150
  );
158
151
  const hasDirList = supportsProtocolV2Message(protocolInfo, FILESYSTEM_DIR_LIST_MESSAGE_TYPE);
159
152
  const hasNftUpdate = supportsProtocolV2Message(protocolInfo, NFT_UPDATE_MESSAGE_TYPE);
160
- if (!hasFileWrite || !hasNftUpdate || (!useHostAssetPackage && (!hasPathInfo || !hasDirList))) {
153
+ if (!hasFileWrite || !hasPathInfo || !hasDirList || !hasNftUpdate) {
161
154
  throw createDeviceNotSupportMethodError(this.name, this.device.getCurrentFirmwareType());
162
155
  }
163
156
  }
@@ -199,32 +192,16 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
199
192
  const { bundle } = this;
200
193
  if (!bundle) throw invalidParameter('NFT data has not been initialized.');
201
194
 
202
- const useHostAssetPackage = supportsPro2HostAssetPackage(
203
- this.device.state?.versions.firmware ?? undefined
204
- );
205
- await this.assertCapabilities(useHostAssetPackage);
195
+ await this.assertCapabilities();
206
196
  this.throwIfAborted();
207
- if (!useHostAssetPackage) await this.assertStorageCapacity(bundle.basename);
197
+ await this.assertStorageCapacity(bundle.basename);
208
198
  this.throwIfAborted();
209
199
 
210
- const extractedFiles = [
200
+ const files = [
211
201
  { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.bin`, data: bundle.image },
212
202
  { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}_m.bin`, data: bundle.thumbnail },
213
203
  { path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.json`, data: bundle.metadata },
214
204
  ];
215
- const files = useHostAssetPackage
216
- ? [
217
- {
218
- path: `${PRO2_NFT_DIRECTORY}/${bundle.basename}.okpkg`,
219
- data: buildPro2HostAssetPackage(
220
- extractedFiles.map(file => ({
221
- name: file.path.slice(`${PRO2_NFT_DIRECTORY}/`.length),
222
- data: file.data,
223
- }))
224
- ),
225
- },
226
- ]
227
- : extractedFiles;
228
205
  const totalSize = files.reduce((sum, file) => sum + file.data.byteLength, 0);
229
206
  let transferredBeforeFile = 0;
230
207
 
@@ -261,9 +238,9 @@ export default class DeviceUploadNft extends BaseMethod<DeviceUploadNftParams> {
261
238
  const response = await this.updateNft(bundle.basename);
262
239
  return {
263
240
  basename: bundle.basename,
264
- imagePath: extractedFiles[0].path,
265
- thumbnailPath: extractedFiles[1].path,
266
- metadataPath: extractedFiles[2].path,
241
+ imagePath: files[0].path,
242
+ thumbnailPath: files[1].path,
243
+ metadataPath: files[2].path,
267
244
  totalSize,
268
245
  nftUpdated: true,
269
246
  message: response.message?.message,
@@ -16,26 +16,14 @@ import {
16
16
  type Pro2WallpaperColorFormat,
17
17
  encodePro2Wallpaper,
18
18
  } from '../../utils/pro2Wallpaper';
19
- import {
20
- buildPro2HostAssetPackage,
21
- supportsPro2HostAssetPackage,
22
- } from '../../utils/pro2HostAssetPackage';
23
19
 
24
20
  export type DeviceUploadWallpaperParams = {
25
21
  jpegBase64: string;
26
- /**
27
- * Legacy firmware uses this name for the uploaded `.bin` file. Firmware
28
- * 1.0.1+ always consumes the fixed `wallpaper.okpkg` package path.
29
- */
30
22
  fileName?: string;
31
23
  chunkSize?: number;
32
24
  };
33
25
 
34
26
  export type DeviceUploadWallpaperResponse = {
35
- /**
36
- * Filesystem path sent to `DeviceSettingsSet`. On firmware 1.0.1+ this is
37
- * the temporary package path; firmware extracts and persists wallpaper.bin.
38
- */
39
27
  path: string;
40
28
  size: number;
41
29
  colorFormat: Pro2WallpaperColorFormat;
@@ -43,8 +31,6 @@ export type DeviceUploadWallpaperResponse = {
43
31
  };
44
32
 
45
33
  const WALLPAPER_DIRECTORY = 'vol1:/wallpapers';
46
- const WALLPAPER_PACKAGE_PATH = `${WALLPAPER_DIRECTORY}/wallpaper.okpkg`;
47
- const WALLPAPER_PACKAGE_ENTRY = 'wallpaper.bin';
48
34
  const SAFE_FILE_NAME = /^[A-Za-z0-9_-]+(?:\.bin)?$/;
49
35
  const DEVICE_SETTINGS_SET_MESSAGE_TYPE = 60412;
50
36
  const FILESYSTEM_FILE_WRITE_MESSAGE_TYPE = 60805;
@@ -130,14 +116,16 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
130
116
  this.directoryReady = true;
131
117
  }
132
118
 
133
- private async upload(path: string, data: Uint8Array) {
119
+ private async upload() {
134
120
  if (this.uploaded) return;
121
+ const { encoded } = this;
122
+ if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
135
123
 
136
124
  await writeProtocolV2File({
137
125
  commands: this.device.commands,
138
- path,
139
- data,
140
- totalSize: data.byteLength,
126
+ path: this.path,
127
+ data: encoded.data,
128
+ totalSize: encoded.data.byteLength,
141
129
  chunkSize: this.params.chunkSize,
142
130
  maxChunkRetries: 3,
143
131
  overwrite: true,
@@ -157,14 +145,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
157
145
  if (!encoded) throw invalidParameter('Wallpaper data has not been initialized.');
158
146
  await this.assertCapabilities();
159
147
  await this.ensureDirectory();
160
- const useHostAssetPackage = supportsPro2HostAssetPackage(
161
- this.device.state?.versions.firmware ?? undefined
162
- );
163
- const data = useHostAssetPackage
164
- ? buildPro2HostAssetPackage([{ name: WALLPAPER_PACKAGE_ENTRY, data: encoded.data }])
165
- : encoded.data;
166
- if (useHostAssetPackage) this.path = WALLPAPER_PACKAGE_PATH;
167
- await this.upload(this.path, data);
148
+ await this.upload();
168
149
  const response = await this.device.commands.typedCall('DeviceSettingsSet', 'Success', {
169
150
  settings: { wallpaper_path: this.path },
170
151
  });
@@ -179,7 +160,7 @@ export default class DeviceUploadWallpaper extends BaseMethod<DeviceUploadWallpa
179
160
  }
180
161
  return {
181
162
  path: this.path,
182
- size: data.byteLength,
163
+ size: encoded.data.byteLength,
183
164
  colorFormat: encoded.colorFormat,
184
165
  message: response.message?.message,
185
166
  };
@@ -1,5 +1,4 @@
1
1
  import { DeviceRebootType } from '@onekeyfe/hd-transport';
2
- import { HardwareErrorCode } from '@onekeyfe/hd-shared';
3
2
 
4
3
  import { invalidParameter, validateNonEmptyString } from '../helpers/filesystemValidation';
5
4
  import { ProtocolV2FirmwareTargetType } from '../../protocols/protocol-v2/firmware';
@@ -104,13 +103,6 @@ export const getProtocolV2UnknownErrorText = (error: unknown) => {
104
103
  };
105
104
 
106
105
  export const isProtocolV2DeviceDisconnectedError = (error: unknown) => {
107
- const errorCode =
108
- error && typeof error === 'object'
109
- ? (error as { errorCode?: number; code?: number }).errorCode ??
110
- (error as { errorCode?: number; code?: number }).code
111
- : undefined;
112
- if (errorCode === HardwareErrorCode.BleDeviceDisconnected) return true;
113
-
114
106
  const message = getProtocolV2UnknownErrorText(error).toLowerCase();
115
107
  const compactMessage = message.replace(/\s+/g, '');
116
108
  return (
package/src/core/index.ts CHANGED
@@ -415,13 +415,6 @@ const onCallDevice = async (
415
415
  if (method.payload?.onlyConnectBleDevice) {
416
416
  preWarmCallbackTask?.resolve();
417
417
  Log.debug('Call API - only connect ble device: ', device?.mainId);
418
- // This early return bypasses the normal-path bookkeeping at the end of the
419
- // call. Without it the task leaks and haunts every later queue snapshot
420
- // and cancel sweep (field log: a completed task lingered for 6 minutes),
421
- // and the request stays in the active maps, so repeated preconnects pile
422
- // up phantom work in diagnostics.
423
- completeMethodRequestContext(method);
424
- requestQueue.releaseTask(method.responseID);
425
418
  return createResponseMessage(method.responseID, true, null);
426
419
  }
427
420
 
@@ -651,8 +644,7 @@ const onCallDevice = async (
651
644
  method.payload?.passphraseState,
652
645
  method.payload?.useEmptyPassphrase,
653
646
  method.payload?.skipPassphraseCheck,
654
- hasDeriveCardano(method),
655
- method.protocolV2UnlockContext?.preflightMainPinSelected
647
+ hasDeriveCardano(method)
656
648
  );
657
649
 
658
650
  // Double check, handles the special case of Touch/Pro
@@ -958,72 +950,11 @@ export function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unkn
958
950
  );
959
951
  }
960
952
 
961
- export function isProtocolV2PeerRemovedPairingError(method: BaseMethod, error: unknown) {
962
- return (
963
- method.payload.connectProtocol === 'V2' &&
964
- (error as { errorCode?: unknown })?.errorCode ===
965
- HardwareErrorCode.BlePeerRemovedPairingInformation
966
- );
967
- }
968
-
969
953
  /**
970
954
  * If the Bluetooth connection times out, retry up to 6 times
971
955
  * @param retryCount - Current retry count (default 0)
972
956
  */
973
- // device.acquire awaits a transport reply with no deadline of its own; a
974
- // transport that never settles (field case: Electron main lost an IPC reply,
975
- // "reply was never sent" after 5 minutes) hangs the call forever and cancel()
976
- // only takes effect at poll checkpoints. Race acquire against a deadline and
977
- // the caller's abort signal so the hang is bounded and cancel is immediate.
978
- const BLE_ACQUIRE_DEADLINE_MS = 60 * 1000;
979
-
980
- function raceBleAcquire<T>(acquirePromise: Promise<T>, abortSignal?: AbortSignal): Promise<T> {
981
- return new Promise<T>((resolve, reject) => {
982
- let settled = false;
983
- const settle = (fn: () => void) => {
984
- if (settled) return;
985
- settled = true;
986
- clearTimeout(deadline);
987
- abortSignal?.removeEventListener('abort', onAbort);
988
- fn();
989
- };
990
- const onAbort = () =>
991
- settle(() => reject(ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled)));
992
- const deadline = setTimeout(
993
- () =>
994
- settle(() =>
995
- reject(
996
- ERRORS.TypedError(
997
- HardwareErrorCode.BleTimeoutError,
998
- `BLE acquire exceeded ${BLE_ACQUIRE_DEADLINE_MS}ms deadline`
999
- )
1000
- )
1001
- ),
1002
- BLE_ACQUIRE_DEADLINE_MS
1003
- );
1004
- // Attach before any early return so a late settlement of acquirePromise
1005
- // is always consumed — an abort or deadline must never leave the acquire
1006
- // rejection unhandled.
1007
- acquirePromise.then(
1008
- value => settle(() => resolve(value)),
1009
- error => settle(() => reject(error))
1010
- );
1011
- if (abortSignal) {
1012
- if (abortSignal.aborted) {
1013
- onAbort();
1014
- return;
1015
- }
1016
- abortSignal.addEventListener('abort', onAbort);
1017
- }
1018
- });
1019
- }
1020
-
1021
- async function connectDeviceForBle(
1022
- method: BaseMethod,
1023
- device: Device,
1024
- abortSignal?: AbortSignal,
1025
- retryCount = 0
1026
- ) {
957
+ async function connectDeviceForBle(method: BaseMethod, device: Device, retryCount = 0) {
1027
958
  try {
1028
959
  if (device.wasInterruptedByUser()) {
1029
960
  throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
@@ -1037,43 +968,9 @@ async function connectDeviceForBle(
1037
968
  !device.commands ||
1038
969
  device.commands.disposed;
1039
970
  if (shouldAcquire) {
1040
- // The deadline/abort guards are scoped to the desktop electron
1041
- // transport: its IPC acquire is the only path with a proven
1042
- // never-settling failure mode, while react-native/lowlevel acquire may
1043
- // legitimately block on a user-driven system bonding prompt for longer
1044
- // than any sane deadline. Other envs keep the plain acquire unchanged.
1045
- const useAcquireGuards = DataManager.getSettings('env') === 'desktop-web-ble';
1046
- // A cancel landing during the retry backoff must not start a new acquire.
1047
- if (useAcquireGuards && abortSignal?.aborted) {
1048
- throw ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled);
1049
- }
1050
- if (!useAcquireGuards) {
1051
- await device.acquire(method.payload.connectProtocol, {
1052
- forceProtocolDetection: method.payload.forceProtocolDetection,
1053
- });
1054
- } else {
1055
- try {
1056
- await raceBleAcquire(
1057
- device.acquire(method.payload.connectProtocol, {
1058
- forceProtocolDetection: method.payload.forceProtocolDetection,
1059
- }),
1060
- abortSignal
1061
- );
1062
- } catch (err) {
1063
- // A deadline hit means the transport is wedged mid-acquire; drop the
1064
- // link before the retry so it cold-connects instead of stacking a
1065
- // second connect onto the half-open one.
1066
- if (
1067
- err.errorCode === HardwareErrorCode.BleTimeoutError &&
1068
- device.mainId &&
1069
- device.deviceConnector
1070
- ) {
1071
- await device.deviceConnector.disconnect(device.mainId).catch(() => undefined);
1072
- device.markTransportDisconnected();
1073
- }
1074
- throw err;
1075
- }
1076
- }
971
+ await device.acquire(method.payload.connectProtocol, {
972
+ forceProtocolDetection: method.payload.forceProtocolDetection,
973
+ });
1077
974
  }
1078
975
  if (method.payload?.onlyConnectBleDevice) {
1079
976
  if (shouldAcquire) {
@@ -1113,7 +1010,7 @@ async function connectDeviceForBle(
1113
1010
  const nextRetry = retryCount + 1;
1114
1011
  Log.debug(`Bluetooth connection will retry, retry count: ${nextRetry}`);
1115
1012
  await wait(3000);
1116
- await connectDeviceForBle(method, device, abortSignal, nextRetry);
1013
+ await connectDeviceForBle(method, device, nextRetry);
1117
1014
  } else {
1118
1015
  throw err;
1119
1016
  }
@@ -1223,7 +1120,7 @@ const ensureConnected = async (
1223
1120
  if (tryCount === 1) {
1224
1121
  device.beginConnectionAttempt();
1225
1122
  }
1226
- await connectDeviceForBle(method, device, abortSignal);
1123
+ await connectDeviceForBle(method, device);
1227
1124
  }
1228
1125
  resolve(device);
1229
1126
  return;
@@ -1267,8 +1164,7 @@ const ensureConnected = async (
1267
1164
  HardwareErrorCode.BridgeNeedsPermission,
1268
1165
  HardwareErrorCode.DeviceInterruptedFromUser,
1269
1166
  HardwareErrorCode.CallQueueActionCancelled,
1270
- ].includes(error.errorCode) ||
1271
- isProtocolV2PeerRemovedPairingError(method, error)
1167
+ ].includes(error.errorCode)
1272
1168
  ) {
1273
1169
  reject(error);
1274
1170
  return;
@@ -1324,9 +1220,6 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1324
1220
  ','
1325
1221
  )}`
1326
1222
  );
1327
- // Abort before rejecting: rejectRequest releases the task and would make
1328
- // its AbortController unreachable to an in-flight method loop.
1329
- requestQueue.abortRequestsByConnectId(connectId);
1330
1223
  const canceledDevices: Device[] = [];
1331
1224
  const interruptDevice = (device: Device | undefined, deviceConnectId: string) => {
1332
1225
  if (!device || canceledDevices.includes(device)) {
@@ -1351,6 +1244,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1351
1244
  }
1352
1245
  }
1353
1246
  interruptDevice(deviceCacheMap.get(connectId), connectId);
1247
+ requestQueue.abortRequestsByConnectId(connectId);
1354
1248
  pollingManager.stop(connectId);
1355
1249
  } catch (e) {
1356
1250
  Log.error('Cancel API Error: ', e);
@@ -1359,8 +1253,6 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1359
1253
  const env = DataManager.getSettings('env');
1360
1254
  if (DataManager.isBleConnect(env)) {
1361
1255
  Log.debug('Cancel Api all _deviceList: ');
1362
- // Keep method abort signals observable until every active task is rejected.
1363
- requestQueue.abortAllRequests();
1364
1256
  const canceledDevices: Device[] = [];
1365
1257
  const interruptDevice = (device?: Device) => {
1366
1258
  if (!device || canceledDevices.includes(device)) {
@@ -1385,6 +1277,7 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1385
1277
  }
1386
1278
  }
1387
1279
  deviceCacheMap.forEach(interruptDevice);
1280
+ requestQueue.abortAllRequests();
1388
1281
  pollingManager.stopAll();
1389
1282
  } else {
1390
1283
  _deviceList?.allDevices().forEach(device => {
@@ -1435,10 +1328,6 @@ const checkPassphraseEnableState = (method: BaseMethod, features?: Features) =>
1435
1328
  const shouldCheckPassphraseState = (method: BaseMethod, device: Device) => {
1436
1329
  if (!method.useDevicePassphraseState) return false;
1437
1330
 
1438
- if (device.isProtocolV2() && method.payload?.useEmptyPassphrase === true) {
1439
- return true;
1440
- }
1441
-
1442
1331
  return device.hasUsePassphrase();
1443
1332
  };
1444
1333