@ledgerhq/live-common 21.5.1 → 21.7.1-patch.1

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 (170) hide show
  1. package/lib/env.js +1 -1
  2. package/lib/env.js.map +1 -1
  3. package/lib/exchange/swap/getExchangeRates.d.ts.map +1 -1
  4. package/lib/exchange/swap/getExchangeRates.js +9 -8
  5. package/lib/exchange/swap/getExchangeRates.js.map +1 -1
  6. package/lib/exchange/swap/hooks/index.d.ts +11 -0
  7. package/lib/exchange/swap/hooks/index.d.ts.map +1 -0
  8. package/lib/exchange/swap/hooks/index.js +23 -0
  9. package/lib/exchange/swap/hooks/index.js.map +1 -0
  10. package/lib/exchange/swap/hooks/useFromState.d.ts +15 -0
  11. package/lib/exchange/swap/hooks/useFromState.d.ts.map +1 -0
  12. package/lib/exchange/swap/hooks/useFromState.js +67 -0
  13. package/lib/exchange/swap/hooks/useFromState.js.map +1 -0
  14. package/lib/exchange/swap/hooks/useFromState.test.d.ts +2 -0
  15. package/lib/exchange/swap/hooks/useFromState.test.d.ts.map +1 -0
  16. package/lib/exchange/swap/hooks/useFromState.test.js +141 -0
  17. package/lib/exchange/swap/hooks/useFromState.test.js.map +1 -0
  18. package/lib/exchange/swap/hooks/usePickDefaultAccount.d.ts +5 -0
  19. package/lib/exchange/swap/hooks/usePickDefaultAccount.d.ts.map +1 -0
  20. package/lib/exchange/swap/hooks/usePickDefaultAccount.js +37 -0
  21. package/lib/exchange/swap/hooks/usePickDefaultAccount.js.map +1 -0
  22. package/lib/exchange/swap/hooks/usePickDefaultAccount.test.d.ts +2 -0
  23. package/lib/exchange/swap/hooks/usePickDefaultAccount.test.d.ts.map +1 -0
  24. package/lib/exchange/swap/hooks/usePickDefaultAccount.test.js +185 -0
  25. package/lib/exchange/swap/hooks/usePickDefaultAccount.test.js.map +1 -0
  26. package/lib/exchange/swap/hooks/usePickDefaultCurrency.d.ts +3 -0
  27. package/lib/exchange/swap/hooks/usePickDefaultCurrency.d.ts.map +1 -0
  28. package/lib/exchange/swap/hooks/usePickDefaultCurrency.js +17 -0
  29. package/lib/exchange/swap/hooks/usePickDefaultCurrency.js.map +1 -0
  30. package/lib/exchange/swap/hooks/usePickDefaultCurrency.test.d.ts +2 -0
  31. package/lib/exchange/swap/hooks/usePickDefaultCurrency.test.d.ts.map +1 -0
  32. package/lib/exchange/swap/hooks/usePickDefaultCurrency.test.js +73 -0
  33. package/lib/exchange/swap/hooks/usePickDefaultCurrency.test.js.map +1 -0
  34. package/lib/exchange/swap/hooks/usePollKYCStatus.d.ts +12 -0
  35. package/lib/exchange/swap/hooks/usePollKYCStatus.d.ts.map +1 -0
  36. package/lib/exchange/swap/hooks/usePollKYCStatus.js +105 -0
  37. package/lib/exchange/swap/hooks/usePollKYCStatus.js.map +1 -0
  38. package/lib/exchange/swap/hooks/usePollKYCStatus.test.d.ts +2 -0
  39. package/lib/exchange/swap/hooks/usePollKYCStatus.test.d.ts.map +1 -0
  40. package/lib/exchange/swap/hooks/usePollKYCStatus.test.js +78 -0
  41. package/lib/exchange/swap/hooks/usePollKYCStatus.test.js.map +1 -0
  42. package/lib/exchange/swap/hooks/useProviderRates.d.ts +20 -0
  43. package/lib/exchange/swap/hooks/useProviderRates.d.ts.map +1 -0
  44. package/lib/exchange/swap/hooks/useProviderRates.js +174 -0
  45. package/lib/exchange/swap/hooks/useProviderRates.js.map +1 -0
  46. package/lib/exchange/swap/hooks/useProviderRates.test.d.ts +2 -0
  47. package/lib/exchange/swap/hooks/useProviderRates.test.d.ts.map +1 -0
  48. package/lib/exchange/swap/hooks/useProviderRates.test.js +261 -0
  49. package/lib/exchange/swap/hooks/useProviderRates.test.js.map +1 -0
  50. package/lib/exchange/swap/hooks/useReverseAccounts.d.ts +15 -0
  51. package/lib/exchange/swap/hooks/useReverseAccounts.d.ts.map +1 -0
  52. package/lib/exchange/swap/hooks/useReverseAccounts.js +35 -0
  53. package/lib/exchange/swap/hooks/useReverseAccounts.js.map +1 -0
  54. package/lib/exchange/swap/hooks/useReverseAccounts.test.d.ts +2 -0
  55. package/lib/exchange/swap/hooks/useReverseAccounts.test.d.ts.map +1 -0
  56. package/lib/exchange/swap/hooks/useReverseAccounts.test.js +79 -0
  57. package/lib/exchange/swap/hooks/useReverseAccounts.test.js.map +1 -0
  58. package/lib/exchange/swap/hooks/useSelectableCurrencies.d.ts +5 -0
  59. package/lib/exchange/swap/hooks/useSelectableCurrencies.d.ts.map +1 -0
  60. package/lib/exchange/swap/hooks/useSelectableCurrencies.js +43 -0
  61. package/lib/exchange/swap/hooks/useSelectableCurrencies.js.map +1 -0
  62. package/lib/exchange/swap/hooks/useSelectableCurrencies.test.d.ts +2 -0
  63. package/lib/exchange/swap/hooks/useSelectableCurrencies.test.d.ts.map +1 -0
  64. package/lib/exchange/swap/hooks/useSelectableCurrencies.test.js +32 -0
  65. package/lib/exchange/swap/hooks/useSelectableCurrencies.test.js.map +1 -0
  66. package/lib/exchange/swap/hooks/useSwapProviders.d.ts +14 -0
  67. package/lib/exchange/swap/hooks/useSwapProviders.d.ts.map +1 -0
  68. package/lib/exchange/swap/hooks/useSwapProviders.js +104 -0
  69. package/lib/exchange/swap/hooks/useSwapProviders.js.map +1 -0
  70. package/lib/exchange/swap/hooks/useSwapProviders.test.d.ts +2 -0
  71. package/lib/exchange/swap/hooks/useSwapProviders.test.d.ts.map +1 -0
  72. package/lib/exchange/swap/hooks/useSwapProviders.test.js +174 -0
  73. package/lib/exchange/swap/hooks/useSwapProviders.test.js.map +1 -0
  74. package/lib/exchange/swap/hooks/useSwapTransaction.d.ts +53 -0
  75. package/lib/exchange/swap/hooks/useSwapTransaction.d.ts.map +1 -0
  76. package/lib/exchange/swap/hooks/useSwapTransaction.js +109 -0
  77. package/lib/exchange/swap/hooks/useSwapTransaction.js.map +1 -0
  78. package/lib/exchange/swap/hooks/useSwapTransaction.test.d.ts +2 -0
  79. package/lib/exchange/swap/hooks/useSwapTransaction.test.d.ts.map +1 -0
  80. package/lib/exchange/swap/hooks/useSwapTransaction.test.js +50 -0
  81. package/lib/exchange/swap/hooks/useSwapTransaction.test.js.map +1 -0
  82. package/lib/exchange/swap/hooks/useToState.d.ts +12 -0
  83. package/lib/exchange/swap/hooks/useToState.d.ts.map +1 -0
  84. package/lib/exchange/swap/hooks/useToState.js +73 -0
  85. package/lib/exchange/swap/hooks/useToState.js.map +1 -0
  86. package/lib/exchange/swap/hooks/useToState.test.d.ts +2 -0
  87. package/lib/exchange/swap/hooks/useToState.test.d.ts.map +1 -0
  88. package/lib/exchange/swap/hooks/useToState.test.js +107 -0
  89. package/lib/exchange/swap/hooks/useToState.test.js.map +1 -0
  90. package/lib/exchange/swap/hooks/useUpdateMaxAmount.d.ts +15 -0
  91. package/lib/exchange/swap/hooks/useUpdateMaxAmount.d.ts.map +1 -0
  92. package/lib/exchange/swap/hooks/useUpdateMaxAmount.js +106 -0
  93. package/lib/exchange/swap/hooks/useUpdateMaxAmount.js.map +1 -0
  94. package/lib/exchange/swap/hooks/useUpdateMaxAmount.test.d.ts +2 -0
  95. package/lib/exchange/swap/hooks/useUpdateMaxAmount.test.d.ts.map +1 -0
  96. package/lib/exchange/swap/hooks/useUpdateMaxAmount.test.js +217 -0
  97. package/lib/exchange/swap/hooks/useUpdateMaxAmount.test.js.map +1 -0
  98. package/lib/exchange/swap/mock.d.ts +6 -2
  99. package/lib/exchange/swap/mock.d.ts.map +1 -1
  100. package/lib/exchange/swap/mock.js +21 -6
  101. package/lib/exchange/swap/mock.js.map +1 -1
  102. package/lib/exchange/swap/types.d.ts +1 -1
  103. package/lib/exchange/swap/types.d.ts.map +1 -1
  104. package/lib/exchange/swap/utils/index.d.ts +16 -0
  105. package/lib/exchange/swap/utils/index.d.ts.map +1 -0
  106. package/lib/exchange/swap/utils/index.js +49 -0
  107. package/lib/exchange/swap/utils/index.js.map +1 -0
  108. package/lib/exchange/swap/utils/index.test.d.ts +2 -0
  109. package/lib/exchange/swap/utils/index.test.d.ts.map +1 -0
  110. package/lib/exchange/swap/utils/index.test.js +238 -0
  111. package/lib/exchange/swap/utils/index.test.js.map +1 -0
  112. package/lib/families/crypto_org/datasets/croeseid.d.ts.map +1 -1
  113. package/lib/families/crypto_org/datasets/croeseid.js +2 -6
  114. package/lib/families/crypto_org/datasets/croeseid.js.map +1 -1
  115. package/lib/families/elrond/api/apiCalls.d.ts +0 -4
  116. package/lib/families/elrond/api/apiCalls.d.ts.map +1 -1
  117. package/lib/families/elrond/api/apiCalls.js +3 -37
  118. package/lib/families/elrond/api/apiCalls.js.map +1 -1
  119. package/lib/families/elrond/api/sdk.js +2 -2
  120. package/lib/families/elrond/api/sdk.js.map +1 -1
  121. package/lib/families/elrond/js-transaction.d.ts +2 -0
  122. package/lib/families/elrond/js-transaction.d.ts.map +1 -1
  123. package/lib/families/elrond/types.d.ts +2 -0
  124. package/lib/families/elrond/types.d.ts.map +1 -1
  125. package/lib/families/elrond/types.js.map +1 -1
  126. package/lib/families/tezos/bridge/js.d.ts.map +1 -1
  127. package/lib/families/tezos/bridge/js.js +11 -1
  128. package/lib/families/tezos/bridge/js.js.map +1 -1
  129. package/lib/mock/account.d.ts +2 -1
  130. package/lib/mock/account.d.ts.map +1 -1
  131. package/lib/mock/account.js +4 -2
  132. package/lib/mock/account.js.map +1 -1
  133. package/package.json +15 -15
  134. package/src/__tests__/__snapshots__/all.libcore.ts.snap +10 -10
  135. package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +30 -15
  136. package/src/env.ts +1 -1
  137. package/src/exchange/swap/getExchangeRates.ts +8 -5
  138. package/src/exchange/swap/hooks/index.ts +10 -0
  139. package/src/exchange/swap/hooks/useFromState.test.ts +134 -0
  140. package/src/exchange/swap/hooks/useFromState.ts +84 -0
  141. package/src/exchange/swap/hooks/usePickDefaultAccount.test.ts +167 -0
  142. package/src/exchange/swap/hooks/usePickDefaultAccount.ts +47 -0
  143. package/src/exchange/swap/hooks/usePickDefaultCurrency.test.ts +107 -0
  144. package/src/exchange/swap/hooks/usePickDefaultCurrency.ts +20 -0
  145. package/src/exchange/swap/hooks/usePollKYCStatus.test.ts +89 -0
  146. package/src/exchange/swap/hooks/usePollKYCStatus.ts +43 -0
  147. package/src/exchange/swap/hooks/useProviderRates.test.ts +200 -0
  148. package/src/exchange/swap/hooks/useProviderRates.ts +130 -0
  149. package/src/exchange/swap/hooks/useReverseAccounts.test.ts +83 -0
  150. package/src/exchange/swap/hooks/useReverseAccounts.ts +56 -0
  151. package/src/exchange/swap/hooks/useSelectableCurrencies.test.ts +39 -0
  152. package/src/exchange/swap/hooks/useSelectableCurrencies.ts +26 -0
  153. package/src/exchange/swap/hooks/useSwapProviders.test.ts +113 -0
  154. package/src/exchange/swap/hooks/useSwapProviders.ts +57 -0
  155. package/src/exchange/swap/hooks/useSwapTransaction.test.ts +54 -0
  156. package/src/exchange/swap/hooks/useSwapTransaction.ts +170 -0
  157. package/src/exchange/swap/hooks/useToState.test.ts +53 -0
  158. package/src/exchange/swap/hooks/useToState.ts +86 -0
  159. package/src/exchange/swap/hooks/useUpdateMaxAmount.test.ts +132 -0
  160. package/src/exchange/swap/hooks/useUpdateMaxAmount.ts +67 -0
  161. package/src/exchange/swap/mock.ts +22 -3
  162. package/src/exchange/swap/types.ts +2 -1
  163. package/src/exchange/swap/utils/index.test.ts +224 -0
  164. package/src/exchange/swap/utils/index.ts +67 -0
  165. package/src/families/crypto_org/datasets/croeseid.ts +7 -9
  166. package/src/families/elrond/api/apiCalls.ts +3 -31
  167. package/src/families/elrond/api/sdk.ts +2 -2
  168. package/src/families/elrond/types.ts +2 -0
  169. package/src/families/tezos/bridge/js.ts +13 -0
  170. package/src/mock/account.ts +2 -2
@@ -0,0 +1,130 @@
1
+ import { useCallback, useEffect, useReducer, useState } from "react";
2
+ import {
3
+ OnNoRatesCallback,
4
+ SetExchangeRateCallback,
5
+ SwapSelectorStateType,
6
+ } from "./useSwapTransaction";
7
+ import { getExchangeRates } from "..";
8
+ import { Exchange, ExchangeRate } from "../types";
9
+ import { pickExchangeRate } from "../utils";
10
+ import { Transaction } from "../../../generated/types";
11
+
12
+ export type RatesReducerState = {
13
+ status?: string | null;
14
+ value?: ExchangeRate[];
15
+ error?: Error;
16
+ };
17
+
18
+ const ratesReducerInitialState: RatesReducerState = {};
19
+ const ratesReducer = (state: RatesReducerState, action): RatesReducerState => {
20
+ switch (action.type) {
21
+ case "set":
22
+ return { value: action.payload, status: null };
23
+ case "idle":
24
+ return { ...state, status: null };
25
+ case "loading":
26
+ return { ...state, status: "loading" };
27
+ case "error":
28
+ return { status: "error", error: action.payload };
29
+ }
30
+ return state;
31
+ };
32
+
33
+ /* Fetch and update provider rates. */
34
+ export const useProviderRates = ({
35
+ fromState,
36
+ toState,
37
+ exchangeRate,
38
+ transaction,
39
+ onNoRates,
40
+ setExchangeRate,
41
+ }: {
42
+ fromState: SwapSelectorStateType;
43
+ toState: SwapSelectorStateType;
44
+ exchangeRate?: ExchangeRate | null | undefined;
45
+ transaction?: Transaction | null | undefined;
46
+ onNoRates?: OnNoRatesCallback | null | undefined;
47
+ setExchangeRate?: SetExchangeRateCallback | null | undefined;
48
+ }): {
49
+ rates: RatesReducerState;
50
+ refetchRates: () => void;
51
+ } => {
52
+ const { account: fromAccount } = fromState;
53
+ const { currency: toCurrency } = toState;
54
+ const [rates, dispatchRates] = useReducer(
55
+ ratesReducer,
56
+ ratesReducerInitialState
57
+ );
58
+ const [getRatesDependency, setGetRatesDependency] = useState<unknown | null>(
59
+ null
60
+ );
61
+ const refetchRates = useCallback(() => setGetRatesDependency({}), []);
62
+
63
+ useEffect(
64
+ () => {
65
+ let abort = false;
66
+ async function getRates() {
67
+ if (
68
+ !transaction ||
69
+ !transaction?.amount ||
70
+ !transaction?.amount.gt(0) ||
71
+ !toCurrency ||
72
+ !fromAccount
73
+ ) {
74
+ setExchangeRate && setExchangeRate(null);
75
+ return dispatchRates({ type: "set", payload: [] });
76
+ }
77
+ dispatchRates({ type: "loading" });
78
+ try {
79
+ let rates: ExchangeRate[] = await getExchangeRates(
80
+ { fromAccount } as Exchange,
81
+ transaction,
82
+ undefined,
83
+ toCurrency
84
+ );
85
+ if (abort) return;
86
+ if (rates.length === 0) {
87
+ onNoRates && onNoRates({ fromState, toState });
88
+ }
89
+ // Discard bad provider rates
90
+ let rateError: Error | null | undefined = null;
91
+ rates = rates.reduce<ExchangeRate[]>((acc, rate) => {
92
+ rateError = rateError ?? rate.error;
93
+ return rate.error ? acc : [...acc, rate];
94
+ }, []);
95
+ if (rates.length === 0 && rateError) {
96
+ // If all the rates are in error
97
+ dispatchRates({ type: "error", payload: rateError });
98
+ } else {
99
+ dispatchRates({ type: "set", payload: rates });
100
+ setExchangeRate &&
101
+ pickExchangeRate(rates, exchangeRate, setExchangeRate);
102
+ }
103
+ } catch (error) {
104
+ !abort && dispatchRates({ type: "error", payload: error });
105
+ }
106
+ }
107
+
108
+ getRates();
109
+
110
+ return () => {
111
+ abort = true;
112
+ dispatchRates({ type: "idle" });
113
+ };
114
+ },
115
+ // eslint-disable-next-line react-hooks/exhaustive-deps
116
+ [
117
+ fromAccount,
118
+ toCurrency,
119
+ transaction,
120
+ getRatesDependency,
121
+ onNoRates,
122
+ setExchangeRate,
123
+ ]
124
+ );
125
+
126
+ return {
127
+ rates,
128
+ refetchRates,
129
+ };
130
+ };
@@ -0,0 +1,83 @@
1
+ import { getCryptoCurrencyById, getTokenById } from "@ledgerhq/cryptoassets";
2
+ import { renderHook, act } from "@testing-library/react-hooks";
3
+ import { genAccount, genTokenAccount } from "../../../mock/account";
4
+ import { Account } from "../../../types";
5
+ import { useReverseAccounts } from "./useReverseAccounts";
6
+
7
+ const BTC = getCryptoCurrencyById("bitcoin");
8
+ const ETH = getCryptoCurrencyById("ethereum");
9
+ const USDT = getTokenById("ethereum/erc20/usd_tether__erc20_");
10
+
11
+ const fromParentAccount = genAccount("mocked-account-2", {
12
+ currency: ETH,
13
+ });
14
+ const fromAccount = genTokenAccount(1, fromParentAccount, USDT);
15
+ const toAccount = genAccount("mocked-account-1", { currency: BTC });
16
+ const allAccounts = [fromAccount, fromParentAccount, toAccount] as Account[];
17
+
18
+ describe("useReverseAccounts", () => {
19
+ const setFromAccount = jest.fn();
20
+ const setToAccount = jest.fn();
21
+
22
+ const defaultProps: Parameters<typeof useReverseAccounts>[0] = {
23
+ accounts: allAccounts,
24
+ fromAccount,
25
+ fromParentAccount,
26
+ toAccount,
27
+ fromCurrency: USDT,
28
+ setFromAccount,
29
+ setToAccount,
30
+ };
31
+
32
+ it("should tell if the accounts are swappable", () => {
33
+ const { result, rerender } = renderHook(
34
+ (props) => useReverseAccounts(props),
35
+ {
36
+ initialProps: defaultProps,
37
+ }
38
+ );
39
+
40
+ expect(result.current.isSwapReversable).toBe(true);
41
+
42
+ rerender({ ...defaultProps, toAccount: null });
43
+ expect(result.current.isSwapReversable).toBe(false);
44
+
45
+ rerender({ ...defaultProps, fromCurrency: null });
46
+ expect(result.current.isSwapReversable).toBe(false);
47
+
48
+ rerender({
49
+ ...defaultProps,
50
+ // detached account - not in the main accounts list
51
+ toAccount: genAccount("mocked-account-3", { currency: BTC }),
52
+ });
53
+ expect(result.current.isSwapReversable).toBe(false);
54
+ });
55
+
56
+ it("should reverse accounts when 'reverseSwap' is called", () => {
57
+ const { result, rerender } = renderHook(useReverseAccounts, {
58
+ initialProps: {
59
+ setToAccount,
60
+ setFromAccount,
61
+ } as any,
62
+ });
63
+
64
+ // if isSwapReversable is false, then reverseSwap should be a no-op
65
+ expect(result.current.isSwapReversable).toBe(false);
66
+ act(() => result.current.reverseSwap());
67
+ expect(setToAccount).toHaveBeenCalledTimes(0);
68
+ expect(setFromAccount).toHaveBeenCalledTimes(0);
69
+
70
+ // Reverse from/to accounts
71
+ rerender(defaultProps);
72
+ expect(result.current.isSwapReversable).toBe(true);
73
+ act(() => result.current.reverseSwap());
74
+ expect(setToAccount).toHaveBeenCalledTimes(1);
75
+ expect(setToAccount.mock.calls[0]).toMatchObject([
76
+ USDT,
77
+ fromAccount,
78
+ fromParentAccount,
79
+ ]);
80
+ expect(setFromAccount).toHaveBeenCalledTimes(1);
81
+ expect(setFromAccount.mock.calls[0]).toMatchObject([toAccount]);
82
+ });
83
+ });
@@ -0,0 +1,56 @@
1
+ import { useCallback, useMemo } from "react";
2
+ import { SwapDataType, SwapSelectorStateType, SwapTransactionType } from ".";
3
+ import { flattenAccounts } from "../../../account";
4
+ import { Account } from "../../../types";
5
+
6
+ export const useReverseAccounts = ({
7
+ accounts,
8
+ toAccount,
9
+ fromAccount,
10
+ fromParentAccount,
11
+ fromCurrency,
12
+ setFromAccount,
13
+ setToAccount,
14
+ }: {
15
+ accounts: Account[] | undefined;
16
+ toAccount: SwapSelectorStateType["account"];
17
+ fromAccount: SwapSelectorStateType["account"];
18
+ fromParentAccount: SwapSelectorStateType["parentAccount"];
19
+ fromCurrency: SwapSelectorStateType["currency"];
20
+ setFromAccount: SwapTransactionType["setFromAccount"];
21
+ setToAccount: SwapTransactionType["setToAccount"];
22
+ }): {
23
+ isSwapReversable: SwapDataType["isSwapReversable"];
24
+ reverseSwap: SwapTransactionType["reverseSwap"];
25
+ } => {
26
+ const isSwapReversable = useMemo(() => {
27
+ if (!toAccount || !fromCurrency) return false;
28
+
29
+ const allAccounstWithSub = accounts ? flattenAccounts(accounts) : [];
30
+ const isToSwappable = !!allAccounstWithSub.find(
31
+ (account) => account.id === toAccount?.id
32
+ );
33
+
34
+ return isToSwappable;
35
+ }, [toAccount, fromCurrency, accounts]);
36
+
37
+ const reverseSwap: SwapTransactionType["reverseSwap"] = useCallback(() => {
38
+ if (isSwapReversable === false) return;
39
+
40
+ setFromAccount(toAccount);
41
+ setToAccount(fromCurrency, fromAccount, fromParentAccount);
42
+ }, [
43
+ toAccount,
44
+ fromCurrency,
45
+ fromAccount,
46
+ fromParentAccount,
47
+ setFromAccount,
48
+ setToAccount,
49
+ isSwapReversable,
50
+ ]);
51
+
52
+ return {
53
+ isSwapReversable,
54
+ reverseSwap,
55
+ };
56
+ };
@@ -0,0 +1,39 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+
3
+ import { useSelectableCurrencies } from "./useSelectableCurrencies";
4
+ import { getCryptoCurrencyById } from "../../../currencies";
5
+
6
+ describe("useSelectableCurrencies", () => {
7
+ test("returns an empty array when empty list are passed", () => {
8
+ const allCurrencies = [];
9
+
10
+ const { result } = renderHook(() =>
11
+ useSelectableCurrencies({ allCurrencies })
12
+ );
13
+
14
+ expect(result.current).toEqual([]);
15
+ });
16
+
17
+ test("returns an empty array when incorrect ids are passed", () => {
18
+ const allCurrencies = ["ethercoin", "bitether", "polkamos"];
19
+
20
+ const { result } = renderHook(() =>
21
+ useSelectableCurrencies({ allCurrencies })
22
+ );
23
+
24
+ expect(result.current).toEqual([]);
25
+ });
26
+
27
+ test("returns correct currencies for all correct ids, in the same order", () => {
28
+ const allCurrencies = ["ethereum", "bitcoin", "polkamos"];
29
+ const ethereumCurrency = getCryptoCurrencyById("ethereum");
30
+ const bitcoinCurrency = getCryptoCurrencyById("bitcoin");
31
+
32
+ const { result } = renderHook(() =>
33
+ useSelectableCurrencies({ allCurrencies })
34
+ );
35
+
36
+ expect(result.current).toHaveLength(2);
37
+ expect(result.current).toEqual([ethereumCurrency, bitcoinCurrency]);
38
+ });
39
+ });
@@ -0,0 +1,26 @@
1
+ import { useMemo } from "react";
2
+ import {
3
+ CryptoCurrency,
4
+ findCryptoCurrencyById,
5
+ findTokenById,
6
+ TokenCurrency,
7
+ } from "@ledgerhq/cryptoassets";
8
+
9
+ export const useSelectableCurrencies = ({
10
+ allCurrencies,
11
+ }: {
12
+ allCurrencies: string[];
13
+ }): (TokenCurrency | CryptoCurrency)[] => {
14
+ const currencies = useMemo(() => {
15
+ const tokens = allCurrencies.map(findTokenById).filter(Boolean);
16
+ const cryptoCurrencies = allCurrencies
17
+ .map(findCryptoCurrencyById)
18
+ .filter(Boolean);
19
+ return [...tokens, ...cryptoCurrencies] as (
20
+ | TokenCurrency
21
+ | CryptoCurrency
22
+ )[];
23
+ }, [allCurrencies]);
24
+
25
+ return currencies;
26
+ };
@@ -0,0 +1,113 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+ import { mocked } from "ts-jest/utils";
3
+
4
+ import { useSwapProviders } from "./useSwapProviders";
5
+ import { getProviders } from "..";
6
+
7
+ jest.mock("..");
8
+
9
+ const mockedGetProviders = mocked(getProviders);
10
+
11
+ describe("useSwapProviders", () => {
12
+ const OLD_ENV = process.env;
13
+ const mockedProviders = [
14
+ {
15
+ provider: "changelly",
16
+ pairs: [
17
+ { from: "ETH", to: "BTC", tradeMethod: "fixed" },
18
+ { from: "DOT", to: "BTC", tradeMethod: "fixed" },
19
+ { from: "EGLD", to: "BTC", tradeMethod: "float" },
20
+ { from: "LINK", to: "ETH", tradeMethod: "fixed" },
21
+ { from: "LINK", to: "GRT", tradeMethod: "float" },
22
+ ],
23
+ },
24
+ {
25
+ provider: "wyre",
26
+ pairs: [
27
+ { from: "ETH", to: "BTC", tradeMethod: "fixed" },
28
+ { from: "BTC", to: "ETH", tradeMethod: "float" },
29
+ ],
30
+ },
31
+ ];
32
+
33
+ beforeEach(() => {
34
+ mockedGetProviders.mockResolvedValue(mockedProviders);
35
+ // Clear the cache
36
+ jest.resetModules();
37
+ // Copy envs already present before running this test to process.env
38
+ process.env = { ...OLD_ENV };
39
+ });
40
+
41
+ afterEach(() => {
42
+ mockedGetProviders.mockClear();
43
+ });
44
+
45
+ afterAll(() => {
46
+ // Restore the old envs
47
+ process.env = OLD_ENV;
48
+ });
49
+
50
+ test("returns the initial state on mount", () => {
51
+ const { result, unmount } = renderHook(() => useSwapProviders());
52
+
53
+ // interrupt the useEffect function by unmounting the component
54
+ unmount();
55
+
56
+ expect(result.current.isLoading).toBeTruthy();
57
+ expect(result.current.error).toBeNull();
58
+ expect(result.current.providers).toBeNull();
59
+ // automatically fired on mount
60
+ expect(mockedGetProviders).toHaveBeenCalledTimes(1);
61
+ });
62
+
63
+ test("returns the fetched providers", async () => {
64
+ const { result, waitForNextUpdate } = renderHook(() => useSwapProviders());
65
+
66
+ await waitForNextUpdate();
67
+
68
+ expect(mockedGetProviders).toHaveBeenCalledTimes(1);
69
+ expect(result.current.isLoading).toBeFalsy();
70
+ expect(result.current.error).toBeNull();
71
+ expect(result.current.providers).toEqual(mockedProviders);
72
+ });
73
+
74
+ test("only returns no-filtered fetched providers", async () => {
75
+ // set the SWAP_DISABLED_PROVIDERS env variable for this test
76
+ process.env.SWAP_DISABLED_PROVIDERS = "wyre";
77
+ const { result, waitForNextUpdate } = renderHook(() => useSwapProviders());
78
+
79
+ await waitForNextUpdate();
80
+
81
+ expect(mockedGetProviders).toHaveBeenCalledTimes(1);
82
+ expect(result.current.isLoading).toBeFalsy();
83
+ expect(result.current.error).toBeNull();
84
+ expect(result.current.providers).toEqual(
85
+ mockedProviders.filter((provider) => provider.provider !== "wyre")
86
+ );
87
+ });
88
+
89
+ test("returns the fetched error", async () => {
90
+ const error = new Error("test error");
91
+ mockedGetProviders.mockRejectedValueOnce(error);
92
+
93
+ const { result, waitForNextUpdate } = renderHook(() => useSwapProviders());
94
+
95
+ await waitForNextUpdate();
96
+
97
+ expect(mockedGetProviders).toHaveBeenCalledTimes(1);
98
+ expect(result.current.isLoading).toBeFalsy();
99
+ expect(result.current.error).toEqual(error);
100
+ expect(result.current.providers).toBeNull();
101
+ });
102
+
103
+ test("only fetch providers on mount", async () => {
104
+ const { rerender, waitForNextUpdate } = renderHook(() =>
105
+ useSwapProviders()
106
+ );
107
+
108
+ await waitForNextUpdate();
109
+ rerender();
110
+
111
+ expect(mockedGetProviders).toHaveBeenCalledTimes(1);
112
+ });
113
+ });
@@ -0,0 +1,57 @@
1
+ import { useEffect, useReducer } from "react";
2
+ import { getProviders } from "..";
3
+ import { AvailableProviderV3 } from "../types";
4
+
5
+ type State = {
6
+ isLoading: boolean;
7
+ error: Error | null | undefined;
8
+ providers: AvailableProviderV3[] | null | undefined;
9
+ };
10
+
11
+ type ActionType =
12
+ | { type: "SAVE_DATA"; payload: State["providers"] }
13
+ | { type: "SAVE_ERROR"; payload: State["error"] };
14
+
15
+ const reducer = (_state: State, action: ActionType): State => {
16
+ switch (action.type) {
17
+ case "SAVE_DATA":
18
+ return { error: null, providers: action.payload, isLoading: false };
19
+ case "SAVE_ERROR":
20
+ return { error: action.payload, providers: null, isLoading: false };
21
+ default:
22
+ throw new Error("Uncorrect action type");
23
+ }
24
+ };
25
+
26
+ export const initialState = { isLoading: true, error: null, providers: null };
27
+
28
+ const filterDisabledProviders = (provider: AvailableProviderV3) =>
29
+ !process.env.SWAP_DISABLED_PROVIDERS?.includes(provider.provider);
30
+
31
+ export const useSwapProviders = (): State => {
32
+ const [state, dispatch] = useReducer(reducer, initialState);
33
+
34
+ useEffect(() => {
35
+ let isMounted = true;
36
+
37
+ const saveProviders = async () => {
38
+ try {
39
+ const allProviders = (await getProviders()) as AvailableProviderV3[];
40
+ const providers = allProviders.filter(filterDisabledProviders);
41
+
42
+ if (isMounted) dispatch({ type: "SAVE_DATA", payload: providers });
43
+ } catch (error) {
44
+ if (isMounted && error instanceof Error)
45
+ dispatch({ type: "SAVE_ERROR", payload: error });
46
+ }
47
+ };
48
+
49
+ saveProviders();
50
+
51
+ return () => {
52
+ isMounted = false;
53
+ };
54
+ }, []);
55
+
56
+ return state;
57
+ };
@@ -0,0 +1,54 @@
1
+ import { renderHook } from "@testing-library/react-hooks";
2
+
3
+ import { AmountRequired } from "@ledgerhq/errors";
4
+ import { useFromAmountError } from "./useSwapTransaction";
5
+
6
+ /**
7
+ * @DEV: The useSwapTransaction hook is a composition hook. It doesn't own specific
8
+ * logic, but it's composed of other hooks. That's why only the useFromAmountError
9
+ * logic is tested here, it's the only logic owned by the useSwapTransaction hook.
10
+ * All other lines are covered by other hooks tests.
11
+ */
12
+ describe("useSwapTransaction", () => {
13
+ test("useFromAmountError - returns nothing when no errors are caught", () => {
14
+ const { result } = renderHook(() =>
15
+ useFromAmountError({ gasPrice: undefined, amount: undefined })
16
+ );
17
+
18
+ expect(result.current).toBeUndefined();
19
+ });
20
+
21
+ test("useFromAmountError - returns the first error caught", () => {
22
+ const gasPriceError = new Error("Gas price is too high");
23
+ const amountError = new Error("Amount is too low");
24
+ const tests = [
25
+ {
26
+ input: { gasPrice: gasPriceError, amount: undefined },
27
+ output: gasPriceError,
28
+ },
29
+ {
30
+ input: { gasPrice: undefined, amount: amountError },
31
+ output: amountError,
32
+ },
33
+ {
34
+ input: { gasPrice: gasPriceError, amount: amountError },
35
+ output: gasPriceError,
36
+ },
37
+ ];
38
+
39
+ tests.forEach(({ input, output }) => {
40
+ const { result } = renderHook(() => useFromAmountError(input));
41
+ expect(result.current).toEqual(output);
42
+ });
43
+ });
44
+
45
+ test("useFromAmountError - do not return AmountRequired error", () => {
46
+ const amountError = new AmountRequired("This error will be filtered");
47
+
48
+ const { result } = renderHook(() =>
49
+ useFromAmountError({ gasPrice: undefined, amount: amountError })
50
+ );
51
+
52
+ expect(result.current).toBeUndefined();
53
+ });
54
+ });