@ledgerhq/live-common 34.52.0-nightly.20251031094135 → 34.52.0-nightly.20251101023821

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 (84) hide show
  1. package/lib/bridge/generic-alpaca/signOperation.d.ts.map +1 -1
  2. package/lib/bridge/generic-alpaca/signOperation.js +5 -3
  3. package/lib/bridge/generic-alpaca/signOperation.js.map +1 -1
  4. package/lib/bridge/generic-alpaca/types.d.ts +56 -2
  5. package/lib/bridge/generic-alpaca/types.d.ts.map +1 -1
  6. package/lib/bridge/generic-alpaca/utils.d.ts.map +1 -1
  7. package/lib/bridge/generic-alpaca/utils.js +97 -1
  8. package/lib/bridge/generic-alpaca/utils.js.map +1 -1
  9. package/lib/e2e/speculosAppVersion.d.ts.map +1 -1
  10. package/lib/e2e/speculosAppVersion.js +1 -0
  11. package/lib/e2e/speculosAppVersion.js.map +1 -1
  12. package/lib/featureFlags/defaultFeatures.d.ts.map +1 -1
  13. package/lib/featureFlags/defaultFeatures.js +1 -0
  14. package/lib/featureFlags/defaultFeatures.js.map +1 -1
  15. package/lib/market/hooks/useLargeMoverCurrencies.d.ts +1 -1
  16. package/lib/market/hooks/useMarketDataProvider.d.ts +2 -2
  17. package/lib/market/hooks/useMarketDataProvider.d.ts.map +1 -1
  18. package/lib/market/utils/currencyFormatter.d.ts +3 -3
  19. package/lib/market/utils/currencyFormatter.d.ts.map +1 -1
  20. package/lib/market/utils/currencyFormatter.js.map +1 -1
  21. package/lib/market/utils/queryKeys.d.ts +0 -1
  22. package/lib/market/utils/queryKeys.d.ts.map +1 -1
  23. package/lib/market/utils/queryKeys.js +0 -1
  24. package/lib/market/utils/queryKeys.js.map +1 -1
  25. package/lib/market/utils/types.d.ts +6 -6
  26. package/lib/market/utils/types.d.ts.map +1 -1
  27. package/lib/postOnboarding/hooks/usePostOnboardingHubState.d.ts.map +1 -1
  28. package/lib/postOnboarding/hooks/usePostOnboardingHubState.js +13 -4
  29. package/lib/postOnboarding/hooks/usePostOnboardingHubState.js.map +1 -1
  30. package/lib/postOnboarding/mock.d.ts +1 -0
  31. package/lib/postOnboarding/mock.d.ts.map +1 -1
  32. package/lib/postOnboarding/mock.js +3 -1
  33. package/lib/postOnboarding/mock.js.map +1 -1
  34. package/lib/postOnboarding/reducer.d.ts +16 -14
  35. package/lib/postOnboarding/reducer.d.ts.map +1 -1
  36. package/lib-es/bridge/generic-alpaca/signOperation.d.ts.map +1 -1
  37. package/lib-es/bridge/generic-alpaca/signOperation.js +5 -3
  38. package/lib-es/bridge/generic-alpaca/signOperation.js.map +1 -1
  39. package/lib-es/bridge/generic-alpaca/types.d.ts +56 -2
  40. package/lib-es/bridge/generic-alpaca/types.d.ts.map +1 -1
  41. package/lib-es/bridge/generic-alpaca/utils.d.ts.map +1 -1
  42. package/lib-es/bridge/generic-alpaca/utils.js +97 -1
  43. package/lib-es/bridge/generic-alpaca/utils.js.map +1 -1
  44. package/lib-es/e2e/speculosAppVersion.d.ts.map +1 -1
  45. package/lib-es/e2e/speculosAppVersion.js +1 -0
  46. package/lib-es/e2e/speculosAppVersion.js.map +1 -1
  47. package/lib-es/featureFlags/defaultFeatures.d.ts.map +1 -1
  48. package/lib-es/featureFlags/defaultFeatures.js +1 -0
  49. package/lib-es/featureFlags/defaultFeatures.js.map +1 -1
  50. package/lib-es/market/hooks/useLargeMoverCurrencies.d.ts +1 -1
  51. package/lib-es/market/hooks/useMarketDataProvider.d.ts +2 -2
  52. package/lib-es/market/hooks/useMarketDataProvider.d.ts.map +1 -1
  53. package/lib-es/market/utils/currencyFormatter.d.ts +3 -3
  54. package/lib-es/market/utils/currencyFormatter.d.ts.map +1 -1
  55. package/lib-es/market/utils/currencyFormatter.js.map +1 -1
  56. package/lib-es/market/utils/queryKeys.d.ts +0 -1
  57. package/lib-es/market/utils/queryKeys.d.ts.map +1 -1
  58. package/lib-es/market/utils/queryKeys.js +0 -1
  59. package/lib-es/market/utils/queryKeys.js.map +1 -1
  60. package/lib-es/market/utils/types.d.ts +6 -6
  61. package/lib-es/market/utils/types.d.ts.map +1 -1
  62. package/lib-es/postOnboarding/hooks/usePostOnboardingHubState.d.ts.map +1 -1
  63. package/lib-es/postOnboarding/hooks/usePostOnboardingHubState.js +13 -4
  64. package/lib-es/postOnboarding/hooks/usePostOnboardingHubState.js.map +1 -1
  65. package/lib-es/postOnboarding/mock.d.ts +1 -0
  66. package/lib-es/postOnboarding/mock.d.ts.map +1 -1
  67. package/lib-es/postOnboarding/mock.js +2 -0
  68. package/lib-es/postOnboarding/mock.js.map +1 -1
  69. package/lib-es/postOnboarding/reducer.d.ts +16 -14
  70. package/lib-es/postOnboarding/reducer.d.ts.map +1 -1
  71. package/package.json +74 -74
  72. package/src/bridge/generic-alpaca/signOperation.ts +5 -3
  73. package/src/bridge/generic-alpaca/types.ts +75 -2
  74. package/src/bridge/generic-alpaca/utils.test.ts +28 -2
  75. package/src/bridge/generic-alpaca/utils.ts +121 -2
  76. package/src/e2e/speculosAppVersion.ts +1 -0
  77. package/src/featureFlags/defaultFeatures.ts +1 -0
  78. package/src/market/hooks/useMarketDataProvider.ts +2 -2
  79. package/src/market/utils/currencyFormatter.ts +3 -3
  80. package/src/market/utils/queryKeys.ts +0 -1
  81. package/src/market/utils/types.ts +6 -6
  82. package/src/postOnboarding/hooks/usePostOnboardingHubState.test.ts +30 -3
  83. package/src/postOnboarding/hooks/usePostOnboardingHubState.ts +20 -6
  84. package/src/postOnboarding/mock.ts +2 -0
@@ -1,4 +1,9 @@
1
- import type { Operation, TransactionCommon } from "@ledgerhq/types-live";
1
+ import type {
2
+ Operation,
3
+ OperationRaw,
4
+ TransactionCommon,
5
+ TransactionCommonRaw,
6
+ } from "@ledgerhq/types-live";
2
7
  import BigNumber from "bignumber.js";
3
8
  import type { Unit } from "@ledgerhq/types-cryptoassets";
4
9
 
@@ -6,6 +11,34 @@ type NetworkInfo = {
6
11
  fees: BigNumber;
7
12
  };
8
13
 
14
+ type NetworkInfoRaw = {
15
+ fees: string;
16
+ };
17
+
18
+ type Strategy = "slow" | "medium" | "fast";
19
+
20
+ export type FeeData = {
21
+ maxFeePerGas: BigNumber | null;
22
+ maxPriorityFeePerGas: BigNumber | null;
23
+ gasPrice: BigNumber | null;
24
+ nextBaseFee: BigNumber | null;
25
+ };
26
+
27
+ export type FeeDataRaw = {
28
+ maxFeePerGas: string | null;
29
+ maxPriorityFeePerGas: string | null;
30
+ gasPrice: string | null;
31
+ nextBaseFee: string | null;
32
+ };
33
+
34
+ export type GasOptions = {
35
+ [key in Strategy]: FeeData;
36
+ };
37
+
38
+ export type GasOptionsRaw = {
39
+ [key in Strategy]: FeeDataRaw;
40
+ };
41
+
9
42
  export type GenericTransaction = TransactionCommon & {
10
43
  family: string;
11
44
  fees?: BigNumber | null;
@@ -14,6 +47,7 @@ export type GenericTransaction = TransactionCommon & {
14
47
  parameters: { fees?: BigNumber | null };
15
48
  };
16
49
  tag?: number | null | undefined;
50
+ nonce?: number | null | undefined;
17
51
  feeCustomUnit?: Unit | null | undefined;
18
52
  memoType?: string | null;
19
53
  memoValue?: string | null;
@@ -31,13 +65,52 @@ export type GenericTransaction = TransactionCommon & {
31
65
  assetReference?: string;
32
66
  assetOwner?: string;
33
67
  networkInfo?: NetworkInfo | null;
34
- chainId?: number;
68
+ chainId?: number | null;
35
69
  gasLimit?: BigNumber | null;
36
70
  gasPrice?: BigNumber | null;
37
71
  maxFeePerGas?: BigNumber | null;
38
72
  maxPriorityFeePerGas?: BigNumber | null;
73
+ gasOptions?: GasOptions;
74
+ };
75
+
76
+ export type GenericTransactionRaw = TransactionCommonRaw & {
77
+ family: string;
78
+ fees?: string | null;
79
+ storageLimit?: string | null;
80
+ customFees?: {
81
+ parameters: { fees?: string | null };
82
+ };
83
+ tag?: number | null | undefined;
84
+ nonce?: number | null | undefined;
85
+ feeCustomUnit?: Unit | null | undefined;
86
+ memoType?: string | null;
87
+ memoValue?: string | null;
88
+ data?: string;
89
+ mode?:
90
+ | "send"
91
+ | "changeTrust"
92
+ | "send-legacy"
93
+ | "send-eip1559"
94
+ | "delegate"
95
+ | "stake"
96
+ | "undelegate"
97
+ | "unstake";
98
+ type?: number | null;
99
+ assetReference?: string | null;
100
+ assetOwner?: string | null;
101
+ networkInfo?: NetworkInfoRaw | null;
102
+ chainId?: number | null;
103
+ gasLimit?: string | null;
104
+ gasPrice?: string | null;
105
+ maxFeePerGas?: string | null;
106
+ maxPriorityFeePerGas?: string | null;
107
+ gasOptions?: GasOptionsRaw;
39
108
  };
40
109
 
41
110
  export interface OperationCommon extends Operation {
42
111
  extra: Record<string, any>;
43
112
  }
113
+
114
+ export interface OperationCommonRaw extends OperationRaw {
115
+ extra: Record<string, any>;
116
+ }
@@ -22,6 +22,7 @@ describe("Alpaca utils", () => {
22
22
  parentType: "OPT_IN",
23
23
  subType: undefined,
24
24
  parentValue: new BigNumber(50),
25
+ parentRecipient: "recipient-address",
25
26
  },
26
27
  ],
27
28
  [
@@ -32,6 +33,7 @@ describe("Alpaca utils", () => {
32
33
  parentType: "DELEGATE",
33
34
  subType: undefined,
34
35
  parentValue: new BigNumber(50),
36
+ parentRecipient: "recipient-address",
35
37
  },
36
38
  ],
37
39
  [
@@ -42,6 +44,7 @@ describe("Alpaca utils", () => {
42
44
  parentType: "DELEGATE",
43
45
  subType: undefined,
44
46
  parentValue: new BigNumber(50),
47
+ parentRecipient: "recipient-address",
45
48
  },
46
49
  ],
47
50
  [
@@ -52,6 +55,7 @@ describe("Alpaca utils", () => {
52
55
  parentType: "UNDELEGATE",
53
56
  subType: undefined,
54
57
  parentValue: new BigNumber(50),
58
+ parentRecipient: "recipient-address",
55
59
  },
56
60
  ],
57
61
  [
@@ -62,6 +66,7 @@ describe("Alpaca utils", () => {
62
66
  parentType: "UNDELEGATE",
63
67
  subType: undefined,
64
68
  parentValue: new BigNumber(50),
69
+ parentRecipient: "recipient-address",
65
70
  },
66
71
  ],
67
72
  [
@@ -72,6 +77,7 @@ describe("Alpaca utils", () => {
72
77
  parentType: "OUT",
73
78
  subType: undefined,
74
79
  parentValue: new BigNumber(50),
80
+ parentRecipient: "recipient-address",
75
81
  },
76
82
  ],
77
83
  [
@@ -82,6 +88,7 @@ describe("Alpaca utils", () => {
82
88
  parentType: "FEES",
83
89
  subType: "OPT_IN",
84
90
  parentValue: new BigNumber(12),
91
+ parentRecipient: "contract-address",
85
92
  },
86
93
  ],
87
94
  [
@@ -92,6 +99,7 @@ describe("Alpaca utils", () => {
92
99
  parentType: "FEES",
93
100
  subType: "DELEGATE",
94
101
  parentValue: new BigNumber(12),
102
+ parentRecipient: "contract-address",
95
103
  },
96
104
  ],
97
105
  [
@@ -102,6 +110,7 @@ describe("Alpaca utils", () => {
102
110
  parentType: "FEES",
103
111
  subType: "DELEGATE",
104
112
  parentValue: new BigNumber(12),
113
+ parentRecipient: "contract-address",
105
114
  },
106
115
  ],
107
116
  [
@@ -112,6 +121,7 @@ describe("Alpaca utils", () => {
112
121
  parentType: "FEES",
113
122
  subType: "UNDELEGATE",
114
123
  parentValue: new BigNumber(12),
124
+ parentRecipient: "contract-address",
115
125
  },
116
126
  ],
117
127
  [
@@ -122,20 +132,26 @@ describe("Alpaca utils", () => {
122
132
  parentType: "FEES",
123
133
  subType: "UNDELEGATE",
124
134
  parentValue: new BigNumber(12),
135
+ parentRecipient: "contract-address",
125
136
  },
126
137
  ],
127
138
  [
128
139
  "token",
129
140
  "send",
130
141
  { subAccountId: "sub-account-id" },
131
- { parentType: "FEES", subType: "OUT", parentValue: new BigNumber(12) },
142
+ {
143
+ parentType: "FEES",
144
+ subType: "OUT",
145
+ parentValue: new BigNumber(12),
146
+ parentRecipient: "contract-address",
147
+ },
132
148
  ],
133
149
  ])("builds an optimistic %s operation with %s mode ", (_s, mode, params, expected) => {
134
150
  const operation = buildOptimisticOperation(
135
151
  {
136
152
  id: "parent-account-id",
137
153
  freshAddress: "account-address",
138
- subAccounts: [{ id: "sub-account-id" }],
154
+ subAccounts: [{ id: "sub-account-id", token: { contractAddress: "contract-address" } }],
139
155
  } as Account,
140
156
  {
141
157
  mode,
@@ -156,6 +172,11 @@ describe("Alpaca utils", () => {
156
172
  fee: new BigNumber(12),
157
173
  blockHash: null,
158
174
  blockHeight: null,
175
+ transactionRaw: {
176
+ amount: expected.subType ? "0" : expected.parentValue.toFixed(),
177
+ fees: "12",
178
+ recipient: expected.parentRecipient,
179
+ },
159
180
  ...(expected.subType
160
181
  ? {
161
182
  subOperations: [
@@ -168,6 +189,11 @@ describe("Alpaca utils", () => {
168
189
  value: new BigNumber(50),
169
190
  blockHash: null,
170
191
  blockHeight: null,
192
+ transactionRaw: {
193
+ amount: "50",
194
+ fees: "12",
195
+ recipient: "recipient-address",
196
+ },
171
197
  },
172
198
  ],
173
199
  }
@@ -9,7 +9,15 @@ import {
9
9
  } from "@ledgerhq/coin-framework/api/types";
10
10
  import { findCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
11
11
  import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
12
- import { GenericTransaction, OperationCommon } from "./types";
12
+ import {
13
+ FeeData,
14
+ FeeDataRaw,
15
+ GasOptions,
16
+ GasOptionsRaw,
17
+ GenericTransaction,
18
+ GenericTransactionRaw,
19
+ OperationCommon,
20
+ } from "./types";
13
21
 
14
22
  export function findCryptoCurrencyByNetwork(network: string): CryptoCurrency | undefined {
15
23
  const networksRemap = {
@@ -189,6 +197,7 @@ export function transactionToIntent(
189
197
  data: Buffer.isBuffer(transaction.data)
190
198
  ? { type: "buffer", value: transaction.data }
191
199
  : { type: "none" },
200
+ sequence: transaction.nonce ?? undefined,
192
201
  };
193
202
  if (transaction.assetReference && transaction.assetOwner) {
194
203
  const { subAccountId } = transaction;
@@ -216,6 +225,105 @@ export function transactionToIntent(
216
225
  return res;
217
226
  }
218
227
 
228
+ function toFeeDataRaw(data: FeeData): FeeDataRaw {
229
+ return {
230
+ gasPrice: data.gasPrice?.toFixed() ?? null,
231
+ maxFeePerGas: data.maxFeePerGas?.toFixed() ?? null,
232
+ maxPriorityFeePerGas: data.maxPriorityFeePerGas?.toFixed() ?? null,
233
+ nextBaseFee: data.nextBaseFee?.toFixed() ?? null,
234
+ };
235
+ }
236
+
237
+ function toGasOptionRaw(options: GasOptions): GasOptionsRaw {
238
+ return {
239
+ fast: toFeeDataRaw(options.fast),
240
+ medium: toFeeDataRaw(options.medium),
241
+ slow: toFeeDataRaw(options.slow),
242
+ };
243
+ }
244
+
245
+ function toGenericTransactionRaw(transaction: GenericTransaction): GenericTransactionRaw {
246
+ const raw: GenericTransactionRaw = {
247
+ amount: transaction.amount.toString(),
248
+ recipient: transaction.recipient,
249
+ family: transaction.family,
250
+ };
251
+
252
+ if ("useAllAmount" in transaction) {
253
+ raw.useAllAmount = transaction.useAllAmount;
254
+ }
255
+
256
+ const stringFieldsToPropagate = [
257
+ "memoType",
258
+ "memoValue",
259
+ "assetReference",
260
+ "assetOwner",
261
+ ] as const;
262
+ for (const field of stringFieldsToPropagate) {
263
+ if (field in transaction) {
264
+ raw[field] = transaction[field];
265
+ }
266
+ }
267
+
268
+ const numberFieldsToPropagate = ["tag", "nonce", "type", "chainId"] as const;
269
+ for (const field of numberFieldsToPropagate) {
270
+ if (field in transaction) {
271
+ raw[field] = transaction[field];
272
+ }
273
+ }
274
+
275
+ const bigNumberFieldsToPropagate = [
276
+ "fees",
277
+ "storageLimit",
278
+ "gasLimit",
279
+ "gasPrice",
280
+ "maxFeePerGas",
281
+ "maxPriorityFeePerGas",
282
+ ] as const;
283
+ for (const field of bigNumberFieldsToPropagate) {
284
+ if (field in transaction) {
285
+ raw[field] = transaction[field]?.toFixed();
286
+ }
287
+ }
288
+
289
+ if ("customFees" in transaction) {
290
+ raw.customFees =
291
+ transaction.customFees && "fees" in transaction.customFees.parameters
292
+ ? {
293
+ parameters: { fees: transaction.customFees.parameters.fees?.toFixed() },
294
+ }
295
+ : { parameters: {} };
296
+ }
297
+
298
+ if ("feesStrategy" in transaction) {
299
+ raw.feesStrategy = transaction.feesStrategy;
300
+ }
301
+
302
+ if ("mode" in transaction) {
303
+ raw.mode = transaction.mode;
304
+ }
305
+
306
+ if ("feeCustomUnit" in transaction) {
307
+ raw.feeCustomUnit = transaction.feeCustomUnit;
308
+ }
309
+
310
+ if ("data" in transaction) {
311
+ raw.data = transaction.data?.toString("hex");
312
+ }
313
+
314
+ if ("networkInfo" in transaction) {
315
+ raw.networkInfo = transaction.networkInfo && {
316
+ fees: transaction.networkInfo.fees.toFixed(),
317
+ };
318
+ }
319
+
320
+ if ("gasOptions" in transaction) {
321
+ raw.gasOptions = transaction.gasOptions && toGasOptionRaw(transaction.gasOptions);
322
+ }
323
+
324
+ return raw;
325
+ }
326
+
219
327
  export const buildOptimisticOperation = (
220
328
  account: Account,
221
329
  transaction: GenericTransaction,
@@ -242,6 +350,7 @@ export const buildOptimisticOperation = (
242
350
  const { subAccountId } = transaction;
243
351
  const { subAccounts } = account;
244
352
  const parentType = subAccountId ? "FEES" : type;
353
+ const tokenAccount = subAccountId ? subAccounts?.find(ta => ta.id === subAccountId) : null;
245
354
 
246
355
  const operation: Operation = {
247
356
  id: encodeOperationId(account.id, "", parentType),
@@ -256,6 +365,13 @@ export const buildOptimisticOperation = (
256
365
  transactionSequenceNumber: sequenceNumber ?? 0,
257
366
  accountId: account.id,
258
367
  date: new Date(),
368
+ transactionRaw: toGenericTransactionRaw({
369
+ ...transaction,
370
+ nonce: sequenceNumber,
371
+ ...(tokenAccount
372
+ ? { recipient: tokenAccount.token.contractAddress, amount: new BigNumber(0) }
373
+ : {}),
374
+ }),
259
375
  extra: {
260
376
  ledgerOpType: type,
261
377
  blockTime: new Date(),
@@ -263,7 +379,6 @@ export const buildOptimisticOperation = (
263
379
  },
264
380
  };
265
381
 
266
- const tokenAccount = subAccountId ? subAccounts?.find(ta => ta.id === subAccountId) : null;
267
382
  if (tokenAccount && subAccountId) {
268
383
  operation.subOperations = [
269
384
  {
@@ -278,6 +393,10 @@ export const buildOptimisticOperation = (
278
393
  recipients: [transaction.recipient],
279
394
  accountId: subAccountId,
280
395
  date: new Date(),
396
+ transactionRaw: toGenericTransactionRaw({
397
+ ...transaction,
398
+ nonce: sequenceNumber,
399
+ }),
281
400
  extra: {
282
401
  ledgerOpType: type,
283
402
  },
@@ -16,6 +16,7 @@ export function getSpeculosModel(): DeviceModelId {
16
16
  case CryptoWallet.STAX.name:
17
17
  return DeviceModelId.stax;
18
18
  case CryptoWallet.FLEX.name:
19
+ case DeviceModelId.europa:
19
20
  return DeviceModelId.europa;
20
21
  case CryptoWallet.LNSP.name:
21
22
  default:
@@ -568,6 +568,7 @@ export const DEFAULT_FEATURES: Features = {
568
568
  accounts: true,
569
569
  settings: true,
570
570
  onboarding: true,
571
+ postOnboarding: true,
571
572
  },
572
573
  },
573
574
  lldNanoSUpsellBanners: {
@@ -18,7 +18,7 @@ import { REFETCH_TIME_ONE_MINUTE, BASIC_REFETCH, ONE_DAY } from "../utils/timers
18
18
  import {
19
19
  MarketCurrencyRequestParams,
20
20
  MarketListRequestParams,
21
- CurrencyData,
21
+ MarketCurrencyData,
22
22
  HashMapBody,
23
23
  MarketItemResponse,
24
24
  MarketListRequestResult,
@@ -118,7 +118,7 @@ export function useMarketData(props: MarketListRequestParams): MarketListRequest
118
118
  function combineMarketData(
119
119
  results: UseQueryResult<
120
120
  {
121
- formattedData: CurrencyData[];
121
+ formattedData: MarketCurrencyData[];
122
122
  page: number;
123
123
  },
124
124
  Error
@@ -1,6 +1,6 @@
1
1
  import { CryptoCurrency, TokenCurrency } from "@ledgerhq/types-cryptoassets";
2
2
  import {
3
- CurrencyData,
3
+ MarketCurrencyData,
4
4
  KeysPriceChange,
5
5
  MarketItemPerformer,
6
6
  MarketItemResponse,
@@ -48,14 +48,14 @@ function sparklineAsSvgData(points: number[]): SparklineSvgData {
48
48
  export function currencyFormatter(
49
49
  data: MarketItemResponse[],
50
50
  cryptoCurrenciesList: (CryptoCurrency | TokenCurrency)[],
51
- ): CurrencyData[] {
51
+ ): MarketCurrencyData[] {
52
52
  return data.map((currency: MarketItemResponse) => format(currency, cryptoCurrenciesList));
53
53
  }
54
54
 
55
55
  export const format = (
56
56
  currency: MarketItemResponse,
57
57
  cryptoCurrenciesList?: (CryptoCurrency | TokenCurrency)[],
58
- ): CurrencyData => {
58
+ ): MarketCurrencyData => {
59
59
  const ledgerIdsSet = new Set(currency.ledgerIds.map(id => id.toLowerCase()));
60
60
 
61
61
  const internalCurrency = cryptoCurrenciesList?.find(({ id }) =>
@@ -1,5 +1,4 @@
1
1
  export const QUERY_KEY = {
2
- CurrencyData: "CurrencyData",
3
2
  CurrencyDataRaw: "CurrencyDataRaw",
4
3
  CurrencyChartData: "CurrencyChartData",
5
4
  SupportedCounterCurrencies: "SupportedCounterCurrencies",
@@ -33,7 +33,7 @@ export type MarketListRequestParams = {
33
33
  };
34
34
 
35
35
  export type MarketListRequestResult = {
36
- data: CurrencyData[];
36
+ data: MarketCurrencyData[];
37
37
  isPending: boolean;
38
38
  isLoading: boolean;
39
39
  isError: boolean;
@@ -45,7 +45,7 @@ export type HashMapBody = {
45
45
  refetch: (options?: RefetchOptions | undefined) => Promise<
46
46
  QueryObserverResult<
47
47
  {
48
- formattedData: CurrencyData[];
48
+ formattedData: MarketCurrencyData[];
49
49
  page: number;
50
50
  },
51
51
  Error
@@ -80,7 +80,7 @@ export enum KeysPriceChange {
80
80
  year = "1y",
81
81
  }
82
82
 
83
- export type CurrencyData = {
83
+ export type MarketCurrencyData = {
84
84
  id: string;
85
85
  ledgerIds: string[];
86
86
  name: string;
@@ -113,13 +113,13 @@ export type SingleCoinState = {
113
113
  loadingChart: boolean;
114
114
  error?: Error;
115
115
  supportedCounterCurrencies: string[];
116
- selectedCoinData?: CurrencyData;
116
+ selectedCoinData?: MarketCurrencyData;
117
117
  counterCurrency?: string;
118
118
  };
119
119
 
120
120
  export type State = SingleCoinState & {
121
121
  ready: boolean;
122
- marketData?: CurrencyData[];
122
+ marketData?: MarketCurrencyData[];
123
123
  requestParams: MarketListRequestParams;
124
124
  page: number;
125
125
  endOfList: boolean;
@@ -187,7 +187,7 @@ export type MarketItemPerformer = {
187
187
 
188
188
  export type MarketDataApi = {
189
189
  setSupportedCoinsList: () => Promise<SupportedCoins>;
190
- listPaginated: (params: MarketListRequestParams) => Promise<CurrencyData[]>;
190
+ listPaginated: (params: MarketListRequestParams) => Promise<MarketCurrencyData[]>;
191
191
  supportedCounterCurrencies: () => Promise<string[]>;
192
192
  currencyChartData: (
193
193
  params: MarketCurrencyChartDataRequestParams,
@@ -4,7 +4,11 @@
4
4
  import { useFeatureFlags } from "../../featureFlags";
5
5
  import { hubStateSelector } from "../reducer";
6
6
  import { usePostOnboardingContext } from "./usePostOnboardingContext";
7
- import { getPostOnboardingAction, mockedFeatureIdToTest } from "../mock";
7
+ import {
8
+ getPostOnboardingAction,
9
+ mockedFeatureIdToTest,
10
+ mockedFeatureParamIdToTest,
11
+ } from "../mock";
8
12
  import { renderHook } from "@testing-library/react";
9
13
  import { DeviceModelId } from "@ledgerhq/types-devices";
10
14
  import { PostOnboardingActionId } from "@ledgerhq/types-live";
@@ -19,10 +23,16 @@ jest.mock("../reducer");
19
23
 
20
24
  const mockedUseFeatureFlags = jest.mocked(useFeatureFlags);
21
25
 
22
- const mockedGetFeatureWithMockFeatureEnabled = enabled => ({
26
+ const mockedGetFeatureWithMockFeatureEnabled = (enabled, paramEnabled = true) => ({
23
27
  isFeature: () => true,
24
28
  getFeature: id => {
25
- if (id === mockedFeatureIdToTest) return { enabled };
29
+ if (id === mockedFeatureIdToTest)
30
+ return {
31
+ enabled,
32
+ params: {
33
+ [mockedFeatureParamIdToTest]: paramEnabled,
34
+ },
35
+ };
26
36
  return { enabled: true };
27
37
  },
28
38
  overrideFeature: () => {},
@@ -134,6 +144,23 @@ describe("usePostOnboardingHubState", () => {
134
144
  expect(lastActionCompleted).toBe(null);
135
145
  });
136
146
 
147
+ it("should not return actions that have a disabled feature param flag ", () => {
148
+ const state = stateAllCompleted;
149
+ mockedHubStateSelector.mockReturnValue(state);
150
+ mockedUseFeatureFlags.mockReturnValue(mockedGetFeatureWithMockFeatureEnabled(true, false));
151
+
152
+ const {
153
+ result: {
154
+ current: { actionsState, lastActionCompleted },
155
+ },
156
+ } = renderHook(() => usePostOnboardingHubState());
157
+
158
+ expect(actionsState.find(action => action.featureFlagId === mockedFeatureIdToTest)).toBe(
159
+ undefined,
160
+ );
161
+ expect(lastActionCompleted).toBe(null);
162
+ });
163
+
137
164
  it("should return actions that have a feature flag enabled", () => {
138
165
  const state = stateAllCompleted;
139
166
  mockedHubStateSelector.mockReturnValue(state);
@@ -1,10 +1,23 @@
1
1
  import { useSelector } from "react-redux";
2
2
  import { useMemo } from "react";
3
- import { PostOnboardingHubState } from "@ledgerhq/types-live";
4
- import { useFeatureFlags } from "../../featureFlags";
3
+ import { PostOnboardingAction, PostOnboardingHubState } from "@ledgerhq/types-live";
4
+ import { FeatureFlagsContextValue, useFeatureFlags } from "../../featureFlags";
5
5
  import { hubStateSelector } from "../reducer";
6
6
  import { usePostOnboardingContext } from "./usePostOnboardingContext";
7
7
 
8
+ const getIsFeatureEnabled = (
9
+ action: PostOnboardingAction | undefined,
10
+ getFeature: FeatureFlagsContextValue["getFeature"],
11
+ ) => {
12
+ if (!action) return false;
13
+ if (!action.featureFlagId) return true;
14
+
15
+ const flag = getFeature(action.featureFlagId);
16
+ if (!flag?.enabled) return false;
17
+
18
+ return !action.featureFlagParamId || !!flag.params?.[action.featureFlagParamId];
19
+ };
20
+
8
21
  /**
9
22
  * @returns an object representing the state that should be rendered on the post
10
23
  * onboarding hub screen.
@@ -26,9 +39,12 @@ export function usePostOnboardingHubState(): PostOnboardingHubState {
26
39
  actionsState: [],
27
40
  postOnboardingInProgress: hubState.postOnboardingInProgress,
28
41
  };
42
+
29
43
  const actionsState = hubState.actionsToComplete.flatMap(actionId => {
30
44
  const action = getPostOnboardingAction(actionId);
31
- if (!action || (action.featureFlagId && !getFeature(action.featureFlagId)?.enabled)) {
45
+ const isFeatureEnabled = getIsFeatureEnabled(action, getFeature);
46
+
47
+ if (!action || !isFeatureEnabled) {
32
48
  return [];
33
49
  }
34
50
  return [{ ...action, completed: !!hubState.actionsCompleted[actionId] }];
@@ -38,9 +54,7 @@ export function usePostOnboardingHubState(): PostOnboardingHubState {
38
54
  : null;
39
55
 
40
56
  const isLastActionCompletedEnabled =
41
- lastActionCompleted &&
42
- (!lastActionCompleted.featureFlagId ||
43
- getFeature(lastActionCompleted.featureFlagId)?.enabled);
57
+ lastActionCompleted && getIsFeatureEnabled(lastActionCompleted, getFeature);
44
58
 
45
59
  return {
46
60
  deviceModelId: hubState.deviceModelId,
@@ -5,6 +5,7 @@ import { FeatureId, PostOnboardingAction, PostOnboardingActionId } from "@ledger
5
5
  const MockIcon = () => null;
6
6
 
7
7
  export const mockedFeatureIdToTest: FeatureId = "mockFeature";
8
+ export const mockedFeatureParamIdToTest: string = "mockFeatureParam";
8
9
 
9
10
  export const claimTestMock: PostOnboardingAction = {
10
11
  id: PostOnboardingActionId.claimMock,
@@ -21,6 +22,7 @@ export const personalizeTestMock: PostOnboardingAction = {
21
22
  id: PostOnboardingActionId.personalizeMock,
22
23
  Icon: MockIcon,
23
24
  featureFlagId: mockedFeatureIdToTest,
25
+ featureFlagParamId: mockedFeatureParamIdToTest,
24
26
  title: `Personalize my device`,
25
27
  titleCompleted: `Personalize my device`,
26
28
  description: "By customizing the screen.",