import { BigNumber } from "ethers"; import { BaseProvider } from "@ethersproject/providers"; import { ether, preciseDiv, preciseMul } from "@setprotocol/index-coop-contracts/dist/utils/common"; import { ChainId, TokenAmount, Pair, Trade, Token, Fetcher, } from "@uniswap/sdk"; import { ExchangeQuote, exchanges, Address } from "../../types"; import { ZERO } from "../../../utils/constants"; import DEPENDENCY from "../../dependencies"; const TEN_BPS_IN_PERCENT = ether(.1); const THIRTY_BPS_IN_PERCENT = ether(.3); const { ETH_ADDRESS, BTC_ADDRESS, USDC_ADDRESS, } = DEPENDENCY; export async function getUniswapV2Quote( provider: BaseProvider, tokenAddress: Address, targetPriceImpact: BigNumber): Promise<ExchangeQuote> { const token: Token = await Fetcher.fetchTokenData(ChainId.MAINNET, tokenAddress, provider); const weth: Token = await Fetcher.fetchTokenData(ChainId.MAINNET, ETH_ADDRESS, provider); const wbtc: Token = await Fetcher.fetchTokenData(ChainId.MAINNET, BTC_ADDRESS, provider); const usdc: Token = await Fetcher.fetchTokenData(ChainId.MAINNET, USDC_ADDRESS, provider); const trades = Trade.bestTradeExactIn( await getUniswapV2Pairs(provider, [token, weth, wbtc, usdc]), new TokenAmount(weth, ether(1).toString()), token, {maxNumResults: 3, maxHops: 2}, ); if (trades.length != 0) { // Use linear approximation of price impact to find out how many 1 ETH trades add to 50 bps price impact (net of fees) const hops = trades[0].route.pairs.length; const priceImpactRatio = preciseDiv( hops > 1 ? targetPriceImpact.sub(TEN_BPS_IN_PERCENT) : targetPriceImpact, // Subtract 10 bps from targetPriceImpact if extra hop used ether(parseFloat(trades[0].priceImpact.toSignificant(18))).sub(THIRTY_BPS_IN_PERCENT.mul(trades[0].route.pairs.length)) ); return { exchange: exchanges.UNISWAP, size: preciseMul( ether(parseFloat(trades[0].outputAmount.toExact())).div(BigNumber.from(10).pow(18 - token.decimals)), priceImpactRatio).toString(), data: hops > 1 ? trades[0].route.path[1].address : "0x", } as ExchangeQuote; } return { exchange: exchanges.UNISWAP, size: ZERO.toString(), data: "0x", } as ExchangeQuote; } async function getUniswapV2Pairs(provider: BaseProvider, tokens: Token[]): Promise<Pair[]> { const pairs: Pair[] = []; for (let i = 0; i < tokens.length - 1; i++) { for (let j = 1; j < tokens.length - i - 1; j++) { const tokenOne = tokens[i]; const tokenTwo = tokens[i + j]; let pair; try { pair = await Fetcher.fetchPairData(tokenOne, tokenTwo, provider); } catch (error) { continue; } pairs.push(pair); } } return pairs; }