@ledgerhq/live-common 34.53.0-nightly.20251112023812 → 34.53.0-nightly.20251113102200

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 (96) hide show
  1. package/lib/__tests__/test-helpers/environment.js +1 -0
  2. package/lib/__tests__/test-helpers/environment.js.map +1 -1
  3. package/lib/bridge/generic-alpaca/buildSubAccounts.js +4 -4
  4. package/lib/bridge/generic-alpaca/buildSubAccounts.js.map +1 -1
  5. package/lib/bridge/useBridgeTransaction.d.ts +4 -0
  6. package/lib/bridge/useBridgeTransaction.d.ts.map +1 -1
  7. package/lib/bridge/useBridgeTransaction.js +48 -5
  8. package/lib/bridge/useBridgeTransaction.js.map +1 -1
  9. package/lib/device/use-cases/getDeviceHasBattery.d.ts +2 -0
  10. package/lib/device/use-cases/getDeviceHasBattery.d.ts.map +1 -0
  11. package/lib/device/use-cases/getDeviceHasBattery.js +6 -0
  12. package/lib/device/use-cases/getDeviceHasBattery.js.map +1 -0
  13. package/lib/deviceSDK/hooks/useBatteryStatuses.d.ts +2 -1
  14. package/lib/deviceSDK/hooks/useBatteryStatuses.d.ts.map +1 -1
  15. package/lib/deviceSDK/hooks/useBatteryStatuses.js +4 -2
  16. package/lib/deviceSDK/hooks/useBatteryStatuses.js.map +1 -1
  17. package/lib/deviceSDK/tasks/getDeviceInfo.d.ts.map +1 -1
  18. package/lib/deviceSDK/tasks/getDeviceInfo.js +2 -0
  19. package/lib/deviceSDK/tasks/getDeviceInfo.js.map +1 -1
  20. package/lib/deviceSDK/tasks/getLatestFirmware.d.ts.map +1 -1
  21. package/lib/deviceSDK/tasks/getLatestFirmware.js +2 -2
  22. package/lib/deviceSDK/tasks/getLatestFirmware.js.map +1 -1
  23. package/lib/deviceSDK/tasks/updateFirmware.d.ts.map +1 -1
  24. package/lib/deviceSDK/tasks/updateFirmware.js +1 -0
  25. package/lib/deviceSDK/tasks/updateFirmware.js.map +1 -1
  26. package/lib/e2e/index.d.ts +169 -1
  27. package/lib/e2e/index.d.ts.map +1 -1
  28. package/lib/e2e/index.js +2 -2
  29. package/lib/e2e/index.js.map +1 -1
  30. package/lib/families/evm/config.d.ts.map +1 -1
  31. package/lib/families/evm/config.js +16 -0
  32. package/lib/families/evm/config.js.map +1 -1
  33. package/lib/featureFlags/defaultFeatures.d.ts +1 -0
  34. package/lib/featureFlags/defaultFeatures.d.ts.map +1 -1
  35. package/lib/featureFlags/defaultFeatures.js +1 -0
  36. package/lib/featureFlags/defaultFeatures.js.map +1 -1
  37. package/lib/modularDrawer/hooks/useCurrenciesUnderFeatureFlag.d.ts.map +1 -1
  38. package/lib/modularDrawer/hooks/useCurrenciesUnderFeatureFlag.js +3 -0
  39. package/lib/modularDrawer/hooks/useCurrenciesUnderFeatureFlag.js.map +1 -1
  40. package/lib-es/__tests__/test-helpers/environment.js +1 -0
  41. package/lib-es/__tests__/test-helpers/environment.js.map +1 -1
  42. package/lib-es/bridge/generic-alpaca/buildSubAccounts.js +4 -4
  43. package/lib-es/bridge/generic-alpaca/buildSubAccounts.js.map +1 -1
  44. package/lib-es/bridge/useBridgeTransaction.d.ts +4 -0
  45. package/lib-es/bridge/useBridgeTransaction.d.ts.map +1 -1
  46. package/lib-es/bridge/useBridgeTransaction.js +46 -4
  47. package/lib-es/bridge/useBridgeTransaction.js.map +1 -1
  48. package/lib-es/device/use-cases/getDeviceHasBattery.d.ts +2 -0
  49. package/lib-es/device/use-cases/getDeviceHasBattery.d.ts.map +1 -0
  50. package/lib-es/device/use-cases/getDeviceHasBattery.js +2 -0
  51. package/lib-es/device/use-cases/getDeviceHasBattery.js.map +1 -0
  52. package/lib-es/deviceSDK/hooks/useBatteryStatuses.d.ts +2 -1
  53. package/lib-es/deviceSDK/hooks/useBatteryStatuses.d.ts.map +1 -1
  54. package/lib-es/deviceSDK/hooks/useBatteryStatuses.js +4 -2
  55. package/lib-es/deviceSDK/hooks/useBatteryStatuses.js.map +1 -1
  56. package/lib-es/deviceSDK/tasks/getDeviceInfo.d.ts.map +1 -1
  57. package/lib-es/deviceSDK/tasks/getDeviceInfo.js +2 -0
  58. package/lib-es/deviceSDK/tasks/getDeviceInfo.js.map +1 -1
  59. package/lib-es/deviceSDK/tasks/getLatestFirmware.d.ts.map +1 -1
  60. package/lib-es/deviceSDK/tasks/getLatestFirmware.js +2 -2
  61. package/lib-es/deviceSDK/tasks/getLatestFirmware.js.map +1 -1
  62. package/lib-es/deviceSDK/tasks/updateFirmware.d.ts.map +1 -1
  63. package/lib-es/deviceSDK/tasks/updateFirmware.js +2 -1
  64. package/lib-es/deviceSDK/tasks/updateFirmware.js.map +1 -1
  65. package/lib-es/e2e/index.d.ts +169 -1
  66. package/lib-es/e2e/index.d.ts.map +1 -1
  67. package/lib-es/e2e/index.js +2 -2
  68. package/lib-es/e2e/index.js.map +1 -1
  69. package/lib-es/families/evm/config.d.ts.map +1 -1
  70. package/lib-es/families/evm/config.js +16 -0
  71. package/lib-es/families/evm/config.js.map +1 -1
  72. package/lib-es/featureFlags/defaultFeatures.d.ts +1 -0
  73. package/lib-es/featureFlags/defaultFeatures.d.ts.map +1 -1
  74. package/lib-es/featureFlags/defaultFeatures.js +1 -0
  75. package/lib-es/featureFlags/defaultFeatures.js.map +1 -1
  76. package/lib-es/modularDrawer/hooks/useCurrenciesUnderFeatureFlag.d.ts.map +1 -1
  77. package/lib-es/modularDrawer/hooks/useCurrenciesUnderFeatureFlag.js +3 -0
  78. package/lib-es/modularDrawer/hooks/useCurrenciesUnderFeatureFlag.js.map +1 -1
  79. package/package.json +50 -50
  80. package/src/__tests__/migration/account-migration.ts +1 -0
  81. package/src/__tests__/test-helpers/environment.ts +1 -0
  82. package/src/bridge/generic-alpaca/buildSubAccounts.test.ts +66 -0
  83. package/src/bridge/generic-alpaca/buildSubAccounts.ts +5 -5
  84. package/src/bridge/useBridgeTransaction.test.ts +64 -0
  85. package/src/bridge/useBridgeTransaction.ts +70 -4
  86. package/src/device/use-cases/getDeviceHasBattery.ts +1 -0
  87. package/src/deviceSDK/hooks/useBatteryStatuses.test.ts +1 -1
  88. package/src/deviceSDK/hooks/useBatteryStatuses.ts +4 -1
  89. package/src/deviceSDK/tasks/getDeviceInfo.ts +2 -0
  90. package/src/deviceSDK/tasks/getLatestFirmware.ts +3 -3
  91. package/src/deviceSDK/tasks/updateFirmware.ts +2 -0
  92. package/src/e2e/index.ts +2 -1
  93. package/src/exchange/swap/hooks/useFromState.test.ts +19 -0
  94. package/src/families/evm/config.ts +16 -0
  95. package/src/featureFlags/defaultFeatures.ts +1 -0
  96. package/src/modularDrawer/hooks/useCurrenciesUnderFeatureFlag.ts +3 -0
@@ -6,6 +6,8 @@ import { getMainAccount } from "../account";
6
6
  import { delay } from "../promise";
7
7
  import type { Account, AccountBridge, AccountLike } from "@ledgerhq/types-live";
8
8
  import type { Transaction, TransactionStatus } from "../generated/types";
9
+ import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
10
+ import { LiveConfig } from "@ledgerhq/live-config/LiveConfig";
9
11
 
10
12
  export type State<T extends Transaction = Transaction> = {
11
13
  account: AccountLike | null | undefined;
@@ -15,6 +17,8 @@ export type State<T extends Transaction = Transaction> = {
15
17
  statusOnTransaction: T | null | undefined;
16
18
  errorAccount: Error | null | undefined;
17
19
  errorStatus: Error | null | undefined;
20
+ syncing: boolean;
21
+ synced: boolean;
18
22
  };
19
23
 
20
24
  export type Result<T extends Transaction = Transaction> = {
@@ -55,6 +59,12 @@ type Actions<T extends Transaction = Transaction> =
55
59
  | {
56
60
  type: "setTransaction";
57
61
  transaction: T;
62
+ }
63
+ | {
64
+ type: "onStartSync";
65
+ }
66
+ | {
67
+ type: "onSync";
58
68
  };
59
69
 
60
70
  type Reducer<T extends Transaction = Transaction> = (
@@ -76,6 +86,18 @@ const initial: State<Transaction> = {
76
86
  statusOnTransaction: null,
77
87
  errorAccount: null,
78
88
  errorStatus: null,
89
+ syncing: false,
90
+ synced: false,
91
+ };
92
+
93
+ export const shouldSyncBeforeTx = (currency: CryptoCurrency): boolean => {
94
+ const currencyConfig = LiveConfig.getValueByKey(`config_currency_${currency.id}`);
95
+ const sharedConfig = LiveConfig.getValueByKey("config_currency");
96
+ if (currencyConfig && "syncBeforeTx" in currencyConfig) {
97
+ return currencyConfig.syncBeforeTx === true;
98
+ } else {
99
+ return sharedConfig && "syncBeforeTx" in sharedConfig && sharedConfig.syncBeforeTx === true;
100
+ }
79
101
  };
80
102
 
81
103
  const makeInit =
@@ -142,6 +164,8 @@ const reducer = <T extends Transaction = Transaction>(
142
164
  account,
143
165
  parentAccount,
144
166
  transaction: t,
167
+ syncing: false,
168
+ synced: false,
145
169
  } as State<T>;
146
170
  } catch (e: any) {
147
171
  return {
@@ -149,6 +173,8 @@ const reducer = <T extends Transaction = Transaction>(
149
173
  account,
150
174
  parentAccount,
151
175
  errorAccount: e,
176
+ syncing: false,
177
+ synced: false,
152
178
  } as State<T>;
153
179
  }
154
180
  }
@@ -177,6 +203,12 @@ const reducer = <T extends Transaction = Transaction>(
177
203
  if (action.error === state.errorStatus) return state;
178
204
  return { ...state, errorStatus: action.error };
179
205
 
206
+ case "onStartSync":
207
+ return { ...state, syncing: true, synced: false };
208
+
209
+ case "onSync":
210
+ return { ...state, syncing: false, synced: true };
211
+
180
212
  default:
181
213
  return state;
182
214
  }
@@ -190,7 +222,17 @@ const useBridgeTransaction = <T extends Transaction = Transaction>(
190
222
  optionalInit?: (() => Partial<State<T>>) | null | undefined,
191
223
  ): Result<T> => {
192
224
  const [
193
- { account, parentAccount, transaction, status, statusOnTransaction, errorAccount, errorStatus },
225
+ {
226
+ account,
227
+ parentAccount,
228
+ transaction,
229
+ status,
230
+ statusOnTransaction,
231
+ syncing,
232
+ synced,
233
+ errorAccount,
234
+ errorStatus,
235
+ },
194
236
  dispatch,
195
237
  ] = useReducer(reducer as Reducer<T>, undefined, makeInit<T>(optionalInit));
196
238
  const setAccount = useCallback(
@@ -223,15 +265,39 @@ const useBridgeTransaction = <T extends Transaction = Transaction>(
223
265
  const mainAccount = account ? getMainAccount(account, parentAccount) : null;
224
266
  const errorDelay = useRef(INITIAL_ERROR_RETRY_DELAY);
225
267
  const statusIsPending = useRef(false); // Stores if status already being processed
268
+ const shouldSync = mainAccount && shouldSyncBeforeTx(mainAccount.currency);
269
+
270
+ useEffect(() => {
271
+ if (mainAccount === null || synced || syncing) return;
272
+
273
+ if (!shouldSync) return; // skip sync if not required by currency config
274
+
275
+ dispatch({ type: "onStartSync" });
276
+ const bridge = getAccountBridge(mainAccount, null);
277
+ const sub = bridge.sync(mainAccount, { paginationConfig: {} }).subscribe({
278
+ error: (_: Error) => {
279
+ // we do not block the user in case of error for now but it should be the case
280
+ dispatch({ type: "onSync" });
281
+ },
282
+ complete: () => {
283
+ dispatch({ type: "onSync" });
284
+ },
285
+ });
286
+
287
+ return () => {
288
+ sub.unsubscribe();
289
+ };
290
+ }, [mainAccount, synced, syncing, shouldSync]);
226
291
 
227
292
  const bridgePending = transaction !== statusOnTransaction;
293
+
228
294
  // when transaction changes, prepare the transaction
229
295
  useEffect(() => {
230
296
  let ignore = false;
231
297
  let errorTimeout: NodeJS.Timeout | null;
232
298
  // If bridge is not pending, transaction change is due to
233
299
  // the last onStatus dispatch (prepareTransaction changed original transaction) and must be ignored
234
- if (!bridgePending) return;
300
+ if (!bridgePending && !synced) return;
235
301
 
236
302
  if (mainAccount && transaction) {
237
303
  // We don't debounce first status refresh, but any subsequent to avoid multiple calls
@@ -301,7 +367,7 @@ const useBridgeTransaction = <T extends Transaction = Transaction>(
301
367
  errorTimeout = null;
302
368
  }
303
369
  };
304
- }, [transaction, mainAccount, bridgePending, dispatch]);
370
+ }, [transaction, mainAccount, bridgePending, dispatch, synced]);
305
371
 
306
372
  const bridgeError = errorAccount || errorStatus;
307
373
 
@@ -320,7 +386,7 @@ const useBridgeTransaction = <T extends Transaction = Transaction>(
320
386
  parentAccount,
321
387
  setAccount,
322
388
  bridgeError,
323
- bridgePending,
389
+ bridgePending: bridgePending && (shouldSync ? !synced : true),
324
390
  };
325
391
  };
326
392
 
@@ -0,0 +1 @@
1
+ export { getDeviceHasBattery } from "@ledgerhq/device-core";
@@ -6,7 +6,7 @@ import { useBatteryStatuses } from "./useBatteryStatuses";
6
6
 
7
7
  describe("useBatteryStatuses", () => {
8
8
  it("should return an initial cancelRequest method that is callable", async () => {
9
- const { result } = renderHook(() => useBatteryStatuses({ statuses: [] }));
9
+ const { result } = renderHook(() => useBatteryStatuses({ statuses: [], enabled: true }));
10
10
  expect(() => result.current.cancelRequest()).not.toThrow();
11
11
  });
12
12
  });
@@ -12,6 +12,7 @@ import { useEnv } from "../../env.react";
12
12
  export type UseBatteryStatusesArgs = {
13
13
  deviceId?: string;
14
14
  statuses: BatteryStatusTypes[];
15
+ enabled: boolean;
15
16
  };
16
17
 
17
18
  /**
@@ -29,6 +30,7 @@ export type UseBatteryStatusesArgs = {
29
30
  export const useBatteryStatuses = ({
30
31
  deviceId,
31
32
  statuses,
33
+ enabled,
32
34
  }: UseBatteryStatusesArgs): {
33
35
  batteryStatusesState: GetBatteryStatusesActionState;
34
36
  requestCompleted: boolean;
@@ -50,6 +52,7 @@ export const useBatteryStatuses = ({
50
52
  const lowBatteryPercentage = useEnv("LOW_BATTERY_PERCENTAGE");
51
53
 
52
54
  useEffect(() => {
55
+ if (!enabled) return;
53
56
  if (nonce > 0 && deviceId) {
54
57
  const sub = getBatteryStatusesAction({
55
58
  deviceId,
@@ -87,7 +90,7 @@ export const useBatteryStatuses = ({
87
90
  sub.unsubscribe();
88
91
  };
89
92
  }
90
- }, [deviceId, lowBatteryPercentage, statuses, nonce]);
93
+ }, [deviceId, lowBatteryPercentage, statuses, nonce, enabled]);
91
94
 
92
95
  const triggerRequest = useCallback(() => {
93
96
  setRequestCompleted(false);
@@ -11,6 +11,7 @@ import { map, switchMap } from "rxjs/operators";
11
11
  import { SharedTaskEvent, retryOnErrorsCommandWrapper, sharedLogicTaskWrapper } from "./core";
12
12
  import { quitApp } from "../commands/quitApp";
13
13
  import { withTransport } from "../transports/core";
14
+ import { SendApduEmptyResponseError } from "@ledgerhq/device-management-kit";
14
15
 
15
16
  const ManagerAllowedFlag = 0x08;
16
17
  const PinValidatedFlag = 0x80;
@@ -42,6 +43,7 @@ export function internalGetDeviceInfoTask({
42
43
  return retryOnErrorsCommandWrapper({
43
44
  command: getVersion,
44
45
  allowedErrors: [{ maxRetries: 3, errorClass: DisconnectedDevice }],
46
+ allowedDmkErrors: [new SendApduEmptyResponseError()],
45
47
  })(transportRef, {});
46
48
  }),
47
49
  map(value => {
@@ -2,11 +2,11 @@ import type { DeviceId, DeviceInfo, FirmwareUpdateContext } from "@ledgerhq/type
2
2
 
3
3
  import { quitApp } from "../commands/quitApp";
4
4
 
5
- import { withDevice } from "../../hw/deviceAccess";
6
5
  import { from, Observable, of } from "rxjs";
7
6
  import { switchMap, catchError } from "rxjs/operators";
8
7
  import { SharedTaskEvent, sharedLogicTaskWrapper } from "./core";
9
8
  import { getLatestFirmwareForDeviceUseCase } from "../../device/use-cases/getLatestFirmwareForDeviceUseCase";
9
+ import { withTransport } from "../transports/core";
10
10
 
11
11
  export type GetLatestFirmwareTaskArgs = {
12
12
  deviceId: DeviceId;
@@ -30,8 +30,8 @@ function internalGetLatestFirmwareTask({
30
30
  deviceInfo,
31
31
  }: GetLatestFirmwareTaskArgs): Observable<GetLatestFirmwareTaskEvent> {
32
32
  return new Observable(subscriber => {
33
- return withDevice(deviceId)(transport =>
34
- quitApp(transport).pipe(
33
+ return withTransport(deviceId)(({ transportRef }) =>
34
+ quitApp(transportRef.current).pipe(
35
35
  switchMap(() => {
36
36
  return from(getLatestFirmwareForDeviceUseCase(deviceInfo));
37
37
  }),
@@ -36,6 +36,7 @@ import { parseDeviceInfo } from "./getDeviceInfo";
36
36
  import {
37
37
  DeviceDisconnectedBeforeSendingApdu,
38
38
  DeviceDisconnectedWhileSendingError,
39
+ SendApduEmptyResponseError,
39
40
  } from "@ledgerhq/device-management-kit";
40
41
 
41
42
  export type UpdateFirmwareTaskArgs = {
@@ -78,6 +79,7 @@ const waitForGetVersion = retryOnErrorsCommandWrapper({
78
79
  allowedDmkErrors: [
79
80
  new DeviceDisconnectedWhileSendingError(),
80
81
  new DeviceDisconnectedBeforeSendingApdu(),
82
+ new SendApduEmptyResponseError(),
81
83
  ],
82
84
  });
83
85
 
package/src/e2e/index.ts CHANGED
@@ -4,11 +4,12 @@ import { getFeature, DEFAULT_FEATURES } from "../featureFlags";
4
4
 
5
5
  export const getAllFeatureFlags = (
6
6
  appLanguage?: string,
7
+ localOverrides?: { [key in FeatureId]?: Feature | undefined },
7
8
  ): Partial<{ [key in FeatureId]: Feature }> => {
8
9
  const res: Partial<{ [key in FeatureId]: Feature }> = {};
9
10
  Object.keys(DEFAULT_FEATURES).forEach(k => {
10
11
  const key = k as keyof typeof DEFAULT_FEATURES;
11
- const value = getFeature({ key, appLanguage });
12
+ const value = getFeature({ key, appLanguage, localOverrides });
12
13
  if (value !== null) res[key] = value;
13
14
  });
14
15
  return res;
@@ -11,9 +11,28 @@ import useBridgeTransaction from "../../../bridge/useBridgeTransaction";
11
11
  import { genTokenAccount } from "@ledgerhq/coin-framework/mocks/account";
12
12
  import { genAccount } from "../../../mock/account";
13
13
  import { useFromState } from "./useFromState";
14
+ import { LiveConfig } from "@ledgerhq/live-config/LiveConfig";
14
15
 
15
16
  const BTC = getCryptoCurrencyById("bitcoin");
16
17
  const ETH = getCryptoCurrencyById("ethereum");
18
+ LiveConfig.setConfig({
19
+ config_currency_bitcoin: {
20
+ type: "object",
21
+ default: {
22
+ status: {
23
+ type: "active",
24
+ },
25
+ },
26
+ },
27
+ config_currency_ethereum: {
28
+ type: "object",
29
+ default: {
30
+ status: {
31
+ type: "active",
32
+ },
33
+ },
34
+ },
35
+ });
17
36
  const USDT = {
18
37
  type: "TokenCurrency" as const,
19
38
  id: "ethereum/erc20/usd_tether__erc20_",
@@ -942,6 +942,22 @@ const evmConfig: CurrencyLiveConfigDefinition = {
942
942
  showNfts: false,
943
943
  },
944
944
  },
945
+ config_currency_somnia: {
946
+ type: "object",
947
+ default: {
948
+ status: {
949
+ type: "active",
950
+ },
951
+ node: {
952
+ type: "external",
953
+ uri: "https://somnia-rpc.publicnode.com",
954
+ },
955
+ explorer: {
956
+ type: "none",
957
+ },
958
+ showNfts: false,
959
+ },
960
+ },
945
961
  };
946
962
 
947
963
  export { evmConfig };
@@ -106,6 +106,7 @@ export const CURRENCY_DEFAULT_FEATURES = {
106
106
  currencyAssetHubPolkadot: DEFAULT_FEATURE,
107
107
  currencyAssetHubWestend: DEFAULT_FEATURE,
108
108
  currencyMonad: DEFAULT_FEATURE,
109
+ currencySomnia: DEFAULT_FEATURE,
109
110
  };
110
111
 
111
112
  /**
@@ -80,6 +80,7 @@ export function useCurrenciesUnderFeatureFlag() {
80
80
  const assetHubPolkadot = useFeature("currencyAssetHubPolkadot");
81
81
  const polkadot = useFeature("currencyPolkadot");
82
82
  const monad = useFeature("currencyMonad");
83
+ const somnia = useFeature("currencySomnia");
83
84
 
84
85
  const featureFlaggedCurrencies = useMemo(
85
86
  (): Partial<Record<CryptoCurrencyId, Feature<unknown> | null>> => ({
@@ -156,6 +157,7 @@ export function useCurrenciesUnderFeatureFlag() {
156
157
  assethub_polkadot: assetHubPolkadot,
157
158
  polkadot,
158
159
  monad,
160
+ somnia,
159
161
  }),
160
162
  [
161
163
  aptos,
@@ -229,6 +231,7 @@ export function useCurrenciesUnderFeatureFlag() {
229
231
  assetHubPolkadot,
230
232
  polkadot,
231
233
  monad,
234
+ somnia,
232
235
  ],
233
236
  );
234
237