diff --git a/packages/internal/dex/sdk-sample-app/src/components/Example.tsx b/packages/internal/dex/sdk-sample-app/src/components/Example.tsx index db7f2da6ad..03b5798891 100644 --- a/packages/internal/dex/sdk-sample-app/src/components/Example.tsx +++ b/packages/internal/dex/sdk-sample-app/src/components/Example.tsx @@ -26,6 +26,7 @@ const allTokens: Token[] = [ { symbol: 'zkWLT', address: '0x8A5b0470ee48248bEb7D1E745c1EbA0DCA77215e' }, { symbol: 'zkSRE', address: '0x43566cAB87CC147C95e2895E7b972E19993520e4' }, { symbol: 'zkCORE', address: '0x4B96E7b7eA673A996F140d5De411a97b7eab934E' }, + { symbol: 'zkWAT', address: '0xaC953a0d7B67Fae17c87abf79f09D0f818AC66A2' }, ]; const buildExchange = (secondaryFeeRecipient: string, secondaryFeePercentage: number) => { diff --git a/packages/internal/dex/sdk/src/exchange.getUnsignedSwapTxFromAmountIn.test.ts b/packages/internal/dex/sdk/src/exchange.getUnsignedSwapTxFromAmountIn.test.ts index baa28c5797..3ccde46359 100644 --- a/packages/internal/dex/sdk/src/exchange.getUnsignedSwapTxFromAmountIn.test.ts +++ b/packages/internal/dex/sdk/src/exchange.getUnsignedSwapTxFromAmountIn.test.ts @@ -1,4 +1,4 @@ -import { JsonRpcProvider } from '@ethersproject/providers'; +import { JsonRpcProvider, JsonRpcBatchProvider } from '@ethersproject/providers'; import { Contract } from '@ethersproject/contracts'; import { BigNumber } from '@ethersproject/bignumber'; import { InvalidAddressError, InvalidMaxHopsError, InvalidSlippageError, NoRoutesAvailableError } from 'errors'; @@ -71,11 +71,14 @@ describe('getUnsignedSwapTxFromAmountIn', () => { })); (JsonRpcProvider as unknown as jest.Mock).mockImplementation(() => ({ + connect: jest.fn().mockResolvedValue(erc20Contract), + })) as unknown as JsonRpcProvider; + + (JsonRpcBatchProvider as unknown as jest.Mock).mockImplementation(() => ({ getFeeData: async () => ({ maxFeePerGas: null, gasPrice: TEST_GAS_PRICE, }), - connect: jest.fn().mockResolvedValue(erc20Contract), })) as unknown as JsonRpcProvider; }); diff --git a/packages/internal/dex/sdk/src/exchange.getUnsignedSwapTxFromAmountOut.test.ts b/packages/internal/dex/sdk/src/exchange.getUnsignedSwapTxFromAmountOut.test.ts index 6e50c74ce6..55a986db69 100644 --- a/packages/internal/dex/sdk/src/exchange.getUnsignedSwapTxFromAmountOut.test.ts +++ b/packages/internal/dex/sdk/src/exchange.getUnsignedSwapTxFromAmountOut.test.ts @@ -1,4 +1,4 @@ -import { JsonRpcProvider } from '@ethersproject/providers'; +import { JsonRpcProvider, JsonRpcBatchProvider } from '@ethersproject/providers'; import { Contract } from '@ethersproject/contracts'; import { BigNumber } from '@ethersproject/bignumber'; import { ethers } from 'ethers'; @@ -67,7 +67,7 @@ describe('getUnsignedSwapTxFromAmountOut', () => { paused: jest.fn().mockResolvedValue(false), })); - (JsonRpcProvider as unknown as jest.Mock).mockImplementation(() => ({ + (JsonRpcBatchProvider as unknown as jest.Mock).mockImplementation(() => ({ getFeeData: async () => ({ maxFeePerGas: null, gasPrice: TEST_GAS_PRICE, diff --git a/packages/internal/dex/sdk/src/exchange.ts b/packages/internal/dex/sdk/src/exchange.ts index 5ade31afd6..bf59e56a0c 100644 --- a/packages/internal/dex/sdk/src/exchange.ts +++ b/packages/internal/dex/sdk/src/exchange.ts @@ -6,7 +6,7 @@ import { fetchGasPrice } from 'lib/transactionUtils/gas'; import { getApproval, prepareApproval } from 'lib/transactionUtils/approval'; import { getOurQuoteReqAmount, prepareUserQuote } from 'lib/transactionUtils/getQuote'; import { Fees } from 'lib/fees'; -import { SecondaryFee__factory } from 'contracts/types'; +import { Multicall__factory, SecondaryFee__factory } from 'contracts/types'; import { NativeTokenService } from 'lib/nativeTokenService'; import { DEFAULT_MAX_HOPS, DEFAULT_SLIPPAGE, MAX_MAX_HOPS, MIN_MAX_HOPS } from './constants'; import { Router } from './lib/router'; @@ -47,7 +47,9 @@ const toPublicQuote = ( }); export class Exchange { - private provider: ethers.providers.JsonRpcProvider; + private provider: ethers.providers.StaticJsonRpcProvider; + + private batchProvider: ethers.providers.JsonRpcBatchProvider; private router: Router; @@ -76,13 +78,24 @@ export class Exchange { this.routerContractAddress = config.chain.contracts.peripheryRouter; this.secondaryFeeContractAddress = config.chain.contracts.secondaryFee; - this.provider = new ethers.providers.JsonRpcProvider(config.chain.rpcUrl); + this.provider = new ethers.providers.StaticJsonRpcProvider({ + url: config.chain.rpcUrl, + skipFetchSetup: true, + }, config.chain.chainId); + + this.batchProvider = new ethers.providers.JsonRpcBatchProvider({ + url: config.chain.rpcUrl, + skipFetchSetup: true, + }, config.chain.chainId); - this.router = new Router(this.provider, config.chain.commonRoutingTokens, { - multicallAddress: config.chain.contracts.multicall, - factoryAddress: config.chain.contracts.coreFactory, - quoterAddress: config.chain.contracts.quoterV2, - }); + const multicallContract = Multicall__factory.connect(config.chain.contracts.multicall, this.provider); + + this.router = new Router( + this.batchProvider, + multicallContract, + config.chain.commonRoutingTokens, + config.chain.contracts, + ); } private static validate( @@ -102,12 +115,12 @@ export class Exchange { assert(slippagePercent >= 0, new InvalidSlippageError('slippage percent must be greater than or equal to 0')); } - private async getSecondaryFees() { + private async getSecondaryFees(provider: ethers.providers.JsonRpcBatchProvider) { if (this.secondaryFees.length === 0) { return []; } - const secondaryFeeContract = SecondaryFee__factory.connect(this.secondaryFeeContractAddress, this.provider); + const secondaryFeeContract = SecondaryFee__factory.connect(this.secondaryFeeContractAddress, provider); if (await secondaryFeeContract.paused()) { // Do not use secondary fees if the contract is paused @@ -143,11 +156,14 @@ export class Exchange { Exchange.validate(tokenInLiteral, tokenOutLiteral, maxHops, slippagePercent, fromAddress); // get the decimals of the tokens that will be swapped - const [tokenInDecimals, tokenOutDecimals, secondaryFees] = await Promise.all([ - getTokenDecimals(tokenInLiteral, this.provider, this.nativeToken), - getTokenDecimals(tokenOutLiteral, this.provider, this.nativeToken), - this.getSecondaryFees(), - ]); + const promises = [ + getTokenDecimals(tokenInLiteral, this.batchProvider, this.nativeToken), + getTokenDecimals(tokenOutLiteral, this.batchProvider, this.nativeToken), + this.getSecondaryFees(this.batchProvider), + fetchGasPrice(this.batchProvider, this.nativeToken), + ] as const; + + const [tokenInDecimals, tokenOutDecimals, secondaryFees, gasPrice] = await Promise.all(promises); const tokenIn = this.parseTokenLiteral(tokenInLiteral, tokenInDecimals); const tokenOut = this.parseTokenLiteral(tokenOutLiteral, tokenOutDecimals); @@ -163,15 +179,12 @@ export class Exchange { const ourQuoteReqAmount = getOurQuoteReqAmount(amountSpecified, fees, tradeType, this.nativeTokenService); // Quotes will always use ERC20s. If the user-specified token is Native, we use the Wrapped Native Token pool - const [ourQuote, gasPrice] = await Promise.all([ - this.router.findOptimalRoute( - ourQuoteReqAmount, - this.nativeTokenService.maybeWrapToken(otherToken), - tradeType, - maxHops, - ), - fetchGasPrice(this.provider, this.nativeToken), - ]); + const ourQuote = await this.router.findOptimalRoute( + ourQuoteReqAmount, + this.nativeTokenService.maybeWrapToken(otherToken), + tradeType, + maxHops, + ); const adjustedQuote = adjustQuoteWithFees(ourQuote, amountSpecified, fees, this.nativeTokenService); diff --git a/packages/internal/dex/sdk/src/lib/getQuotesForRoutes.test.ts b/packages/internal/dex/sdk/src/lib/getQuotesForRoutes.test.ts index 5e77dab947..504e7da89f 100644 --- a/packages/internal/dex/sdk/src/lib/getQuotesForRoutes.test.ts +++ b/packages/internal/dex/sdk/src/lib/getQuotesForRoutes.test.ts @@ -4,7 +4,7 @@ import { import { TradeType } from '@uniswap/sdk-core'; import { BigNumber, utils } from 'ethers'; import { ProviderCallError } from 'errors'; -import { getQuotesForRoutes } from './getQuotesForRoutes'; +import { getQuotesForRoutes, Provider } from './getQuotesForRoutes'; import { IMX_TEST_TOKEN, TEST_QUOTER_ADDRESS, @@ -13,7 +13,6 @@ import { newAmountFromString, } from '../test/utils'; import { erc20ToUniswapToken, newAmount } from './utils'; -import { Multicall } from './multicall'; const UNISWAP_IMX = erc20ToUniswapToken(IMX_TEST_TOKEN); const UNISWAP_WETH = erc20ToUniswapToken(WETH_TEST_TOKEN); @@ -36,10 +35,10 @@ const types = [ 'uint256', // gasEstimate ]; -const buildMulticallContract = (multicall: jest.Mock): Multicall => ({ callStatic: { multicall } }); +const buildProvider = (send: jest.Mock): Provider => ({ send }); describe('getQuotesForRoutes', () => { - it('uses a suitable gas limit', async () => { + it('makes an eth_call against the provider', async () => { const expectedAmountOut = utils.parseEther('1000'); const expectedGasEstimate = '100000'; @@ -50,18 +49,10 @@ describe('getQuotesForRoutes', () => { expectedGasEstimate, ]); - const multicallContract = buildMulticallContract( - jest.fn().mockResolvedValue({ - returnData: [ - { - returnData, - }, - ], - }), - ); + const provider = buildProvider(jest.fn().mockResolvedValueOnce(returnData)); const quoteResults = await getQuotesForRoutes( - multicallContract, + provider, TEST_QUOTER_ADDRESS, [route], newAmountFromString('1', WETH_TEST_TOKEN), @@ -69,29 +60,60 @@ describe('getQuotesForRoutes', () => { ); expect(quoteResults).toHaveLength(1); - expect(multicallContract.callStatic.multicall).toHaveBeenCalledWith([{ + expect(provider.send).toHaveBeenCalledWith('eth_call', [{ // eslint-disable-next-line max-len - callData: expect.any(String), - gasLimit: 2000000, - target: TEST_QUOTER_ADDRESS, - }]); + data: '0xc6a5026a0000000000000000000000004f062a3eaec3730560ab89b5ce5ac0ab2c5517ae00000000000000000000000072958b06abdf2701ace6ceb3ce0b8b1ce11e08510000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000', + to: '0x9B323E56215aAdcD4f45a6Be660f287DE154AFC5', + }, 'latest']); }); - describe('when multicall fails', () => { - it('should throw ProviderCallError', async () => { - const mockedMulticallContract = buildMulticallContract( + describe('when all calls in the batch fail', () => { + it('returns no quote results', async () => { + const provider = buildProvider( jest.fn().mockRejectedValue(new ProviderCallError('an rpc error message')), ); const amount = newAmount(BigNumber.from('123123'), WETH_TEST_TOKEN); - await expect(getQuotesForRoutes( - mockedMulticallContract, + const quoteResults = await getQuotesForRoutes( + provider, TEST_QUOTER_ADDRESS, [route], amount, TradeType.EXACT_INPUT, - )).rejects.toThrow(new ProviderCallError('failed multicall: an rpc error message')); + ); + expect(quoteResults).toHaveLength(0); + }); + }); + + describe('when one call of two in the batch fail', () => { + it('returns one quote results', async () => { + const expectedAmountOut = utils.parseEther('1000'); + const expectedGasEstimate = '100000'; + + const returnData = utils.defaultAbiCoder.encode(types, [ + expectedAmountOut, + '100', + '1', + expectedGasEstimate, + ]); + + const provider = buildProvider( + jest.fn() + .mockRejectedValueOnce(new ProviderCallError('an rpc error message')) + .mockResolvedValueOnce(returnData), + ); + + const amount = newAmount(BigNumber.from('123123'), WETH_TEST_TOKEN); + + const quoteResults = await getQuotesForRoutes( + provider, + TEST_QUOTER_ADDRESS, + [route, route], + amount, + TradeType.EXACT_INPUT, + ); + expect(quoteResults).toHaveLength(1); }); }); @@ -107,19 +129,13 @@ describe('getQuotesForRoutes', () => { expectedGasEstimate, ]); - const multicallContract = buildMulticallContract( - jest.fn().mockResolvedValue({ - returnData: [ - { - returnData, - }, - ], - }), + const provider = buildProvider( + jest.fn().mockResolvedValue(returnData), ); const amount = newAmount(BigNumber.from('123123'), WETH_TEST_TOKEN); const amountOutReceived = await getQuotesForRoutes( - multicallContract, + provider, TEST_QUOTER_ADDRESS, [route], amount, @@ -152,22 +168,13 @@ describe('getQuotesForRoutes', () => { expectedGasEstimate2, ]); - const multicallContract = buildMulticallContract( - jest.fn().mockResolvedValueOnce({ - returnData: [ - { - returnData: returnData1, - }, - { - returnData: returnData2, - }, - ], - }), + const provider = buildProvider( + jest.fn().mockResolvedValueOnce(returnData1).mockResolvedValueOnce(returnData2), ); const amount = newAmount(BigNumber.from('123123'), WETH_TEST_TOKEN); const amountOutReceived = await getQuotesForRoutes( - multicallContract, + provider, TEST_QUOTER_ADDRESS, [route, route], amount, diff --git a/packages/internal/dex/sdk/src/lib/getQuotesForRoutes.ts b/packages/internal/dex/sdk/src/lib/getQuotesForRoutes.ts index 4d0c534938..22a91e9670 100644 --- a/packages/internal/dex/sdk/src/lib/getQuotesForRoutes.ts +++ b/packages/internal/dex/sdk/src/lib/getQuotesForRoutes.ts @@ -1,9 +1,7 @@ import { Route, SwapQuoter } from '@uniswap/v3-sdk'; import { TradeType, Token } from '@uniswap/sdk-core'; -import { BigNumber, ethers } from 'ethers'; -import { ProviderCallError } from 'errors'; +import { BigNumber, utils } from 'ethers'; import { CoinAmount, ERC20 } from 'types'; -import { Multicall, multicallMultipleCallDataSingContract, MulticallResponse } from './multicall'; import { newAmount, quoteReturnMapping, toCurrencyAmount, uniswapTokenToERC20, } from './utils'; @@ -11,60 +9,54 @@ import { const amountIndex = 0; const gasEstimateIndex = 3; +export interface Provider { + send: (method: string, params: any[]) => Promise; +} + export type QuoteResult = { route: Route; - gasEstimate: ethers.BigNumber + gasEstimate: BigNumber amountIn: CoinAmount; amountOut: CoinAmount; tradeType: TradeType; }; export async function getQuotesForRoutes( - multicallContract: Multicall, + provider: Provider, quoterContractAddress: string, routes: Route[], amountSpecified: CoinAmount, tradeType: TradeType, ): Promise { - const callData = routes.map( + const callDatas = routes.map( (route) => SwapQuoter.quoteCallParameters(route, toCurrencyAmount(amountSpecified), tradeType, { useQuoterV2: true, }).calldata, ); - let quoteResults: MulticallResponse; - try { - quoteResults = await multicallMultipleCallDataSingContract( - multicallContract, - callData, - quoterContractAddress, - ); - } catch (e) { - const message = e instanceof Error ? e.message : 'Unknown Error'; - throw new ProviderCallError(`failed multicall: ${message}`); - } + const promises = await Promise.allSettled(callDatas.map((data) => + provider.send('eth_call', [ + { to: quoterContractAddress, data }, 'latest', + ]))); - const decodedQuoteResults: QuoteResult[] = []; - // TODO: for..in loops iterate over the entire prototype chain, - // Use Object.{keys,values,entries}, and iterate over the resulting array. - // eslint-disable-next-line no-restricted-syntax, guard-for-in - for (const i in quoteResults.returnData) { - const functionSig = callData[i].substring(0, 10); + const decodedQuoteResults = promises.reduce((quoteResults, promiseResult, i) => { + if (promiseResult.status === 'rejected') return quoteResults; + + const functionSig = callDatas[i].substring(0, 10); const returnTypes = quoteReturnMapping[functionSig]; if (!returnTypes) { throw new Error('No quoting function signature found'); } - if (quoteResults.returnData[i].returnData === '0x') { + if (promiseResult.value === '0x') { // There is no quote result for the swap using this route, so don't include it in results - // eslint-disable-next-line no-continue - continue; + return quoteResults; } try { - const decodedQuoteResult = ethers.utils.defaultAbiCoder.decode( + const decodedQuoteResult = utils.defaultAbiCoder.decode( returnTypes, - quoteResults.returnData[i].returnData, + promiseResult.value, ); if (decodedQuoteResult) { @@ -75,11 +67,11 @@ export async function getQuotesForRoutes( const input = uniswapTokenToERC20(routes[i].input); const output = uniswapTokenToERC20(routes[i].output); - decodedQuoteResults.push({ + quoteResults.push({ route: routes[i], amountIn: tradeType === TradeType.EXACT_INPUT ? amountSpecified : newAmount(quoteAmount, input), amountOut: tradeType === TradeType.EXACT_INPUT ? newAmount(quoteAmount, output) : amountSpecified, - gasEstimate: ethers.BigNumber.from(decodedQuoteResult[gasEstimateIndex]), + gasEstimate: BigNumber.from(decodedQuoteResult[gasEstimateIndex]), tradeType, }); } @@ -88,7 +80,9 @@ export async function getQuotesForRoutes( // Other quotes for routes may still succeed, so do nothing // and continue processing } - } + + return quoteResults; + }, new Array()); return decodedQuoteResults; } diff --git a/packages/internal/dex/sdk/src/lib/router.test.ts b/packages/internal/dex/sdk/src/lib/router.test.ts new file mode 100644 index 0000000000..ff359ab472 --- /dev/null +++ b/packages/internal/dex/sdk/src/lib/router.test.ts @@ -0,0 +1,114 @@ +/* eslint-disable arrow-body-style */ +import { TradeType } from '@uniswap/sdk-core'; +import { SUPPORTED_SANDBOX_CHAINS } from 'config'; +import { WIMX_IMMUTABLE_TESTNET } from 'constants/tokens'; +import { IMMUTABLE_TESTNET_CHAIN_ID } from 'constants/chains'; +import { providers, utils } from 'ethers'; +import { newAmountFromString } from 'test/utils'; +import { Multicall__factory, QuoterV2__factory } from 'contracts/types'; +import { Router } from './router'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const zkONE = { + address: '0x12739A8f1A8035F439092D016DAE19A2874F30d2', + chainId: IMMUTABLE_TESTNET_CHAIN_ID, + decimals: 18, + type: 'erc20', +} as const; + +const zkWAT = { + address: '0xaC953a0d7B67Fae17c87abf79f09D0f818AC66A2', + chainId: IMMUTABLE_TESTNET_CHAIN_ID, + decimals: 18, + type: 'erc20', +} as const; + +const fees = [100, 500, 10000]; + +const config = SUPPORTED_SANDBOX_CHAINS[IMMUTABLE_TESTNET_CHAIN_ID]; +const provider = new providers.JsonRpcBatchProvider(config.rpcUrl, config.chainId); + +const q = QuoterV2__factory.connect(config.contracts.quoterV2, provider); +const m = Multicall__factory.connect(config.contracts.multicall, provider); + +const callDatas = () => fees.map((fee) => { + // const rand = Math.floor(Math.random() * 100); + const amountIn = utils.parseEther('20700000'); // BigNumber.from(rand).mul(BigNumber.from(10).pow(18)); + return q.interface.encodeFunctionData('quoteExactInputSingle', [ + { + amountIn, + tokenIn: zkWAT.address, + tokenOut: WIMX_IMMUTABLE_TESTNET.address, + fee, + sqrtPriceLimitX96: 0, + }, + ]); +}); + +describe('router', () => { + describe.skip('for realsies', () => { + it('calculates an amount out', async () => { + const amountSpecified = newAmountFromString('20700000', zkWAT); + const router = new Router(provider, m, config.commonRoutingTokens, config.contracts); + const quoteResult = await router.findOptimalRoute(amountSpecified, WIMX_IMMUTABLE_TESTNET, TradeType.EXACT_INPUT); + expect(utils.formatEther(quoteResult.amountOut.value)).toEqual('7089.43335507464515036'); + }); + + it.each(Array(10).fill(null))('parallel', async () => { + const promises = callDatas().map(async (data) => { + const res = await provider.call({ + to: config.contracts.quoterV2, + data, + }); + const { amountOut } = q.interface.decodeFunctionResult( + 'quoteExactInputSingle', + res, + ); + return utils.formatEther(amountOut); + }); + const results = await Promise.all(promises); + + expect(results).toEqual(['516.580710655537430041', '7089.43335507464515036', '3.718255837400445597']); + }); + + it.each(Array(10).fill(null))('multicall', async () => { + const calls = callDatas().map((callData) => { + return { + target: config.contracts.quoterV2, + callData, + gasLimit: 24_000_000, + }; + }); + + const { returnData } = await m.callStatic.multicall(calls); + + const results = returnData.map((data) => { + if (data.returnData === '0x') return []; + const res = q.interface.decodeFunctionResult('quoteExactInputSingle', data.returnData); + return utils.formatEther(res.amountOut); + }); + + expect(results).toEqual(['516.580710655537430041', '7089.43335507464515036', '3.718255837400445597']); + }); + + it.each(Array(1).fill(null))('batch', async () => { + const promises = callDatas().map((callData) => { + return provider.send('eth_call', [{ + to: config.contracts.quoterV2, + data: callData, + }, 'latest']); + }); + + const promise = await Promise.allSettled(promises); + + const results = promise.map((data) => { + if (data.status === 'rejected') return []; + if (data.value === '0x') return []; + const res = q.interface.decodeFunctionResult('quoteExactInputSingle', data.value); + return utils.formatEther(res.amountOut); + }); + + expect(results).toEqual(['516.580710655537430041', '7089.43335507464515036', '3.718255837400445597']); + }); + }); +}); diff --git a/packages/internal/dex/sdk/src/lib/router.ts b/packages/internal/dex/sdk/src/lib/router.ts index 961aafbd67..be1ca1624b 100644 --- a/packages/internal/dex/sdk/src/lib/router.ts +++ b/packages/internal/dex/sdk/src/lib/router.ts @@ -1,29 +1,29 @@ -import { ethers } from 'ethers'; import { Token, TradeType } from '@uniswap/sdk-core'; import { Pool, Route } from '@uniswap/v3-sdk'; import { NoRoutesAvailableError } from 'errors'; import { CoinAmount, ERC20 } from 'types'; +import { providers } from 'ethers'; import { erc20ToUniswapToken, poolEquals, uniswapTokenToERC20 } from './utils'; import { getQuotesForRoutes, QuoteResult } from './getQuotesForRoutes'; import { fetchValidPools } from './poolUtils/fetchValidPools'; import { ERC20Pair } from './poolUtils/generateERC20Pairs'; -import { Multicall, Multicall__factory } from '../contracts/types'; +import type { Multicall } from '../contracts/types'; export type RoutingContracts = { - multicallAddress: string; - factoryAddress: string; - quoterAddress: string; + multicall: string; + coreFactory: string; + quoterV2: string; }; export class Router { - public provider: ethers.providers.JsonRpcProvider; - - public routingTokens: ERC20[]; - - public routingContracts: RoutingContracts; - - constructor(provider: ethers.providers.JsonRpcProvider, routingTokens: ERC20[], routingContracts: RoutingContracts) { + constructor( + public provider: providers.JsonRpcBatchProvider, + public multicallContract: Multicall, + public routingTokens: ERC20[], + public routingContracts: RoutingContracts, + ) { this.provider = provider; + this.multicallContract = multicallContract; this.routingTokens = routingTokens; this.routingContracts = routingContracts; } @@ -36,15 +36,14 @@ export class Router { ): Promise { const [tokenIn, tokenOut] = this.determineERC20InAndERC20Out(tradeType, amountSpecified, otherToken); - const multicallContract = Multicall__factory.connect(this.routingContracts.multicallAddress, this.provider); const erc20Pair: ERC20Pair = [tokenIn, tokenOut]; // Get all pools and use these to get all possible routes. const pools = await fetchValidPools( - multicallContract, + this.multicallContract, erc20Pair, this.routingTokens, - this.routingContracts.factoryAddress, + this.routingContracts.coreFactory, ); const noValidPools = pools.length === 0; @@ -63,18 +62,17 @@ export class Router { } // Get the best quote from all of the given routes - return await this.getBestQuoteFromRoutes(multicallContract, routes, amountSpecified, tradeType); + return await this.getBestQuoteFromRoutes(routes, amountSpecified, tradeType); } private async getBestQuoteFromRoutes( - multicallContract: Multicall, routes: Route[], amountSpecified: CoinAmount, tradeType: TradeType, ): Promise { const quotes = await getQuotesForRoutes( - multicallContract, - this.routingContracts.quoterAddress, + this.provider, + this.routingContracts.quoterV2, routes, amountSpecified, tradeType, diff --git a/packages/internal/dex/sdk/src/test/utils.ts b/packages/internal/dex/sdk/src/test/utils.ts index 7b2a15ac7a..a30513d93b 100644 --- a/packages/internal/dex/sdk/src/test/utils.ts +++ b/packages/internal/dex/sdk/src/test/utils.ts @@ -33,9 +33,9 @@ export const TEST_TICK_LENS_ADDRESSES = '0x3aC4F8094b21A6c5945453007d9c52B7e1534 export const TEST_SECONDARY_FEE_ADDRESS = '0x8dBE1f0900C5e92ad87A54521902a33ba1598C51'; export const TEST_ROUTING_CONTRACTS: RoutingContracts = { - factoryAddress: TEST_V3_CORE_FACTORY_ADDRESS, - quoterAddress: TEST_QUOTER_ADDRESS, - multicallAddress: TEST_MULTICALL_ADDRESS, + coreFactory: TEST_V3_CORE_FACTORY_ADDRESS, + quoterV2: TEST_QUOTER_ADDRESS, + multicall: TEST_MULTICALL_ADDRESS, }; export const IMX_TEST_TOKEN: ERC20 = {