@ledgerhq/live-common 24.0.0-next.0 → 24.0.0-next.3
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.
- package/lib/env.d.ts +16 -6
- package/lib/env.d.ts.map +1 -1
- package/lib/env.js +11 -1
- package/lib/env.js.map +1 -1
- package/lib/env.react.d.ts +1 -1
- package/lib/env.react.d.ts.map +1 -1
- package/lib/errors.d.ts +0 -3
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +2 -3
- package/lib/errors.js.map +1 -1
- package/lib/exchange/index.d.ts +5 -0
- package/lib/exchange/index.d.ts.map +1 -1
- package/lib/exchange/index.js.map +1 -1
- package/lib/exchange/swap/checkQuote.d.ts +4 -0
- package/lib/exchange/swap/checkQuote.d.ts.map +1 -0
- package/lib/exchange/swap/checkQuote.js +80 -0
- package/lib/exchange/swap/checkQuote.js.map +1 -0
- package/lib/exchange/swap/checkQuote.test.d.ts +2 -0
- package/lib/exchange/swap/checkQuote.test.d.ts.map +1 -0
- package/lib/exchange/swap/checkQuote.test.js +103 -0
- package/lib/exchange/swap/checkQuote.test.js.map +1 -0
- package/lib/exchange/swap/completeExchange.d.ts.map +1 -1
- package/lib/exchange/swap/completeExchange.js +9 -9
- package/lib/exchange/swap/completeExchange.js.map +1 -1
- package/lib/exchange/swap/getExchangeRates.d.ts.map +1 -1
- package/lib/exchange/swap/getExchangeRates.js +3 -1
- package/lib/exchange/swap/getExchangeRates.js.map +1 -1
- package/lib/exchange/swap/getProviders.d.ts.map +1 -1
- package/lib/exchange/swap/getProviders.js +10 -4
- package/lib/exchange/swap/getProviders.js.map +1 -1
- package/lib/exchange/swap/getStatus.d.ts +1 -3
- package/lib/exchange/swap/getStatus.d.ts.map +1 -1
- package/lib/exchange/swap/getStatus.js +2 -30
- package/lib/exchange/swap/getStatus.js.map +1 -1
- package/lib/exchange/swap/hooks/useProviderRates.d.ts +3 -4
- package/lib/exchange/swap/hooks/useProviderRates.d.ts.map +1 -1
- package/lib/exchange/swap/hooks/useProviderRates.js +35 -5
- package/lib/exchange/swap/hooks/useProviderRates.js.map +1 -1
- package/lib/exchange/swap/hooks/useProviderRates.test.js +106 -1
- package/lib/exchange/swap/hooks/useProviderRates.test.js.map +1 -1
- package/lib/exchange/swap/hooks/useSwapTransaction.d.ts +5 -4
- package/lib/exchange/swap/hooks/useSwapTransaction.d.ts.map +1 -1
- package/lib/exchange/swap/hooks/useSwapTransaction.js +6 -6
- package/lib/exchange/swap/hooks/useSwapTransaction.js.map +1 -1
- package/lib/exchange/swap/hooks/useUpdateMaxAmount.d.ts +4 -3
- package/lib/exchange/swap/hooks/useUpdateMaxAmount.d.ts.map +1 -1
- package/lib/exchange/swap/hooks/useUpdateMaxAmount.js +8 -5
- package/lib/exchange/swap/hooks/useUpdateMaxAmount.js.map +1 -1
- package/lib/exchange/swap/hooks/useUpdateMaxAmount.test.js +35 -4
- package/lib/exchange/swap/hooks/useUpdateMaxAmount.test.js.map +1 -1
- package/lib/exchange/swap/index.d.ts +10 -8
- package/lib/exchange/swap/index.d.ts.map +1 -1
- package/lib/exchange/swap/index.js +43 -18
- package/lib/exchange/swap/index.js.map +1 -1
- package/lib/exchange/swap/index.test.d.ts +2 -0
- package/lib/exchange/swap/index.test.d.ts.map +1 -0
- package/lib/exchange/swap/index.test.js +30 -0
- package/lib/exchange/swap/index.test.js.map +1 -0
- package/lib/exchange/swap/initSwap.d.ts +1 -1
- package/lib/exchange/swap/initSwap.d.ts.map +1 -1
- package/lib/exchange/swap/initSwap.js +30 -25
- package/lib/exchange/swap/initSwap.js.map +1 -1
- package/lib/exchange/swap/mock.d.ts +5 -2
- package/lib/exchange/swap/mock.d.ts.map +1 -1
- package/lib/exchange/swap/mock.js +127 -6
- package/lib/exchange/swap/mock.js.map +1 -1
- package/lib/exchange/swap/postSwapState.d.ts +4 -0
- package/lib/exchange/swap/postSwapState.d.ts.map +1 -0
- package/lib/exchange/swap/postSwapState.js +121 -0
- package/lib/exchange/swap/postSwapState.js.map +1 -0
- package/lib/exchange/swap/types.d.ts +30 -2
- package/lib/exchange/swap/types.d.ts.map +1 -1
- package/lib/exchange/swap/updateAccountSwapStatus.d.ts.map +1 -1
- package/lib/exchange/swap/updateAccountSwapStatus.js +2 -2
- package/lib/exchange/swap/updateAccountSwapStatus.js.map +1 -1
- package/lib/exchange/swap/utils/index.d.ts +19 -2
- package/lib/exchange/swap/utils/index.d.ts.map +1 -1
- package/lib/exchange/swap/utils/index.js +96 -20
- package/lib/exchange/swap/utils/index.js.map +1 -1
- package/lib/exchange/swap/utils/index.test.js +95 -48
- package/lib/exchange/swap/utils/index.test.js.map +1 -1
- package/lib/families/ethereum/modules/erc1155.d.ts.map +1 -1
- package/lib/families/ethereum/modules/erc1155.js +5 -1
- package/lib/families/ethereum/modules/erc1155.js.map +1 -1
- package/lib/families/ethereum/modules/erc721.d.ts.map +1 -1
- package/lib/families/ethereum/modules/erc721.js +5 -0
- package/lib/families/ethereum/modules/erc721.js.map +1 -1
- package/lib/families/ethereum/nft.unit.test.js +35 -15
- package/lib/families/ethereum/nft.unit.test.js.map +1 -1
- package/lib/families/ethereum/synchronisation.d.ts.map +1 -1
- package/lib/families/ethereum/synchronisation.js +17 -13
- package/lib/families/ethereum/synchronisation.js.map +1 -1
- package/lib/families/solana/api/chain/account/token.d.ts +1 -1
- package/lib/families/solana/api/chain/account/vote.d.ts +12 -12
- package/lib/families/solana/api/chain/instruction/system/types.d.ts +4 -4
- package/lib/featureFlags/defaultFeatures.d.ts.map +1 -1
- package/lib/featureFlags/defaultFeatures.js +3 -0
- package/lib/featureFlags/defaultFeatures.js.map +1 -1
- package/lib/featureFlags/types.d.ts +1 -1
- package/lib/featureFlags/types.d.ts.map +1 -1
- package/lib/hw/actions/initSwap.d.ts +4 -3
- package/lib/hw/actions/initSwap.d.ts.map +1 -1
- package/lib/hw/actions/initSwap.js +7 -5
- package/lib/hw/actions/initSwap.js.map +1 -1
- package/lib/nft/hook.d.ts +3 -0
- package/lib/nft/hook.d.ts.map +1 -0
- package/lib/nft/hook.js +73 -0
- package/lib/nft/hook.js.map +1 -0
- package/lib/nft/index.d.ts +1 -0
- package/lib/nft/index.d.ts.map +1 -1
- package/lib/nft/index.js +1 -0
- package/lib/nft/index.js.map +1 -1
- package/lib/nft/nftOperationId.d.ts +15 -0
- package/lib/nft/nftOperationId.d.ts.map +1 -0
- package/lib/nft/nftOperationId.js +50 -0
- package/lib/nft/nftOperationId.js.map +1 -0
- package/lib/operation.d.ts +1 -1
- package/lib/operation.d.ts.map +1 -1
- package/lib/operation.js +23 -1
- package/lib/operation.js.map +1 -1
- package/lib/types/nft.d.ts +4 -0
- package/lib/types/nft.d.ts.map +1 -1
- package/package.json +4 -1
- package/src/env.ts +12 -2
- package/src/errors.ts +0 -1
- package/src/exchange/index.ts +7 -0
- package/src/exchange/swap/checkQuote.test.ts +53 -0
- package/src/exchange/swap/checkQuote.ts +34 -0
- package/src/exchange/swap/completeExchange.ts +9 -11
- package/src/exchange/swap/getExchangeRates.ts +5 -3
- package/src/exchange/swap/getProviders.ts +12 -4
- package/src/exchange/swap/getStatus.ts +2 -12
- package/src/exchange/swap/hooks/useProviderRates.test.ts +103 -1
- package/src/exchange/swap/hooks/useProviderRates.ts +57 -9
- package/src/exchange/swap/hooks/useSwapTransaction.ts +14 -13
- package/src/exchange/swap/hooks/useUpdateMaxAmount.test.ts +31 -4
- package/src/exchange/swap/hooks/useUpdateMaxAmount.ts +13 -7
- package/src/exchange/swap/index.test.ts +36 -0
- package/src/exchange/swap/index.ts +51 -30
- package/src/exchange/swap/initSwap.ts +54 -40
- package/src/exchange/swap/mock.ts +128 -13
- package/src/exchange/swap/postSwapState.ts +61 -0
- package/src/exchange/swap/types.ts +68 -5
- package/src/exchange/swap/updateAccountSwapStatus.ts +4 -4
- package/src/exchange/swap/utils/index.test.ts +127 -63
- package/src/exchange/swap/utils/index.ts +128 -23
- package/src/families/ethereum/modules/erc1155.ts +12 -5
- package/src/families/ethereum/modules/erc721.ts +11 -0
- package/src/families/ethereum/nft.unit.test.ts +24 -17
- package/src/families/ethereum/synchronisation.ts +22 -18
- package/src/featureFlags/defaultFeatures.ts +3 -0
- package/src/featureFlags/types.ts +1 -0
- package/src/hw/actions/initSwap.ts +20 -9
- package/src/nft/hook.ts +29 -0
- package/src/nft/index.ts +1 -0
- package/src/nft/nftOperationId.ts +47 -0
- package/src/operation.ts +50 -1
- package/src/types/nft.ts +5 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { renderHook, act } from "@testing-library/react-hooks";
|
|
2
1
|
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets";
|
|
2
|
+
import { act, renderHook } from "@testing-library/react-hooks";
|
|
3
3
|
import BigNumber from "bignumber.js";
|
|
4
4
|
import { useProviderRates } from ".";
|
|
5
5
|
import { getExchangeRates } from "..";
|
|
6
|
+
import {
|
|
7
|
+
SwapExchangeRateAmountTooHigh,
|
|
8
|
+
SwapExchangeRateAmountTooLow,
|
|
9
|
+
} from "../../../errors";
|
|
6
10
|
import { genAccount } from "../../../mock/account";
|
|
7
11
|
import { mockGetExchangeRates } from "../mock";
|
|
8
12
|
|
|
@@ -184,6 +188,104 @@ describe("useProviderRates", () => {
|
|
|
184
188
|
expect(result.current.rates.value).toBeUndefined();
|
|
185
189
|
});
|
|
186
190
|
|
|
191
|
+
it("should sort SwapExchangeRateAmountTooLow based on amount", async () => {
|
|
192
|
+
const swapExchangeRateAmountTooLowError1 = new SwapExchangeRateAmountTooLow(
|
|
193
|
+
undefined,
|
|
194
|
+
{
|
|
195
|
+
amount: new BigNumber(1),
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const swapExchangeRateAmountTooLowError2 = new SwapExchangeRateAmountTooLow(
|
|
200
|
+
undefined,
|
|
201
|
+
{
|
|
202
|
+
amount: new BigNumber(2),
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
mockedRates[0].error = swapExchangeRateAmountTooLowError2;
|
|
207
|
+
mockedRates[1].error = swapExchangeRateAmountTooLowError1;
|
|
208
|
+
|
|
209
|
+
mockedGetExchangeRates.mockResolvedValue(mockedRates);
|
|
210
|
+
const { result, waitForNextUpdate } = renderHook(useProviderRates, {
|
|
211
|
+
initialProps: baseInitalProps,
|
|
212
|
+
});
|
|
213
|
+
await waitForNextUpdate({ timeout: 1000 });
|
|
214
|
+
expect(result.current.rates.status).toBe("error");
|
|
215
|
+
expect(result.current.rates.error).toBe(swapExchangeRateAmountTooLowError1);
|
|
216
|
+
expect(result.current.rates.value).toBeUndefined();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("should sort SwapExchangeRateAmountTooHigh based on amount", async () => {
|
|
220
|
+
const swapExchangeRateAmountTooHighError1 =
|
|
221
|
+
new SwapExchangeRateAmountTooHigh(undefined, {
|
|
222
|
+
amount: new BigNumber(1),
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const swapExchangeRateAmountTooHighError2 =
|
|
226
|
+
new SwapExchangeRateAmountTooHigh(undefined, {
|
|
227
|
+
amount: new BigNumber(2),
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
mockedRates[0].error = swapExchangeRateAmountTooHighError1;
|
|
231
|
+
mockedRates[1].error = swapExchangeRateAmountTooHighError2;
|
|
232
|
+
|
|
233
|
+
mockedGetExchangeRates.mockResolvedValue(mockedRates);
|
|
234
|
+
const { result, waitForNextUpdate } = renderHook(useProviderRates, {
|
|
235
|
+
initialProps: baseInitalProps,
|
|
236
|
+
});
|
|
237
|
+
await waitForNextUpdate({ timeout: 1000 });
|
|
238
|
+
expect(result.current.rates.status).toBe("error");
|
|
239
|
+
expect(result.current.rates.error).toBe(
|
|
240
|
+
swapExchangeRateAmountTooHighError2
|
|
241
|
+
);
|
|
242
|
+
expect(result.current.rates.value).toBeUndefined();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("SwapExchangeRateAmountTooLow should take precedence over standard errors", async () => {
|
|
246
|
+
const swapExchangeRateAmountTooLowError = new SwapExchangeRateAmountTooLow(
|
|
247
|
+
undefined,
|
|
248
|
+
{
|
|
249
|
+
amount: new BigNumber(1),
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const standardError = new Error("Error");
|
|
254
|
+
|
|
255
|
+
mockedRates[0].error = standardError;
|
|
256
|
+
mockedRates[1].error = swapExchangeRateAmountTooLowError;
|
|
257
|
+
|
|
258
|
+
mockedGetExchangeRates.mockResolvedValue(mockedRates);
|
|
259
|
+
const { result, waitForNextUpdate } = renderHook(useProviderRates, {
|
|
260
|
+
initialProps: baseInitalProps,
|
|
261
|
+
});
|
|
262
|
+
await waitForNextUpdate({ timeout: 1000 });
|
|
263
|
+
expect(result.current.rates.status).toBe("error");
|
|
264
|
+
expect(result.current.rates.error).toBe(swapExchangeRateAmountTooLowError);
|
|
265
|
+
expect(result.current.rates.value).toBeUndefined();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("SwapExchangeRateAmountTooHigh should take precedence over standard errors", async () => {
|
|
269
|
+
const SwapExchangeRateAmountTooHighError =
|
|
270
|
+
new SwapExchangeRateAmountTooHigh(undefined, {
|
|
271
|
+
amount: new BigNumber(1),
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const standardError = new Error("Error");
|
|
275
|
+
|
|
276
|
+
mockedRates[0].error = standardError;
|
|
277
|
+
mockedRates[1].error = SwapExchangeRateAmountTooHighError;
|
|
278
|
+
|
|
279
|
+
mockedGetExchangeRates.mockResolvedValue(mockedRates);
|
|
280
|
+
const { result, waitForNextUpdate } = renderHook(useProviderRates, {
|
|
281
|
+
initialProps: baseInitalProps,
|
|
282
|
+
});
|
|
283
|
+
await waitForNextUpdate({ timeout: 1000 });
|
|
284
|
+
expect(result.current.rates.status).toBe("error");
|
|
285
|
+
expect(result.current.rates.error).toBe(SwapExchangeRateAmountTooHighError);
|
|
286
|
+
expect(result.current.rates.value).toBeUndefined();
|
|
287
|
+
});
|
|
288
|
+
|
|
187
289
|
it("should call 'onNoRates' if there are no rates returned by the server", async () => {
|
|
188
290
|
const onNoRates = jest.fn(() => {});
|
|
189
291
|
mockedGetExchangeRates.mockResolvedValue([]);
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { useCallback, useEffect, useReducer, useState } from "react";
|
|
2
|
+
import { getExchangeRates } from "..";
|
|
3
|
+
import { Transaction } from "../../../generated/types";
|
|
4
|
+
import type { CustomMinOrMaxError, Exchange, ExchangeRate } from "../types";
|
|
2
5
|
import {
|
|
3
6
|
OnNoRatesCallback,
|
|
4
7
|
SetExchangeRateCallback,
|
|
5
8
|
SwapSelectorStateType,
|
|
6
9
|
} from "./useSwapTransaction";
|
|
7
|
-
import { getExchangeRates } from "..";
|
|
8
|
-
import { Exchange, ExchangeRate } from "../types";
|
|
9
|
-
import { pickExchangeRate } from "../utils";
|
|
10
|
-
import { Transaction } from "../../../generated/types";
|
|
11
10
|
|
|
12
11
|
export type RatesReducerState = {
|
|
13
12
|
status?: string | null;
|
|
@@ -34,14 +33,12 @@ const ratesReducer = (state: RatesReducerState, action): RatesReducerState => {
|
|
|
34
33
|
export const useProviderRates = ({
|
|
35
34
|
fromState,
|
|
36
35
|
toState,
|
|
37
|
-
exchangeRate,
|
|
38
36
|
transaction,
|
|
39
37
|
onNoRates,
|
|
40
38
|
setExchangeRate,
|
|
41
39
|
}: {
|
|
42
40
|
fromState: SwapSelectorStateType;
|
|
43
41
|
toState: SwapSelectorStateType;
|
|
44
|
-
exchangeRate?: ExchangeRate | null | undefined;
|
|
45
42
|
transaction?: Transaction | null | undefined;
|
|
46
43
|
onNoRates?: OnNoRatesCallback | null | undefined;
|
|
47
44
|
setExchangeRate?: SetExchangeRateCallback | null | undefined;
|
|
@@ -82,23 +79,74 @@ export const useProviderRates = ({
|
|
|
82
79
|
undefined,
|
|
83
80
|
toCurrency
|
|
84
81
|
);
|
|
82
|
+
|
|
85
83
|
if (abort) return;
|
|
86
84
|
if (rates.length === 0) {
|
|
87
85
|
onNoRates && onNoRates({ fromState, toState });
|
|
88
86
|
}
|
|
87
|
+
|
|
89
88
|
// Discard bad provider rates
|
|
90
|
-
let rateError: Error | null | undefined = null;
|
|
89
|
+
let rateError: Error | CustomMinOrMaxError | null | undefined = null;
|
|
91
90
|
rates = rates.reduce<ExchangeRate[]>((acc, rate) => {
|
|
92
91
|
rateError = rateError ?? rate.error;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* If we have an error linked to the ammount, this error takes
|
|
95
|
+
* precedence over the other (like a "CurrencyNotSupportedError" one
|
|
96
|
+
* for example)
|
|
97
|
+
*/
|
|
98
|
+
if (
|
|
99
|
+
rateError?.name !== rate.error?.name &&
|
|
100
|
+
(rate.error?.name === "SwapExchangeRateAmountTooLow" ||
|
|
101
|
+
rate.error?.name === "SwapExchangeRateAmountTooHigh")
|
|
102
|
+
) {
|
|
103
|
+
rateError = rate.error;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (
|
|
107
|
+
rateError?.name === rate.error?.name &&
|
|
108
|
+
(rate.error?.name === "SwapExchangeRateAmountTooLow" ||
|
|
109
|
+
rate.error?.name === "SwapExchangeRateAmountTooHigh")
|
|
110
|
+
) {
|
|
111
|
+
/**
|
|
112
|
+
* Comparison pivot, depending on the order in which we want to sort errors
|
|
113
|
+
* If the order is ascending, the pivot is -1, otherwise it's 1
|
|
114
|
+
* Based on returns from https://mikemcl.github.io/bignumber.js/#cmp
|
|
115
|
+
*/
|
|
116
|
+
const cmp =
|
|
117
|
+
rateError?.name === "SwapExchangeRateAmountTooLow" ? -1 : 1;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* If the amount is too low, the user should put at least the
|
|
121
|
+
* minimum amount possible
|
|
122
|
+
* If the amount is too high, the user should put at most the
|
|
123
|
+
* maximum amount possible
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
rateError =
|
|
127
|
+
(rateError as CustomMinOrMaxError).amount.comparedTo(
|
|
128
|
+
(rate.error as CustomMinOrMaxError)?.amount
|
|
129
|
+
) === cmp
|
|
130
|
+
? rateError
|
|
131
|
+
: rate.error;
|
|
132
|
+
}
|
|
133
|
+
|
|
93
134
|
return rate.error ? acc : [...acc, rate];
|
|
94
135
|
}, []);
|
|
136
|
+
|
|
95
137
|
if (rates.length === 0 && rateError) {
|
|
96
138
|
// If all the rates are in error
|
|
97
139
|
dispatchRates({ type: "error", payload: rateError });
|
|
98
140
|
} else {
|
|
99
141
|
dispatchRates({ type: "set", payload: rates });
|
|
100
|
-
|
|
101
|
-
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Select the first rate returned by the API. Should be the prefered
|
|
145
|
+
* rate for the user. Rate ordering logic is handeled on backend side
|
|
146
|
+
*/
|
|
147
|
+
const rate = rates?.length > 0 ? rates[0] : null;
|
|
148
|
+
|
|
149
|
+
setExchangeRate && setExchangeRate(rate);
|
|
102
150
|
}
|
|
103
151
|
} catch (error) {
|
|
104
152
|
!abort && dispatchRates({ type: "error", payload: error });
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AmountRequired } from "@ledgerhq/errors";
|
|
2
2
|
import { BigNumber } from "bignumber.js";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import {
|
|
5
|
+
RatesReducerState,
|
|
6
|
+
useFromState,
|
|
7
|
+
useProviderRates,
|
|
8
|
+
useToState,
|
|
9
|
+
} from ".";
|
|
3
10
|
import useBridgeTransaction, {
|
|
4
11
|
Result as UseBridgeTransactionReturnType,
|
|
5
12
|
} from "../../../bridge/useBridgeTransaction";
|
|
6
13
|
import type {
|
|
7
14
|
Account,
|
|
15
|
+
CryptoCurrency,
|
|
8
16
|
TokenAccount,
|
|
9
17
|
TokenCurrency,
|
|
10
|
-
CryptoCurrency,
|
|
11
18
|
} from "../../../types";
|
|
12
19
|
import { ExchangeRate } from "../types";
|
|
13
|
-
import { AmountRequired } from "@ledgerhq/errors";
|
|
14
|
-
import { useUpdateMaxAmount } from "./useUpdateMaxAmount";
|
|
15
|
-
import {
|
|
16
|
-
RatesReducerState,
|
|
17
|
-
useFromState,
|
|
18
|
-
useProviderRates,
|
|
19
|
-
useToState,
|
|
20
|
-
} from ".";
|
|
21
20
|
import { useReverseAccounts } from "./useReverseAccounts";
|
|
21
|
+
import { useUpdateMaxAmount } from "./useUpdateMaxAmount";
|
|
22
22
|
|
|
23
23
|
export type SwapSelectorStateType = {
|
|
24
24
|
currency: null | undefined | TokenCurrency | CryptoCurrency;
|
|
@@ -65,6 +65,7 @@ export type OnNoRatesCallback = (arg: {
|
|
|
65
65
|
export type SetExchangeRateCallback = (
|
|
66
66
|
exchangeRate?: ExchangeRate | null
|
|
67
67
|
) => void;
|
|
68
|
+
export type SetIsSendMaxLoading = (boolean) => void;
|
|
68
69
|
|
|
69
70
|
export const useFromAmountError = (
|
|
70
71
|
errors: Record<string, Error | undefined>
|
|
@@ -82,16 +83,16 @@ export const useFromAmountError = (
|
|
|
82
83
|
|
|
83
84
|
export const useSwapTransaction = ({
|
|
84
85
|
accounts,
|
|
85
|
-
exchangeRate,
|
|
86
86
|
setExchangeRate,
|
|
87
|
+
setIsSendMaxLoading,
|
|
87
88
|
defaultCurrency = selectorStateDefaultValues.currency,
|
|
88
89
|
defaultAccount = selectorStateDefaultValues.account,
|
|
89
90
|
defaultParentAccount = selectorStateDefaultValues.parentAccount,
|
|
90
91
|
onNoRates,
|
|
91
92
|
}: {
|
|
92
93
|
accounts?: Account[];
|
|
93
|
-
exchangeRate?: ExchangeRate;
|
|
94
94
|
setExchangeRate?: SetExchangeRateCallback;
|
|
95
|
+
setIsSendMaxLoading?: SetIsSendMaxLoading;
|
|
95
96
|
defaultCurrency?: SwapSelectorStateType["currency"];
|
|
96
97
|
defaultAccount?: SwapSelectorStateType["account"];
|
|
97
98
|
defaultParentAccount?: SwapSelectorStateType["parentAccount"];
|
|
@@ -136,12 +137,12 @@ export const useSwapTransaction = ({
|
|
|
136
137
|
parentAccount: fromParentAccount,
|
|
137
138
|
transaction,
|
|
138
139
|
feesStrategy: transaction?.feesStrategy,
|
|
140
|
+
setIsSendMaxLoading,
|
|
139
141
|
});
|
|
140
142
|
|
|
141
143
|
const { rates, refetchRates } = useProviderRates({
|
|
142
144
|
fromState,
|
|
143
145
|
toState,
|
|
144
|
-
exchangeRate,
|
|
145
146
|
transaction,
|
|
146
147
|
onNoRates,
|
|
147
148
|
setExchangeRate,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { renderHook, act } from "@testing-library/react-hooks";
|
|
2
1
|
import { getCryptoCurrencyById, getTokenById } from "@ledgerhq/cryptoassets";
|
|
3
|
-
import {
|
|
4
|
-
import { genAccount, genTokenAccount } from "../../../mock/account";
|
|
2
|
+
import { act, renderHook } from "@testing-library/react-hooks";
|
|
5
3
|
import BigNumber from "bignumber.js";
|
|
6
|
-
import ethBridge from "../../../families/ethereum/bridge/mock";
|
|
7
4
|
import { checkAccountSupported } from "../../../account/support";
|
|
5
|
+
import ethBridge from "../../../families/ethereum/bridge/mock";
|
|
6
|
+
import { genAccount, genTokenAccount } from "../../../mock/account";
|
|
7
|
+
import { useUpdateMaxAmount, ZERO } from "./useUpdateMaxAmount";
|
|
8
8
|
|
|
9
9
|
// Needs to be mocked since userSupportedCurrencies is initially empty.
|
|
10
10
|
jest.mock("../../../account/support");
|
|
@@ -77,6 +77,33 @@ describe("updateAmountUsingMax", () => {
|
|
|
77
77
|
expect(setFromAmount.mock.calls[0][0]).toBe(ZERO);
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
it("should toggle setIsSendMaxLoading", async () => {
|
|
81
|
+
const setIsSendMaxLoading = jest.fn();
|
|
82
|
+
|
|
83
|
+
const amount = new BigNumber(0.5);
|
|
84
|
+
mockedEstimateMaxSpendable.mockResolvedValue(amount);
|
|
85
|
+
const { result } = renderHook(useUpdateMaxAmount, {
|
|
86
|
+
initialProps: { ...defaultProps, setIsSendMaxLoading },
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
act(() => result.current.toggleMax());
|
|
90
|
+
expect(setIsSendMaxLoading).toBeCalledTimes(1);
|
|
91
|
+
expect(setIsSendMaxLoading).toBeCalledWith(true);
|
|
92
|
+
setIsSendMaxLoading.mockClear();
|
|
93
|
+
|
|
94
|
+
// Lest resort solution, since waitFor and other helpers will not work here.
|
|
95
|
+
await wait();
|
|
96
|
+
|
|
97
|
+
expect(setIsSendMaxLoading).toBeCalledTimes(1);
|
|
98
|
+
expect(setIsSendMaxLoading).toBeCalledWith(false);
|
|
99
|
+
setIsSendMaxLoading.mockClear();
|
|
100
|
+
|
|
101
|
+
act(() => result.current.toggleMax());
|
|
102
|
+
expect(setIsSendMaxLoading).toBeCalledTimes(1);
|
|
103
|
+
expect(setIsSendMaxLoading).toBeCalledWith(false);
|
|
104
|
+
setIsSendMaxLoading.mockClear();
|
|
105
|
+
});
|
|
106
|
+
|
|
80
107
|
it("should update the max amount whenever the dependencies change", async () => {
|
|
81
108
|
const { result, rerender } = renderHook(useUpdateMaxAmount, {
|
|
82
109
|
initialProps: defaultProps,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
1
2
|
import { useCallback, useEffect, useState } from "react";
|
|
2
3
|
import { getAccountBridge } from "../../../bridge";
|
|
3
|
-
|
|
4
|
+
import { Transaction } from "../../../generated/types";
|
|
4
5
|
import {
|
|
5
|
-
|
|
6
|
+
SetIsSendMaxLoading,
|
|
6
7
|
SwapDataType,
|
|
7
8
|
SwapSelectorStateType,
|
|
9
|
+
SwapTransactionType,
|
|
8
10
|
} from "./useSwapTransaction";
|
|
9
|
-
import { Transaction } from "../../../generated/types";
|
|
10
|
-
import BigNumber from "bignumber.js";
|
|
11
11
|
|
|
12
12
|
export const ZERO = new BigNumber(0);
|
|
13
13
|
|
|
@@ -17,27 +17,31 @@ export const useUpdateMaxAmount = ({
|
|
|
17
17
|
parentAccount,
|
|
18
18
|
transaction,
|
|
19
19
|
feesStrategy,
|
|
20
|
+
setIsSendMaxLoading,
|
|
20
21
|
}: {
|
|
21
22
|
setFromAmount: SwapTransactionType["setFromAmount"];
|
|
22
23
|
account: SwapSelectorStateType["account"];
|
|
23
24
|
parentAccount: SwapSelectorStateType["parentAccount"];
|
|
24
25
|
transaction: SwapTransactionType["transaction"];
|
|
25
26
|
feesStrategy: Transaction["feesStrategy"];
|
|
27
|
+
setIsSendMaxLoading?: SetIsSendMaxLoading;
|
|
26
28
|
}): {
|
|
27
29
|
isMaxEnabled: SwapDataType["isMaxEnabled"];
|
|
28
30
|
toggleMax: SwapTransactionType["toggleMax"];
|
|
29
31
|
} => {
|
|
30
|
-
const [isMaxEnabled,
|
|
32
|
+
const [isMaxEnabled, setIsMaxEnabled] =
|
|
33
|
+
useState<SwapDataType["isMaxEnabled"]>(false);
|
|
31
34
|
|
|
32
35
|
const toggleMax: SwapTransactionType["toggleMax"] = useCallback(
|
|
33
36
|
() =>
|
|
34
|
-
|
|
37
|
+
setIsMaxEnabled((previous) => {
|
|
35
38
|
if (previous) {
|
|
36
39
|
setFromAmount(ZERO);
|
|
40
|
+
setIsSendMaxLoading?.(false);
|
|
37
41
|
}
|
|
38
42
|
return !previous;
|
|
39
43
|
}),
|
|
40
|
-
[setFromAmount]
|
|
44
|
+
[setFromAmount, setIsSendMaxLoading]
|
|
41
45
|
);
|
|
42
46
|
|
|
43
47
|
/* UPDATE from amount to the estimate max spendable on account
|
|
@@ -47,11 +51,13 @@ export const useUpdateMaxAmount = ({
|
|
|
47
51
|
const updateAmountUsingMax = async () => {
|
|
48
52
|
if (!account) return;
|
|
49
53
|
const bridge = getAccountBridge(account, parentAccount);
|
|
54
|
+
setIsSendMaxLoading?.(true);
|
|
50
55
|
const amount = await bridge.estimateMaxSpendable({
|
|
51
56
|
account,
|
|
52
57
|
parentAccount,
|
|
53
58
|
transaction,
|
|
54
59
|
});
|
|
60
|
+
setIsSendMaxLoading?.(false);
|
|
55
61
|
setFromAmount(amount);
|
|
56
62
|
};
|
|
57
63
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { isSwapOperationPending } from ".";
|
|
2
|
+
|
|
3
|
+
describe("swap/index", () => {
|
|
4
|
+
describe("isSwapOperationPending", () => {
|
|
5
|
+
test("should return false for ok status", () => {
|
|
6
|
+
const result = isSwapOperationPending("finished");
|
|
7
|
+
|
|
8
|
+
expect(result).toBe(false);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("should return false for ko status", () => {
|
|
12
|
+
const result = isSwapOperationPending("refunded");
|
|
13
|
+
|
|
14
|
+
expect(result).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("should return true for other status", () => {
|
|
18
|
+
const result = isSwapOperationPending("RANDOM_STATUS");
|
|
19
|
+
|
|
20
|
+
expect(result).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("should return true if not status provided", () => {
|
|
24
|
+
/**
|
|
25
|
+
* Since this TS function can be used in loosly typed JS code, we need
|
|
26
|
+
* to test the case of the parameter not being provided
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
const result = isSwapOperationPending();
|
|
32
|
+
|
|
33
|
+
expect(result).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -1,45 +1,60 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import getExchangeRates from "./getExchangeRates";
|
|
3
|
-
import getStatus from "./getStatus";
|
|
4
|
-
import getProviders from "./getProviders";
|
|
5
|
-
import getCompleteSwapHistory from "./getCompleteSwapHistory";
|
|
6
|
-
import getKYCStatus from "./getKYCStatus";
|
|
7
|
-
import submitKYC from "./submitKYC";
|
|
8
|
-
import initSwap from "./initSwap";
|
|
1
|
+
import type { SwapProviderConfig } from "../";
|
|
9
2
|
import { getEnv } from "../../env";
|
|
10
3
|
import {
|
|
11
|
-
|
|
12
|
-
JSONDecodeError,
|
|
13
|
-
NoIPHeaderError,
|
|
14
|
-
CurrencyNotSupportedError,
|
|
15
|
-
CurrencyDisabledError,
|
|
4
|
+
AccessDeniedError,
|
|
16
5
|
CurrencyDisabledAsInputError,
|
|
17
6
|
CurrencyDisabledAsOutputError,
|
|
7
|
+
CurrencyDisabledError,
|
|
18
8
|
CurrencyNotSupportedByProviderError,
|
|
9
|
+
CurrencyNotSupportedError,
|
|
10
|
+
JSONDecodeError,
|
|
11
|
+
JSONRPCResponseError,
|
|
12
|
+
NoIPHeaderError,
|
|
13
|
+
NotImplementedError,
|
|
19
14
|
TradeMethodNotSupportedError,
|
|
20
15
|
UnexpectedError,
|
|
21
|
-
NotImplementedError,
|
|
22
16
|
ValidationError,
|
|
23
|
-
AccessDeniedError,
|
|
24
17
|
} from "../../errors";
|
|
18
|
+
import checkQuote from "./checkQuote";
|
|
19
|
+
import getCompleteSwapHistory from "./getCompleteSwapHistory";
|
|
20
|
+
import getExchangeRates from "./getExchangeRates";
|
|
21
|
+
import getKYCStatus from "./getKYCStatus";
|
|
22
|
+
import getProviders from "./getProviders";
|
|
23
|
+
import initSwap from "./initSwap";
|
|
24
|
+
import { postSwapAccepted, postSwapCancelled } from "./postSwapState";
|
|
25
|
+
import submitKYC from "./submitKYC";
|
|
25
26
|
|
|
26
27
|
export const operationStatusList = {
|
|
27
28
|
finishedOK: ["finished"],
|
|
28
29
|
finishedKO: ["refunded"],
|
|
29
|
-
pending: ["pending", "onhold", "expired"],
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
// A swap operation is considered pending if it is not in a finishedOK or finishedKO state
|
|
33
|
+
export const isSwapOperationPending: (status: string) => boolean = (status) =>
|
|
34
|
+
!operationStatusList.finishedOK.includes(status) &&
|
|
35
|
+
!operationStatusList.finishedKO.includes(status);
|
|
36
|
+
|
|
32
37
|
const getSwapAPIBaseURL: () => string = () => getEnv("SWAP_API_BASE");
|
|
33
38
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
const ftx = {
|
|
40
|
+
nameAndPubkey: Buffer.concat([
|
|
41
|
+
Buffer.from([3]),
|
|
42
|
+
Buffer.from("FTX", "ascii"),
|
|
43
|
+
Buffer.from(
|
|
44
|
+
"04c89f3e48cde252f6cd6fcccc47c2f6ca6cf05f9f921703d31b7a7dddbf0bd6a690744662fe599f8761612021ba1fc0e8a5a4b7d5910c625b6dd09aa40762e5cd",
|
|
45
|
+
"hex"
|
|
46
|
+
),
|
|
47
|
+
]),
|
|
48
|
+
signature: Buffer.from(
|
|
49
|
+
"3044022029c0fb80d6e524f811f30cc04a349fa7f8896ce1ba84010da55f7be5eb9d528802202727985361cab969ad9b4f56570f3f6120c1d77d04ba10e5d99366d8eecee8e2",
|
|
50
|
+
"hex"
|
|
51
|
+
),
|
|
52
|
+
curve: "secp256k1",
|
|
53
|
+
needsKYC: true,
|
|
54
|
+
needsBearerToken: true,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const swapProviders: Record<string, SwapProviderConfig> = {
|
|
43
58
|
changelly: {
|
|
44
59
|
nameAndPubkey: Buffer.from(
|
|
45
60
|
"094368616e67656c6c790480d7c0d3a9183597395f58dda05999328da6f18fabd5cda0aff8e8e3fc633436a2dbf48ecb23d40df7c3c7d3e774b77b4b5df0e9f7e08cf1cdf2dba788eb085b",
|
|
@@ -51,6 +66,7 @@ const swapProviders: Record<
|
|
|
51
66
|
),
|
|
52
67
|
curve: "secpk256k1",
|
|
53
68
|
needsKYC: false,
|
|
69
|
+
needsBearerToken: false,
|
|
54
70
|
},
|
|
55
71
|
wyre: {
|
|
56
72
|
nameAndPubkey: Buffer.from(
|
|
@@ -63,12 +79,13 @@ const swapProviders: Record<
|
|
|
63
79
|
),
|
|
64
80
|
curve: "secpk256k1",
|
|
65
81
|
needsKYC: true,
|
|
82
|
+
needsBearerToken: false,
|
|
66
83
|
},
|
|
84
|
+
ftx,
|
|
85
|
+
ftxus: ftx,
|
|
67
86
|
};
|
|
68
87
|
|
|
69
|
-
const
|
|
70
|
-
providerName: string
|
|
71
|
-
): ExchangeProviderNameAndSignature => {
|
|
88
|
+
const getProviderConfig = (providerName: string): SwapProviderConfig => {
|
|
72
89
|
const res = swapProviders[providerName.toLowerCase()];
|
|
73
90
|
|
|
74
91
|
if (!res) {
|
|
@@ -78,6 +95,8 @@ const getProviderNameAndSignature = (
|
|
|
78
95
|
return res;
|
|
79
96
|
};
|
|
80
97
|
|
|
98
|
+
export const getAvailableProviders = (): string[] => Object.keys(swapProviders);
|
|
99
|
+
|
|
81
100
|
const USStates = {
|
|
82
101
|
AL: "Alabama",
|
|
83
102
|
AK: "Alaska",
|
|
@@ -160,14 +179,16 @@ export const getSwapAPIError = (errorCode: number, errorMessage?: string) => {
|
|
|
160
179
|
|
|
161
180
|
export {
|
|
162
181
|
getSwapAPIBaseURL,
|
|
163
|
-
|
|
182
|
+
getProviderConfig,
|
|
164
183
|
getProviders,
|
|
165
|
-
getStatus,
|
|
166
184
|
getExchangeRates,
|
|
167
185
|
getCompleteSwapHistory,
|
|
186
|
+
postSwapAccepted,
|
|
187
|
+
postSwapCancelled,
|
|
168
188
|
initSwap,
|
|
169
189
|
getKYCStatus,
|
|
170
190
|
submitKYC,
|
|
191
|
+
checkQuote,
|
|
171
192
|
USStates,
|
|
172
193
|
countries,
|
|
173
194
|
};
|