@ledgerhq/hw-app-eth 6.38.2 → 7.0.0-next.0

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 (60) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/lib/modules/EIP712/index.d.ts.map +1 -1
  3. package/lib/modules/EIP712/index.js +44 -8
  4. package/lib/modules/EIP712/index.js.map +1 -1
  5. package/lib/modules/EIP712/types.d.ts +4 -0
  6. package/lib/modules/EIP712/types.d.ts.map +1 -1
  7. package/lib/services/ledger/erc20.js +1 -1
  8. package/lib/services/ledger/erc20.js.map +1 -1
  9. package/lib-es/modules/EIP712/index.d.ts.map +1 -1
  10. package/lib-es/modules/EIP712/index.js +44 -8
  11. package/lib-es/modules/EIP712/index.js.map +1 -1
  12. package/lib-es/modules/EIP712/types.d.ts +4 -0
  13. package/lib-es/modules/EIP712/types.d.ts.map +1 -1
  14. package/lib-es/services/ledger/erc20.js +1 -1
  15. package/lib-es/services/ledger/erc20.js.map +1 -1
  16. package/package.json +5 -5
  17. package/src/modules/EIP712/index.ts +63 -8
  18. package/src/modules/EIP712/types.ts +4 -0
  19. package/src/services/ledger/erc20.ts +1 -1
  20. package/tests/EIP712/filtered-signMessage.unit.test.ts +226 -138
  21. package/tests/EIP712/noFilter-signMessage.unit.test.ts +20 -20
  22. package/tests/ERC20/ERC20-CAL-KO.unit.test.ts +14 -4
  23. package/tests/ERC20/ERC20-CAL-OK.unit.test.ts +14 -3
  24. package/tests/Eth.unit.test.ts +6 -2
  25. package/tests/fixtures/CAL.ts +301 -208
  26. package/tests/fixtures/apdus/{0-filtered.apdus → 0-filtered-v1.apdus} +5 -5
  27. package/tests/fixtures/apdus/1-filtered-empty-array-1-level-v2.apdus +72 -0
  28. package/tests/fixtures/apdus/1-filtered-empty-array-2-levels-v2.apdus +68 -0
  29. package/tests/fixtures/apdus/{1-filtered.apdus → 1-filtered-v1.apdus} +8 -8
  30. package/tests/fixtures/apdus/{10-filtered.apdus → 10-filtered-v1.apdus} +4 -4
  31. package/tests/fixtures/apdus/{11-filtered.apdus → 11-filtered-v1.apdus} +9 -9
  32. package/tests/fixtures/apdus/12-filtered-v1.apdus +38 -0
  33. package/tests/fixtures/apdus/13-filtered-v1.apdus +108 -0
  34. package/tests/fixtures/apdus/14-filtered-v1.apdus +28 -0
  35. package/tests/fixtures/apdus/15-filtered-v1.apdus +6 -6
  36. package/tests/fixtures/apdus/15-filtered-v2.apdus +7 -7
  37. package/tests/fixtures/apdus/16-filtered-v1.apdus +6 -6
  38. package/tests/fixtures/apdus/16-filtered-v2.apdus +7 -7
  39. package/tests/fixtures/apdus/17-filtered-v1.apdus +164 -0
  40. package/tests/fixtures/apdus/17-filtered-v2.apdus +168 -0
  41. package/tests/fixtures/apdus/18-filtered-v1.apdus +8 -8
  42. package/tests/fixtures/apdus/18-filtered-v2.apdus +10 -10
  43. package/tests/fixtures/apdus/{2-filtered.apdus → 2-filtered-v1.apdus} +7 -7
  44. package/tests/fixtures/apdus/3-filtered-v1.apdus +60 -0
  45. package/tests/fixtures/apdus/{4-filtered.apdus → 4-filtered-v1.apdus} +5 -5
  46. package/tests/fixtures/apdus/5-filtered-v1.apdus +176 -0
  47. package/tests/fixtures/apdus/{6-filtered.apdus → 6-filtered-v1.apdus} +9 -9
  48. package/tests/fixtures/apdus/{7-filtered.apdus → 7-filtered-v1.apdus} +7 -7
  49. package/tests/fixtures/apdus/{8-filtered.apdus → 8-filtered-v1.apdus} +8 -8
  50. package/tests/fixtures/apdus/{9-filtered.apdus → 9-filtered-v1.apdus} +8 -8
  51. package/tests/fixtures/apdus/ERC20-KO.apdus +1 -1
  52. package/tests/fixtures/apdus/ERC20-OK.apdus +3 -3
  53. package/tests/fixtures/apdus/version-1.12.0.apdus +2 -0
  54. package/tests/fixtures/messages/1-empty-array-1-level.json +79 -0
  55. package/tests/fixtures/messages/1-empty-array-2-levels.json +74 -0
  56. package/tests/fixtures/apdus/12-filtered.apdus +0 -38
  57. package/tests/fixtures/apdus/13-filtered.apdus +0 -108
  58. package/tests/fixtures/apdus/17-filtered.apdus +0 -0
  59. package/tests/fixtures/apdus/3-filtered.apdus +0 -60
  60. package/tests/fixtures/apdus/5-filtered.apdus +0 -176
@@ -26,6 +26,7 @@ import {
26
26
  makeTypeEntryStructBuffer,
27
27
  } from "./utils";
28
28
  import {
29
+ FilteringInfoDiscardField,
29
30
  FilteringInfoContractName,
30
31
  FilteringInfoShowField,
31
32
  StructImplemData,
@@ -40,6 +41,7 @@ type MakeRecursiveFieldStructImplemParams = {
40
41
  types: EIP712MessageTypes;
41
42
  filters: MessageFilters | undefined;
42
43
  shouldUseV1Filters: boolean;
44
+ shouldUseDiscardedFields: boolean;
43
45
  coinRefsTokensMap: Record<number, { token: string; coinRefMemorySlot?: number }>;
44
46
  };
45
47
 
@@ -61,6 +63,7 @@ const makeRecursiveFieldStructImplem = ({
61
63
  types,
62
64
  filters,
63
65
  shouldUseV1Filters,
66
+ shouldUseDiscardedFields,
64
67
  coinRefsTokensMap,
65
68
  }: MakeRecursiveFieldStructImplemParams): ((
66
69
  destructedType: ReturnType<typeof destructTypeFromString>,
@@ -88,8 +91,33 @@ const makeRecursiveFieldStructImplem = ({
88
91
  structType: "array",
89
92
  value: data.length,
90
93
  });
94
+
95
+ const entryPath = `${path}.[]`;
96
+ if (!data.length) {
97
+ // If the array is empty and a filter exists, we need to let the app know that the filter can be discarded
98
+ const entryFilters = filters?.fields.filter(f => f.path.startsWith(entryPath));
99
+ if (entryFilters && shouldUseDiscardedFields) {
100
+ for (const entryFilter of entryFilters) {
101
+ await sendFilteringInfo(transport, "discardField", loadConfig, {
102
+ path: entryFilter.path,
103
+ });
104
+ await sendFilteringInfo(transport, "showField", loadConfig, {
105
+ displayName: entryFilter.label,
106
+ sig: entryFilter.signature,
107
+ format: entryFilter.format,
108
+ coinRef: entryFilter.coin_ref,
109
+ chainId,
110
+ erc20SignaturesBlob,
111
+ shouldUseV1Filters,
112
+ coinRefsTokensMap,
113
+ isDiscarded: true,
114
+ });
115
+ }
116
+ }
117
+ }
118
+ // If the array is not empty, we need to send the struct implementation for each entry
91
119
  for (const entry of data) {
92
- await recursiveFieldStructImplem([typeDescription, restSizes], entry, `${path}.[]`);
120
+ await recursiveFieldStructImplem([typeDescription, restSizes], entry, entryPath);
93
121
  }
94
122
  } else if (isCustomType) {
95
123
  for (const [fieldName, fieldValue] of Object.entries(data as EIP712Message["message"])) {
@@ -116,6 +144,7 @@ const makeRecursiveFieldStructImplem = ({
116
144
  erc20SignaturesBlob,
117
145
  shouldUseV1Filters,
118
146
  coinRefsTokensMap,
147
+ isDiscarded: false,
119
148
  });
120
149
  }
121
150
 
@@ -282,15 +311,23 @@ async function sendFilteringInfo(
282
311
  ): Promise<Buffer>;
283
312
  async function sendFilteringInfo(
284
313
  transport: Transport,
285
- type: "activate" | "contractName" | "showField",
314
+ type: "discardField",
286
315
  loadConfig: LoadConfig,
287
- data?: FilteringInfoContractName | FilteringInfoShowField,
316
+ data: FilteringInfoDiscardField,
317
+ ): Promise<Buffer>;
318
+ async function sendFilteringInfo(
319
+ transport: Transport,
320
+ type: "activate" | "contractName" | "showField" | "discardField",
321
+ loadConfig: LoadConfig,
322
+ data?: FilteringInfoContractName | FilteringInfoShowField | FilteringInfoDiscardField,
288
323
  ): Promise<Buffer | void> {
289
324
  enum APDU_FIELDS {
290
325
  CLA = 0xe0,
291
326
  INS = 0x1e,
292
- P1 = 0x00,
327
+ P1_standard = 0x00,
328
+ P1_discarded = 0x01,
293
329
  P2_activate = 0x00,
330
+ P2_discarded = 0x01,
294
331
  P2_show_field = 0xff, // for v1 of filters
295
332
  P2_message_info = 0x0f,
296
333
  P2_datetime = 0xfc,
@@ -304,7 +341,7 @@ async function sendFilteringInfo(
304
341
  return transport.send(
305
342
  APDU_FIELDS.CLA,
306
343
  APDU_FIELDS.INS,
307
- APDU_FIELDS.P1,
344
+ APDU_FIELDS.P1_discarded,
308
345
  APDU_FIELDS.P2_activate,
309
346
  );
310
347
 
@@ -317,7 +354,7 @@ async function sendFilteringInfo(
317
354
  return transport.send(
318
355
  APDU_FIELDS.CLA,
319
356
  APDU_FIELDS.INS,
320
- APDU_FIELDS.P1,
357
+ APDU_FIELDS.P1_standard,
321
358
  APDU_FIELDS.P2_message_info,
322
359
  payload,
323
360
  );
@@ -333,6 +370,7 @@ async function sendFilteringInfo(
333
370
  coinRefsTokensMap,
334
371
  shouldUseV1Filters,
335
372
  erc20SignaturesBlob,
373
+ isDiscarded,
336
374
  } = data as FilteringInfoShowField;
337
375
  const { displayNameBuffer, sigBuffer } = getFilterDisplayNameAndSigBuffers(displayName, sig);
338
376
 
@@ -341,7 +379,7 @@ async function sendFilteringInfo(
341
379
  return transport.send(
342
380
  APDU_FIELDS.CLA,
343
381
  APDU_FIELDS.INS,
344
- APDU_FIELDS.P1,
382
+ APDU_FIELDS.P1_standard,
345
383
  APDU_FIELDS.P2_show_field,
346
384
  payload,
347
385
  );
@@ -408,11 +446,26 @@ async function sendFilteringInfo(
408
446
  return transport.send(
409
447
  APDU_FIELDS.CLA,
410
448
  APDU_FIELDS.INS,
411
- APDU_FIELDS.P1,
449
+ isDiscarded ? APDU_FIELDS.P1_discarded : APDU_FIELDS.P1_standard,
412
450
  P2FormatMap[format],
413
451
  payload,
414
452
  );
415
453
  }
454
+
455
+ case "discardField": {
456
+ const { path } = data as FilteringInfoDiscardField;
457
+ const pathBuffer = Buffer.from(path);
458
+ const pathLengthBuffer = Buffer.from(intAsHexBytes(pathBuffer.length, 1), "hex");
459
+
460
+ const payload = Buffer.concat([pathLengthBuffer, pathBuffer]);
461
+ return transport.send(
462
+ APDU_FIELDS.CLA,
463
+ APDU_FIELDS.INS,
464
+ APDU_FIELDS.P1_standard,
465
+ APDU_FIELDS.P2_discarded,
466
+ payload,
467
+ );
468
+ }
416
469
  }
417
470
  }
418
471
 
@@ -474,6 +527,7 @@ export const signEIP712Message = async (
474
527
 
475
528
  const { version } = await getAppAndVersion(transport);
476
529
  const shouldUseV1Filters = !semver.gte(version, "1.11.1-0", { includePrerelease: true });
530
+ const shouldUseDiscardedFields = semver.gte(version, "1.12.0-0", { includePrerelease: true });
477
531
  const filters = await getFiltersForMessage(typedMessage, shouldUseV1Filters, calServiceURL);
478
532
  const coinRefsTokensMap = getCoinRefTokensMap(filters, shouldUseV1Filters, typedMessage);
479
533
 
@@ -514,6 +568,7 @@ export const signEIP712Message = async (
514
568
  types,
515
569
  filters,
516
570
  shouldUseV1Filters,
571
+ shouldUseDiscardedFields,
517
572
  coinRefsTokensMap,
518
573
  });
519
574
 
@@ -47,6 +47,10 @@ export type FilteringInfoShowField = {
47
47
  deviceTokenIndex?: number;
48
48
  }
49
49
  >;
50
+ isDiscarded: boolean;
51
+ };
52
+ export type FilteringInfoDiscardField = {
53
+ path: string;
50
54
  };
51
55
 
52
56
  export type FilteringInfoContractName = {
@@ -1,6 +1,6 @@
1
1
  import axios from "axios";
2
2
  import { log } from "@ledgerhq/logs";
3
- import { signatures as signaturesByChainId } from "@ledgerhq/cryptoassets/data/evm/index";
3
+ import { signatures as signaturesByChainId } from "@ledgerhq/cryptoassets-evm-signatures/data/evm/index";
4
4
  import { getLoadConfig } from "./loadConfig";
5
5
  import { LoadConfig } from "../types";
6
6