0xweb
Version:
Contract package manager and other web3 tools
130 lines (118 loc) • 4.9 kB
text/typescript
import di from 'a-di';
import alot from 'alot';
import { BlockchainExplorerFactory } from '@dequanto/explorer/BlockchainExplorerFactory';
import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory';
import { IToken } from '@dequanto/models/IToken';
import { TPlatform } from '@dequanto/models/TPlatform';
import { $bigint } from '@dequanto/utils/$bigint';
import { $is } from '@dequanto/utils/$is';
import { AmmV2ExchangeBase } from './TokenExchanges/AmmV2ExchangeBase';
import { PancakeswapExchange } from './TokenExchanges/PancakeswapExchange';
import { TokensService } from './TokensService';
import { TokenUtils } from './utils/TokenUtils';
import { UniswapV2Exchange } from './TokenExchanges/UniswapV2Exchange';
import { SushiswapPolygonExchange } from './TokenExchanges/SushiswapPolygonExchange';
import { $logger } from '@dequanto/utils/$logger';
import { $require } from '@dequanto/utils/$require';
import { Web3Client } from '@dequanto/clients/Web3Client';
import { IBlockchainExplorer } from '@dequanto/explorer/IBlockchainExplorer';
export class TokenExchangeService {
client: Web3Client;
explorer: IBlockchainExplorer;
exchange: AmmV2ExchangeBase
stables: string[]
constructor (public platform: TPlatform) {
switch (platform) {
case 'bsc':
this.exchange = di.resolve(PancakeswapExchange, this.client, this.explorer);
this.stables = ['BUSD', 'USDT'];
break;
case 'eth':
this.exchange = di.resolve(UniswapV2Exchange, this.client, this.explorer);
this.stables = ['USDC', 'USDT', 'DAI'];
break;
case 'polygon':
this.exchange = di.resolve(SushiswapPolygonExchange, this.client, this.explorer);
this.stables = ['USDC', 'USDT', 'DAI'];
break;
default:
throw new Error(`Unsupported Platform for exchange yet: ${platform}`);
}
this.client = Web3ClientFactory.get(this.platform);
this.explorer = BlockchainExplorerFactory.get(this.platform);
}
async calcUSD (from: string | Partial<IToken>, fromAmount: number | bigint, date?: Date): Promise<{
from: IToken & { amount },
to: IToken & { amount },
priceImpact: number
}> {
let tokensService = di.resolve(TokensService, this.platform, this.explorer);
let fromToken = typeof from === 'string'
? await tokensService.getToken(from)
: <IToken> from;
let $fromAmount = typeof fromAmount === 'bigint'
? fromAmount
: $bigint.toWei(fromAmount, fromToken.decimals ?? 18);
$require.Address(fromToken?.address, `Token 404 ${from}`);
let converted = TokenUtils.isStable(fromToken.symbol) ? {
to: {
...fromToken,
amount: fromAmount,
},
priceImpact: 0
} : await alot(this.stables)
.mapAsync(async stableSymbol => {
let toToken = await tokensService.getToken(stableSymbol);
try {
let swapped = await this.exchange.calcSwap(fromToken, toToken, $fromAmount) ?? {
amount: 0n,
priceImpact: 0
};
return {
to: {
...toToken,
amount: swapped.amount
},
priceImpact: swapped.priceImpact,
};
} catch (error) {
$logger.log('CalcStable error', error);
return null;
}
})
.filterAsync(x => x != null)
.firstAsync();
if (converted == null) {
throw new Error(`Do not know how to convert ${from} Token`);
}
return {
from: {
...fromToken,
amount: $fromAmount,
},
to: converted.to,
priceImpact: converted.priceImpact
};
}
async calc (from: string, to: string, fromAmount: bigint): Promise<{
from: IToken & { amount },
to: IToken & { amount },
priceImpact: number
}> {
let tokensService = di.resolve(TokensService, this.platform, this.explorer);
let fromToken = await tokensService.getToken(from);
let toToken = await tokensService.getToken(to);
let swapped = await this.exchange.calcSwap(fromToken, toToken, fromAmount);
return {
from: {
...fromToken,
amount: fromAmount,
},
to: {
...toToken,
amount: swapped.amount,
},
priceImpact: swapped.priceImpact,
};
}
}