Skip to content

Commit

Permalink
Take the current involved swaps in the getting swap preview
Browse files Browse the repository at this point in the history
  • Loading branch information
skubarenko committed Aug 30, 2022
1 parent b5ecab3 commit 7fd529b
Show file tree
Hide file tree
Showing 5 changed files with 500 additions and 21 deletions.
59 changes: 46 additions & 13 deletions src/atomex/atomexSwapPreviewManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@ import type { AtomexProtocolV1, CurrencyInfo, FeesInfo } from '../blockchain/ind
import type { Currency } from '../common/index';
import { Mutable, Cache, InMemoryCache } from '../core/index';
import { ExchangeSymbolsProvider, ordersHelper, symbolsHelper, type NormalizedOrderPreviewParameters, type OrderPreview } from '../exchange/index';
import { converters } from '../utils';
import type { Swap } from '../swaps/index';
import { converters } from '../utils/index';
import type { AtomexContext } from './atomexContext';
import type { NormalizedSwapPreviewParameters, SwapPreview, SwapPreviewFee, SwapPreviewParameters } from './models/index';

interface UserInvolvedSwapsInfo {
swaps: readonly Swap[];
fromCurrencyId: string;
fromTotalAmount: BigNumber;
}

export class AtomexSwapPreviewManager {
private readonly swapPreviewFeesCache: Cache = new InMemoryCache({ absoluteExpirationMs: 10 * 1000 });
private readonly userInvolvedSwapsCache: Cache = new InMemoryCache({ absoluteExpirationMs: 10 * 1000 });

constructor(protected readonly atomexContext: AtomexContext) {
}
Expand Down Expand Up @@ -166,6 +174,7 @@ export class AtomexSwapPreviewManager {

maxOrderPreview = await this.getMaxOrderPreview(
normalizedSwapPreviewParameters,
fromAddress,
fromAvailableAmount,
fromCurrencyBalance,
fromCurrencyInfo,
Expand Down Expand Up @@ -199,27 +208,25 @@ export class AtomexSwapPreviewManager {

protected async getMaxOrderPreview(
normalizedSwapPreviewParameters: NormalizedOrderPreviewParameters,
fromAddress: string,
fromAvailableAmount: BigNumber,
fromCurrencyBalance: BigNumber,
fromCurrencyInfo: CurrencyInfo,
_fromNativeCurrencyBalance: BigNumber,
fromNativeCurrencyInfo: CurrencyInfo,
fromNativeCurrencyNetworkFee: BigNumber,
errors: Mutable<SwapPreview['errors']>,
_errors: Mutable<SwapPreview['errors']>,
_warnings: Mutable<SwapPreview['warnings']>
): Promise<OrderPreview | undefined> {
// TODO: add sum of the in progress swaps
const maxAmount = BigNumber.min(
fromCurrencyInfo.currency.id === fromNativeCurrencyInfo.currency.id
? fromCurrencyBalance.minus(fromNativeCurrencyNetworkFee)
: fromCurrencyBalance,
fromAvailableAmount
);

if (maxAmount.isLessThanOrEqualTo(0)) {
errors.push({ id: 'not-enough-funds' });
const userInvolvedSwapsInfo = await this.getUserInvolvedSwapsInfo(fromAddress, fromCurrencyInfo.currency.id);
let maxAmount = fromCurrencyInfo.currency.id === fromNativeCurrencyInfo.currency.id
? fromCurrencyBalance.minus(fromNativeCurrencyNetworkFee)
: fromCurrencyBalance;
maxAmount = maxAmount.minus(userInvolvedSwapsInfo.fromTotalAmount);
if (maxAmount.isLessThanOrEqualTo(0))
return undefined;
}

maxAmount = BigNumber.min(maxAmount, fromAvailableAmount);

return this.atomexContext.managers.exchangeManager.getOrderPreview({
type: normalizedSwapPreviewParameters.type,
Expand All @@ -230,6 +237,25 @@ export class AtomexSwapPreviewManager {
});
}

protected async getUserInvolvedSwapsInfo(userAddress: string, fromCurrencyId: Currency['id']): Promise<UserInvolvedSwapsInfo> {
const cacheKey = this.getUserInvolvedSwapsCacheKey(userAddress, fromCurrencyId);
let swapsInfo = this.userInvolvedSwapsCache.get<UserInvolvedSwapsInfo>(cacheKey);
if (swapsInfo)
return swapsInfo;

const swaps = (await this.atomexContext.managers.swapManager.getSwaps(userAddress, { active: true }))
.filter(swap => swap.user.status === 'Involved' && swap.from.currencyId === fromCurrencyId);
const fromTotalAmount = swaps.reduce(
(total, swap) => total.plus(swap.from.amount),
new BigNumber(0)
);

swapsInfo = { fromCurrencyId, swaps, fromTotalAmount };
this.userInvolvedSwapsCache.set(cacheKey, swapsInfo);

return swapsInfo;
}

protected async calculateSwapPreviewFees(
fromCurrencyInfo: CurrencyInfo,
fromNativeCurrencyInfo: CurrencyInfo,
Expand Down Expand Up @@ -364,6 +390,13 @@ export class AtomexSwapPreviewManager {
return `${fromCurrencyInfo.currency.id}_${toCurrencyInfo.currency.id}_${useWatchTower}`;
}

private getUserInvolvedSwapsCacheKey(
userAddress: string,
fromCurrencyId: Currency['id']
) {
return `${userAddress}_${fromCurrencyId}`;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
static isNormalizedSwapPreviewParameters(swapPreviewParameters: any): swapPreviewParameters is NormalizedSwapPreviewParameters {
return ordersHelper.isNormalizedOrderPreviewParameters(swapPreviewParameters);
Expand Down
25 changes: 24 additions & 1 deletion tests/atomex/swapPreview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Atomex } from '../../src/index';
import { tezosTestnetCurrencies } from '../../src/tezos';
import { testnetTezosTaquitoAtomexProtocolV1Options } from '../../src/tezos/config/atomexProtocol';
import { createMockedAtomexContext, createMockedBlockchainOptions, MockAtomexBlockchainNetworkOptions, MockAtomexContext } from '../mocks';
import { Accounts, AtomexProtocolV1Fees, swapPreviewWithAccountTestCases, swapPreviewWithoutAccountTestCases } from './testCases';
import { Accounts, AtomexProtocolV1Fees, swapPreviewWithAccountAndInvolvedSwapsTestCases, swapPreviewWithAccountTestCases, swapPreviewWithoutAccountTestCases } from './testCases';

describe('Atomex | Swap Preview', () => {
let mockedAtomexContext: MockAtomexContext;
Expand Down Expand Up @@ -60,6 +60,7 @@ describe('Atomex | Swap Preview', () => {

throw new Error('Expected symbol');
});
mockedAtomexContext.services.swapService.getSwaps.mockResolvedValue([]);
mockPriceManagerByOrderBook(environment.orderBooks);
mockAtomexProtocolV1Fees(environment.atomexProtocolFees);
await atomex.start();
Expand All @@ -80,7 +81,29 @@ describe('Atomex | Swap Preview', () => {

throw new Error('Expected symbol');
});
mockedAtomexContext.services.swapService.getSwaps.mockResolvedValue([]);
mockPriceManagerByOrderBook(environment.orderBooks);
mockAccounts(environment.accounts);
mockAtomexProtocolV1Fees(environment.atomexProtocolFees);
await atomex.start();

const swapPreview = await atomex.getSwapPreview(swapPreviewParameters);

expect(swapPreview).toEqual(expectedSwapPreview);
}
);

test.each(swapPreviewWithAccountAndInvolvedSwapsTestCases)(
'getting swap preview with account and involved swaps: %s\n\tSwap Preview Parameters: %j',
async (_, swapPreviewParameters, expectedSwapPreview, environment) => {
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');
});
mockedAtomexContext.services.swapService.getSwaps.mockResolvedValue(environment.swaps);
mockPriceManagerByOrderBook(environment.orderBooks);
mockAccounts(environment.accounts);
mockAtomexProtocolV1Fees(environment.atomexProtocolFees);
Expand Down
1 change: 1 addition & 0 deletions tests/atomex/testCases/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { type Accounts } from './accounts';

export { default as swapPreviewWithoutAccountTestCases } from './swapPreviewWithoutAccountTestCases';
export { default as swapPreviewWithAccountTestCases } from './swapPreviewWithAccountTestCases';
export { default as swapPreviewWithAccountAndInvolvedSwapsTestCases } from './swapPreviewWithAccountAndInvolvedSwapsTestCases';
Loading

0 comments on commit 7fd529b

Please sign in to comment.