Commit 60536862 authored by Moody Salem's avatar Moody Salem

surface the best v3 route to the swap page

parent ca2b84ec
......@@ -14,37 +14,43 @@ import { BigNumber, utils } from 'ethers'
* @param route the v3 path to convert to an encoded path
* @param chainId the current chain ID, used to wrap the route's input currency
*/
function routeToPath(route: Route, chainId: ChainId): string {
function routeToPath(route: Route, chainId: ChainId, exactIn: boolean): string {
const firstInputToken = wrappedCurrency(route.input, chainId)
if (!firstInputToken) throw new Error('Could not wrap input currency')
return route.pools.reduce(
const { path, types } = route.pools.reduce(
(
{ inputToken, path }: { inputToken: Token; path: string },
{ inputToken, path, types }: { inputToken: Token; path: (string | number)[]; types: string[] },
pool: Pool,
index
): { inputToken: Token; path: string } => {
): { inputToken: Token; path: (string | number)[]; types: string[] } => {
const outputToken: Token = pool.token0.equals(inputToken) ? pool.token1 : pool.token0
if (index === 0) {
return {
inputToken: outputToken,
path: utils.solidityPack(
['address', 'uint24', 'address'],
[inputToken.address, pool.fee, outputToken.address]
),
types: ['address', 'uint24', 'address'],
path: [inputToken.address, pool.fee, outputToken.address],
}
} else {
return {
inputToken: outputToken,
path: `${path}${utils.solidityPack(['uint24', 'address'], [pool.fee, outputToken.address]).slice(2)}`,
types: [...types, 'uint24', 'address'],
path: [...path, pool.fee, outputToken.address],
}
}
},
{ inputToken: firstInputToken, path: '' }
).path
{ inputToken: firstInputToken, path: [], types: [] }
)
return exactIn ? utils.solidityPack(types, path) : utils.solidityPack(types.reverse(), path.reverse())
}
/**
* Returns the best route for a given exact input swap, and the amount received for it
* @param amountIn the amount to swap in
* @param currencyOut the desired output currency
*/
export function useBestV3RouteExactIn(
amountIn?: CurrencyAmount,
currencyOut?: Currency
......@@ -54,14 +60,14 @@ export function useBestV3RouteExactIn(
const routes = useAllV3Routes(amountIn?.currency, currencyOut)
const paths = useMemo(() => {
if (!chainId) return []
return routes.map((route) => routeToPath(route, chainId))
return routes.map((route) => routeToPath(route, chainId, true))
}, [chainId, routes])
const quoteInputs = useMemo(() => {
const quoteExactInInputs = useMemo(() => {
return paths.map((path) => [path, amountIn ? `0x${amountIn.raw.toString(16)}` : undefined])
}, [amountIn, paths])
const quotesResults = useSingleContractMultipleData(quoter, 'quoteExactInput', quoteInputs)
const quotesResults = useSingleContractMultipleData(quoter, 'quoteExactInput', quoteExactInInputs)
return useMemo(() => {
const { bestRoute, amountOut } = quotesResults.reduce(
......@@ -97,3 +103,63 @@ export function useBestV3RouteExactIn(
}
}, [currencyOut, quotesResults, routes])
}
/**
* Returns the best route for a given exact output swap, and the amount required for it
* @param currencyIn the current to swap in
* @param amountOut the desired amount out
*/
export function useBestV3RouteExactOut(
currencyIn?: Currency,
amountOut?: CurrencyAmount
): { route: Route; amountIn: CurrencyAmount } | null {
const { chainId } = useActiveWeb3React()
const quoter = useV3Quoter()
const routes = useAllV3Routes(currencyIn, amountOut?.currency)
const paths = useMemo(() => {
if (!chainId) return []
return routes.map((route) => routeToPath(route, chainId, true))
}, [chainId, routes])
const quoteExactOutInputs = useMemo(() => {
const amountOutEncoded = amountOut ? `0x${amountOut.raw.toString(16)}` : undefined
return paths.map((path) => [path, amountOutEncoded])
}, [amountOut, paths])
const quotesResults = useSingleContractMultipleData(quoter, 'quoteExactInput', quoteExactOutInputs)
return useMemo(() => {
const { bestRoute, amountIn } = quotesResults.reduce(
(best: { bestRoute: Route | null; amountIn: BigNumber | null }, { valid, loading, result }, i) => {
if (loading || !valid || !result) return best
if (best.amountIn === null) {
return {
bestRoute: routes[i],
amountIn: result.amountIn,
}
} else if (best.amountIn.gt(result.amountIn)) {
return {
bestRoute: routes[i],
amountIn: result.amountIn,
}
}
return best
},
{
bestRoute: null,
amountIn: null,
}
)
if (!bestRoute || !amountIn) return null
return {
route: bestRoute,
amountIn:
currencyIn instanceof Token
? new TokenAmount(currencyIn, amountIn.toString())
: CurrencyAmount.ether(amountIn.toString()),
}
}, [currencyIn, quotesResults, routes])
}
......@@ -88,7 +88,7 @@ function useSwapCallArguments(
// returns a function that will execute a swap, if the parameters are all valid
// and the user has approved the slippage adjusted input amount for the trade
export function useSwapCallback(
export function useV2SwapCallback(
trade: Trade | undefined, // trade to execute, required
allowedSlippage: number = INITIAL_ALLOWED_SLIPPAGE, // in bips
recipientAddressOrName: string | null // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
......
......@@ -2,7 +2,7 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
import { Pair, Trade } from '@uniswap/v2-sdk'
import { useMemo } from 'react'
import { useUserSingleHopOnly } from 'state/user/hooks'
import { isTradeBetter } from 'utils/trades'
import { isTradeBetter } from 'utils/isTradeBetter'
import { BETTER_TRADE_LESS_HOPS_THRESHOLD } from '../constants'
import { useAllCurrencyCombinations } from './useAllCurrencyCombinations'
import { PairState, useV2Pairs } from './useV2Pairs'
......
......@@ -28,7 +28,7 @@ import { useCurrency, useAllTokens } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallbackFromTrade } from '../../hooks/useApproveCallback'
import useENSAddress from '../../hooks/useENSAddress'
import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported'
import { useSwapCallback } from '../../hooks/useSwapCallback'
import { useV2SwapCallback } from '../../hooks/useV2SwapCallback'
import useToggledVersion, { Version } from '../../hooks/useToggledVersion'
import useWrapCallback, { WrapType } from '../../hooks/useWrapCallback'
import { useToggleSettingsMenu, useWalletModalToggle } from '../../state/application/hooks'
......@@ -89,7 +89,14 @@ export default function Swap({ history }: RouteComponentProps) {
// swap state
const { independentField, typedValue, recipient } = useSwapState()
const { v2Trade, currencyBalances, parsedAmount, currencies, inputError: swapInputError } = useDerivedSwapInfo()
const {
v2Trade,
currencyBalances,
parsedAmount,
currencies,
v3Route,
inputError: swapInputError,
} = useDerivedSwapInfo()
const { wrapType, execute: onWrap, inputError: wrapInputError } = useWrapCallback(
currencies[Field.INPUT],
......@@ -183,7 +190,7 @@ export default function Swap({ history }: RouteComponentProps) {
const atMaxAmountInput = Boolean(maxAmountInput && parsedAmounts[Field.INPUT]?.equalTo(maxAmountInput))
// the callback to execute the swap
const { callback: swapCallback, error: swapCallbackError } = useSwapCallback(trade, allowedSlippage, recipient)
const { callback: swapCallback, error: swapCallbackError } = useV2SwapCallback(trade, allowedSlippage, recipient)
const { priceImpactWithoutFee } = computeTradePriceBreakdown(trade)
......
import { useBestV3RouteExactIn } from '../../hooks/useBestV3Route'
import { Route } from '@uniswap/v3-sdk'
import { useBestV3RouteExactIn, useBestV3RouteExactOut } from '../../hooks/useBestV3Route'
import useENS from '../../hooks/useENS'
import { parseUnits } from '@ethersproject/units'
import { Currency, CurrencyAmount, ETHER, Token, TokenAmount } from '@uniswap/sdk-core'
......@@ -112,6 +113,16 @@ export function useDerivedSwapInfo(): {
parsedAmount: CurrencyAmount | undefined
v2Trade: Trade | undefined
inputError?: string
v3Route:
| {
route: Route
amountIn: CurrencyAmount
}
| {
route: Route
amountOut: CurrencyAmount
}
| undefined
} {
const { account } = useActiveWeb3React()
......@@ -140,10 +151,10 @@ export function useDerivedSwapInfo(): {
const bestTradeExactOut = useV2TradeExactOut(inputCurrency ?? undefined, !isExactIn ? parsedAmount : undefined)
const bestRouteExactInV3 = useBestV3RouteExactIn(isExactIn ? parsedAmount : undefined, outputCurrency ?? undefined)
// todo: do something with this information
console.log('best v3 route for the swap', bestRouteExactInV3)
const bestRouteExactOutV3 = useBestV3RouteExactOut(inputCurrency ?? undefined, !isExactIn ? parsedAmount : undefined)
const v2Trade = isExactIn ? bestTradeExactIn : bestTradeExactOut
const v3Route = (isExactIn ? bestRouteExactInV3 : bestRouteExactOutV3) ?? undefined
const currencyBalances = {
[Field.INPUT]: relevantTokenBalances[0],
......@@ -201,6 +212,7 @@ export function useDerivedSwapInfo(): {
parsedAmount,
v2Trade: v2Trade ?? undefined,
inputError,
v3Route,
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment