@ledgerhq/live-common
Version:
Common ground for the Ledger Live apps
122 lines • 5.63 kB
JavaScript
import network from "@ledgerhq/live-network/network";
import { BigNumber } from "bignumber.js";
import { getAccountCurrency } from "../../account";
import { formatCurrencyUnit } from "../../currencies";
import { SwapExchangeRateAmountTooHigh, SwapExchangeRateAmountTooLow, SwapExchangeRateAmountTooLowOrTooHigh, } from "../../errors";
import { getSwapAPIBaseURL, getSwapAPIError, getSwapUserIP } from "./";
import { mockGetExchangeRates } from "./mock";
import { isIntegrationTestEnv } from "./utils/isIntegrationTestEnv";
import { getSwapProvider } from "../providers/swap";
const getExchangeRates = async ({ exchange, transaction, currencyTo, providers = [], timeout, timeoutErrorMessage, }) => {
if (isIntegrationTestEnv())
return mockGetExchangeRates(exchange, transaction, currencyTo);
const from = getAccountCurrency(exchange.fromAccount).id;
const unitFrom = getAccountCurrency(exchange.fromAccount).units[0];
const to = (currencyTo ?? getAccountCurrency(exchange.toAccount)).id;
const unitTo = (currencyTo && currencyTo.units[0]) ?? getAccountCurrency(exchange.toAccount).units[0];
const amountFrom = transaction.amount;
const tenPowMagnitude = new BigNumber(10).pow(unitFrom.magnitude);
const apiAmount = new BigNumber(amountFrom).div(tenPowMagnitude);
const providerList = providers
.filter(provider => provider.pairs.some(pair => pair.from === from && pair.to === to))
.map(item => item.provider);
const request = {
from,
to,
amountFrom: apiAmount.toString(),
providers: providerList,
};
const headers = getSwapUserIP();
const res = await network({
method: "POST",
url: `${getSwapAPIBaseURL()}/rate`,
...(timeout ? { timeout } : {}),
...(timeoutErrorMessage ? { timeoutErrorMessage } : {}),
data: request,
...(headers !== undefined ? headers : {}),
});
const rates = res.data.map(async (responseData) => {
const { rate: maybeRate, payoutNetworkFees: maybePayoutNetworkFees, rateId, provider, providerType, amountFrom, amountTo, tradeMethod, providerURL, expirationTime, } = responseData;
const error = await inferError(apiAmount, unitFrom, responseData);
if (error) {
return {
provider,
tradeMethod,
error,
};
}
const payoutNetworkFees = new BigNumber(maybePayoutNetworkFees || 0);
const magnitudeAwarePayoutNetworkFees = payoutNetworkFees.times(new BigNumber(10).pow(unitTo.magnitude));
const rate = maybeRate
? new BigNumber(maybeRate)
: new BigNumber(amountTo).minus(payoutNetworkFees).div(amountFrom);
// NB Allows us to simply multiply satoshi values from/to
const magnitudeAwareRate = rate.div(new BigNumber(10).pow(unitFrom.magnitude - unitTo.magnitude));
const toAmount = new BigNumber(amountTo).minus(payoutNetworkFees);
const magnitudeAwareToAmount = toAmount.times(new BigNumber(10).pow(unitTo.magnitude));
// Nb no longer need to break it down on UI
const out = {
magnitudeAwareRate,
provider,
providerType,
rate,
rateId,
expirationTime,
toAmount: magnitudeAwareToAmount,
tradeMethod,
providerURL,
};
if (tradeMethod === "fixed") {
return {
...out,
payoutNetworkFees: magnitudeAwarePayoutNetworkFees,
rateId,
};
}
else {
return {
...out,
payoutNetworkFees: magnitudeAwarePayoutNetworkFees,
};
}
});
return rates;
};
const inferError = async (apiAmount, unitFrom, responseData) => {
const tenPowMagnitude = new BigNumber(10).pow(unitFrom.magnitude);
const { amountTo, minAmountFrom, maxAmountFrom, errorCode, errorMessage, provider, status } = responseData;
const isDex = (await getSwapProvider(provider)).type === "DEX";
// DEX quotes are out of limits error. We do not know if it is a low or high limit, neither the amount.
if ((!minAmountFrom || !maxAmountFrom) && status === "error" && errorCode !== 300 && isDex) {
return new SwapExchangeRateAmountTooLowOrTooHigh(undefined, {
message: "",
});
}
if (!amountTo) {
// We are in an error case regardless of api version.
if (errorCode) {
return getSwapAPIError(errorCode, errorMessage);
}
// For out of range errors we will have a min/max pairing
const hasAmountLimit = minAmountFrom || maxAmountFrom;
if (hasAmountLimit) {
const isTooSmall = minAmountFrom ? new BigNumber(apiAmount).lte(minAmountFrom) : false;
const MinOrMaxError = isTooSmall
? SwapExchangeRateAmountTooLow
: SwapExchangeRateAmountTooHigh;
const key = isTooSmall ? "minAmountFromFormatted" : "maxAmountFromFormatted";
const amount = isTooSmall ? minAmountFrom : maxAmountFrom;
return new MinOrMaxError(undefined, {
[key]: formatCurrencyUnit(unitFrom, new BigNumber(amount).times(tenPowMagnitude), {
alwaysShowSign: false,
disableRounding: true,
showCode: true,
}),
amount: new BigNumber(amount),
});
}
}
return;
};
export default getExchangeRates;
//# sourceMappingURL=getExchangeRates.js.map