Skip to content

Commit

Permalink
Use the price manager when converting fees
Browse files Browse the repository at this point in the history
  • Loading branch information
skubarenko committed Aug 30, 2022
1 parent 5472129 commit 00e2c60
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 92 deletions.
59 changes: 26 additions & 33 deletions src/atomex/atomexSwapPreviewManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import BigNumber from 'bignumber.js';
import type { AtomexProtocolV1, CurrencyInfo, FeesInfo } from '../blockchain/index';
import type { Currency } from '../common/index';
import { Mutable, Cache, InMemoryCache } from '../core/index';
import { ExchangeSymbolsProvider, ordersHelper, type NormalizedOrderPreviewParameters, type OrderPreview } from '../exchange/index';
import { ExchangeSymbolsProvider, ordersHelper, symbolsHelper, type NormalizedOrderPreviewParameters, type OrderPreview } from '../exchange/index';
import { converters } from '../utils';
import type { AtomexContext } from './atomexContext';
import type { NormalizedSwapPreviewParameters, SwapPreview, SwapPreviewFee, SwapPreviewParameters } from './models/index';

Expand Down Expand Up @@ -262,9 +263,9 @@ export class AtomexSwapPreviewManager {
max: fromInitiateFees.max,
};
const makerFee = await this.calculateMakerFees(
fromCurrencyInfo.currency.id,
fromNativeCurrencyInfo.currency.id,
toNativeCurrencyInfo.currency.id,
fromCurrencyInfo.currency,
fromNativeCurrencyInfo.currency,
toNativeCurrencyInfo.currency,
toInitiateFees,
fromRedeemFees
);
Expand Down Expand Up @@ -297,15 +298,15 @@ export class AtomexSwapPreviewManager {
}

protected async calculateMakerFees(
fromCurrencyId: Currency['id'],
fromNativeCurrencyId: Currency['id'],
toNativeCurrencyId: Currency['id'],
fromCurrency: Currency,
fromNativeCurrency: Currency,
toNativeCurrency: Currency,
toInitiateFees: FeesInfo,
fromRedeemFees: FeesInfo
): Promise<SwapPreviewFee> {
const toInitiateFeeConversationPromise = this.convertFeesToFromCurrency(toInitiateFees, toNativeCurrencyId, fromCurrencyId);
const fromRedeemFeeConversationPromise = fromCurrencyId !== fromNativeCurrencyId
? this.convertFeesToFromCurrency(fromRedeemFees, fromNativeCurrencyId, fromCurrencyId)
const toInitiateFeeConversationPromise = this.convertFeesFromNativeCurrencyToCustom(toInitiateFees, toNativeCurrency, fromCurrency);
const fromRedeemFeeConversationPromise = fromCurrency !== fromNativeCurrency
? this.convertFeesFromNativeCurrencyToCustom(fromRedeemFees, fromNativeCurrency, fromCurrency)
: undefined;

let estimatedToInitiateFeesInFromCurrency: BigNumber;
Expand All @@ -329,37 +330,29 @@ export class AtomexSwapPreviewManager {

return {
name: 'maker-fee',
currencyId: fromCurrencyId,
currencyId: fromCurrency.id,
estimated: estimatedToInitiateFeesInFromCurrency.plus(estimatedFromRedeemFeesInFromCurrency),
max: maxToInitiateFeesInFromCurrency.plus(maxFromRedeemFeesInFromCurrency)
};
}

protected async convertFeesToFromCurrency(fees: FeesInfo, from: Currency['id'], to: Currency['id']): Promise<FeesInfo> {
const [estimatedInFromCurrency, maxInFromCurrency] = await Promise.all([
this.atomexContext.managers.exchangeManager.getOrderPreview({
type: 'SolidFillOrKill',
from,
to,
amount: fees.estimated,
isFromAmount: true
}).then(orderPreview => orderPreview?.to.amount),

this.atomexContext.managers.exchangeManager.getOrderPreview({
type: 'SolidFillOrKill',
from,
to,
amount: fees.max,
isFromAmount: true
}).then(orderPreview => orderPreview?.to.amount)
]);
protected async convertFeesFromNativeCurrencyToCustom(fees: FeesInfo, nativeCurrency: Currency, customCurrency: Currency): Promise<FeesInfo> {
const price = await this.atomexContext.managers.priceManager.getPrice({ baseCurrency: nativeCurrency, quoteCurrency: customCurrency, provider: 'atomex' });
if (!price)
throw new Error(`It's no possible to convert fees from "${nativeCurrency.id}" to "${customCurrency.id}" currency`);

if (!estimatedInFromCurrency || !maxInFromCurrency)
throw new Error(`It's no possible to convert fees from "${from}" to "${to}" currency`);
const [exchangeSymbol, _] = symbolsHelper.convertFromAndToCurrenciesToSymbolAndSide(
this.atomexContext.providers.exchangeSymbolsProvider.getSymbolsMap(),
nativeCurrency.id,
customCurrency.id
);
const customCurrencyDecimals = exchangeSymbol.baseCurrency === customCurrency.id ? exchangeSymbol.decimals.baseCurrency : exchangeSymbol.decimals.quoteCurrency;
const estimatedInCustomCurrency = converters.toFixedBigNumber(fees.estimated.multipliedBy(price), customCurrencyDecimals, BigNumber.ROUND_CEIL);
const maxInCustomCurrency = converters.toFixedBigNumber(fees.max.multipliedBy(price), customCurrencyDecimals, BigNumber.ROUND_CEIL);

return {
estimated: estimatedInFromCurrency,
max: maxInFromCurrency
estimated: estimatedInCustomCurrency,
max: maxInCustomCurrency
};
}

Expand Down
46 changes: 44 additions & 2 deletions tests/atomex/swapPreview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import BigNumber from 'bignumber.js';

import { ethereumTestnetCurrencies } from '../../src/ethereum';
import { testnetEthereumWeb3AtomexProtocolV1Options } from '../../src/ethereum/config';
import type { OrderBook } from '../../src/exchange';
import { Atomex } from '../../src/index';
import { tezosTestnetCurrencies } from '../../src/tezos';
import { testnetTezosTaquitoAtomexProtocolV1Options } from '../../src/tezos/config/atomexProtocol';
Expand Down Expand Up @@ -52,14 +53,14 @@ describe('Atomex | Swap Preview', () => {
test.each(swapPreviewWithoutAccountTestCases)(
'getting swap preview without account: %s\n\tSwap Preview Parameters: %j',
async (_, swapPreviewParameters, expectedSwapPreview, environment) => {
mockedAtomexContext.managers.priceManager.getAveragePrice.mockResolvedValue(new BigNumber(1));
mockedAtomexContext.services.exchangeService.getSymbols.mockResolvedValue(environment.symbols);
mockedAtomexContext.services.exchangeService.getOrderBook.mockImplementation(symbol => {
if (typeof symbol === 'string')
return Promise.resolve(environment.orderBooks.find(ob => ob.symbol === symbol));

throw new Error('Expected symbol');
});
mockPriceManagerByOrderBook(environment.orderBooks);
mockAtomexProtocolV1Fees(environment.atomexProtocolFees);
await atomex.start();

Expand All @@ -72,7 +73,6 @@ describe('Atomex | Swap Preview', () => {
test.each(swapPreviewWithAccountTestCases)(
'getting swap preview with account: %s\n\tSwap Preview Parameters: %j',
async (_, swapPreviewParameters, expectedSwapPreview, environment) => {
mockedAtomexContext.managers.priceManager.getAveragePrice.mockResolvedValue(new BigNumber(1));
mockedAtomexContext.services.exchangeService.getSymbols.mockResolvedValue(environment.symbols);
mockedAtomexContext.services.exchangeService.getOrderBook.mockImplementation(symbol => {
if (typeof symbol === 'string')
Expand All @@ -81,6 +81,7 @@ describe('Atomex | Swap Preview', () => {
throw new Error('Expected symbol');
});

mockPriceManagerByOrderBook(environment.orderBooks);
mockAccounts(environment.accounts);
mockAtomexProtocolV1Fees(environment.atomexProtocolFees);
await atomex.start();
Expand Down Expand Up @@ -132,6 +133,47 @@ describe('Atomex | Swap Preview', () => {
});
};

const mockPriceManagerByOrderBook = (orderBooks: OrderBook[]) => {
const getMiddlePriceByOrderBook = (orderBook: OrderBook): BigNumber | undefined => {
let buyPrice: BigNumber | undefined;
let sellPrice: BigNumber | undefined;

for (const entry of orderBook.entries) {
if (entry.side === 'Buy') {
buyPrice = entry.price;
continue;
}

if (entry.side === 'Sell') {
sellPrice = entry.price;
break;
}
}

return buyPrice && sellPrice
? buyPrice.plus(sellPrice).div(2)
: undefined;

};

mockedAtomexContext.managers.priceManager.getPrice.mockImplementation(async params => {
if (params.provider !== 'atomex')
return undefined;

const baseCurrencyId = typeof params.baseCurrency === 'string' ? params.baseCurrency : params.baseCurrency.id;
const quoteCurrencyId = typeof params.quoteCurrency === 'string' ? params.quoteCurrency : params.quoteCurrency.id;

for (const orderBook of orderBooks) {
if (orderBook.baseCurrency === baseCurrencyId && orderBook.quoteCurrency === quoteCurrencyId)
return getMiddlePriceByOrderBook(orderBook);
else if (orderBook.baseCurrency === quoteCurrencyId && orderBook.quoteCurrency === baseCurrencyId)
return getMiddlePriceByOrderBook(orderBook)?.pow(-1);
}

return undefined;
});
};

const mockAtomexProtocolV1Fees = (allFees: AtomexProtocolV1Fees) => {
for (const [currencyId, fees] of Object.entries(allFees)) {
const blockchainId = getBlockchainNameByCurrencyId(currencyId);
Expand Down
50 changes: 25 additions & 25 deletions tests/atomex/testCases/swapPreviewWithAccountTestCases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
price: new BigNumber('0.000948468')
},
max: {
amount: new BigNumber('90.58509'),
amount: new BigNumber('90.445209'),
price: new BigNumber('0.000948468')
}
},
Expand All @@ -58,7 +58,7 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
price: new BigNumber('1054.331827747')
},
max: {
amount: new BigNumber('0.085917059'),
amount: new BigNumber('0.085784386'),
price: new BigNumber('1054.331827747')
}
},
Expand All @@ -73,8 +73,8 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
name: 'maker-fee',
currencyId: 'XTZ',
estimated: new BigNumber('5.419531'),
max: new BigNumber('9.27491')
estimated: new BigNumber('5.501128'),
max: new BigNumber('9.414791')
},
{
name: 'redeem-reward',
Expand All @@ -93,8 +93,8 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
name: 'maker-fee',
currencyId: 'XTZ',
estimated: new BigNumber('5.419531'),
max: new BigNumber('9.27491')
estimated: new BigNumber('5.501128'),
max: new BigNumber('9.414791')
},
{
name: 'refund-fee',
Expand Down Expand Up @@ -150,7 +150,7 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
price: new BigNumber('0.000948468')
},
max: {
amount: new BigNumber('90.58509'),
amount: new BigNumber('90.445209'),
price: new BigNumber('0.000948468')
}
},
Expand All @@ -166,7 +166,7 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
price: new BigNumber('1054.331827747')
},
max: {
amount: new BigNumber('0.085917059'),
amount: new BigNumber('0.085784386'),
price: new BigNumber('1054.331827747')
}
},
Expand All @@ -181,8 +181,8 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
name: 'maker-fee',
currencyId: 'XTZ',
estimated: new BigNumber('5.419531'),
max: new BigNumber('9.27491')
estimated: new BigNumber('5.501128'),
max: new BigNumber('9.414791')
},
{
name: 'redeem-fee',
Expand All @@ -201,8 +201,8 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
name: 'maker-fee',
currencyId: 'XTZ',
estimated: new BigNumber('5.419531'),
max: new BigNumber('9.27491')
estimated: new BigNumber('5.501128'),
max: new BigNumber('9.414791')
},
{
name: 'refund-fee',
Expand Down Expand Up @@ -289,8 +289,8 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
name: 'maker-fee',
currencyId: 'XTZ',
estimated: new BigNumber('5.419531'),
max: new BigNumber('9.27491')
estimated: new BigNumber('5.501128'),
max: new BigNumber('9.414791')
},
{
name: 'redeem-reward',
Expand All @@ -309,8 +309,8 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
name: 'maker-fee',
currencyId: 'XTZ',
estimated: new BigNumber('5.419531'),
max: new BigNumber('9.27491')
estimated: new BigNumber('5.501128'),
max: new BigNumber('9.414791')
},
{
name: 'refund-fee',
Expand All @@ -324,7 +324,7 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
id: 'not-enough-funds-network-fee',
data: {
requiredAmount: new BigNumber('9.41491')
requiredAmount: new BigNumber('9.554791')
}
},
{
Expand Down Expand Up @@ -406,8 +406,8 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
name: 'maker-fee',
currencyId: 'USDT_XTZ',
estimated: new BigNumber('8.541664'),
max: new BigNumber('14.585536')
estimated: new BigNumber('8.615812'),
max: new BigNumber('14.712051')
},
{
name: 'redeem-reward',
Expand All @@ -426,8 +426,8 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
name: 'maker-fee',
currencyId: 'USDT_XTZ',
estimated: new BigNumber('8.541664'),
max: new BigNumber('14.585536')
estimated: new BigNumber('8.615812'),
max: new BigNumber('14.712051')
},
{
name: 'refund-fee',
Expand Down Expand Up @@ -522,8 +522,8 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
name: 'maker-fee',
currencyId: 'USDT_XTZ',
estimated: new BigNumber('8.541664'),
max: new BigNumber('14.585536')
estimated: new BigNumber('8.615812'),
max: new BigNumber('14.712051')
},
{
name: 'redeem-reward',
Expand All @@ -542,8 +542,8 @@ const swapPreviewWithoutAccountTestCases: ReadonlyArray<[
{
name: 'maker-fee',
currencyId: 'USDT_XTZ',
estimated: new BigNumber('8.541664'),
max: new BigNumber('14.585536')
estimated: new BigNumber('8.615812'),
max: new BigNumber('14.712051')
},
{
name: 'refund-fee',
Expand Down
Loading

0 comments on commit 00e2c60

Please sign in to comment.