UNPKG

rubic-sdk

Version:
311 lines • 15.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OnChainManager = void 0; const rxjs_1 = require("rxjs"); const innerFrom_1 = require("rxjs/internal/observable/innerFrom"); const errors_1 = require("../../../common/errors"); const tokens_1 = require("../../../common/tokens"); const object_1 = require("../../../common/utils/object"); const options_1 = require("../../../common/utils/options"); const p_timeout_1 = __importDefault(require("../../../common/utils/p-timeout")); const blockchains_info_1 = require("../../../core/blockchain/utils/blockchains-info/blockchains-info"); const get_from_without_fee_1 = require("../../common/utils/get-from-without-fee"); const get_price_tokens_from_input_tokens_1 = require("../../common/utils/get-price-tokens-from-input-tokens"); const default_provider_addresses_1 = require("../../cross-chain/calculation-manager/constants/default-provider-addresses"); const cross_chain_provider_1 = require("../../cross-chain/calculation-manager/providers/common/cross-chain-provider"); const deflation_token_manager_1 = require("../../deflation-token-manager/deflation-token-manager"); const typed_trade_providers_1 = require("./constants/trade-providers/typed-trade-providers"); const evm_wrap_trade_1 = require("./providers/common/evm-wrap-trade/evm-wrap-trade"); const on_chain_trade_type_1 = require("./providers/common/models/on-chain-trade-type"); const on_chain_proxy_service_1 = require("./providers/common/on-chain-proxy-service/on-chain-proxy-service"); const on_chain_trade_1 = require("./providers/common/on-chain-trade/on-chain-trade"); const provider_default_options_1 = require("./providers/dexes/common/on-chain-provider/constants/provider-default-options"); const on_chain_manager_aggregators_types_1 = require("./models/on-chain-manager-aggregators-types"); const assert_evm_token_1 = require("./utils/assert-evm-token"); /** * Contains methods to calculate on-chain trades. */ class OnChainManager { constructor(providerAddress) { this.providerAddress = providerAddress; /** * List of all on-chain trade providers, combined by blockchains. */ this.tradeProviders = typed_trade_providers_1.typedTradeProviders; this.deflationTokenManager = new deflation_token_manager_1.DeflationTokenManager(); this.AGGREGATORS = on_chain_manager_aggregators_types_1.AGGREGATORS_ON_CHAIN; this.LIFI_DISABLED_PROVIDERS = []; } calculateTradeReactively(fromToken, fromAmount, toToken, options) { if (toToken instanceof tokens_1.Token && fromToken.blockchain !== toToken.blockchain) { throw new errors_1.RubicSdkError('Blockchains of from and to tokens must be same'); } return (0, rxjs_1.from)((0, get_price_tokens_from_input_tokens_1.getPriceTokensFromInputTokens)(fromToken, fromAmount.toString(), toToken)).pipe((0, rxjs_1.switchMap)(({ from, to }) => (0, rxjs_1.forkJoin)([(0, rxjs_1.of)(from), (0, rxjs_1.of)(to), this.getFullOptions(from, to, options)])), (0, rxjs_1.switchMap)(([from, to, fullOptions]) => { if ((from.isNative && to.isWrapped) || (from.isWrapped && to.isNative)) { return this.getWrappedWrapTrade(from, to, fullOptions); } const nativeProviders = Object.entries(this.tradeProviders[from.blockchain]).filter(([type]) => !fullOptions.disabledProviders.includes(type)); const aggregatorsTrades = this.getAggregatorsCalculationPromises(from, to, fullOptions); const totalTrades = [...nativeProviders, ...aggregatorsTrades].length; return (0, rxjs_1.merge)(...nativeProviders.map(([_, provider]) => (0, innerFrom_1.fromPromise)(this.getProviderCalculationPromise(provider, from, to, fullOptions))), ...aggregatorsTrades).pipe((0, rxjs_1.map)((wrappedTrade, index) => ({ total: totalTrades, calculated: index + 1, wrappedTrade: wrappedTrade || null })), (0, rxjs_1.startWith)({ total: totalTrades, calculated: 0, wrappedTrade: null })); })); } /** * Calculates on-chain trades, sorted by output amount. * * @example * ```ts * const blockchain = BLOCKCHAIN_NAME.ETHEREUM; * // ETH * const fromTokenAddress = '0x0000000000000000000000000000000000000000'; * const fromAmount = 1; * // USDT * const toTokenAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7'; * * const trades = await sdk.onChainManager.calculateTrade( * { blockchain, address: fromTokenAddress }, * fromAmount, * toTokenAddress * ); * const bestTrade = trades[0]; * * trades.forEach(trade => { * if (trade instanceof OnChainTrade) { * console.log(trade.type, `to amount: ${trade.to.tokenAmount.toFormat(3)}`) * } * }) * ``` * * @param fromToken Token to sell. * @param fromAmount Amount to sell. * @param toToken Token to get. * @param options Additional options. * @returns List of calculated on-chain trades. */ async calculateTrade(fromToken, fromAmount, toToken, options) { if (toToken instanceof tokens_1.Token && fromToken.blockchain !== toToken.blockchain) { throw new errors_1.RubicSdkError('Blockchains of from and to tokens must be same'); } const { from, to } = await (0, get_price_tokens_from_input_tokens_1.getPriceTokensFromInputTokens)(fromToken, fromAmount.toString(), toToken); const fullOptions = await this.getFullOptions(from, to, options); return this.calculateTradeFromTokens(from, to, fullOptions); } async getFullOptions(from, to, options) { const chainType = blockchains_info_1.BlockchainsInfo.getChainType(from.blockchain); const [isDeflationFrom, isDeflationTo] = await Promise.all([ this.isDeflationToken(from), this.isDeflationToken(to) ]); let useProxy; if (options?.useProxy === false) { useProxy = options.useProxy; } else { useProxy = on_chain_proxy_service_1.OnChainProxyService.isSupportedBlockchain(from.blockchain) && (!isDeflationFrom.isDeflation || isDeflationFrom.isWhitelisted); } return (0, options_1.combineOptions)({ ...options, useProxy }, { timeout: OnChainManager.defaultCalculationTimeout, disabledProviders: [], providerAddress: options?.providerAddress || this.providerAddress?.[chainType]?.onChain || default_provider_addresses_1.defaultProviderAddresses.onChain, useProxy, withDeflation: { from: isDeflationFrom, to: isDeflationTo } }); } async calculateTradeFromTokens(from, to, options) { if ((from.isNative && to.isWrapped) || (from.isWrapped && to.isNative)) { (0, assert_evm_token_1.assertEvmToken)(from); return [ { trade: await OnChainManager.getWrapTrade(from, to, options), tradeType: on_chain_trade_type_1.ON_CHAIN_TRADE_TYPE.WRAPPED } ]; } const dexesProviders = Object.entries(this.tradeProviders[from.blockchain]).filter(([type]) => !options.disabledProviders.includes(type)); const dexesTradesPromise = this.calculateDexes(from, to, dexesProviders, options); const aggregatorsTradesPromises = this.getAggregatorsCalculationPromises(from, to, options); const allTrades = (await Promise.all([dexesTradesPromise, ...aggregatorsTradesPromises])).flat(); return allTrades.filter(object_1.notNull).sort((tradeA, tradeB) => { if (tradeA instanceof on_chain_trade_1.OnChainTrade || tradeB instanceof on_chain_trade_1.OnChainTrade) { if (tradeA instanceof on_chain_trade_1.OnChainTrade && tradeB instanceof on_chain_trade_1.OnChainTrade) { return tradeA.to.tokenAmount.comparedTo(tradeB.to.tokenAmount); } return tradeA instanceof on_chain_trade_1.OnChainTrade ? 1 : -1; } return 0; }); } isDeflationToken(token) { return this.deflationTokenManager.isDeflationToken(token); } async calculateDexes(from, to, dexesProviders, options) { return Promise.all(dexesProviders.map(([type, provider]) => (0, p_timeout_1.default)(provider.calculate(from, to, options), options.timeout) .then(trade => { if (trade) { return { tradeType: type, trade }; } return null; }) .catch(error => { console.debug(`[RUBIC_SDK] Trade calculation error occurred for ${type} trade provider.`, error); return { tradeType: type, trade: null, error }; }))); } async calculateLifiTrade(from, to, options) { try { // throw Error('Lifi has been compromised'); const disabledProviders = [ ...this.LIFI_DISABLED_PROVIDERS, ...options.disabledProviders ]; const calculationOptions = { ...options, slippageTolerance: options?.slippageTolerance || provider_default_options_1.providerDefaultOptions.slippageTolerance, gasCalculation: options.gasCalculation === 'disabled' ? 'disabled' : 'calculate', disabledProviders }; const lifiAggregator = new this.AGGREGATORS.LIFI(); const lifiCalculationCall = lifiAggregator.calculate(from, to, calculationOptions); return (0, p_timeout_1.default)(lifiCalculationCall, options.timeout); } catch (err) { console.debug('[RUBIC_SDK] Trade calculation error occurred for lifi.', err); return { type: on_chain_trade_type_1.ON_CHAIN_TRADE_TYPE.LIFI, error: err }; } } static async getWrapTrade(from, to, _options) { const fromToken = from; const toToken = to; const ZERO_FEE_ADDRESS = '0x51c276f1392E87D4De6203BdD80c83f5F62724d4'; let hasProxyContract; let proxyFeeInfo = undefined; try { proxyFeeInfo = await this.onChainProxyService.getFeeInfo(from, ZERO_FEE_ADDRESS); hasProxyContract = true; } catch { hasProxyContract = false; } const fromWithoutFee = (0, get_from_without_fee_1.getFromWithoutFee)(from, proxyFeeInfo?.platformFee.percent); return new evm_wrap_trade_1.EvmWrapTrade({ from: fromToken, to: new tokens_1.PriceTokenAmount({ ...toToken.asStruct, weiAmount: from.weiAmount }), slippageTolerance: 0, path: [from, to], gasFeeInfo: null, useProxy: hasProxyContract, proxyFeeInfo, fromWithoutFee, withDeflation: { from: { isDeflation: false }, to: { isDeflation: false } } }, ZERO_FEE_ADDRESS); } async getProviderCalculationPromise(provider, from, to, options) { try { const wrappedTrade = await (0, p_timeout_1.default)(provider.calculate(from, to, options), options.timeout); if (!wrappedTrade) { return null; } return { trade: wrappedTrade, tradeType: provider.type }; } catch (err) { console.debug(`[RUBIC_SDK] Trade calculation error occurred for ${provider.type} trade provider.`, err); return { trade: null, tradeType: provider.type, error: cross_chain_provider_1.CrossChainProvider.parseError(err) }; } } getAggregatorsCalculationPromises(from, to, options) { const availableAggregators = Object.values(this.AGGREGATORS) .map(AggregatorClass => new AggregatorClass()) .filter(aggregator => { return (!this.isDisabledAggregator(options.disabledProviders, aggregator.tradeType) && aggregator.isSupportedBlockchain(from.blockchain)); }); return availableAggregators.map(aggregator => { const promise = this.getCalcPromise(from, to, options, aggregator); return this.handleTradePromise(promise, aggregator); }); } getCalcPromise(from, to, options, aggregator) { if (aggregator.tradeType === on_chain_trade_type_1.ON_CHAIN_TRADE_TYPE.LIFI) { return this.calculateLifiTrade(from, to, options); } return (0, p_timeout_1.default)(aggregator.calculate(from, to, options), options.timeout); } handleTradePromise(promise, aggregator) { return promise .then(wrappedTrade => { if ('error' in wrappedTrade) { return { trade: null, tradeType: wrappedTrade.type, error: wrappedTrade.error }; } if (!wrappedTrade) { return null; } return { trade: wrappedTrade, tradeType: aggregator.tradeType }; }) .catch(err => { console.debug(`[RUBIC_SDK] Trade calculation error occurred for ${aggregator.tradeType} trade provider.`, err); return { trade: null, tradeType: aggregator.tradeType, error: cross_chain_provider_1.CrossChainProvider.parseError(err) }; }); } isDisabledAggregator(disabledProviders, provider) { return disabledProviders.map(provider => provider.toUpperCase()).includes(provider); } getWrappedWrapTrade(fromToken, toToken, fullOptions) { (0, assert_evm_token_1.assertEvmToken)(fromToken); return (0, rxjs_1.from)(OnChainManager.getWrapTrade(fromToken, toToken, fullOptions)).pipe((0, rxjs_1.map)(wrapTrade => ({ total: 1, calculated: 1, wrappedTrade: { error: undefined, tradeType: on_chain_trade_type_1.ON_CHAIN_TRADE_TYPE.WRAPPED, trade: wrapTrade } })), (0, rxjs_1.catchError)(err => (0, rxjs_1.of)({ total: 1, calculated: 1, wrappedTrade: { error: err, tradeType: on_chain_trade_type_1.ON_CHAIN_TRADE_TYPE.WRAPPED, trade: null } }))); } } exports.OnChainManager = OnChainManager; OnChainManager.defaultCalculationTimeout = 20000; OnChainManager.onChainProxyService = new on_chain_proxy_service_1.OnChainProxyService(); //# sourceMappingURL=on-chain-manager.js.map