@onekeyfe/hd-core 1.2.0-alpha.106 → 1.2.0-alpha.107

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 (61) hide show
  1. package/__tests__/DeviceCommands.test.ts +28 -100
  2. package/__tests__/device-lifecycle-events.test.ts +2 -113
  3. package/__tests__/device-pool-state.test.ts +0 -25
  4. package/__tests__/deviceSettings.test.ts +1 -0
  5. package/__tests__/deviceUploadNft.test.ts +4 -33
  6. package/__tests__/get-device-state.test.ts +13 -52
  7. package/__tests__/logBlockEvent.test.ts +1 -13
  8. package/__tests__/protocol-v2.test.ts +121 -758
  9. package/__tests__/refresh-device-state.test.ts +1 -3
  10. package/__tests__/search-devices.test.ts +38 -32
  11. package/dist/api/FirmwareUpdateV4.d.ts +3 -10
  12. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  13. package/dist/api/UploadPortfolio.d.ts +1 -1
  14. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  15. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +3 -2
  16. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  17. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +3 -2
  18. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  19. package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
  20. package/dist/core/index.d.ts.map +1 -1
  21. package/dist/device/Device.d.ts +2 -7
  22. package/dist/device/Device.d.ts.map +1 -1
  23. package/dist/device/DeviceCommands.d.ts.map +1 -1
  24. package/dist/device/DevicePool.d.ts.map +1 -1
  25. package/dist/events/logBlockEvent.d.ts.map +1 -1
  26. package/dist/index.d.ts +16 -12
  27. package/dist/index.js +201 -446
  28. package/dist/protocols/protocol-v2/features.d.ts +1 -9
  29. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  30. package/dist/protocols/protocol-v2/index.d.ts +2 -2
  31. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  32. package/dist/types/api/protocolV2.d.ts +1 -1
  33. package/dist/types/api/protocolV2.d.ts.map +1 -1
  34. package/dist/types/device.d.ts.map +1 -1
  35. package/dist/utils/pro2Nft.d.ts +0 -7
  36. package/dist/utils/pro2Nft.d.ts.map +1 -1
  37. package/package.json +4 -6
  38. package/src/api/FirmwareUpdateV4.ts +174 -312
  39. package/src/api/SearchDevices.ts +2 -2
  40. package/src/api/UploadPortfolio.ts +2 -9
  41. package/src/api/protocol-v2/DeviceUploadNft.ts +8 -56
  42. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +18 -37
  43. package/src/api/ton/TonSignMessage.ts +3 -5
  44. package/src/core/index.ts +2 -6
  45. package/src/device/Device.ts +31 -53
  46. package/src/device/DeviceCommands.ts +14 -4
  47. package/src/device/DevicePool.ts +2 -6
  48. package/src/events/logBlockEvent.ts +1 -14
  49. package/src/protocols/protocol-v2/features.ts +2 -19
  50. package/src/protocols/protocol-v2/index.ts +0 -2
  51. package/src/types/api/protocolV2.ts +1 -1
  52. package/src/types/device.ts +2 -1
  53. package/src/utils/deviceSettings.ts +1 -1
  54. package/src/utils/pro2Nft.ts +8 -31
  55. package/__tests__/base64Data.test.ts +0 -70
  56. package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +0 -145
  57. package/__tests__/resourceBase64Boundary.test.ts +0 -48
  58. package/__tests__/ton-sign-message.test.ts +0 -84
  59. package/dist/api/helpers/base64Data.d.ts +0 -16
  60. package/dist/api/helpers/base64Data.d.ts.map +0 -1
  61. package/src/api/helpers/base64Data.ts +0 -85
@@ -19,12 +19,12 @@ const createCommands = () => {
19
19
  };
20
20
 
21
21
  describe('DeviceCommands failure mapping', () => {
22
- it('logs passphrase request and canonical response without exposing the passphrase', async () => {
22
+ it('logs passphrase call request and response without exposing the passphrase', async () => {
23
23
  const commands = createCommands();
24
- const log = getLogger(LoggerNames.DeviceCommands);
25
- const coreLog = getLogger(LoggerNames.Core);
26
- log.messages.length = 0;
27
- coreLog.messages.length = 0;
24
+ const requestLog = getLogger(LoggerNames.DeviceCommands);
25
+ const responseLog = getLogger(LoggerNames.Core);
26
+ requestLog.messages.length = 0;
27
+ responseLog.messages.length = 0;
28
28
  commands.mainId = 'main-id';
29
29
  commands.transport = {
30
30
  call: jest.fn().mockResolvedValue({
@@ -36,13 +36,13 @@ describe('DeviceCommands failure mapping', () => {
36
36
  } as any;
37
37
 
38
38
  await expect(
39
- commands._commonCall('DeviceSessionAskPassphrase', {
39
+ commands.call('DeviceSessionAskPassphrase', {
40
40
  passphrase: 'hidden-wallet-secret',
41
41
  on_device: false,
42
42
  })
43
43
  ).resolves.toMatchObject({ type: 'Success' });
44
44
 
45
- expect(log.messages.at(-2)?.message).toEqual([
45
+ expect(requestLog.messages.at(-1)?.message).toEqual([
46
46
  '[DeviceCommands] [call] Sending',
47
47
  'DeviceSessionAskPassphrase',
48
48
  {
@@ -50,27 +50,21 @@ describe('DeviceCommands failure mapping', () => {
50
50
  on_device: false,
51
51
  },
52
52
  ]);
53
- expect(log.messages.at(-1)?.message).toEqual([
54
- '_filterCommonTypes: ',
53
+ expect(responseLog.messages.at(-1)?.message).toEqual([
54
+ '[DeviceCommands] [call] Received',
55
+ 'Success',
55
56
  {
56
- request: 'DeviceSessionAskPassphrase',
57
- response: {
58
- type: 'Success',
59
- message: {
60
- message: 'Passphrase accepted',
61
- },
62
- },
57
+ message: 'Passphrase accepted',
63
58
  },
64
59
  ]);
65
- expect(
66
- coreLog.messages.some(entry => entry.message[0] === '[DeviceCommands] [call] Received')
67
- ).toBe(false);
68
- expect(JSON.stringify(log.messages)).not.toContain('hidden-wallet-secret');
60
+ expect(JSON.stringify([...requestLog.messages, ...responseLog.messages])).not.toContain(
61
+ 'hidden-wallet-secret'
62
+ );
69
63
  });
70
64
 
71
- it('logs canonical DeviceStatus response fields without exposing the device ID', async () => {
65
+ it('logs DeviceStatus response fields without exposing the device ID', async () => {
72
66
  const commands = createCommands();
73
- const log = getLogger(LoggerNames.DeviceCommands);
67
+ const log = getLogger(LoggerNames.Core);
74
68
  log.messages.length = 0;
75
69
  commands.mainId = 'main-id';
76
70
  commands.transport = {
@@ -88,27 +82,22 @@ describe('DeviceCommands failure mapping', () => {
88
82
  }),
89
83
  } as any;
90
84
 
91
- await expect(commands._commonCall('DeviceStatusGet', {})).resolves.toMatchObject({
85
+ await expect(commands.call('DeviceStatusGet', {})).resolves.toMatchObject({
92
86
  type: 'DeviceStatus',
93
87
  });
94
88
 
95
89
  const receivedLog = log.messages.at(-1)?.message;
96
90
  expect(receivedLog).toEqual([
97
- '_filterCommonTypes: ',
91
+ '[DeviceCommands] [call] Received',
92
+ 'DeviceStatus',
98
93
  {
99
- request: 'DeviceStatusGet',
100
- response: {
101
- type: 'DeviceStatus',
102
- message: {
103
- device_id: '[REDACTED]',
104
- unlocked: true,
105
- init_states: true,
106
- backup_required: false,
107
- passphrase_enabled: true,
108
- attach_to_pin_enabled: false,
109
- unlocked_by_attach_to_pin: false,
110
- },
111
- },
94
+ device_id: '[REDACTED]',
95
+ unlocked: true,
96
+ init_states: true,
97
+ backup_required: false,
98
+ passphrase_enabled: true,
99
+ attach_to_pin_enabled: false,
100
+ unlocked_by_attach_to_pin: false,
112
101
  },
113
102
  ]);
114
103
  expect(JSON.stringify(receivedLog)).not.toContain('sensitive-device-id');
@@ -136,59 +125,6 @@ describe('DeviceCommands failure mapping', () => {
136
125
  expect(JSON.stringify(log.messages)).not.toContain('secret-wallet-address');
137
126
  });
138
127
 
139
- it('logs the sanitized DeviceInfo response payload', async () => {
140
- const commands = createCommands();
141
- const log = getLogger(LoggerNames.DeviceCommands);
142
- log.messages.length = 0;
143
-
144
- await expect(
145
- commands._filterCommonTypes(
146
- {
147
- type: 'DeviceInfo',
148
- message: {
149
- protocol_version: 2,
150
- hw: {
151
- Device_type: 7,
152
- hardware_version: '1.0.0',
153
- },
154
- fw: {
155
- application: {
156
- version: '5.0.0',
157
- build_id: '20260811',
158
- hash: 'firmware-hash',
159
- },
160
- },
161
- },
162
- } as any,
163
- 'DeviceInfoGet'
164
- )
165
- ).resolves.toMatchObject({ type: 'DeviceInfo' });
166
-
167
- expect(log.messages.at(-1)?.message).toEqual([
168
- '_filterCommonTypes: ',
169
- {
170
- request: 'DeviceInfoGet',
171
- response: {
172
- type: 'DeviceInfo',
173
- message: {
174
- protocol_version: 2,
175
- hw: {
176
- Device_type: 7,
177
- hardware_version: '1.0.0',
178
- },
179
- fw: {
180
- application: {
181
- version: '5.0.0',
182
- build_id: '20260811',
183
- hash: 'firmware-hash',
184
- },
185
- },
186
- },
187
- },
188
- },
189
- ]);
190
- });
191
-
192
128
  it('logs the sanitized DeviceFirmwareUpdateStatus response payload', async () => {
193
129
  const commands = createCommands();
194
130
  const log = getLogger(LoggerNames.DeviceCommands);
@@ -625,22 +561,17 @@ describe('DeviceCommands failure mapping', () => {
625
561
  });
626
562
 
627
563
  describe('DeviceCommands Protocol V2 interactive response compatibility', () => {
628
- it('auto-acks Pro2 ButtonRequest and registers Cancel instead of Initialize', async () => {
564
+ it('still auto-acks ButtonRequest when no hardware UI listener is registered', async () => {
629
565
  const commands = createCommands();
630
566
  const emit = jest.fn();
631
- const cancelDevice = jest.spyOn(commands, 'cancelDevice').mockResolvedValue(undefined);
632
- const cancelDeviceOnOneKeyDevice = jest
633
- .spyOn(commands, 'cancelDeviceOnOneKeyDevice')
634
- .mockResolvedValue(undefined);
635
567
  const commonCall = jest.fn().mockResolvedValue({
636
568
  type: 'Success',
637
569
  message: {},
638
570
  });
639
- const setCancelableAction = jest.fn();
640
571
  commands.device = {
641
572
  ...commands.device,
642
573
  getCurrentDeviceType: jest.fn(() => 'pro2'),
643
- setCancelableAction,
574
+ setCancelableAction: jest.fn(),
644
575
  emit,
645
576
  listenerCount: jest.fn(() => 0),
646
577
  } as any;
@@ -662,9 +593,6 @@ describe('DeviceCommands Protocol V2 interactive response compatibility', () =>
662
593
  expect.objectContaining({ code: 'ButtonRequest_PinEntry' })
663
594
  );
664
595
  expect(commonCall).toHaveBeenCalledWith('ButtonAck', {}, undefined);
665
- await setCancelableAction.mock.calls[0][0]();
666
- expect(cancelDevice).toHaveBeenCalledTimes(1);
667
- expect(cancelDeviceOnOneKeyDevice).not.toHaveBeenCalled();
668
596
  });
669
597
 
670
598
  it('rejects PassphraseRequest when the Pro2 UI listener is not registered', async () => {
@@ -1,14 +1,12 @@
1
- import { EDeviceType, HardwareErrorCode } from '@onekeyfe/hd-shared';
2
- import { DeviceType, TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
1
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
2
+ import { TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
3
3
 
4
4
  import { initConnector, initCore } from '../src/core';
5
5
  import { DataManager } from '../src/data-manager';
6
6
  import TransportManager from '../src/data-manager/TransportManager';
7
7
  import { Device } from '../src/device/Device';
8
- import { cancelDeviceInPrompt, cancelDeviceWithInitialize } from '../src/device/DeviceCommands';
9
8
  import { DevicePool } from '../src/device/DevicePool';
10
9
  import { CORE_EVENT, DEVICE, IFRAME } from '../src/events';
11
- import { PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE } from '../src/protocols/protocol-v2';
12
10
 
13
11
  import type Core from '../src/core';
14
12
  import type { CoreMessage } from '../src/events';
@@ -273,115 +271,6 @@ describe('public device lifecycle events', () => {
273
271
  expect(device.isUsedHere()).toBe(false);
274
272
  });
275
273
 
276
- test.each([
277
- ['V2', 'Cancel', 'webusb'],
278
- ['V2', 'Cancel', 'desktop-web-ble'],
279
- ['V2', 'Cancel', 'react-native'],
280
- ['V1', 'Initialize', 'webusb'],
281
- ['V1', 'Cancel', 'desktop-web-ble'],
282
- ] as const)(
283
- 'sends Protocol %s %s without treating the acquired %s session as disconnected',
284
- async (protocol, messageType, env) => {
285
- jest.spyOn(DataManager, 'getSettings').mockReturnValue(env as never);
286
- const device = createInitializedDevice(protocol);
287
- const post = jest.fn().mockResolvedValue(undefined);
288
- const call = jest.fn().mockResolvedValue(undefined);
289
- const cancel = jest.fn().mockResolvedValue(undefined);
290
- device.originalDescriptor.session = device.mainId;
291
- (device as unknown as { deviceAcquired: boolean }).deviceAcquired = true;
292
- (device as any).commands = { transport: { post, call }, cancel };
293
- device.setCancelableAction(() =>
294
- messageType === 'Cancel'
295
- ? cancelDeviceInPrompt(device, false)
296
- : cancelDeviceWithInitialize(device)
297
- );
298
-
299
- await device.interruptionFromUser();
300
-
301
- if (messageType === 'Cancel') {
302
- expect(post).toHaveBeenCalledWith(device.mainId, 'Cancel', {});
303
- expect(call).not.toHaveBeenCalled();
304
- } else {
305
- expect(call).toHaveBeenCalledWith(device.mainId, 'Initialize', {});
306
- expect(post).not.toHaveBeenCalled();
307
- }
308
- expect(cancel).toHaveBeenCalledTimes(1);
309
- expect(device.hasDeviceAcquire()).toBe(true);
310
- }
311
- );
312
-
313
- test.each([
314
- [EDeviceType.Pro2, 'webusb', false, DeviceType.PRO2],
315
- [EDeviceType.Neo, 'webusb', false, DeviceType.NEO],
316
- [EDeviceType.Pro2, 'desktop-web-ble', true, DeviceType.PRO2],
317
- [EDeviceType.Neo, 'desktop-web-ble', true, DeviceType.NEO],
318
- [EDeviceType.Pro2, 'react-native', true, DeviceType.PRO2],
319
- [EDeviceType.Neo, 'react-native', true, DeviceType.NEO],
320
- [EDeviceType.Pro2, 'lowlevel', false, DeviceType.PRO2],
321
- ] as const)(
322
- 'refreshes %s runtime state after acquiring a fresh %s session',
323
- async (deviceType, env, isBle, protocolV2DeviceType) => {
324
- jest.spyOn(DataManager, 'getSettings').mockReturnValue(env as never);
325
- const device = createInitializedDevice('V2');
326
- const staleProtocolInfo = {
327
- version: 1,
328
- build_fingerprint: 'bootloader__1.0.0__abcdef0__PROD__RELEASE',
329
- supported_messages: [],
330
- };
331
- const freshProtocolInfo = {
332
- version: 1,
333
- build_fingerprint: 'application__5.0.0__abcdef0__PROD__RELEASE',
334
- supported_messages: [PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE],
335
- };
336
- device.updateState(
337
- {
338
- identity: { deviceType },
339
- status: { mode: 'bootloader' },
340
- raw: { protocolV2ProtocolInfo: staleProtocolInfo },
341
- },
342
- 'initialize'
343
- );
344
- const acquire = jest
345
- .fn()
346
- .mockResolvedValue(isBle ? { uuid: 'fresh-session', protocolType: 'V2' } : 'fresh-session');
347
- device.deviceConnector = { acquire } as never;
348
-
349
- await device.acquire('V2', { throwOnRunPromiseError: true });
350
- const typedCall = jest.fn().mockImplementation((requestType: string) => {
351
- if (requestType === 'DeviceInfoGet') {
352
- return {
353
- message: {
354
- protocol_version: 2,
355
- hw: { Device_type: protocolV2DeviceType, serial_no: 'SERIAL-001' },
356
- fw: { application: { version: '5.0.0' } },
357
- },
358
- };
359
- }
360
- if (requestType === 'ProtocolInfoRequest') {
361
- return { message: freshProtocolInfo };
362
- }
363
- if (requestType === 'DeviceStatusGet') {
364
- return {
365
- message: { init_states: true, unlocked: true, device_id: 'wallet-device-id' },
366
- };
367
- }
368
- throw new Error(`Unexpected request: ${requestType}`);
369
- });
370
- device.commands.typedCall = typedCall as never;
371
-
372
- await device.initialize();
373
-
374
- expect(typedCall.mock.calls.map(callArgs => callArgs[0])).toEqual([
375
- 'DeviceInfoGet',
376
- 'ProtocolInfoRequest',
377
- 'DeviceStatusGet',
378
- ]);
379
- expect(device.state?.status.mode).toBe('normal');
380
- expect(device.state?.identity.deviceType).toBe(deviceType);
381
- expect(device.hasDeviceAcquire()).toBe(true);
382
- }
383
- );
384
-
385
274
  test('exposes the current transport usage state in public device snapshots', () => {
386
275
  const getSettings = jest
387
276
  .spyOn(DataManager, 'getSettings')
@@ -38,31 +38,6 @@ describe('DevicePool state lifecycle', () => {
38
38
  expect(getDeviceState).toHaveBeenNthCalledWith(2, { refreshSections: ['settings'] });
39
39
  });
40
40
 
41
- test('refreshes Protocol V2 discovery state without legacy-specific options', async () => {
42
- const getDeviceState = jest.fn().mockResolvedValue({ status: { mode: 'bootloader' } });
43
- const run = jest.fn(async (callback: () => Promise<void>) => callback());
44
- const device = {
45
- isProtocolV2: () => true,
46
- getDeviceState,
47
- run,
48
- } as any;
49
-
50
- await (DevicePool as any)._refreshRuntimeState(device, {
51
- refreshRuntimeState: true,
52
- });
53
-
54
- expect(run).toHaveBeenCalledWith(expect.any(Function), {
55
- connectProtocol: undefined,
56
- forceProtocolDetection: undefined,
57
- });
58
- expect(getDeviceState).toHaveBeenNthCalledWith(1, {
59
- refreshSections: ['status'],
60
- });
61
- expect(getDeviceState).toHaveBeenNthCalledWith(2, {
62
- refreshSections: ['settings'],
63
- });
64
- });
65
-
66
41
  test('actively re-detects a cached device when protocol detection is forced', async () => {
67
42
  const descriptor = { path: 'cached-path', protocolType: 'V1' } as any;
68
43
  const device = Device.fromDescriptor(descriptor);
@@ -67,6 +67,7 @@ describe('Pro 2 device settings options', () => {
67
67
  { label: '2 minutes', valueMs: 120_000 },
68
68
  { label: '5 minutes', valueMs: 300_000 },
69
69
  { label: '10 minutes', valueMs: 600_000 },
70
+ { label: '30 minutes', valueMs: 1_800_000 },
70
71
  { label: 'Never', valueMs: PROTOCOL_V2_NEVER_TIMEOUT_MS },
71
72
  ]);
72
73
  });
@@ -1,5 +1,4 @@
1
1
  import { HardwareErrorCode } from '@onekeyfe/hd-shared';
2
- import { encode as encodeJpeg } from 'jpeg-js';
3
2
 
4
3
  import DeviceUploadNft from '../src/api/protocol-v2/DeviceUploadNft';
5
4
 
@@ -14,19 +13,6 @@ const createRgba = (width: number, height: number) => {
14
13
  return data;
15
14
  };
16
15
 
17
- const jpegBase64Cache = new Map<string, string>();
18
-
19
- const createJpegBase64 = (width: number, height: number) => {
20
- const key = `${width}x${height}`;
21
- const cached = jpegBase64Cache.get(key);
22
- if (cached) return cached;
23
- const value = encodeJpeg({ width, height, data: createRgba(width, height) }, 80).data.toString(
24
- 'base64'
25
- );
26
- jpegBase64Cache.set(key, value);
27
- return value;
28
- };
29
-
30
16
  const createMethod = ({
31
17
  typedCall,
32
18
  supportedMessages = [60802, 60805, 60808, 61500],
@@ -40,8 +26,8 @@ const createMethod = ({
40
26
  id: 1,
41
27
  payload: {
42
28
  method: 'deviceUploadNft',
43
- imageJpegBase64: createJpegBase64(540, 540),
44
- thumbnailJpegBase64: createJpegBase64(263, 263),
29
+ image: { width: 540, height: 540, rgba: createRgba(540, 540) },
30
+ thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
45
31
  title: 'CryptoPunk #3100',
46
32
  subtitle: 'CryptoPunks',
47
33
  timestampMs: 1_760_000_000_000,
@@ -100,8 +86,8 @@ describe('DeviceUploadNft', () => {
100
86
  id: 1,
101
87
  payload: {
102
88
  method: 'deviceUploadNft',
103
- imageJpegBase64: createJpegBase64(540, 540),
104
- thumbnailJpegBase64: createJpegBase64(263, 263),
89
+ image: { width: 540, height: 540, rgba: createRgba(540, 540) },
90
+ thumbnail: { width: 263, height: 263, rgba: createRgba(263, 263) },
105
91
  title: 'CryptoPunk #3100',
106
92
  subtitle: 'CryptoPunks',
107
93
  timestampMs: 1_760_000_000_000,
@@ -116,21 +102,6 @@ describe('DeviceUploadNft', () => {
116
102
  });
117
103
  });
118
104
 
119
- test('rejects invalid image Base64 before device communication', () => {
120
- const method = new DeviceUploadNft({
121
- id: 1,
122
- payload: {
123
- method: 'deviceUploadNft',
124
- imageJpegBase64: 'not-base64',
125
- thumbnailJpegBase64: createJpegBase64(263, 263),
126
- title: 'NFT',
127
- subtitle: '',
128
- },
129
- });
130
-
131
- expect(() => method.init()).toThrow('canonical Base64');
132
- });
133
-
134
105
  test('uploads the triplet in order without creating the firmware-owned directory', async () => {
135
106
  const typedCall = jest.fn((request: string, _response: string, params: any) => {
136
107
  if (request === 'FilesystemPathInfoQuery') {
@@ -202,61 +202,25 @@ describe('getDeviceState', () => {
202
202
  expect(state.versions.se01).toBe('1.0.0');
203
203
  });
204
204
 
205
- test.each([
206
- ['bootloader', EDeviceType.Pro2, DeviceType.PRO2],
207
- ['romloader', EDeviceType.Pro2, DeviceType.PRO2],
208
- ['bootloader', EDeviceType.Neo, DeviceType.NEO],
209
- ['romloader', EDeviceType.Neo, DeviceType.NEO],
210
- ] as const)(
211
- 'refreshes cached %s state after %s reboots into the application',
212
- async (mode, deviceType, protocolV2DeviceType) => {
205
+ test.each(['bootloader', 'romloader'] as const)(
206
+ 'uses ProtocolInfo to preserve %s mode without DeviceStatusGet',
207
+ async mode => {
213
208
  const typedCall = jest.fn().mockImplementation((requestType: string) => {
214
- if (requestType === 'ProtocolInfoRequest') {
215
- return { message: protocolV2ApplicationInfo };
216
- }
217
- if (requestType === 'DeviceStatusGet') {
209
+ if (requestType === 'DeviceInfoGet') {
218
210
  return {
219
- message: { init_states: true, unlocked: true, device_id: 'wallet-1' },
220
- };
221
- }
222
- throw new Error(`Unexpected request: ${requestType}`);
223
- });
224
- const device = createV2Device(typedCall);
225
- device.updateState(
226
- {
227
- protocol: 'V2',
228
- identity: { deviceType },
229
- status: { mode },
230
- raw: {
231
- protocolV2DeviceInfo: {
232
- hw: { Device_type: protocolV2DeviceType, serial_no: 'SERIAL-1' },
211
+ message: {
212
+ hw: { Device_type: DeviceType.PRO2, serial_no: 'SERIAL-1' },
233
213
  fw:
234
214
  mode === 'romloader'
235
215
  ? { romloader: { version: '1.0.0' } }
236
216
  : { bootloader: { version: '1.0.0' } },
237
217
  },
238
- protocolV2ProtocolInfo: getProtocolV2LoaderInfo(mode),
239
- },
240
- },
241
- 'initialize'
242
- );
243
-
244
- const state = await device.getDeviceState({ refreshSections: ['status'] });
245
-
246
- expect(state.status.mode).toBe('normal');
247
- expect(state.identity.deviceId).toBe('wallet-1');
248
- expect(typedCall.mock.calls.map(call => call[0])).toEqual([
249
- 'ProtocolInfoRequest',
250
- 'DeviceStatusGet',
251
- ]);
252
- }
253
- );
254
-
255
- test.each(['bootloader', 'romloader'] as const)(
256
- 'keeps live %s mode after refreshing cached loader state',
257
- async mode => {
258
- const typedCall = jest.fn().mockResolvedValue({
259
- message: getProtocolV2LoaderInfo(mode),
218
+ };
219
+ }
220
+ if (requestType === 'ProtocolInfoRequest') {
221
+ return { message: getProtocolV2LoaderInfo(mode) };
222
+ }
223
+ throw new Error(`Unexpected request: ${requestType}`);
260
224
  });
261
225
  const device = createV2Device(typedCall);
262
226
  device.updateState(
@@ -272,10 +236,7 @@ describe('getDeviceState', () => {
272
236
  const state = await device.getDeviceState({ refreshSections: ['status'] });
273
237
 
274
238
  expect(state.status.mode).toBe(mode);
275
- expect(typedCall).toHaveBeenCalledTimes(1);
276
- expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
277
- eventless_wallet_session: true,
278
- });
239
+ expect(typedCall).not.toHaveBeenCalled();
279
240
  }
280
241
  );
281
242
 
@@ -104,19 +104,7 @@ describe('getLogBlockLabel', () => {
104
104
  });
105
105
  });
106
106
 
107
- it.each(['deviceUploadNft', 'deviceUploadWallpaper', 'uploadPortfolio'])(
108
- 'skips large Base64 resource payload logging for %s',
109
- method => {
110
- const request = { method, path: 'resource.bin', data: 'A'.repeat(1024 * 1024) };
111
- expect(getLogBlockLabel(request)).toBe(method);
112
- expect(getSafeLogPayload(request, method)).toEqual({
113
- method,
114
- payload: '[REDACTED]',
115
- });
116
- }
117
- );
118
-
119
- it.each(['fileWrite', 'fileRead'])(
107
+ it.each(['deviceUploadNft', 'deviceUploadWallpaper', 'uploadPortfolio', 'fileWrite', 'fileRead'])(
120
108
  'keeps metadata and replaces binary payloads with their size for %s',
121
109
  method => {
122
110
  const request = { method, path: 'resource.bin', data: new Uint8Array(1024) };