diff --git a/package.json b/package.json index 2ab5df530b..692f46dda7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rubic-sdk", - "version": "4.14.3", + "version": "4.15.0", "description": "Simplify dApp creation", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/features/common/constants/proxy-supported-blockchain.ts b/src/features/common/constants/proxy-supported-blockchain.ts index 5de58e62a0..61d2281b5f 100644 --- a/src/features/common/constants/proxy-supported-blockchain.ts +++ b/src/features/common/constants/proxy-supported-blockchain.ts @@ -14,7 +14,8 @@ export const proxySupportedBlockchains = [ BLOCKCHAIN_NAME.CRONOS, BLOCKCHAIN_NAME.ZK_SYNC, BLOCKCHAIN_NAME.PULSECHAIN, - BLOCKCHAIN_NAME.AURORA + BLOCKCHAIN_NAME.AURORA, + BLOCKCHAIN_NAME.LINEA // BLOCKCHAIN_NAME.OKE_X_CHAIN, // BLOCKCHAIN_NAME.GNOSIS, // BLOCKCHAIN_NAME.FUSE, diff --git a/src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address.ts b/src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address.ts index 44dac51286..801035bd32 100644 --- a/src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address.ts +++ b/src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address.ts @@ -17,6 +17,9 @@ export const rubicProxyContractAddress: Record< router = '0xa63c029612ddaD00A269383Ab016D1e7c14E851D'; gateway = '0x8E70e517057e7380587Ea6990dAe81cB1Ba405ce'; } + if (blockchain === BLOCKCHAIN_NAME.LINEA) { + router = '0xAf14797CcF963B1e3d028a9d51853acE16aedBA1'; + } return { ...acc, [blockchain]: { gateway, router } }; }, diff --git a/src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v3-trade-providers.ts b/src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v3-trade-providers.ts index 9b2a2829c7..047a9e12cf 100644 --- a/src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v3-trade-providers.ts +++ b/src/features/on-chain/calculation-manager/constants/trade-providers/uniswap-v3-trade-providers.ts @@ -1,6 +1,7 @@ import { UniSwapV3ArbitrumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-provider'; import { UniSwapV3EthereumProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum/uni-swap-v3-ethereum/uni-swap-v3-ethereum-provider'; import { UniSwapV3EthereumPowProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-provider'; +import { HorizondexProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-provider'; import { UniSwapV3PolygonProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider'; import { UniSwapV3PulsechainProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/pulsechain/uni-swap-v3-pulsechain/uni-swap-v3-ethereum-provider'; @@ -9,5 +10,6 @@ export const UniswapV3TradeProviders = [ UniSwapV3PolygonProvider, UniSwapV3ArbitrumProvider, UniSwapV3EthereumPowProvider, - UniSwapV3PulsechainProvider + UniSwapV3PulsechainProvider, + HorizondexProvider ] as const; diff --git a/src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type.ts b/src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type.ts index 8bb5937765..e9cadf2790 100644 --- a/src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type.ts +++ b/src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type.ts @@ -35,6 +35,7 @@ export const ON_CHAIN_TRADE_TYPE = { ELK: 'ELK', HONEY_SWAP: 'HONEY_SWAP', + HORIZONDEX: 'HORIZONDEX', JET_SWAP: 'JET_SWAP', JOE: 'JOE', diff --git a/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-provider.ts b/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-provider.ts index 288832dc0b..d10f24152b 100644 --- a/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-provider.ts +++ b/src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-provider.ts @@ -3,6 +3,7 @@ import { UNI_SWAP_V3_ARBITRUM_PROVIDER_CONFIGURATION } from 'src/features/on-cha import { UNI_SWAP_V3_ARBITRUM_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/constants/router-configuration'; import { UniSwapV3ArbitrumTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/arbitrum/uni-swap-v3-arbitrum/uni-swap-v3-arbitrum-trade'; import { UniswapV3AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; export class UniSwapV3ArbitrumProvider extends UniswapV3AbstractProvider { public readonly blockchain = BLOCKCHAIN_NAME.ARBITRUM; @@ -12,4 +13,9 @@ export class UniSwapV3ArbitrumProvider extends UniswapV3AbstractProvider, + quoterContractAddress: string = UNISWAP_V3_QUOTER_CONTRACT_ADDRESS, + quoterContractABI: AbiItem[] = UNISWAP_V3_QUOTER_CONTRACT_ABI + ) { + super( + blockchain, + routerConfiguration, + quoterContractAddress, + quoterContractABI, + [300, 500, 1000, 3000, 10000] + ); + } + + /** + * Returns swap method's name and arguments to pass it to Quoter contract. + * @param poolsPath Pools, included in the route. + * @param from From token. + * @param to To token. + * @param exact Is exact input or output trade. + * @param weiAmount Amount of tokens to trade. + */ + @Cache + protected static getQuoterMethodData( + poolsPath: LiquidityPool[], + from: Token, + to: Token, + exact: Exact, + weiAmount: string + ): { + poolsPath: LiquidityPool[]; + methodData: MethodData; + } { + if (poolsPath.length === 1 && poolsPath?.[0]) { + const methodName = + exact === 'input' ? 'quoteExactInputSingle' : 'quoteExactOutputSingle'; + const sqrtPriceLimitX96 = 0; + return { + poolsPath, + methodData: { + methodName, + methodArguments: [ + from.address, + to.address, + weiAmount, + poolsPath[0].fee, + sqrtPriceLimitX96 + ] + } + }; + } + + const methodName = exact === 'input' ? 'quoteExactInput' : 'quoteExactOutput'; + const tokensPath = exact === 'input' ? poolsPath : poolsPath.reverse(); + const initialTokenAddress = exact === 'input' ? from.address : to.address; + return { + poolsPath, + methodData: { + methodName, + methodArguments: [ + UniswapV3QuoterController.getEncodedPoolsPath(tokensPath, initialTokenAddress), + weiAmount + ] + } + }; + } + + public async getAllRoutes( + from: Token, + to: Token, + exact: Exact, + weiAmount: string, + routeMaxTransitTokens: number + ): Promise { + const routesLiquidityPools = await this.getAllLiquidityPools(from, to); + const options: Omit = { + routesLiquidityPools, + from, + to, + exact, + weiAmount + }; + const quoterMethodsData = [...Array(routeMaxTransitTokens + 1)] + .map((_, maxTransitTokens) => + this.getQuoterMethodsData( + { + ...options, + maxTransitTokens + }, + [], + from.address + ) + ) + .flat(); + + const results = await this.web3Public.multicallContractMethods< + string | { returnedAmount: string } + >( + this.quoterContractAddress, + this.quoterContractABI, + quoterMethodsData.map(quoterMethodData => { + if (quoterMethodData.methodData.methodName.toLowerCase().includes('single')) { + return { + methodName: quoterMethodData.methodData.methodName, + methodArguments: [quoterMethodData.methodData.methodArguments] + }; + } + + return quoterMethodData.methodData; + }) + ); + + return results + .map((result, index) => { + const pool = quoterMethodsData?.[index]; + if (!pool) { + throw new RubicSdkError('Pool has to be defined'); + } + if (result.success) { + return { + outputAbsoluteAmount: new BigNumber( + result?.output! instanceof Object + ? result?.output?.returnedAmount + : result.output! + ), + poolsPath: pool.poolsPath, + initialTokenAddress: from.address + }; + } + return null; + }) + .filter(notNull); + } + + /** + * Returns swap methods' names and arguments, built with passed pools' addresses, to use it in Quoter contract. + */ + protected getQuoterMethodsData( + options: GetQuoterMethodsDataOptions, + path: LiquidityPool[], + lastTokenAddress: string + ): { poolsPath: LiquidityPool[]; methodData: MethodData }[] { + const { routesLiquidityPools, from, to, exact, weiAmount, maxTransitTokens } = options; + + if (path.length === maxTransitTokens) { + const pools = routesLiquidityPools.filter(pool => + pool.isPoolWithTokens(lastTokenAddress, to.address) + ); + return pools.map(pool => + HorizondexUniswapV3QuoterController.getQuoterMethodData( + path.concat(pool), + from, + to, + exact, + weiAmount + ) + ); + } + + return routesLiquidityPools + .filter(pool => !path.includes(pool)) + .map(pool => { + const methodsData: { poolsPath: LiquidityPool[]; methodData: MethodData }[] = []; + if (compareAddresses(pool.token0.address, lastTokenAddress)) { + const extendedPath = path.concat(pool); + methodsData.push( + ...this.getQuoterMethodsData(options, extendedPath, pool.token1.address) + ); + } + if (compareAddresses(pool.token1.address, lastTokenAddress)) { + const extendedPath = path.concat(pool); + methodsData.push( + ...this.getQuoterMethodsData(options, extendedPath, pool.token0.address) + ); + } + return methodsData; + }) + .flat(); + } +} diff --git a/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool.ts b/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool.ts index 76945e5b89..93b4ece537 100644 --- a/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool.ts +++ b/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool.ts @@ -1,7 +1,7 @@ import { Token } from 'src/common/tokens'; import { compareAddresses } from 'src/common/utils/blockchain'; -export type FeeAmount = 500 | 3000 | 10000; +export type FeeAmount = 300 | 500 | 1000 | 3000 | 10000; /** * Represents liquidity pool in uni v3. diff --git a/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller.ts b/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller.ts index 7303512154..a83e6d2aec 100644 --- a/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller.ts +++ b/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller.ts @@ -25,6 +25,7 @@ import { LiquidityPool } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/models/liquidity-pool'; import { UniswapV3AlgebraQuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-quoter-controller'; +import { AbiItem } from 'web3-utils'; interface GetQuoterMethodsDataOptions { routesLiquidityPools: LiquidityPool[]; @@ -71,7 +72,7 @@ export class UniswapV3QuoterController implements UniswapV3AlgebraQuoterControll * @param weiAmount Amount of tokens to trade. */ @Cache - private static getQuoterMethodData( + protected static getQuoterMethodData( poolsPath: LiquidityPool[], from: Token, to: Token, @@ -119,15 +120,16 @@ export class UniswapV3QuoterController implements UniswapV3AlgebraQuoterControll private routerLiquidityPools: LiquidityPool[] | undefined; - private readonly feeAmounts: FeeAmount[] = [500, 3000, 10000]; - - private get web3Public(): EvmWeb3Public { + protected get web3Public(): EvmWeb3Public { return Injector.web3PublicService.getWeb3Public(this.blockchain); } constructor( - private readonly blockchain: EvmBlockchainName, - private readonly routerConfiguration: UniswapV3RouterConfiguration + protected readonly blockchain: EvmBlockchainName, + protected readonly routerConfiguration: UniswapV3RouterConfiguration, + protected readonly quoterContractAddress: string = UNISWAP_V3_QUOTER_CONTRACT_ADDRESS, + protected readonly quoterContractABI: AbiItem[] = UNISWAP_V3_QUOTER_CONTRACT_ABI, + protected readonly feeAmounts: FeeAmount[] = [500, 3000, 10000] ) {} private async getOrCreateRouterTokensAndLiquidityPools(): Promise<{ @@ -172,7 +174,7 @@ export class UniswapV3QuoterController implements UniswapV3AlgebraQuoterControll @Cache({ maxAge: 1000 * 60 * 10 }) - private async getAllLiquidityPools( + protected async getAllLiquidityPools( firstToken: Token, secondToken: Token ): Promise { @@ -274,8 +276,8 @@ export class UniswapV3QuoterController implements UniswapV3AlgebraQuoterControll return this.web3Public .multicallContractMethods( - UNISWAP_V3_QUOTER_CONTRACT_ADDRESS, - UNISWAP_V3_QUOTER_CONTRACT_ABI, + this.quoterContractAddress, + this.quoterContractABI, quoterMethodsData.map(quoterMethodData => quoterMethodData.methodData) ) .then(results => { @@ -301,7 +303,7 @@ export class UniswapV3QuoterController implements UniswapV3AlgebraQuoterControll /** * Returns swap methods' names and arguments, built with passed pools' addresses, to use it in Quoter contract. */ - private getQuoterMethodsData( + protected getQuoterMethodsData( options: GetQuoterMethodsDataOptions, path: LiquidityPool[], lastTokenAddress: string diff --git a/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/unwrapWethMethodName.ts b/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/unwrapWethMethodName.ts new file mode 100644 index 0000000000..f49bcc9416 --- /dev/null +++ b/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/unwrapWethMethodName.ts @@ -0,0 +1 @@ +export type UnwrapWethMethodName = 'unwrapWETH9' | 'unwrapWNativeToken' | 'unwrapWeth'; diff --git a/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider.ts b/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider.ts index f735157f3f..91681234aa 100644 --- a/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider.ts +++ b/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-provider.ts @@ -96,7 +96,7 @@ export abstract class UniswapV3AlgebraAbstractProvider< return (await this.calculateExactOutput(fromToken, to, options)).from.tokenAmount; } - private async calculateDifficultTrade( + protected async calculateDifficultTrade( fromToken: PriceToken, toToken: PriceToken, exact: Exact, @@ -181,7 +181,7 @@ export abstract class UniswapV3AlgebraAbstractProvider< ); } - private async getRoute( + protected async getRoute( from: PriceToken, to: PriceToken, exact: Exact, diff --git a/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade.ts b/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade.ts index d57491af40..916a12adcf 100644 --- a/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade.ts +++ b/src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/uniswap-v3-algebra-abstract-trade.ts @@ -23,6 +23,7 @@ import { UniswapV3AlgebraTradeStruct, UniswapV3AlgebraTradeStructOmitPath } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/uniswap-v3-algebra-trade-struct'; +import { UnwrapWethMethodName } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-algebra-abstract/models/unwrapWethMethodName'; import { getFromToTokensAmountsByExact } from 'src/features/on-chain/calculation-manager/providers/dexes/common/utils/get-from-to-tokens-amounts-by-exact'; import { AbiItem } from 'web3-utils'; @@ -161,7 +162,7 @@ export abstract class UniswapV3AlgebraAbstractTrade extends EvmOnChainTrade { protected abstract readonly contractAbi: AbiItem[]; - protected abstract readonly unwrapWethMethodName: 'unwrapWETH9' | 'unwrapWNativeToken'; + protected abstract readonly unwrapWethMethodName: UnwrapWethMethodName; protected readonly exact: Exact; diff --git a/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-provider.ts b/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-provider.ts index e7be7bb8c9..130e2b833f 100644 --- a/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-provider.ts +++ b/src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-provider.ts @@ -1,5 +1,6 @@ import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; import { UniswapV3AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; import { UNI_SWAP_V3_ETHEREUM_POW_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/constants/provider-configuration'; import { UNI_SWAP_V3_ETHEREUM_POW_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/constants/router-configuration'; import { UniSwapV3EthereumPowTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/ethereum-pow/uni-swap-v3-ethereum-pow/uni-swap-v3-ethereum-pow-trade'; @@ -12,4 +13,9 @@ export class UniSwapV3EthereumPowProvider extends UniswapV3AbstractProvider = { + WETH: wrappedNativeTokensList[BLOCKCHAIN_NAME.LINEA]!.address, + BNB: '0xf5C6825015280CdfD0b56903F9F8B5A2233476F5', + BUSD: '0x7d43AABC515C356145049227CeE54B608342c0ad', + MATIC: '0x265b25e22bcd7f10a5bd6e6410f10537cc7567e8', + HZN: '0x0B1A02A7309dFbfAD1Cd4adC096582C87e8A3Ac1', + NFTE: '0x2140Ea50bc3B6Ac3971F9e9Ea93A1442665670e4' +}; + +const routerLiquidityPools: UniswapV3RouterLiquidityPool[] = [ + { + poolAddress: '0xe2df725e44ab983e8513ecfc9c3e13bc21ea867e', + tokenSymbolA: 'BUSD', + tokenSymbolB: 'WETH', + fee: 300 + }, + { + poolAddress: '0x0330fddd733ea64f92b348ff19a2bb4d29d379d5', + tokenSymbolA: 'MATIC', + tokenSymbolB: 'WETH', + fee: 300 + }, + { + poolAddress: '0xfe7a3ab43d8db17643ba5dc2f132a74049dcf42f', + tokenSymbolA: 'BUSD', + tokenSymbolB: 'BNB', + fee: 300 + }, + { + poolAddress: '0xa6a69fddec12e7ee44474a92e9c549a612519411', + tokenSymbolA: 'WETH', + tokenSymbolB: 'BNB', + fee: 300 + }, + { + poolAddress: '0x0003ecc56c19953ea2e2de626e44111ade02ad6e', + tokenSymbolA: 'HZN', + tokenSymbolB: 'WETH', + fee: 300 + }, + { + poolAddress: '0xa24b6a8698ee173ccf5a97f73f1f2d8bb7032feb', + tokenSymbolA: 'HZN', + tokenSymbolB: 'WETH', + fee: 1000 + }, + { + poolAddress: '0xc9dc3eda8f6b664e2e25d632bb4d4f28ab58ee3c', + tokenSymbolA: 'NFTE', + tokenSymbolB: 'WETH', + fee: 1000 + } +]; + +export const HORIZONDEX_ROUTER_CONFIGURATION: UniswapV3RouterConfiguration = { + tokens: routerTokens, + liquidityPools: routerLiquidityPools +}; + +export const HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS = + '0x272e156df8da513c69cb41cc7a99185d53f926bb'; + +export const HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'address', name: '_factory', type: 'address' }, + { internalType: 'address', name: '_WETH', type: 'address' } + ], + stateMutability: 'nonpayable', + type: 'constructor' + }, + { + inputs: [], + name: 'WETH', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [], + name: 'factory', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function' + }, + { + inputs: [{ internalType: 'bytes[]', name: 'data', type: 'bytes[]' }], + name: 'multicall', + outputs: [{ internalType: 'bytes[]', name: 'results', type: 'bytes[]' }], + stateMutability: 'payable', + type: 'function' + }, + { inputs: [], name: 'refundEth', outputs: [], stateMutability: 'payable', type: 'function' }, + { + inputs: [ + { internalType: 'int256', name: 'deltaQty0', type: 'int256' }, + { internalType: 'int256', name: 'deltaQty1', type: 'int256' }, + { internalType: 'bytes', name: 'data', type: 'bytes' } + ], + name: 'swapCallback', + outputs: [], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'minAmountOut', type: 'uint256' } + ], + internalType: 'struct IRouter.ExactInputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'swapExactInput', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint256', name: 'minAmountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtP', type: 'uint160' } + ], + internalType: 'struct IRouter.ExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'swapExactInputSingle', + outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'maxAmountIn', type: 'uint256' } + ], + internalType: 'struct IRouter.ExactOutputParams', + name: 'params', + type: 'tuple' + } + ], + name: 'swapExactOutput', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint256', name: 'maxAmountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'limitSqrtP', type: 'uint160' } + ], + internalType: 'struct IRouter.ExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'swapExactOutputSingle', + outputs: [{ internalType: 'uint256', name: 'amountIn', type: 'uint256' }], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'transferAllTokens', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'feeUnits', type: 'uint256' }, + { internalType: 'address', name: 'feeRecipient', type: 'address' } + ], + name: 'transferAllTokensWithFee', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' } + ], + name: 'unwrapWeth', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { + inputs: [ + { internalType: 'uint256', name: 'minAmount', type: 'uint256' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'feeUnits', type: 'uint256' }, + { internalType: 'address', name: 'feeRecipient', type: 'address' } + ], + name: 'unwrapWethWithFee', + outputs: [], + stateMutability: 'payable', + type: 'function' + }, + { stateMutability: 'payable', type: 'receive' } +] as AbiItem[]; diff --git a/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-provider.ts b/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-provider.ts new file mode 100644 index 0000000000..e75b3777e4 --- /dev/null +++ b/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-provider.ts @@ -0,0 +1,46 @@ +import BigNumber from 'bignumber.js'; +import { PriceToken } from 'src/common/tokens'; +import { combineOptions } from 'src/common/utils/options'; +import { BLOCKCHAIN_NAME, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name'; +import { OnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options'; +import { Exact } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/exact'; +import { UniswapV3AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider'; +import { HorizondexUniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/horizondex-uniswap-v3-quoter-controller'; +import { HORIZONDEX_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/provider-configuration'; +import { HORIZONDEX_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/router-configuration'; +import { HorizondexTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-trade'; +import { + HORIZONDEX_QUOTER_CONTRACT_ABI, + HORIZONDEX_QUOTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/utils/quoter-controller/constants/quoter-contract-data'; + +export class HorizondexProvider extends UniswapV3AbstractProvider { + public readonly blockchain = BLOCKCHAIN_NAME.LINEA; + + public readonly OnChainTradeClass = HorizondexTrade; + + public readonly providerConfiguration = HORIZONDEX_PROVIDER_CONFIGURATION; + + public readonly routerConfiguration = HORIZONDEX_ROUTER_CONFIGURATION; + + protected readonly quoterController = new HorizondexUniswapV3QuoterController( + this.blockchain, + this.routerConfiguration, + HORIZONDEX_QUOTER_CONTRACT_ADDRESS, + HORIZONDEX_QUOTER_CONTRACT_ABI + ); + + // @TODO Remove when method's will be whitelisted + protected async calculateDifficultTrade( + fromToken: PriceToken, + toToken: PriceToken, + exact: Exact, + weiAmount: BigNumber, + options?: OnChainCalculationOptions + ): Promise { + const fullOptions = combineOptions(options, this.defaultOptions); + + const newOptions = { ...fullOptions, useProxy: false }; + return super.calculateDifficultTrade(fromToken, toToken, exact, weiAmount, newOptions); + } +} diff --git a/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-trade.ts b/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-trade.ts new file mode 100644 index 0000000000..4c87d28372 --- /dev/null +++ b/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/horizondex-trade.ts @@ -0,0 +1,84 @@ +import { RubicSdkError } from 'src/common/errors'; +import { compareAddresses } from 'src/common/utils/blockchain'; +import { MethodData } from 'src/core/blockchain/web3-public-service/web3-public/models/method-data'; +import { + ON_CHAIN_TRADE_TYPE, + OnChainTradeType +} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type'; +import { UniswapV3AbstractTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-trade'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; +import { + HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI, + HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS +} from 'src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/constants/router-configuration'; + +export class HorizondexTrade extends UniswapV3AbstractTrade { + public static get type(): OnChainTradeType { + return ON_CHAIN_TRADE_TYPE.HORIZONDEX; + } + + public readonly dexContractAddress = HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ADDRESS; + + protected readonly contractAbi = HORIZONDEX_UNISWAP_V3_SWAP_ROUTER_CONTRACT_ABI; + + protected readonly unwrapWethMethodName = 'unwrapWeth'; + + /** + * Returns swap `exactInput` method's name and arguments to use in Swap contract. + */ + protected getSwapRouterExactInputMethodData(walletAddress: string): MethodData { + const amountParams = this.getAmountParams(); + + if (this.route.poolsPath.length === 1) { + const methodName = + this.exact === 'input' ? 'swapExactInputSingle' : 'swapExactOutputSingle'; + + const pool = this.route.poolsPath[0]; + if (!pool) { + throw new RubicSdkError('Initial pool has to be defined'); + } + const toTokenAddress = compareAddresses( + pool.token0.address, + this.route.initialTokenAddress + ) + ? pool.token1.address + : pool.token0.address; + + if (!this.route?.poolsPath?.[0]) { + throw new RubicSdkError('PoolsPath[0] has to be defined'); + } + + return { + methodName, + methodArguments: [ + [ + this.route.initialTokenAddress, + toTokenAddress, + this.route.poolsPath[0].fee, + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams, + 0 + ] + ] + }; + } + + const methodName = this.exact === 'input' ? 'swapExactInput' : 'swapExactOutput'; + + return { + methodName, + methodArguments: [ + [ + UniswapV3QuoterController.getEncodedPoolsPath( + this.route.poolsPath, + this.route.initialTokenAddress + ), + walletAddress, + this.deadlineMinutesTimestamp, + ...amountParams + ] + ] + }; + } +} diff --git a/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/utils/quoter-controller/constants/quoter-contract-data.ts b/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/utils/quoter-controller/constants/quoter-contract-data.ts new file mode 100644 index 0000000000..dcde401b30 --- /dev/null +++ b/src/features/on-chain/calculation-manager/providers/dexes/linea/horizondex/utils/quoter-controller/constants/quoter-contract-data.ts @@ -0,0 +1,102 @@ +import { AbiItem } from 'web3-utils'; + +export const HORIZONDEX_QUOTER_CONTRACT_ADDRESS = '0x07AceD5690e09935b1c0e6E88B772d9440F64718'; + +export const HORIZONDEX_QUOTER_CONTRACT_ABI = [ + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' } + ], + name: 'quoteExactInput', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160[]', name: 'afterSqrtPList', type: 'uint160[]' }, + { internalType: 'uint32[]', name: 'initializedTicksCrossedList', type: 'uint32[]' }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint24', name: 'feeUnits', type: 'uint24' }, + { internalType: 'uint160', name: 'limitSqrtP', type: 'uint160' } + ], + internalType: 'struct IQuoterV2.QuoteExactInputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'quoteExactInputSingle', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'usedAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'returnedAmount', type: 'uint256' }, + { internalType: 'uint160', name: 'afterSqrtP', type: 'uint160' }, + { internalType: 'uint32', name: 'initializedTicksCrossed', type: 'uint32' }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' } + ], + internalType: 'struct IQuoterV2.QuoteOutput', + name: 'output', + type: 'tuple' + } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' } + ], + name: 'quoteExactOutput', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160[]', name: 'afterSqrtPList', type: 'uint160[]' }, + { internalType: 'uint32[]', name: 'initializedTicksCrossedList', type: 'uint32[]' }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' } + ], + stateMutability: 'nonpayable', + type: 'function' + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint24', name: 'feeUnits', type: 'uint24' }, + { internalType: 'uint160', name: 'limitSqrtP', type: 'uint160' } + ], + internalType: 'struct IQuoterV2.QuoteExactOutputSingleParams', + name: 'params', + type: 'tuple' + } + ], + name: 'quoteExactOutputSingle', + outputs: [ + { + components: [ + { internalType: 'uint256', name: 'usedAmount', type: 'uint256' }, + { internalType: 'uint256', name: 'returnedAmount', type: 'uint256' }, + { internalType: 'uint160', name: 'afterSqrtP', type: 'uint160' }, + { internalType: 'uint32', name: 'initializedTicksCrossed', type: 'uint32' }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' } + ], + internalType: 'struct IQuoterV2.QuoteOutput', + name: 'output', + type: 'tuple' + } + ], + stateMutability: 'nonpayable', + type: 'function' + } +] as AbiItem[]; diff --git a/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider.ts b/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider.ts index dfa7b9b7a6..78907a4b2a 100644 --- a/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider.ts +++ b/src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-provider.ts @@ -1,5 +1,6 @@ import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name'; import { UniswapV3AbstractProvider } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/uniswap-v3-abstract-provider'; +import { UniswapV3QuoterController } from 'src/features/on-chain/calculation-manager/providers/dexes/common/uniswap-v3-abstract/utils/quoter-controller/uniswap-v3-quoter-controller'; import { UNI_SWAP_V3_POLYGON_PROVIDER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/constants/provider-configuration'; import { UNI_SWAP_V3_POLYGON_ROUTER_CONFIGURATION } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/constants/router-configuration'; import { UniSwapV3PolygonTrade } from 'src/features/on-chain/calculation-manager/providers/dexes/polygon/uni-swap-v3-polygon/uni-swap-v3-polygon-trade'; @@ -12,4 +13,9 @@ export class UniSwapV3PolygonProvider extends UniswapV3AbstractProvider