@onekeyfe/hd-core 1.2.0-alpha.105 → 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 (67) hide show
  1. package/__tests__/DeviceCommands.test.ts +2 -10
  2. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
  3. package/__tests__/device-lifecycle-events.test.ts +2 -113
  4. package/__tests__/device-pool-state.test.ts +0 -29
  5. package/__tests__/deviceUploadNft.test.ts +4 -33
  6. package/__tests__/get-device-state.test.ts +13 -52
  7. package/__tests__/protocol-v2.test.ts +18 -330
  8. package/__tests__/refresh-device-state.test.ts +1 -4
  9. package/__tests__/search-devices.test.ts +38 -34
  10. package/dist/api/FirmwareUpdateV4.d.ts +0 -3
  11. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  12. package/dist/api/GetDeviceState.d.ts.map +1 -1
  13. package/dist/api/SearchDevices.d.ts.map +1 -1
  14. package/dist/api/UploadPortfolio.d.ts +1 -1
  15. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  16. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -1
  17. package/dist/api/protocol-v2/DeviceUploadNft.d.ts +3 -2
  18. package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
  19. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +3 -2
  20. package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
  21. package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
  22. package/dist/core/index.d.ts.map +1 -1
  23. package/dist/device/Device.d.ts +2 -10
  24. package/dist/device/Device.d.ts.map +1 -1
  25. package/dist/device/DevicePool.d.ts +1 -1
  26. package/dist/device/DevicePool.d.ts.map +1 -1
  27. package/dist/index.d.ts +16 -18
  28. package/dist/index.js +99 -277
  29. package/dist/protocols/protocol-v2/features.d.ts +0 -8
  30. package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
  31. package/dist/protocols/protocol-v2/index.d.ts +2 -2
  32. package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
  33. package/dist/types/api/getDeviceState.d.ts +0 -1
  34. package/dist/types/api/getDeviceState.d.ts.map +1 -1
  35. package/dist/types/api/protocolV2.d.ts +1 -1
  36. package/dist/types/api/protocolV2.d.ts.map +1 -1
  37. package/dist/types/device.d.ts.map +1 -1
  38. package/dist/utils/pro2Nft.d.ts +0 -7
  39. package/dist/utils/pro2Nft.d.ts.map +1 -1
  40. package/package.json +4 -6
  41. package/src/api/FirmwareUpdateV4.ts +24 -72
  42. package/src/api/GetDeviceState.ts +0 -1
  43. package/src/api/SearchDevices.ts +2 -4
  44. package/src/api/UploadPortfolio.ts +2 -9
  45. package/src/api/firmware/protocolV2Release.ts +0 -1
  46. package/src/api/protocol-v2/DeviceUploadNft.ts +8 -56
  47. package/src/api/protocol-v2/DeviceUploadWallpaper.ts +18 -37
  48. package/src/api/ton/TonSignMessage.ts +3 -5
  49. package/src/core/index.ts +0 -4
  50. package/src/device/Device.ts +31 -90
  51. package/src/device/DevicePool.ts +5 -17
  52. package/src/protocols/protocol-v2/features.ts +0 -10
  53. package/src/protocols/protocol-v2/index.ts +0 -2
  54. package/src/types/api/getDeviceState.ts +0 -2
  55. package/src/types/api/protocolV2.ts +1 -1
  56. package/src/types/device.ts +2 -1
  57. package/src/utils/pro2Nft.ts +8 -31
  58. package/__tests__/base64Data.test.ts +0 -70
  59. package/__tests__/protocol-v2-legacy-recovery.test.ts +0 -29
  60. package/__tests__/resourceBase64Boundary.test.ts +0 -48
  61. package/__tests__/ton-sign-message.test.ts +0 -84
  62. package/dist/api/helpers/base64Data.d.ts +0 -16
  63. package/dist/api/helpers/base64Data.d.ts.map +0 -1
  64. package/dist/core/protocolV2LegacyRecovery.d.ts +0 -5
  65. package/dist/core/protocolV2LegacyRecovery.d.ts.map +0 -1
  66. package/src/api/helpers/base64Data.ts +0 -85
  67. package/src/core/protocolV2LegacyRecovery.ts +0 -12
package/src/core/index.ts CHANGED
@@ -64,7 +64,6 @@ import {
64
64
  isProtocolV2UiEnabled,
65
65
  } from '../protocols/protocol-v2/uiInteraction';
66
66
  import { createUiProgressMessageFilter } from '../utils/uiProgressThrottle';
67
- import { isLegacyProtocolV2FirmwareRecoveryMethod } from './protocolV2LegacyRecovery';
68
67
 
69
68
  import type { ConnectSettings, Features, KnownDevice } from '../types';
70
69
  import type { CoreMessage, IFrameCallMessage, UiPromise, UiPromiseResponse } from '../events';
@@ -110,9 +109,6 @@ const parseInitOptions = (method?: BaseMethod): InitOptions => ({
110
109
  connectProtocol: method?.payload.connectProtocol,
111
110
  forceProtocolDetection: method?.payload.forceProtocolDetection,
112
111
  protocolV2DeviceInfoTimeoutMs: method?.payload.protocolV2DeviceInfoTimeoutMs,
113
- ...(isLegacyProtocolV2FirmwareRecoveryMethod(method)
114
- ? { allowLegacyProtocolV2ProtocolInfo: true }
115
- : {}),
116
112
  });
117
113
 
118
114
  let _core: Core | undefined;
@@ -61,7 +61,6 @@ import {
61
61
  PROTOCOL_V2_VERSIONS_DEVICE_INFO_REQUEST,
62
62
  type ProtocolV2RuntimeMode,
63
63
  getProtocolV2RuntimeMode,
64
- isLegacyProtocolV2ProtocolInfo,
65
64
  requestProtocolV2DeviceInfo,
66
65
  requestProtocolV2DeviceStatus,
67
66
  requestProtocolV2ProtocolInfo,
@@ -100,8 +99,6 @@ export type InitOptions = {
100
99
  protocolV2DeviceInfoTimeoutMs?: number;
101
100
  /** Refresh Protocol V2 runtime state before returning discovery results. */
102
101
  refreshRuntimeState?: boolean;
103
- /** Recovery-only compatibility used by discovery and Protocol V2 firmware updates. */
104
- allowLegacyProtocolV2ProtocolInfo?: boolean;
105
102
  /**
106
103
  * Protocol V1 Initialize response timeout override. Reboot-wait polling passes a
107
104
  * short value so an unanswered probe settles before the next poll tick.
@@ -488,10 +485,6 @@ export class Device extends EventEmitter {
488
485
  }
489
486
 
490
487
  this.commands = new DeviceCommands(this, this.mainId ?? '');
491
- // Protocol V2 runtime metadata belongs to one active transport link. A
492
- // successful acquire creates a fresh link/session, so never carry cached
493
- // ProtocolInfo or pre-initialize state across that boundary.
494
- this.invalidateProtocolV2RuntimeState();
495
488
  } catch (error) {
496
489
  if (options?.forceProtocolDetection) {
497
490
  this.originalDescriptor.protocolType = previousProtocol;
@@ -984,16 +977,10 @@ export class Device extends EventEmitter {
984
977
  });
985
978
  // The default request excludes SE/hash data and therefore uses basic scope.
986
979
  // Full version and verification data require getDeviceState({ scope: 'firmware' }).
987
- const features = options?.allowLegacyProtocolV2ProtocolInfo
988
- ? await this.probeProtocolV2RuntimeState(
989
- deviceInfo,
990
- options.protocolV2DeviceInfoTimeoutMs,
991
- { allowLegacyProtocolV2ProtocolInfo: true }
992
- )
993
- : await this.probeProtocolV2RuntimeState(
994
- deviceInfo,
995
- options?.protocolV2DeviceInfoTimeoutMs
996
- );
980
+ const features = await this.probeProtocolV2RuntimeState(
981
+ deviceInfo,
982
+ options?.protocolV2DeviceInfoTimeoutMs
983
+ );
997
984
  Log.debug('Protocol V2 features:', features);
998
985
  } catch (error) {
999
986
  Log.error('Protocol V2 initialization failed:', error);
@@ -1030,13 +1017,7 @@ export class Device extends EventEmitter {
1030
1017
  commands: this.commands,
1031
1018
  request: getProtocolV2DeviceInfoRequest(),
1032
1019
  });
1033
- if (params.allowLegacyProtocolV2ProtocolInfo) {
1034
- await this.probeProtocolV2RuntimeState(deviceInfo, undefined, {
1035
- allowLegacyProtocolV2ProtocolInfo: true,
1036
- });
1037
- } else {
1038
- await this.probeProtocolV2RuntimeState(deviceInfo);
1039
- }
1020
+ await this.probeProtocolV2RuntimeState(deviceInfo);
1040
1021
  refreshedDeviceInfo = deviceInfo;
1041
1022
  initializedWithDeviceInfo = true;
1042
1023
  } else {
@@ -1072,16 +1053,7 @@ export class Device extends EventEmitter {
1072
1053
  }
1073
1054
 
1074
1055
  if (refresh.has('status') && !initializedWithDeviceInfo) {
1075
- const cachedMode = this.state?.status.mode;
1076
- await this.probeProtocolV2RuntimeState(refreshedDeviceInfo, undefined, {
1077
- // Loader firmware does not support DeviceStatusGet. Renegotiate ProtocolInfo
1078
- // during an explicit refresh so a device rebooted into application firmware
1079
- // can leave the cached loader state.
1080
- forceRuntimeContextRefresh: cachedMode === 'bootloader' || cachedMode === 'romloader',
1081
- ...(params.allowLegacyProtocolV2ProtocolInfo
1082
- ? { allowLegacyProtocolV2ProtocolInfo: true }
1083
- : {}),
1084
- });
1056
+ await this.probeProtocolV2RuntimeState(refreshedDeviceInfo);
1085
1057
  }
1086
1058
 
1087
1059
  if (refresh.has('settings') && this.state?.status.mode === 'normal') {
@@ -1152,39 +1124,19 @@ export class Device extends EventEmitter {
1152
1124
  return this.features;
1153
1125
  }
1154
1126
 
1155
- async ensureProtocolV2RuntimeContext(
1156
- timeoutMs?: number,
1157
- options?: {
1158
- forceRefresh?: boolean;
1159
- allowLegacyProtocolV2ProtocolInfo?: boolean;
1160
- }
1161
- ): Promise<ProtocolInfo> {
1162
- const assertProtocolInfoAllowed = (protocolInfo: ProtocolInfo) => {
1163
- if (
1164
- isLegacyProtocolV2ProtocolInfo(protocolInfo) &&
1165
- options?.allowLegacyProtocolV2ProtocolInfo !== true
1166
- ) {
1167
- throw ERRORS.TypedError(
1168
- HardwareErrorCode.DeviceInitializeFailed,
1169
- 'Legacy Protocol V2 ProtocolInfo is supported only during device discovery and firmware update.'
1170
- );
1171
- }
1172
- return protocolInfo;
1173
- };
1127
+ async ensureProtocolV2RuntimeContext(timeoutMs?: number): Promise<ProtocolInfo> {
1174
1128
  const cachedProtocolInfo =
1175
- options?.forceRefresh === true
1176
- ? undefined
1177
- : this.protocolV2RuntimeContext ??
1178
- (!this.protocolV2StateNeedsReload
1179
- ? this.state?.raw?.protocolV2ProtocolInfo ?? undefined
1180
- : undefined);
1129
+ this.protocolV2RuntimeContext ??
1130
+ (!this.protocolV2StateNeedsReload
1131
+ ? this.state?.raw?.protocolV2ProtocolInfo ?? undefined
1132
+ : undefined);
1181
1133
  if (cachedProtocolInfo) {
1182
1134
  this.protocolV2RuntimeContext = cachedProtocolInfo;
1183
- return assertProtocolInfoAllowed(cachedProtocolInfo);
1135
+ return cachedProtocolInfo;
1184
1136
  }
1185
1137
 
1186
1138
  if (this.protocolV2RuntimeContextPromise) {
1187
- return assertProtocolInfoAllowed(await this.protocolV2RuntimeContextPromise);
1139
+ return this.protocolV2RuntimeContextPromise;
1188
1140
  }
1189
1141
 
1190
1142
  const requestToken = {};
@@ -1206,7 +1158,7 @@ export class Device extends EventEmitter {
1206
1158
  this.protocolV2RuntimeContextPromise = pendingRequest;
1207
1159
 
1208
1160
  try {
1209
- return assertProtocolInfoAllowed(await pendingRequest);
1161
+ return await pendingRequest;
1210
1162
  } finally {
1211
1163
  if (this.protocolV2RuntimeContextPromise === pendingRequest) {
1212
1164
  this.protocolV2RuntimeContextPromise = undefined;
@@ -1217,20 +1169,9 @@ export class Device extends EventEmitter {
1217
1169
  }
1218
1170
  }
1219
1171
 
1220
- async probeProtocolV2RuntimeState(
1221
- deviceInfo?: ProtocolV2DeviceInfo,
1222
- timeoutMs?: number,
1223
- options?: {
1224
- forceRuntimeContextRefresh?: boolean;
1225
- allowLegacyProtocolV2ProtocolInfo?: boolean;
1226
- }
1227
- ) {
1228
- const protocolInfo = await this.ensureProtocolV2RuntimeContext(timeoutMs, {
1229
- forceRefresh: options?.forceRuntimeContextRefresh,
1230
- allowLegacyProtocolV2ProtocolInfo: options?.allowLegacyProtocolV2ProtocolInfo,
1231
- });
1172
+ async probeProtocolV2RuntimeState(deviceInfo?: ProtocolV2DeviceInfo, timeoutMs?: number) {
1173
+ const protocolInfo = await this.ensureProtocolV2RuntimeContext(timeoutMs);
1232
1174
  const runtimeMode = getProtocolV2RuntimeMode(protocolInfo);
1233
- const legacyProtocolInfo = isLegacyProtocolV2ProtocolInfo(protocolInfo);
1234
1175
  const runtimeDeviceInfo = deviceInfo ?? this.state?.raw?.protocolV2DeviceInfo;
1235
1176
  const protocolV2DeviceType = runtimeDeviceInfo
1236
1177
  ? resolveProtocolV2DeviceIdentity(runtimeDeviceInfo.hw?.Device_type).deviceType
@@ -1245,9 +1186,10 @@ export class Device extends EventEmitter {
1245
1186
  'Protocol V2 romloader mode is only supported for Pro2 and Neo.'
1246
1187
  );
1247
1188
  }
1248
- const deviceStatusSupported =
1249
- legacyProtocolInfo ||
1250
- supportsProtocolV2Message(protocolInfo, PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE);
1189
+ const deviceStatusSupported = supportsProtocolV2Message(
1190
+ protocolInfo,
1191
+ PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE
1192
+ );
1251
1193
 
1252
1194
  if (runtimeMode === 'bootloader' || runtimeMode === 'romloader') {
1253
1195
  return this.updateProtocolV2Features(deviceInfo, null, runtimeMode, protocolInfo);
@@ -1308,7 +1250,8 @@ export class Device extends EventEmitter {
1308
1250
  });
1309
1251
  }
1310
1252
 
1311
- private invalidateProtocolV2RuntimeState() {
1253
+ markTransportDisconnected() {
1254
+ this.deviceAcquired = false;
1312
1255
  if (!this.isProtocolV2()) return;
1313
1256
  this.protocolV2StateNeedsReload = true;
1314
1257
  this.protocolV2RuntimeContext = undefined;
@@ -1317,11 +1260,6 @@ export class Device extends EventEmitter {
1317
1260
  this.clearPreInitialized();
1318
1261
  }
1319
1262
 
1320
- markTransportDisconnected() {
1321
- this.deviceAcquired = false;
1322
- this.invalidateProtocolV2RuntimeState();
1323
- }
1324
-
1325
1263
  invalidateAfterWipe() {
1326
1264
  const deviceId = this.getCurrentDeviceId();
1327
1265
  if (deviceId) {
@@ -1331,7 +1269,10 @@ export class Device extends EventEmitter {
1331
1269
  if (this.originalDescriptor.path !== deviceId) {
1332
1270
  deviceWalletSessionStore.deleteDevice(this.originalDescriptor.path);
1333
1271
  }
1334
- this.invalidateProtocolV2RuntimeState();
1272
+ this.protocolV2StateNeedsReload = true;
1273
+ this.protocolV2RuntimeContext = undefined;
1274
+ this.protocolV2RuntimeContextPromise = undefined;
1275
+ this.protocolV2RuntimeContextRequestToken = undefined;
1335
1276
  }
1336
1277
 
1337
1278
  this.passphraseState = undefined;
@@ -1343,7 +1284,11 @@ export class Device extends EventEmitter {
1343
1284
  markProtocolV2Reboot(rebootType: DeviceRebootType) {
1344
1285
  if (!this.isProtocolV2()) return;
1345
1286
 
1346
- this.invalidateProtocolV2RuntimeState();
1287
+ this.protocolV2StateNeedsReload = true;
1288
+ this.protocolV2RuntimeContext = undefined;
1289
+ this.protocolV2RuntimeContextPromise = undefined;
1290
+ this.protocolV2RuntimeContextRequestToken = undefined;
1291
+ this.clearPreInitialized();
1347
1292
  let loaderMode: 'bootloader' | 'romloader' | undefined;
1348
1293
  if (rebootType === DeviceRebootType.Bootloader) {
1349
1294
  loaderMode = 'bootloader';
@@ -1417,10 +1362,6 @@ export class Device extends EventEmitter {
1417
1362
  if (device.features) {
1418
1363
  this._updateFeatures(device.features);
1419
1364
  }
1420
- // Adopting another Device instance's command channel also crosses an active
1421
- // link boundary. Renegotiate runtime metadata on that channel instead of
1422
- // retaining ProtocolInfo from the previous instance/session.
1423
- this.invalidateProtocolV2RuntimeState();
1424
1365
  }
1425
1366
 
1426
1367
  async run(fn?: () => Promise<void>, options?: RunOptions) {
@@ -182,7 +182,7 @@ export class DevicePool extends EventEmitter {
182
182
  try {
183
183
  await device.initialize(initOptions);
184
184
  if (initOptions?.refreshRuntimeState && device.isProtocolV2()) {
185
- await this._refreshProtocolV2DiscoveryState(device, initOptions);
185
+ await this._refreshProtocolV2DiscoveryState(device);
186
186
  }
187
187
  } finally {
188
188
  await device.release();
@@ -197,7 +197,7 @@ export class DevicePool extends EventEmitter {
197
197
  await device.run(
198
198
  async () => {
199
199
  try {
200
- await this._refreshProtocolV2DiscoveryState(device, initOptions);
200
+ await this._refreshProtocolV2DiscoveryState(device);
201
201
  } catch (error) {
202
202
  // Device.run releases after the callback; then propagate the actual read error.
203
203
  refreshError = error;
@@ -206,9 +206,6 @@ export class DevicePool extends EventEmitter {
206
206
  {
207
207
  connectProtocol: initOptions.connectProtocol,
208
208
  forceProtocolDetection: initOptions.forceProtocolDetection,
209
- ...(initOptions.allowLegacyProtocolV2ProtocolInfo
210
- ? { allowLegacyProtocolV2ProtocolInfo: true }
211
- : {}),
212
209
  }
213
210
  );
214
211
  if (refreshError instanceof Error) throw refreshError;
@@ -220,19 +217,10 @@ export class DevicePool extends EventEmitter {
220
217
  * error; settings only supplies a label, so its failure retains the existing name.
221
218
  * Device.getDeviceState skips unsupported status/settings calls in loader mode.
222
219
  */
223
- static async _refreshProtocolV2DiscoveryState(device: Device, initOptions?: InitOptions) {
224
- const stateReadOptions = initOptions?.allowLegacyProtocolV2ProtocolInfo
225
- ? { allowLegacyProtocolV2ProtocolInfo: true as const }
226
- : {};
227
- await device.getDeviceState({
228
- refreshSections: ['status'],
229
- ...stateReadOptions,
230
- });
220
+ static async _refreshProtocolV2DiscoveryState(device: Device) {
221
+ await device.getDeviceState({ refreshSections: ['status'] });
231
222
  try {
232
- await device.getDeviceState({
233
- refreshSections: ['settings'],
234
- ...stateReadOptions,
235
- });
223
+ await device.getDeviceState({ refreshSections: ['settings'] });
236
224
  } catch (error) {
237
225
  Log.debug('Unable to refresh Protocol V2 device label during discovery', error);
238
226
  }
@@ -56,16 +56,6 @@ export const getProtocolV2SeType = (se?: DeviceSEInfo): string | null =>
56
56
 
57
57
  export type ProtocolV2RuntimeMode = 'normal' | 'bootloader' | 'romloader';
58
58
 
59
- export type ProtocolV2ProtocolInfo = ProtocolInfo & {
60
- /** Present only when hd-transport decoded the pre-build-fingerprint wire layout. */
61
- protobuf_definition?: string | null;
62
- };
63
-
64
- export const isLegacyProtocolV2ProtocolInfo = (
65
- protocolInfo: ProtocolInfo
66
- ): protocolInfo is ProtocolV2ProtocolInfo & { protobuf_definition: string | null } =>
67
- Object.prototype.hasOwnProperty.call(protocolInfo, 'protobuf_definition');
68
-
69
59
  /**
70
60
  * Protocol V2 fingerprints use:
71
61
  * <binary>__<version>__<commit>__<PROD|DEV>__<DEBUG|RELEASE>
@@ -7,7 +7,6 @@ export {
7
7
  getProtocolV2RuntimeMode,
8
8
  getProtocolV2SeState,
9
9
  getProtocolV2SeType,
10
- isLegacyProtocolV2ProtocolInfo,
11
10
  parseProtocolV2BuildFingerprint,
12
11
  requestProtocolV2ProtocolInfo,
13
12
  supportsProtocolV2Message,
@@ -19,7 +18,6 @@ export type {
19
18
  ProtocolV2SEInfo,
20
19
  ProtocolV2SeStateLabel,
21
20
  ProtocolV2RuntimeMode,
22
- ProtocolV2ProtocolInfo,
23
21
  } from './features';
24
22
  export * from './firmware';
25
23
  export * from './walletSession';
@@ -11,8 +11,6 @@ export type GetDeviceStateParams = CommonParams & {
11
11
  export type DeviceStateReadOptions = {
12
12
  refreshSections?: DeviceStateSection[];
13
13
  includeRaw?: boolean;
14
- /** Internal recovery-only compatibility used by discovery and firmware update flows. */
15
- allowLegacyProtocolV2ProtocolInfo?: boolean;
16
14
  };
17
15
 
18
16
  export declare function getDeviceState(
@@ -68,7 +68,7 @@ export declare function deviceUploadNft(
68
68
  export declare function uploadPortfolio(
69
69
  connectId: string,
70
70
  params: {
71
- packageBase64: string;
71
+ packageBytes: ArrayBuffer | Uint8Array | Blob;
72
72
  timeoutMs?: number | string;
73
73
  }
74
74
  ): Response<FileInfo & { portfolioUpdated: true }>;
@@ -75,7 +75,8 @@ export type SearchDevice = {
75
75
  serialNo?: string | null;
76
76
  /**
77
77
  * @deprecated Ambiguous legacy identifier. Use serialNo for hardware identity
78
- * and connectId for transport routing.
78
+ * and connectId for transport routing. Empty during BLE discovery until the
79
+ * physical serial number is available after initialization.
79
80
  */
80
81
  uuid: string;
81
82
  deviceId: string | null;
@@ -109,34 +109,6 @@ export function buildPro2NftBundle(options: {
109
109
  const { image, thumbnail, title, subtitle, timestampMs } = options;
110
110
  assertImage('image', image, PRO2_NFT_IMAGE_WIDTH, PRO2_NFT_IMAGE_HEIGHT);
111
111
  assertImage('thumbnail', thumbnail, PRO2_NFT_THUMBNAIL_WIDTH, PRO2_NFT_THUMBNAIL_HEIGHT);
112
- const encodedImage = encodePro2Image({ ...image, alphaMode: 'black-background' }).data;
113
- const encodedThumbnail = encodePro2Image({
114
- ...thumbnail,
115
- alphaMode: 'black-background',
116
- }).data;
117
- return buildPro2NftBundleFromEncodedImages({
118
- image: encodedImage,
119
- thumbnail: encodedThumbnail,
120
- title,
121
- subtitle,
122
- timestampMs,
123
- });
124
- }
125
-
126
- export function buildPro2NftBundleFromEncodedImages(options: {
127
- image: Uint8Array;
128
- thumbnail: Uint8Array;
129
- title: string;
130
- subtitle: string;
131
- timestampMs: number;
132
- }): Pro2NftBundle {
133
- const { image, thumbnail, title, subtitle, timestampMs } = options;
134
- if (!(image instanceof Uint8Array) || image.byteLength === 0) {
135
- throw invalidParameter('Parameter [image] must contain encoded NFT image data.');
136
- }
137
- if (!(thumbnail instanceof Uint8Array) || thumbnail.byteLength === 0) {
138
- throw invalidParameter('Parameter [thumbnail] must contain encoded NFT thumbnail data.');
139
- }
140
112
  const titleLength = typeof title === 'string' ? utf8Length(title) : 0;
141
113
  const subtitleLength =
142
114
  typeof subtitle === 'string' ? utf8Length(subtitle) : Number.POSITIVE_INFINITY;
@@ -150,16 +122,21 @@ export function buildPro2NftBundleFromEncodedImages(options: {
150
122
  throw invalidParameter('Parameter [timestampMs] must be a positive safe integer.');
151
123
  }
152
124
 
125
+ const encodedImage = encodePro2Image({ ...image, alphaMode: 'black-background' }).data;
126
+ const encodedThumbnail = encodePro2Image({
127
+ ...thumbnail,
128
+ alphaMode: 'black-background',
129
+ }).data;
153
130
  const metadata = new TextEncoder().encode(JSON.stringify({ title, subtitle }));
154
131
  if (metadata.byteLength === 0 || metadata.byteLength > 512) {
155
132
  throw invalidParameter('Pro2 NFT metadata must contain 1 to 512 UTF-8 bytes.');
156
133
  }
157
134
 
158
- const hash8 = bytesToHex(blake2s(image)).slice(0, 8);
135
+ const hash8 = bytesToHex(blake2s(encodedImage)).slice(0, 8);
159
136
  return {
160
137
  basename: `nft-${hash8}-${timestampMs}`,
161
- image,
162
- thumbnail,
138
+ image: encodedImage,
139
+ thumbnail: encodedThumbnail,
163
140
  metadata,
164
141
  };
165
142
  }
@@ -1,70 +0,0 @@
1
- import { Buffer } from 'buffer';
2
-
3
- import { encode as encodeJpeg } from 'jpeg-js';
4
-
5
- import { decodeCanonicalBase64, decodeJpegBase64ToRgba } from '../src/api/helpers/base64Data';
6
-
7
- const createJpegBase64 = (width: number, height: number) => {
8
- const rgba = new Uint8Array(width * height * 4).fill(0xff);
9
- return encodeJpeg({ width, height, data: rgba }, 80).data.toString('base64');
10
- };
11
-
12
- describe('Base64 resource data', () => {
13
- test('decodes canonical Base64 to a detached Uint8Array', () => {
14
- const decoded = decodeCanonicalBase64({
15
- value: 'AQID',
16
- parameterName: 'packageBase64',
17
- maxBytes: 3,
18
- });
19
-
20
- expect(decoded).toBeInstanceOf(Uint8Array);
21
- expect(Array.from(decoded)).toEqual([1, 2, 3]);
22
- });
23
-
24
- test.each(['', 'not-base64', 'AB==', 'data:image/jpeg;base64,AQID'])(
25
- 'rejects non-canonical Base64 input %p',
26
- value => {
27
- expect(() =>
28
- decodeCanonicalBase64({ value, parameterName: 'data', maxBytes: 1024 })
29
- ).toThrow();
30
- }
31
- );
32
-
33
- test('rejects oversized Base64 before decoding', () => {
34
- const value = Buffer.alloc(64 * 1024 + 17).toString('base64');
35
- expect(() =>
36
- decodeCanonicalBase64({ value, parameterName: 'data', maxBytes: 64 * 1024 })
37
- ).toThrow('maximum supported size');
38
- });
39
-
40
- test('decodes and validates a JPEG with the expected dimensions', () => {
41
- const decoded = decodeJpegBase64ToRgba({
42
- jpegBase64: createJpegBase64(2, 1),
43
- parameterName: 'jpegBase64',
44
- expectedWidth: 2,
45
- expectedHeight: 1,
46
- });
47
-
48
- expect(decoded).toMatchObject({ width: 2, height: 1 });
49
- expect(decoded.data).toHaveLength(8);
50
- });
51
-
52
- test('rejects non-JPEG bytes and unexpected dimensions', () => {
53
- expect(() =>
54
- decodeJpegBase64ToRgba({
55
- jpegBase64: 'AQID',
56
- parameterName: 'jpegBase64',
57
- expectedWidth: 2,
58
- expectedHeight: 1,
59
- })
60
- ).toThrow('JPEG image');
61
- expect(() =>
62
- decodeJpegBase64ToRgba({
63
- jpegBase64: createJpegBase64(2, 1),
64
- parameterName: 'jpegBase64',
65
- expectedWidth: 1,
66
- expectedHeight: 1,
67
- })
68
- ).toThrow('1x1');
69
- });
70
- });
@@ -1,29 +0,0 @@
1
- import { isLegacyProtocolV2FirmwareRecoveryMethod } from '../src/core/protocolV2LegacyRecovery';
2
-
3
- import type { BaseMethod } from '../src/api/BaseMethod';
4
-
5
- const createMethod = (
6
- name: string,
7
- payload: Record<string, unknown>
8
- ): Pick<BaseMethod, 'name' | 'payload'> => ({
9
- name,
10
- payload: { method: name, ...payload },
11
- });
12
-
13
- describe('Protocol V2 legacy firmware recovery policy', () => {
14
- test.each([
15
- ['firmware state read', 'getDeviceState', { scope: 'firmware' }],
16
- ['firmware release check', 'checkAllFirmwareRelease', {}],
17
- ['firmware update', 'firmwareUpdateV4', {}],
18
- ])('allows legacy ProtocolInfo for %s', (_label, name, payload) => {
19
- expect(isLegacyProtocolV2FirmwareRecoveryMethod(createMethod(name, payload))).toBe(true);
20
- });
21
-
22
- test.each([
23
- ['runtime state read', 'getDeviceState', { scope: 'runtime' }],
24
- ['settings state read', 'getDeviceState', { scope: 'settings' }],
25
- ['ordinary API', 'getFeatures', {}],
26
- ])('rejects legacy ProtocolInfo for %s', (_label, name, payload) => {
27
- expect(isLegacyProtocolV2FirmwareRecoveryMethod(createMethod(name, payload))).toBe(false);
28
- });
29
- });
@@ -1,48 +0,0 @@
1
- import { HardwareTopLevelSdk } from '../src';
2
- import { findMethod } from '../src/api/utils';
3
-
4
- import type { LowLevelCoreApi } from '../src';
5
-
6
- jest.mock('../src/data/config', () => ({
7
- DEFAULT_DOMAIN: 'https://example.com/',
8
- getSDKVersion: () => '0.0.0-test',
9
- }));
10
-
11
- const crossJsonOnlyBridge = (value: unknown): unknown => JSON.parse(JSON.stringify(value));
12
-
13
- describe('Base64 resource boundary', () => {
14
- test('keeps Portfolio, wallpaper and NFT payloads JSON-safe across TopLevel and LowLevel', async () => {
15
- const restoredPayloads: Array<Record<string, any>> = [];
16
- const lowLevelApi = {
17
- call: jest.fn(params => {
18
- const method = findMethod({ id: 1, payload: crossJsonOnlyBridge(params) } as any);
19
- restoredPayloads.push(method.payload);
20
- return Promise.resolve({ success: true, payload: {} });
21
- }),
22
- init: jest.fn(() => Promise.resolve(true)),
23
- } as unknown as LowLevelCoreApi;
24
- const sdk = HardwareTopLevelSdk();
25
- await sdk.init({}, lowLevelApi);
26
-
27
- await sdk.uploadPortfolio('connect-id', { packageBase64: 'AQID' });
28
- await sdk.deviceUploadWallpaper('connect-id', { jpegBase64: '/9j/' });
29
- await sdk.deviceUploadNft('connect-id', {
30
- imageJpegBase64: '/9j/',
31
- thumbnailJpegBase64: '/9j/',
32
- title: 'NFT',
33
- subtitle: '',
34
- });
35
-
36
- expect(restoredPayloads.map(payload => payload.method)).toEqual([
37
- 'uploadPortfolio',
38
- 'deviceUploadWallpaper',
39
- 'deviceUploadNft',
40
- ]);
41
- expect(restoredPayloads[0].packageBase64).toBe('AQID');
42
- expect(restoredPayloads[1].jpegBase64).toBe('/9j/');
43
- expect(restoredPayloads[2]).toMatchObject({
44
- imageJpegBase64: '/9j/',
45
- thumbnailJpegBase64: '/9j/',
46
- });
47
- });
48
- });
@@ -1,84 +0,0 @@
1
- import { EDeviceType } from '@onekeyfe/hd-shared';
2
-
3
- import TonSignMessage from '../src/api/ton/TonSignMessage';
4
-
5
- jest.mock('../src/data/config', () => ({
6
- getSDKVersion: jest.fn(() => '1.0.0'),
7
- DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
8
- }));
9
-
10
- const createMethod = (deviceType: EDeviceType) => {
11
- const method = new TonSignMessage({
12
- id: 1,
13
- payload: {
14
- method: 'tonSignMessage',
15
- },
16
- });
17
-
18
- Object.defineProperty(method, 'device', {
19
- value: {
20
- getCurrentDeviceType: () => deviceType,
21
- },
22
- });
23
-
24
- return method;
25
- };
26
-
27
- describe('TonSignMessage response compatibility', () => {
28
- test.each([EDeviceType.Pro2, EDeviceType.Neo])(
29
- 'uses the corrected signing_message field for %s',
30
- async deviceType => {
31
- const method = createMethod(deviceType);
32
-
33
- await expect(
34
- method.processTxRequest(
35
- {
36
- signature: 'signature',
37
- signing_message: 'serialized-message',
38
- },
39
- ''
40
- )
41
- ).resolves.toEqual({
42
- signature: 'signature',
43
- signing_message: 'serialized-message',
44
- skip_validate: false,
45
- });
46
- }
47
- );
48
-
49
- test('normalizes the legacy Protocol V1 field without removing it', async () => {
50
- const method = createMethod(EDeviceType.Classic1s);
51
-
52
- await expect(
53
- method.processTxRequest(
54
- {
55
- signature: 'signature',
56
- signning_message: 'message-hash',
57
- },
58
- ''
59
- )
60
- ).resolves.toEqual({
61
- signature: 'signature',
62
- signning_message: 'message-hash',
63
- signing_message: 'message-hash',
64
- skip_validate: true,
65
- });
66
- });
67
-
68
- test('skips validation when blind signing omits the signing message', async () => {
69
- const method = createMethod(EDeviceType.Pro2);
70
-
71
- await expect(
72
- method.processTxRequest(
73
- {
74
- signature: 'signature',
75
- },
76
- ''
77
- )
78
- ).resolves.toEqual({
79
- signature: 'signature',
80
- signing_message: undefined,
81
- skip_validate: true,
82
- });
83
- });
84
- });
@@ -1,16 +0,0 @@
1
- export declare function decodeCanonicalBase64({ value, parameterName, maxBytes, }: {
2
- value: unknown;
3
- parameterName: string;
4
- maxBytes: number;
5
- }): Uint8Array;
6
- export declare function decodeJpegBase64ToRgba({ jpegBase64, parameterName, expectedWidth, expectedHeight, }: {
7
- jpegBase64: unknown;
8
- parameterName: string;
9
- expectedWidth: number;
10
- expectedHeight: number;
11
- }): {
12
- width: number;
13
- height: number;
14
- data: Uint8Array;
15
- };
16
- //# sourceMappingURL=base64Data.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"base64Data.d.ts","sourceRoot":"","sources":["../../../src/api/helpers/base64Data.ts"],"names":[],"mappings":"AAUA,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,aAAa,EACb,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,UAAU,CAmBb;AAED,wBAAgB,sBAAsB,CAAC,EACrC,UAAU,EACV,aAAa,EACb,aAAa,EACb,cAAc,GACf,EAAE;IACD,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAmCtD"}