@ledgerhq/live-common 34.43.0-nightly.3 → 34.44.0-nightly.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 (145) hide show
  1. package/lib/DataModel.test.js +6 -0
  2. package/lib/DataModel.test.js.map +1 -1
  3. package/lib/__fixtures__/solana-spl-epjfwdd5aufqssqem2qn1xzybapc8g4weggkzwytdt1v.json +53 -0
  4. package/lib/__tests__/accounts/groupPerDay.js +10 -0
  5. package/lib/__tests__/accounts/groupPerDay.js.map +1 -1
  6. package/lib/__tests__/test-helpers/setup.d.ts.map +1 -1
  7. package/lib/__tests__/test-helpers/setup.js +6 -0
  8. package/lib/__tests__/test-helpers/setup.js.map +1 -1
  9. package/lib/account/serialization.test.js +17 -1
  10. package/lib/account/serialization.test.js.map +1 -1
  11. package/lib/bridge/crypto-assets/index.d.ts.map +1 -1
  12. package/lib/bridge/crypto-assets/index.js +5 -5
  13. package/lib/bridge/crypto-assets/index.js.map +1 -1
  14. package/lib/bridge/generic-alpaca/getAccountShape.d.ts.map +1 -1
  15. package/lib/bridge/generic-alpaca/getAccountShape.js +5 -6
  16. package/lib/bridge/generic-alpaca/getAccountShape.js.map +1 -1
  17. package/lib/bridge/generic-alpaca/utils.d.ts +2 -1
  18. package/lib/bridge/generic-alpaca/utils.d.ts.map +1 -1
  19. package/lib/bridge/generic-alpaca/utils.js +5 -1
  20. package/lib/bridge/generic-alpaca/utils.js.map +1 -1
  21. package/lib/bridge/generic-alpaca/utils.test.d.ts +2 -0
  22. package/lib/bridge/generic-alpaca/utils.test.d.ts.map +1 -0
  23. package/lib/bridge/generic-alpaca/utils.test.js +20 -0
  24. package/lib/bridge/generic-alpaca/utils.test.js.map +1 -0
  25. package/lib/currencies/sortByMarketcap.test.js +5 -0
  26. package/lib/currencies/sortByMarketcap.test.js.map +1 -1
  27. package/lib/deviceSDK/hooks/__tests__/filterIgnoredFirmwareUpdates.test.d.ts +2 -0
  28. package/lib/deviceSDK/hooks/__tests__/filterIgnoredFirmwareUpdates.test.d.ts.map +1 -0
  29. package/lib/deviceSDK/hooks/__tests__/filterIgnoredFirmwareUpdates.test.js +114 -0
  30. package/lib/deviceSDK/hooks/__tests__/filterIgnoredFirmwareUpdates.test.js.map +1 -0
  31. package/lib/deviceSDK/hooks/filterIgnoredFirmwareUpdates.d.ts +11 -0
  32. package/lib/deviceSDK/hooks/filterIgnoredFirmwareUpdates.d.ts.map +1 -0
  33. package/lib/deviceSDK/hooks/filterIgnoredFirmwareUpdates.js +26 -0
  34. package/lib/deviceSDK/hooks/filterIgnoredFirmwareUpdates.js.map +1 -0
  35. package/lib/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.d.ts +12 -0
  36. package/lib/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.d.ts.map +1 -0
  37. package/lib/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.js +24 -0
  38. package/lib/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.js.map +1 -0
  39. package/lib/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.test.d.ts +2 -0
  40. package/lib/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.test.d.ts.map +1 -0
  41. package/lib/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.test.js +118 -0
  42. package/lib/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.test.js.map +1 -0
  43. package/lib/deviceSDK/hooks/useGetLatestAvailableFirmware.d.ts +3 -1
  44. package/lib/deviceSDK/hooks/useGetLatestAvailableFirmware.d.ts.map +1 -1
  45. package/lib/deviceSDK/hooks/useGetLatestAvailableFirmware.js +7 -3
  46. package/lib/deviceSDK/hooks/useGetLatestAvailableFirmware.js.map +1 -1
  47. package/lib/e2e/index.d.ts +2 -0
  48. package/lib/e2e/index.d.ts.map +1 -1
  49. package/lib/families/bitcoin/satstack.test.js +6 -0
  50. package/lib/families/bitcoin/satstack.test.js.map +1 -1
  51. package/lib/families/stellar/ui.d.ts +6 -0
  52. package/lib/families/stellar/ui.d.ts.map +1 -0
  53. package/lib/families/stellar/ui.js +18 -0
  54. package/lib/families/stellar/ui.js.map +1 -0
  55. package/lib/families/tron/data.mock.d.ts.map +1 -1
  56. package/lib/families/tron/data.mock.js +6 -0
  57. package/lib/families/tron/data.mock.js.map +1 -1
  58. package/lib/featureFlags/defaultFeatures.d.ts.map +1 -1
  59. package/lib/featureFlags/defaultFeatures.js +4 -0
  60. package/lib/featureFlags/defaultFeatures.js.map +1 -1
  61. package/lib/featureFlags/useFeature.d.ts +1 -1
  62. package/lib/featureFlags/useFeature.d.ts.map +1 -1
  63. package/lib-es/DataModel.test.js +6 -0
  64. package/lib-es/DataModel.test.js.map +1 -1
  65. package/lib-es/__fixtures__/solana-spl-epjfwdd5aufqssqem2qn1xzybapc8g4weggkzwytdt1v.json +53 -0
  66. package/lib-es/__tests__/accounts/groupPerDay.js +10 -0
  67. package/lib-es/__tests__/accounts/groupPerDay.js.map +1 -1
  68. package/lib-es/__tests__/test-helpers/setup.d.ts.map +1 -1
  69. package/lib-es/__tests__/test-helpers/setup.js +6 -0
  70. package/lib-es/__tests__/test-helpers/setup.js.map +1 -1
  71. package/lib-es/account/serialization.test.js +15 -2
  72. package/lib-es/account/serialization.test.js.map +1 -1
  73. package/lib-es/bridge/crypto-assets/index.d.ts.map +1 -1
  74. package/lib-es/bridge/crypto-assets/index.js +4 -4
  75. package/lib-es/bridge/crypto-assets/index.js.map +1 -1
  76. package/lib-es/bridge/generic-alpaca/getAccountShape.d.ts.map +1 -1
  77. package/lib-es/bridge/generic-alpaca/getAccountShape.js +6 -7
  78. package/lib-es/bridge/generic-alpaca/getAccountShape.js.map +1 -1
  79. package/lib-es/bridge/generic-alpaca/utils.d.ts +2 -1
  80. package/lib-es/bridge/generic-alpaca/utils.d.ts.map +1 -1
  81. package/lib-es/bridge/generic-alpaca/utils.js +3 -0
  82. package/lib-es/bridge/generic-alpaca/utils.js.map +1 -1
  83. package/lib-es/bridge/generic-alpaca/utils.test.d.ts +2 -0
  84. package/lib-es/bridge/generic-alpaca/utils.test.d.ts.map +1 -0
  85. package/lib-es/bridge/generic-alpaca/utils.test.js +18 -0
  86. package/lib-es/bridge/generic-alpaca/utils.test.js.map +1 -0
  87. package/lib-es/currencies/sortByMarketcap.test.js +5 -0
  88. package/lib-es/currencies/sortByMarketcap.test.js.map +1 -1
  89. package/lib-es/deviceSDK/hooks/__tests__/filterIgnoredFirmwareUpdates.test.d.ts +2 -0
  90. package/lib-es/deviceSDK/hooks/__tests__/filterIgnoredFirmwareUpdates.test.d.ts.map +1 -0
  91. package/lib-es/deviceSDK/hooks/__tests__/filterIgnoredFirmwareUpdates.test.js +112 -0
  92. package/lib-es/deviceSDK/hooks/__tests__/filterIgnoredFirmwareUpdates.test.js.map +1 -0
  93. package/lib-es/deviceSDK/hooks/filterIgnoredFirmwareUpdates.d.ts +11 -0
  94. package/lib-es/deviceSDK/hooks/filterIgnoredFirmwareUpdates.d.ts.map +1 -0
  95. package/lib-es/deviceSDK/hooks/filterIgnoredFirmwareUpdates.js +22 -0
  96. package/lib-es/deviceSDK/hooks/filterIgnoredFirmwareUpdates.js.map +1 -0
  97. package/lib-es/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.d.ts +12 -0
  98. package/lib-es/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.d.ts.map +1 -0
  99. package/lib-es/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.js +20 -0
  100. package/lib-es/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.js.map +1 -0
  101. package/lib-es/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.test.d.ts +2 -0
  102. package/lib-es/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.test.d.ts.map +1 -0
  103. package/lib-es/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.test.js +116 -0
  104. package/lib-es/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.test.js.map +1 -0
  105. package/lib-es/deviceSDK/hooks/useGetLatestAvailableFirmware.d.ts +3 -1
  106. package/lib-es/deviceSDK/hooks/useGetLatestAvailableFirmware.d.ts.map +1 -1
  107. package/lib-es/deviceSDK/hooks/useGetLatestAvailableFirmware.js +7 -3
  108. package/lib-es/deviceSDK/hooks/useGetLatestAvailableFirmware.js.map +1 -1
  109. package/lib-es/e2e/index.d.ts +2 -0
  110. package/lib-es/e2e/index.d.ts.map +1 -1
  111. package/lib-es/families/bitcoin/satstack.test.js +6 -0
  112. package/lib-es/families/bitcoin/satstack.test.js.map +1 -1
  113. package/lib-es/families/stellar/ui.d.ts +6 -0
  114. package/lib-es/families/stellar/ui.d.ts.map +1 -0
  115. package/lib-es/families/stellar/ui.js +14 -0
  116. package/lib-es/families/stellar/ui.js.map +1 -0
  117. package/lib-es/families/tron/data.mock.d.ts.map +1 -1
  118. package/lib-es/families/tron/data.mock.js +6 -0
  119. package/lib-es/families/tron/data.mock.js.map +1 -1
  120. package/lib-es/featureFlags/defaultFeatures.d.ts.map +1 -1
  121. package/lib-es/featureFlags/defaultFeatures.js +4 -0
  122. package/lib-es/featureFlags/defaultFeatures.js.map +1 -1
  123. package/lib-es/featureFlags/useFeature.d.ts +1 -1
  124. package/lib-es/featureFlags/useFeature.d.ts.map +1 -1
  125. package/package.json +42 -42
  126. package/src/DataModel.test.ts +8 -0
  127. package/src/__fixtures__/solana-spl-epjfwdd5aufqssqem2qn1xzybapc8g4weggkzwytdt1v.json +53 -0
  128. package/src/__tests__/accounts/groupPerDay.ts +13 -0
  129. package/src/__tests__/test-helpers/setup.ts +8 -0
  130. package/src/account/serialization.test.ts +20 -2
  131. package/src/bridge/crypto-assets/index.ts +6 -6
  132. package/src/bridge/generic-alpaca/getAccountShape.ts +6 -7
  133. package/src/bridge/generic-alpaca/utils.test.ts +19 -0
  134. package/src/bridge/generic-alpaca/utils.ts +9 -1
  135. package/src/currencies/sortByMarketcap.test.ts +7 -0
  136. package/src/deviceSDK/hooks/__tests__/filterIgnoredFirmwareUpdates.test.ts +132 -0
  137. package/src/deviceSDK/hooks/filterIgnoredFirmwareUpdates.ts +27 -0
  138. package/src/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.test.ts +192 -0
  139. package/src/deviceSDK/hooks/getIgnoredOSUpdatesForDeviceModelAndPlatform.ts +28 -0
  140. package/src/deviceSDK/hooks/useGetLatestAvailableFirmware.ts +9 -2
  141. package/src/families/bitcoin/satstack.test.ts +7 -0
  142. package/src/families/stellar/__snapshots__/bridge.integration.test.ts.snap +1930 -94
  143. package/src/families/stellar/ui.ts +15 -0
  144. package/src/families/tron/data.mock.ts +8 -0
  145. package/src/featureFlags/defaultFeatures.ts +4 -0
@@ -2,12 +2,6 @@ import { LiveConfig } from "@ledgerhq/live-config/LiveConfig";
2
2
  import { CryptoAssetsStore } from "@ledgerhq/coin-framework/crypto-assets/type";
3
3
  import * as legacy from "@ledgerhq/cryptoassets/tokens";
4
4
 
5
- let cryptoAssetsStore: CryptoAssetsStore | undefined = undefined;
6
-
7
- export function setCryptoAssetsStore(store: CryptoAssetsStore) {
8
- cryptoAssetsStore = store;
9
- }
10
-
11
5
  const legacyStore: CryptoAssetsStore = {
12
6
  findTokenByAddress: legacy.findTokenByAddress,
13
7
  getTokenById: legacy.getTokenById,
@@ -16,6 +10,12 @@ const legacyStore: CryptoAssetsStore = {
16
10
  findTokenByTicker: legacy.findTokenByTicker,
17
11
  };
18
12
 
13
+ let cryptoAssetsStore: CryptoAssetsStore | undefined = undefined;
14
+
15
+ export function setCryptoAssetsStore(store: CryptoAssetsStore) {
16
+ cryptoAssetsStore = store;
17
+ }
18
+
19
19
  export function getCryptoAssetsStore(): CryptoAssetsStore {
20
20
  const featureEnabled = LiveConfig.getValueByKey("feature_cal_lazy_loading");
21
21
  if (!featureEnabled) {
@@ -2,7 +2,7 @@ import { encodeAccountId } from "@ledgerhq/coin-framework/account/index";
2
2
  import { GetAccountShape, mergeOps } from "@ledgerhq/coin-framework/bridge/jsHelpers";
3
3
  import BigNumber from "bignumber.js";
4
4
  import { getAlpacaApi } from "./alpaca";
5
- import { adaptCoreOperationToLiveOperation } from "./utils";
5
+ import { adaptCoreOperationToLiveOperation, extractBalance } from "./utils";
6
6
 
7
7
  export function genericGetAccountShape(network: string, kind: "local" | "remote"): GetAccountShape {
8
8
  return async info => {
@@ -18,15 +18,14 @@ export function genericGetAccountShape(network: string, kind: "local" | "remote"
18
18
 
19
19
  const blockInfo = await getAlpacaApi(network, kind).lastBlock();
20
20
 
21
- const balanceRes = await getAlpacaApi(network, kind).getBalance(address);
22
- // FIXME: fix type Balance -> check "native" balance
23
- // is balance[0] always the native ?
24
- const balance = BigNumber(balanceRes[0].value.toString());
21
+ const balances = await getAlpacaApi(network, kind).getBalance(address);
22
+ const nativeBalance = extractBalance(balances, "native");
23
+ const balance = BigNumber(nativeBalance.value.toString());
25
24
 
26
25
  let spendableBalance: BigNumber;
27
- if (balanceRes[0]?.locked) {
26
+ if (nativeBalance.locked) {
28
27
  spendableBalance = BigNumber.max(
29
- balance.minus(BigNumber(balanceRes[0].locked.toString())),
28
+ balance.minus(BigNumber(nativeBalance.locked.toString())),
30
29
  BigNumber(0),
31
30
  );
32
31
  } else {
@@ -0,0 +1,19 @@
1
+ import { extractBalance } from "./utils";
2
+
3
+ describe("Alpaca utils", () => {
4
+ describe("extractBalance", () => {
5
+ it("extracts an existing balance", () => {
6
+ expect(extractBalance([{ value: 4n, asset: { type: "type1" } }], "type1")).toEqual({
7
+ value: 4n,
8
+ asset: { type: "type1" },
9
+ });
10
+ });
11
+
12
+ it("generates an empty balance for a missing type", () => {
13
+ expect(extractBalance([{ value: 4n, asset: { type: "type1" } }], "type2")).toEqual({
14
+ value: 0n,
15
+ asset: { type: "type2" },
16
+ });
17
+ });
18
+ });
19
+ });
@@ -1,9 +1,17 @@
1
1
  import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
2
2
  import { Account, Operation, OperationType, TransactionCommon } from "@ledgerhq/types-live";
3
- import { Operation as CoreOperation, TransactionIntent } from "@ledgerhq/coin-framework/api/types";
3
+ import {
4
+ Balance,
5
+ Operation as CoreOperation,
6
+ TransactionIntent,
7
+ } from "@ledgerhq/coin-framework/api/types";
4
8
  import BigNumber from "bignumber.js";
5
9
  import { fromBigNumberToBigInt } from "@ledgerhq/coin-framework/utils";
6
10
 
11
+ export function extractBalance(balances: Balance[], type: string): Balance {
12
+ return balances.find(balance => balance.asset.type === type) ?? { asset: { type }, value: 0n };
13
+ }
14
+
7
15
  export function adaptCoreOperationToLiveOperation(accountId: string, op: CoreOperation): Operation {
8
16
  return {
9
17
  id: encodeOperationId(accountId, op.tx.hash, op.type),
@@ -2,6 +2,13 @@ import { sortCurrenciesByIds } from "./sortByMarketcap";
2
2
  import { findCurrencyByTicker, listCryptoCurrencies, listTokens } from ".";
3
3
  import { getBTCValues } from "@ledgerhq/live-countervalues/mock";
4
4
  import { CURRENCIES_LIST, IDS } from "./mock";
5
+ import { setCryptoAssetsStore as setCryptoAssetsStoreForCoinFramework } from "@ledgerhq/coin-framework/crypto-assets/index";
6
+ import { CryptoAssetsStore } from "@ledgerhq/coin-framework/crypto-assets/type";
7
+
8
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
9
+ setCryptoAssetsStoreForCoinFramework({
10
+ findTokenByTicker: (_: string) => undefined,
11
+ } as CryptoAssetsStore);
5
12
 
6
13
  test("sortCurrenciesByIds snapshot", () => {
7
14
  const list = [...listCryptoCurrencies(), ...listTokens()];
@@ -0,0 +1,132 @@
1
+ import { filterIgnoredFirmwareUpdates } from "../filterIgnoredFirmwareUpdates";
2
+ import type { GetLatestAvailableFirmwareActionState } from "../../actions/getLatestAvailableFirmware";
3
+
4
+ describe("filterIgnoredFirmwareUpdates", () => {
5
+ const createMockState = (
6
+ status: GetLatestAvailableFirmwareActionState["status"],
7
+ firmwareName?: string,
8
+ ): GetLatestAvailableFirmwareActionState => ({
9
+ status,
10
+ firmwareUpdateContext: firmwareName
11
+ ? {
12
+ final: { name: firmwareName } as any,
13
+ osu: {} as any,
14
+ shouldFlashMCU: false,
15
+ }
16
+ : null,
17
+ deviceInfo: null,
18
+ error: null,
19
+ lockedDevice: false,
20
+ });
21
+
22
+ describe("when status is not 'available-firmware'", () => {
23
+ it("should return the original state unchanged", () => {
24
+ const state = createMockState("no-available-firmware");
25
+ const result = filterIgnoredFirmwareUpdates(state, ["2.1.0"]);
26
+ expect(result).toEqual(state);
27
+ });
28
+
29
+ it("should return the original state when status is 'error'", () => {
30
+ const state = createMockState("error");
31
+ const result = filterIgnoredFirmwareUpdates(state, ["2.1.0"]);
32
+ expect(result).toEqual(state);
33
+ });
34
+ });
35
+
36
+ describe("when ignoredOSUpdates is undefined", () => {
37
+ it("should return the original state unchanged", () => {
38
+ const state = createMockState("available-firmware", "2.1.0");
39
+ const result = filterIgnoredFirmwareUpdates(state, undefined);
40
+ expect(result).toEqual(state);
41
+ });
42
+ });
43
+
44
+ describe("when ignoredOSUpdates is empty array", () => {
45
+ it("should return the original state unchanged", () => {
46
+ const state = createMockState("available-firmware", "2.1.0");
47
+ const result = filterIgnoredFirmwareUpdates(state, []);
48
+ expect(result).toEqual(state);
49
+ });
50
+ });
51
+
52
+ describe("when firmwareUpdateContext is null", () => {
53
+ it("should return the original state unchanged", () => {
54
+ const state = createMockState("available-firmware");
55
+ const result = filterIgnoredFirmwareUpdates(state, ["2.1.0"]);
56
+ expect(result).toEqual(state);
57
+ });
58
+ });
59
+
60
+ describe("when firmwareUpdateContext.final.name is undefined", () => {
61
+ it("should return the original state unchanged", () => {
62
+ const state = createMockState("available-firmware", "2.1.0");
63
+ state.firmwareUpdateContext!.final.name = undefined as any;
64
+ const result = filterIgnoredFirmwareUpdates(state, ["2.1.0"]);
65
+ expect(result).toEqual(state);
66
+ });
67
+ });
68
+
69
+ describe("when firmware version is not in ignored list", () => {
70
+ it("should return the original state unchanged", () => {
71
+ const state = createMockState("available-firmware", "2.1.0");
72
+ const result = filterIgnoredFirmwareUpdates(state, ["2.0.0", "2.2.0"]);
73
+ expect(result).toEqual(state);
74
+ });
75
+ });
76
+
77
+ describe("when firmware version is in ignored list", () => {
78
+ it("should filter out the firmware and change status to 'no-available-firmware'", () => {
79
+ const state = createMockState("available-firmware", "2.1.0");
80
+ const result = filterIgnoredFirmwareUpdates(state, ["2.1.0", "2.2.0"]);
81
+
82
+ expect(result).toEqual({
83
+ ...state,
84
+ firmwareUpdateContext: null,
85
+ status: "no-available-firmware",
86
+ });
87
+ });
88
+
89
+ it("should filter out the firmware when it's the only ignored version", () => {
90
+ const state = createMockState("available-firmware", "2.1.0");
91
+ const result = filterIgnoredFirmwareUpdates(state, ["2.1.0"]);
92
+
93
+ expect(result).toEqual({
94
+ ...state,
95
+ firmwareUpdateContext: null,
96
+ status: "no-available-firmware",
97
+ });
98
+ });
99
+ });
100
+
101
+ describe("edge cases", () => {
102
+ it("should handle case-sensitive firmware version matching", () => {
103
+ const state = createMockState("available-firmware", "2.1.0");
104
+ const result = filterIgnoredFirmwareUpdates(state, ["2.1.0", "2.1.0-rc1"]);
105
+ expect(result).toEqual({
106
+ ...state,
107
+ firmwareUpdateContext: null,
108
+ status: "no-available-firmware",
109
+ });
110
+ });
111
+
112
+ it("should not filter when firmware version is similar but not exact match", () => {
113
+ const state = createMockState("available-firmware", "2.1.0");
114
+ const result = filterIgnoredFirmwareUpdates(state, ["2.1.0-rc1", "2.1.1"]);
115
+ expect(result).toEqual(state);
116
+ });
117
+
118
+ it("should preserve all other state properties when filtering", () => {
119
+ const state = createMockState("available-firmware", "2.1.0");
120
+ state.lockedDevice = true;
121
+ state.error = { type: "SharedError", message: "Test", name: "TestError", retrying: false };
122
+
123
+ const result = filterIgnoredFirmwareUpdates(state, ["2.1.0"]);
124
+
125
+ expect(result).toEqual({
126
+ ...state,
127
+ firmwareUpdateContext: null,
128
+ status: "no-available-firmware",
129
+ });
130
+ });
131
+ });
132
+ });
@@ -0,0 +1,27 @@
1
+ import { IgnoredOSUpdates } from "@ledgerhq/types-live";
2
+ import { GetLatestAvailableFirmwareActionState } from "../actions/getLatestAvailableFirmware";
3
+
4
+ /**
5
+ * Filters out ignored firmware updates from the action state
6
+ *
7
+ * @param newValue The current action state
8
+ * @param ignoredOSUpdates Array of firmware versions to ignore
9
+ * @returns The filtered action state
10
+ */
11
+ export const filterIgnoredFirmwareUpdates = (
12
+ newValue: GetLatestAvailableFirmwareActionState,
13
+ ignoredOSUpdates?: IgnoredOSUpdates,
14
+ ): GetLatestAvailableFirmwareActionState => {
15
+ // There is an available firmware update
16
+ if (newValue.status === "available-firmware" && newValue.firmwareUpdateContext?.final.name) {
17
+ // Filter out the ignored firmware versions
18
+ if (ignoredOSUpdates && ignoredOSUpdates.includes(newValue.firmwareUpdateContext?.final.name)) {
19
+ return {
20
+ ...newValue,
21
+ firmwareUpdateContext: null,
22
+ status: "no-available-firmware",
23
+ };
24
+ }
25
+ }
26
+ return newValue;
27
+ };
@@ -0,0 +1,192 @@
1
+ import { type Feature_OnboardingIgnoredOSUpdates } from "@ledgerhq/types-live";
2
+ import { DeviceModelId } from "@ledgerhq/types-devices";
3
+ import { getIgnoredOSUpdatesForDeviceModelAndPlatform } from "./getIgnoredOSUpdatesForDeviceModelAndPlatform";
4
+
5
+ describe("getIgnoredOSUpdatesForDeviceModelAndPlatform", () => {
6
+ const mockConfig: Feature_OnboardingIgnoredOSUpdates["params"] = {
7
+ ios: {
8
+ [DeviceModelId.nanoS]: ["1.6.0", "1.6.1"],
9
+ [DeviceModelId.nanoX]: ["2.0.0"],
10
+ [DeviceModelId.nanoSP]: ["1.0.0", "1.0.1", "1.0.2"],
11
+ },
12
+ android: {
13
+ [DeviceModelId.nanoS]: ["1.5.0"],
14
+ [DeviceModelId.nanoX]: ["2.1.0", "2.1.1"],
15
+ },
16
+ macos: {
17
+ [DeviceModelId.nanoX]: ["2.2.0"],
18
+ },
19
+ windows: {
20
+ [DeviceModelId.stax]: ["3.0.0"],
21
+ },
22
+ linux: {
23
+ [DeviceModelId.europa]: ["4.0.0", "4.0.1"],
24
+ },
25
+ };
26
+
27
+ describe("when config is undefined", () => {
28
+ it("should return empty array", () => {
29
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
30
+ undefined,
31
+ DeviceModelId.nanoS,
32
+ "ios",
33
+ );
34
+ expect(result).toEqual([]);
35
+ });
36
+ });
37
+
38
+ describe("when config is null", () => {
39
+ it("should return empty array", () => {
40
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
41
+ null as unknown as Feature_OnboardingIgnoredOSUpdates["params"],
42
+ DeviceModelId.nanoS,
43
+ "ios",
44
+ );
45
+ expect(result).toEqual([]);
46
+ });
47
+ });
48
+
49
+ describe("when platform does not exist in config", () => {
50
+ it("should return empty array", () => {
51
+ const configWithoutPlatform: Feature_OnboardingIgnoredOSUpdates["params"] = {
52
+ ios: { [DeviceModelId.nanoS]: ["1.6.0"] },
53
+ };
54
+
55
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
56
+ configWithoutPlatform,
57
+ DeviceModelId.nanoS,
58
+ "android",
59
+ );
60
+ expect(result).toEqual([]);
61
+ });
62
+ });
63
+
64
+ describe("when device model does not exist for platform", () => {
65
+ it("should return empty array", () => {
66
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
67
+ mockConfig,
68
+ DeviceModelId.stax,
69
+ "ios",
70
+ );
71
+ expect(result).toEqual([]);
72
+ });
73
+ });
74
+
75
+ describe("when device model exists but has no ignored updates", () => {
76
+ it("should return empty array", () => {
77
+ const configWithEmptyArray: Feature_OnboardingIgnoredOSUpdates["params"] = {
78
+ ios: {
79
+ [DeviceModelId.nanoS]: [],
80
+ },
81
+ };
82
+
83
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
84
+ configWithEmptyArray,
85
+ DeviceModelId.nanoS,
86
+ "ios",
87
+ );
88
+ expect(result).toEqual([]);
89
+ });
90
+ });
91
+
92
+ describe("when device model exists and has ignored updates", () => {
93
+ it("should return the ignored updates for iOS", () => {
94
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
95
+ mockConfig,
96
+ DeviceModelId.nanoS,
97
+ "ios",
98
+ );
99
+ expect(result).toEqual(["1.6.0", "1.6.1"]);
100
+ });
101
+
102
+ it("should return the ignored updates for Android", () => {
103
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
104
+ mockConfig,
105
+ DeviceModelId.nanoX,
106
+ "android",
107
+ );
108
+ expect(result).toEqual(["2.1.0", "2.1.1"]);
109
+ });
110
+
111
+ it("should return multiple ignored updates", () => {
112
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
113
+ mockConfig,
114
+ DeviceModelId.nanoSP,
115
+ "ios",
116
+ );
117
+ expect(result).toEqual(["1.0.0", "1.0.1", "1.0.2"]);
118
+ });
119
+
120
+ it("should return ignored updates for macOS", () => {
121
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
122
+ mockConfig,
123
+ DeviceModelId.nanoX,
124
+ "macos",
125
+ );
126
+ expect(result).toEqual(["2.2.0"]);
127
+ });
128
+
129
+ it("should return ignored updates for Windows", () => {
130
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
131
+ mockConfig,
132
+ DeviceModelId.stax,
133
+ "windows",
134
+ );
135
+ expect(result).toEqual(["3.0.0"]);
136
+ });
137
+
138
+ it("should return ignored updates for Linux", () => {
139
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
140
+ mockConfig,
141
+ DeviceModelId.europa,
142
+ "linux",
143
+ );
144
+ expect(result).toEqual(["4.0.0", "4.0.1"]);
145
+ });
146
+ });
147
+
148
+ describe("edge cases", () => {
149
+ it("should handle empty config object", () => {
150
+ const emptyConfig: Feature_OnboardingIgnoredOSUpdates["params"] = {};
151
+
152
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
153
+ emptyConfig,
154
+ DeviceModelId.nanoS,
155
+ "ios",
156
+ );
157
+ expect(result).toEqual([]);
158
+ });
159
+
160
+ it("should handle config with empty platform objects", () => {
161
+ const configWithEmptyPlatforms: Feature_OnboardingIgnoredOSUpdates["params"] = {
162
+ ios: {},
163
+ android: {},
164
+ macos: {},
165
+ windows: {},
166
+ linux: {},
167
+ };
168
+
169
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
170
+ configWithEmptyPlatforms,
171
+ DeviceModelId.nanoS,
172
+ "ios",
173
+ );
174
+ expect(result).toEqual([]);
175
+ });
176
+
177
+ it("should handle undefined device model entries", () => {
178
+ const configWithUndefined: Feature_OnboardingIgnoredOSUpdates["params"] = {
179
+ ios: {
180
+ [DeviceModelId.nanoS]: undefined as unknown as string[],
181
+ },
182
+ };
183
+
184
+ const result = getIgnoredOSUpdatesForDeviceModelAndPlatform(
185
+ configWithUndefined,
186
+ DeviceModelId.nanoS,
187
+ "ios",
188
+ );
189
+ expect(result).toEqual([]);
190
+ });
191
+ });
192
+ });
@@ -0,0 +1,28 @@
1
+ import type {
2
+ Feature_OnboardingIgnoredOSUpdates,
3
+ Platform,
4
+ IgnoredOSUpdates,
5
+ } from "@ledgerhq/types-live";
6
+ import type { DeviceModelId } from "@ledgerhq/types-devices";
7
+
8
+ /**
9
+ * Extracts the ignored OS updates for a specific device model and platform from the configuration
10
+ *
11
+ * @param ignoredOSUpdatesConfig The configuration object containing ignored OS updates per platform and device model
12
+ * @param deviceModelId The device model identifier
13
+ * @param platform The platform to get updates for
14
+ * @returns Array of firmware versions that should be ignored for the given device model and platform
15
+ */
16
+ export const getIgnoredOSUpdatesForDeviceModelAndPlatform = (
17
+ ignoredOSUpdatesConfig: Feature_OnboardingIgnoredOSUpdates["params"] | undefined,
18
+ deviceModelId: DeviceModelId,
19
+ platform: Platform,
20
+ ): IgnoredOSUpdates => {
21
+ // No ignored OS updates configuration
22
+ if (!ignoredOSUpdatesConfig) return [];
23
+ // No configuration for the specified platform
24
+ const configForPlatform = ignoredOSUpdatesConfig[platform];
25
+ if (!configForPlatform) return [];
26
+ // Return the ignored OS updates for the specified device model, empty array if not configured for that model
27
+ return configForPlatform[deviceModelId] ?? [];
28
+ };
@@ -5,11 +5,14 @@ import {
5
5
  getLatestAvailableFirmwareAction as defaultGetLatestAvailableFirmwareAction,
6
6
  initialState,
7
7
  } from "../actions/getLatestAvailableFirmware";
8
+ import { filterIgnoredFirmwareUpdates } from "./filterIgnoredFirmwareUpdates";
9
+ import type { IgnoredOSUpdates } from "@ledgerhq/types-live";
8
10
 
9
11
  export type UseGetLatestAvailableFirmwareArgs = {
10
12
  getLatestAvailableFirmwareAction?: typeof defaultGetLatestAvailableFirmwareAction;
11
13
  deviceId: string;
12
14
  isHookEnabled?: boolean;
15
+ ignoredOSUpdates?: IgnoredOSUpdates;
13
16
  };
14
17
 
15
18
  /**
@@ -30,6 +33,7 @@ export const useGetLatestAvailableFirmware = ({
30
33
  getLatestAvailableFirmwareAction = defaultGetLatestAvailableFirmwareAction,
31
34
  deviceId,
32
35
  isHookEnabled = true,
36
+ ignoredOSUpdates,
33
37
  }: UseGetLatestAvailableFirmwareArgs): {
34
38
  state: GetLatestAvailableFirmwareActionState;
35
39
  } => {
@@ -47,7 +51,10 @@ export const useGetLatestAvailableFirmware = ({
47
51
  const subscription = getLatestAvailableFirmwareAction({
48
52
  deviceId,
49
53
  }).subscribe({
50
- next: setState,
54
+ next: newValue => {
55
+ const filteredValue = filterIgnoredFirmwareUpdates(newValue, ignoredOSUpdates);
56
+ setState(filteredValue);
57
+ },
51
58
  error: (error: unknown) => {
52
59
  // Error from an action should be handled like an event and should not reach here
53
60
  log("useGetLatestAvailableFirmware", "Unknown error", error);
@@ -57,7 +64,7 @@ export const useGetLatestAvailableFirmware = ({
57
64
  return () => {
58
65
  subscription.unsubscribe();
59
66
  };
60
- }, [deviceId, getLatestAvailableFirmwareAction, isHookEnabled]);
67
+ }, [deviceId, getLatestAvailableFirmwareAction, ignoredOSUpdates, isHookEnabled]);
61
68
 
62
69
  return { state };
63
70
  };
@@ -18,6 +18,8 @@ import { inferDescriptorFromAccount, AccountDescriptor } from "@ledgerhq/coin-bi
18
18
  import { setEnv } from "@ledgerhq/live-env";
19
19
  import { fromAccountRaw } from "../../account";
20
20
  import { setSupportedCurrencies } from "../../currencies";
21
+ import { setCryptoAssetsStore as setCryptoAssetsStoreForCoinFramework } from "@ledgerhq/coin-framework/crypto-assets/index";
22
+ import { CryptoAssetsStore } from "@ledgerhq/coin-framework/crypto-assets/type";
21
23
 
22
24
  setSupportedCurrencies(["bitcoin"]);
23
25
  jest.setTimeout(10000);
@@ -230,6 +232,11 @@ describe("stringifySatStackConfig", () => {
230
232
  });
231
233
  });
232
234
  describe("editSatStackConfig", () => {
235
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
236
+ setCryptoAssetsStoreForCoinFramework({
237
+ findTokenById: (_: string) => undefined,
238
+ findTokenByAddressInCurrency: (_: string, __: string) => undefined,
239
+ } as CryptoAssetsStore);
233
240
  const config = {
234
241
  node: { ...mockConfig, tls: false },
235
242
  extra: {