fix: Remove double client side quoting (#7121)

* remove double client side quoting

* remove additional file

* remove file

* remove file

* MOVE YET ANOTHER FILE

* remove unused tokens

* fix export

* remove unused token constant and swap out for wbtc
This commit is contained in:
Tina 2023-08-11 19:25:57 -04:00 committed by GitHub
parent b98e62cac8
commit cc52da9fc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 93 additions and 704 deletions

@ -2,11 +2,9 @@
import { ChainId, Currency, Token } from '@uniswap/sdk-core'
import {
AMPL,
ARB,
BTC_BSC,
BUSD_BSC,
CAKE_BSC,
CEUR_CELO,
CEUR_CELO_ALFAJORES,
CMC02_CELO,
@ -19,21 +17,10 @@ import {
DAI_OPTIMISM,
DAI_POLYGON,
ETH_BSC,
ETH2X_FLI,
FEI,
FRAX,
FRAX_BSC,
FXS,
MATIC_BSC,
nativeOnChain,
OP,
PORTAL_ETH_CELO,
PORTAL_USDC_CELO,
renBTC,
rETH2,
sETH2,
SWISE,
TRIBE,
USDC_ARBITRUM,
USDC_ARBITRUM_GOERLI,
USDC_AVALANCHE,
@ -72,66 +59,6 @@ const WRAPPED_NATIVE_CURRENCIES_ONLY: ChainTokenList = Object.fromEntries(
.filter(Boolean)
)
// used to construct intermediary pairs for trading
export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = {
...WRAPPED_NATIVE_CURRENCIES_ONLY,
[ChainId.MAINNET]: [...WRAPPED_NATIVE_CURRENCIES_ONLY[ChainId.MAINNET], DAI, USDC_MAINNET, USDT, WBTC],
[ChainId.OPTIMISM]: [...WRAPPED_NATIVE_CURRENCIES_ONLY[ChainId.OPTIMISM], DAI_OPTIMISM, USDT_OPTIMISM, WBTC_OPTIMISM],
[ChainId.ARBITRUM_ONE]: [
...WRAPPED_NATIVE_CURRENCIES_ONLY[ChainId.ARBITRUM_ONE],
DAI_ARBITRUM_ONE,
USDT_ARBITRUM_ONE,
WBTC_ARBITRUM_ONE,
],
[ChainId.POLYGON]: [
...WRAPPED_NATIVE_CURRENCIES_ONLY[ChainId.POLYGON],
DAI_POLYGON,
USDC_POLYGON,
USDT_POLYGON,
WETH_POLYGON,
],
[ChainId.BNB]: [
...WRAPPED_NATIVE_CURRENCIES_ONLY[ChainId.BNB],
DAI_BSC,
USDC_BSC,
USDT_BSC,
BUSD_BSC,
FRAX_BSC,
MATIC_BSC,
CAKE_BSC,
],
[ChainId.AVALANCHE]: [
...WRAPPED_NATIVE_CURRENCIES_ONLY[ChainId.AVALANCHE],
DAI_AVALANCHE,
USDC_AVALANCHE,
USDT_AVALANCHE,
WETH_AVALANCHE,
],
[ChainId.CELO]: [CUSD_CELO, CEUR_CELO, CMC02_CELO, PORTAL_USDC_CELO, PORTAL_ETH_CELO],
}
export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = {
[ChainId.MAINNET]: {
'0xF16E4d813f4DcfDe4c5b44f305c908742De84eF0': [ETH2X_FLI],
[rETH2.address]: [sETH2],
[SWISE.address]: [sETH2],
[FEI.address]: [TRIBE],
[TRIBE.address]: [FEI],
[FRAX.address]: [FXS],
[FXS.address]: [FRAX],
[WBTC.address]: [renBTC],
[renBTC.address]: [WBTC],
},
}
/**
* Some tokens can only be swapped via certain pairs, so we override the list of bases that are considered for these
* tokens.
*/
export const CUSTOM_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = {
[ChainId.MAINNET]: {
[AMPL.address]: [DAI, WRAPPED_NATIVE_CURRENCY[ChainId.MAINNET] as Token],
},
}
/**
* Shows up in the currency select for swap and add liquidity
*/

@ -80,7 +80,7 @@ export const USDC_BASE = new Token(
'USD Base Coin',
'USDbC'
)
export const AMPL = new Token(ChainId.MAINNET, '0xD46bA6D942050d489DBd938a2C909A5d5039A161', 9, 'AMPL', 'Ampleforth')
export const DAI = new Token(ChainId.MAINNET, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'Dai Stablecoin')
export const DAI_ARBITRUM_ONE = new Token(
ChainId.ARBITRUM_ONE,
@ -147,33 +147,7 @@ export const WBTC_OPTIMISM = new Token(
'WBTC',
'Wrapped BTC'
)
export const FEI = new Token(ChainId.MAINNET, '0x956F47F50A910163D8BF957Cf5846D573E7f87CA', 18, 'FEI', 'Fei USD')
export const TRIBE = new Token(ChainId.MAINNET, '0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B', 18, 'TRIBE', 'Tribe')
export const FRAX = new Token(ChainId.MAINNET, '0x853d955aCEf822Db058eb8505911ED77F175b99e', 18, 'FRAX', 'Frax')
export const FXS = new Token(ChainId.MAINNET, '0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0', 18, 'FXS', 'Frax Share')
export const renBTC = new Token(ChainId.MAINNET, '0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D', 8, 'renBTC', 'renBTC')
export const ETH2X_FLI = new Token(
ChainId.MAINNET,
'0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD',
18,
'ETH2x-FLI',
'ETH 2x Flexible Leverage Index'
)
export const sETH2 = new Token(
ChainId.MAINNET,
'0xFe2e637202056d30016725477c5da089Ab0A043A',
18,
'sETH2',
'StakeWise Staked ETH2'
)
export const rETH2 = new Token(
ChainId.MAINNET,
'0x20BC832ca081b91433ff6c17f85701B6e92486c5',
18,
'rETH2',
'StakeWise Reward ETH2'
)
export const SWISE = new Token(ChainId.MAINNET, '0x48C3399719B582dD63eB5AADf12A40B4C3f52FA2', 18, 'SWISE', 'StakeWise')
export const WETH_POLYGON_MUMBAI = new Token(
ChainId.POLYGON_MUMBAI,
'0xa6fa4fb5f76172d178d61b04b0ecd319c5d1c0aa',
@ -243,10 +217,7 @@ export const CEUR_CELO_ALFAJORES = new Token(
export const USDC_BSC = new Token(ChainId.BNB, '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', 18, 'USDC', 'USDC')
export const USDT_BSC = new Token(ChainId.BNB, '0x55d398326f99059fF775485246999027B3197955', 18, 'USDT', 'USDT')
export const ETH_BSC = new Token(ChainId.BNB, '0x2170Ed0880ac9A755fd29B2688956BD959F933F8', 18, 'ETH', 'Ethereum')
export const MATIC_BSC = new Token(ChainId.BNB, '0xCC42724C6683B7E57334c4E856f4c9965ED682bD', 18, 'MATIC', 'Matic')
export const FRAX_BSC = new Token(ChainId.BNB, '0x90C97F71E18723b0Cf0dfa30ee176Ab653E89F40', 18, 'FRAX', 'FRAX')
export const BTC_BSC = new Token(ChainId.BNB, '0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c', 18, 'BTCB', 'BTCB')
export const CAKE_BSC = new Token(ChainId.BNB, '0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82', 18, 'CAKE', 'Cake')
export const BUSD_BSC = new Token(ChainId.BNB, '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', 18, 'BUSD', 'BUSD')
export const DAI_BSC = new Token(ChainId.BNB, '0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3', 18, 'DAI', 'DAI')

@ -1,72 +0,0 @@
import { Currency, Token } from '@uniswap/sdk-core'
import { useMemo } from 'react'
import { ADDITIONAL_BASES, BASES_TO_CHECK_TRADES_AGAINST, CUSTOM_BASES } from '../constants/routing'
export function useAllCurrencyCombinations(currencyA?: Currency, currencyB?: Currency): [Token, Token][] {
const chainId = currencyA?.chainId
const [tokenA, tokenB] = chainId ? [currencyA?.wrapped, currencyB?.wrapped] : [undefined, undefined]
const bases: Token[] = useMemo(() => {
if (!chainId || chainId !== tokenB?.chainId) return []
const common = BASES_TO_CHECK_TRADES_AGAINST[chainId] ?? []
const additionalA = tokenA ? ADDITIONAL_BASES[chainId]?.[tokenA.address] ?? [] : []
const additionalB = tokenB ? ADDITIONAL_BASES[chainId]?.[tokenB.address] ?? [] : []
return [...common, ...additionalA, ...additionalB]
}, [chainId, tokenA, tokenB])
const basePairs: [Token, Token][] = useMemo(
() =>
bases
.flatMap((base): [Token, Token][] => bases.map((otherBase) => [base, otherBase]))
// though redundant with the first filter below, that expression runs more often, so this is probably worthwhile
.filter(([t0, t1]) => !t0.equals(t1)),
[bases]
)
return useMemo(
() =>
tokenA && tokenB
? [
// the direct pair
[tokenA, tokenB] as [Token, Token],
// token A against all bases
...bases.map((base): [Token, Token] => [tokenA, base]),
// token B against all bases
...bases.map((base): [Token, Token] => [tokenB, base]),
// each base against all bases
...basePairs,
]
// filter out invalid pairs comprised of the same asset (e.g. WETH<>WETH)
.filter(([t0, t1]) => !t0.equals(t1))
// filter out duplicate pairs
.filter(([t0, t1], i, otherPairs) => {
// find the first index in the array at which there are the same 2 tokens as the current
const firstIndexInOtherPairs = otherPairs.findIndex(([t0Other, t1Other]) => {
return (t0.equals(t0Other) && t1.equals(t1Other)) || (t0.equals(t1Other) && t1.equals(t0Other))
})
// only accept the first occurrence of the same 2 tokens
return firstIndexInOtherPairs === i
})
// optionally filter out some pairs for tokens with custom bases defined
.filter(([tokenA, tokenB]) => {
if (!chainId) return true
const customBases = CUSTOM_BASES[chainId]
const customBasesA: Token[] | undefined = customBases?.[tokenA.address]
const customBasesB: Token[] | undefined = customBases?.[tokenB.address]
if (!customBasesA && !customBasesB) return true
if (customBasesA && !customBasesA.find((base) => tokenB.equals(base))) return false
if (customBasesB && !customBasesB.find((base) => tokenA.equals(base))) return false
return true
})
: [],
[tokenA, tokenB, bases, basePairs, chainId]
)
}

@ -1,75 +0,0 @@
import { Currency } from '@uniswap/sdk-core'
import { Pool, Route } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core'
import { useMemo } from 'react'
import { useV3SwapPools } from './useV3SwapPools'
/**
* Returns true if poolA is equivalent to poolB
* @param poolA one of the two pools
* @param poolB the other pool
*/
function poolEquals(poolA: Pool, poolB: Pool): boolean {
return (
poolA === poolB ||
(poolA.token0.equals(poolB.token0) && poolA.token1.equals(poolB.token1) && poolA.fee === poolB.fee)
)
}
function computeAllRoutes(
currencyIn: Currency,
currencyOut: Currency,
pools: Pool[],
chainId: number,
currentPath: Pool[] = [],
allPaths: Route<Currency, Currency>[] = [],
startCurrencyIn: Currency = currencyIn,
maxHops = 2
): Route<Currency, Currency>[] {
const tokenIn = currencyIn?.wrapped
const tokenOut = currencyOut?.wrapped
if (!tokenIn || !tokenOut) throw new Error('Missing tokenIn/tokenOut')
for (const pool of pools) {
if (!pool.involvesToken(tokenIn) || currentPath.find((pathPool) => poolEquals(pool, pathPool))) continue
const outputToken = pool.token0.equals(tokenIn) ? pool.token1 : pool.token0
if (outputToken.equals(tokenOut)) {
allPaths.push(new Route([...currentPath, pool], startCurrencyIn, currencyOut))
} else if (maxHops > 1) {
computeAllRoutes(
outputToken,
currencyOut,
pools,
chainId,
[...currentPath, pool],
allPaths,
startCurrencyIn,
maxHops - 1
)
}
}
return allPaths
}
/**
* Returns all the routes from an input currency to an output currency
* @param currencyIn the input currency
* @param currencyOut the output currency
*/
export function useAllV3Routes(
currencyIn?: Currency,
currencyOut?: Currency
): { loading: boolean; routes: Route<Currency, Currency>[] } {
const { chainId } = useWeb3React()
const { pools, loading: poolsLoading } = useV3SwapPools(currencyIn, currencyOut)
return useMemo(() => {
if (poolsLoading || !chainId || !pools || !currencyIn || !currencyOut) return { loading: true, routes: [] }
const routes = computeAllRoutes(currencyIn, currencyOut, pools, chainId, [], [], currencyIn, 2)
return { loading: false, routes }
}, [chainId, currencyIn, currencyOut, pools, poolsLoading])
}

@ -1,200 +0,0 @@
import { renderHook } from '@testing-library/react'
import { CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { DAI, USDC_MAINNET } from 'constants/tokens'
import { RouterPreference, TradeState } from 'state/routing/types'
import { useRouterPreference } from 'state/user/hooks'
import { mocked } from 'test-utils/mocked'
import { useRoutingAPITrade } from '../state/routing/useRoutingAPITrade'
import useAutoRouterSupported from './useAutoRouterSupported'
import { useBestTrade } from './useBestTrade'
import { useClientSideV3Trade } from './useClientSideV3Trade'
import useDebounce from './useDebounce'
import useIsWindowVisible from './useIsWindowVisible'
const USDCAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '10000')
const DAIAmount = CurrencyAmount.fromRawAmount(DAI, '10000')
jest.mock('./useAutoRouterSupported')
jest.mock('./useClientSideV3Trade')
jest.mock('./useDebounce')
jest.mock('./useIsWindowVisible')
jest.mock('state/routing/useRoutingAPITrade')
jest.mock('state/user/hooks')
// helpers to set mock expectations
const expectRouterMock = (state: TradeState) => {
mocked(useRoutingAPITrade).mockReturnValue({ state, trade: undefined })
}
const expectClientSideMock = (state: TradeState) => {
mocked(useClientSideV3Trade).mockReturnValue({ state, trade: undefined })
}
beforeEach(() => {
// ignore debounced value
mocked(useDebounce).mockImplementation((value) => value)
mocked(useIsWindowVisible).mockReturnValue(true)
mocked(useAutoRouterSupported).mockReturnValue(true)
mocked(useRouterPreference).mockReturnValue([RouterPreference.CLIENT, () => undefined])
})
describe('#useBestV3Trade ExactIn', () => {
it('does not compute routing api trade when routing API is not supported', async () => {
mocked(useAutoRouterSupported).mockReturnValue(false)
expectRouterMock(TradeState.INVALID)
expectClientSideMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
expect(useRoutingAPITrade).toHaveBeenCalledWith(
TradeType.EXACT_INPUT,
USDCAmount,
DAI,
RouterPreference.CLIENT,
true, // skipFetch
undefined
)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})
it('does not compute routing api trade when window is not focused', async () => {
mocked(useIsWindowVisible).mockReturnValue(false)
expectRouterMock(TradeState.NO_ROUTE_FOUND)
expectClientSideMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
expect(useRoutingAPITrade).toHaveBeenCalledWith(
TradeType.EXACT_INPUT,
USDCAmount,
DAI,
RouterPreference.CLIENT,
true, // skipFetch
undefined
)
expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined })
})
describe('when routing api is in non-error state', () => {
it('does not compute client side v3 trade if routing api is LOADING', () => {
expectRouterMock(TradeState.LOADING)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.LOADING, trade: undefined })
})
it('does not compute client side v3 trade if routing api is VALID', () => {
expectRouterMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})
})
describe('when routing api is in error state', () => {
it('does not compute client side v3 trade if routing api is INVALID', () => {
expectRouterMock(TradeState.INVALID)
expectClientSideMock(TradeState.VALID)
renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, undefined, undefined)
})
it('computes client side v3 trade if routing api is NO_ROUTE_FOUND', () => {
expectRouterMock(TradeState.NO_ROUTE_FOUND)
expectClientSideMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_INPUT, USDCAmount, DAI)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})
})
})
describe('#useBestV3Trade ExactOut', () => {
it('does not compute routing api trade when routing API is not supported', () => {
mocked(useAutoRouterSupported).mockReturnValue(false)
expectRouterMock(TradeState.INVALID)
expectClientSideMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(useRoutingAPITrade).toHaveBeenCalledWith(
TradeType.EXACT_OUTPUT,
DAIAmount,
USDC_MAINNET,
RouterPreference.CLIENT,
true, // skipFetch
undefined
)
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})
it('does not compute routing api trade when window is not focused', () => {
mocked(useIsWindowVisible).mockReturnValue(false)
expectRouterMock(TradeState.NO_ROUTE_FOUND)
expectClientSideMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(useRoutingAPITrade).toHaveBeenCalledWith(
TradeType.EXACT_OUTPUT,
DAIAmount,
USDC_MAINNET,
RouterPreference.CLIENT,
true, // skipFetch
undefined
)
expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined })
})
describe('when routing api is in non-error state', () => {
it('does not compute client side v3 trade if routing api is LOADING', () => {
expectRouterMock(TradeState.LOADING)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.LOADING, trade: undefined })
})
it('does not compute client side v3 trade if routing api is VALID', () => {
expectRouterMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})
})
describe('when routing api is in error state', () => {
it('computes client side v3 trade if routing api is INVALID', () => {
expectRouterMock(TradeState.INVALID)
expectClientSideMock(TradeState.VALID)
renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, undefined, undefined)
})
it('computes client side v3 trade if routing api is NO_ROUTE_FOUND', () => {
expectRouterMock(TradeState.NO_ROUTE_FOUND)
expectClientSideMock(TradeState.VALID)
const { result } = renderHook(() => useBestTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(useClientSideV3Trade).toHaveBeenCalledWith(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET)
expect(result.current).toEqual({ state: TradeState.VALID, trade: undefined })
})
})
})

@ -1,155 +0,0 @@
import { ChainId, Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { Route, SwapQuoter } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core'
import JSBI from 'jsbi'
import { useSingleContractWithCallData } from 'lib/hooks/multicall'
import { useMemo } from 'react'
import { ClassicTrade, InterfaceTrade, QuoteMethod, TradeState } from 'state/routing/types'
import { isCelo } from '../constants/tokens'
import { useAllV3Routes } from './useAllV3Routes'
import { useQuoter } from './useContract'
const QUOTE_GAS_OVERRIDES: { [chainId: number]: number } = {
[ChainId.ARBITRUM_ONE]: 25_000_000,
[ChainId.ARBITRUM_GOERLI]: 25_000_000,
[ChainId.CELO]: 50_000_000,
[ChainId.CELO_ALFAJORES]: 50_000_000,
[ChainId.POLYGON]: 40_000_000,
[ChainId.POLYGON_MUMBAI]: 40_000_000,
[ChainId.BNB]: 50_000_000,
[ChainId.AVALANCHE]: 50_000_000,
}
const DEFAULT_GAS_QUOTE = 2_000_000
// TODO (UniswapX or in general): Deprecate this?
/**
* Returns the best v3 trade for a desired swap
* @param tradeType whether the swap is an exact in/out
* @param amountSpecified the exact amount to swap in/out
* @param otherCurrency the desired output/payment currency
*/
export function useClientSideV3Trade<TTradeType extends TradeType>(
tradeType: TTradeType,
amountSpecified?: CurrencyAmount<Currency>,
otherCurrency?: Currency
): { state: TradeState; trade?: InterfaceTrade } {
const [currencyIn, currencyOut] =
tradeType === TradeType.EXACT_INPUT
? [amountSpecified?.currency, otherCurrency]
: [otherCurrency, amountSpecified?.currency]
const { routes, loading: routesLoading } = useAllV3Routes(currencyIn, currencyOut)
const { chainId } = useWeb3React()
// Chains deployed using the deploy-v3 script only deploy QuoterV2.
const useQuoterV2 = useMemo(() => Boolean(chainId && isCelo(chainId)), [chainId])
const quoter = useQuoter(useQuoterV2)
const callData = useMemo(
() =>
amountSpecified
? routes.map(
(route) => SwapQuoter.quoteCallParameters(route, amountSpecified, tradeType, { useQuoterV2 }).calldata
)
: [],
[amountSpecified, routes, tradeType, useQuoterV2]
)
const quotesResults = useSingleContractWithCallData(quoter, callData, {
gasRequired: chainId ? QUOTE_GAS_OVERRIDES[chainId] ?? DEFAULT_GAS_QUOTE : undefined,
})
const currenciesAreTheSame = useMemo(
() => currencyIn && currencyOut && (currencyIn.equals(currencyOut) || currencyIn.wrapped.equals(currencyOut)),
[currencyIn, currencyOut]
)
return useMemo(() => {
if (
!amountSpecified ||
!currencyIn ||
!currencyOut ||
quotesResults.some(({ valid }) => !valid) ||
currenciesAreTheSame
) {
return {
state: TradeState.INVALID,
trade: undefined,
}
}
if (routesLoading || quotesResults.some(({ loading }) => loading)) {
return {
state: TradeState.LOADING,
trade: undefined,
}
}
const { bestRoute, amountIn, amountOut } = quotesResults.reduce(
(
currentBest: {
bestRoute: Route<Currency, Currency> | null
amountIn: CurrencyAmount<Currency> | null
amountOut: CurrencyAmount<Currency> | null
},
{ result },
i
) => {
if (!result) return currentBest
// overwrite the current best if it's not defined or if this route is better
if (tradeType === TradeType.EXACT_INPUT) {
const amountOut = CurrencyAmount.fromRawAmount(currencyOut, result.amountOut.toString())
if (currentBest.amountOut === null || JSBI.lessThan(currentBest.amountOut.quotient, amountOut.quotient)) {
return {
bestRoute: routes[i],
amountIn: amountSpecified,
amountOut,
}
}
} else {
const amountIn = CurrencyAmount.fromRawAmount(currencyIn, result.amountIn.toString())
if (currentBest.amountIn === null || JSBI.greaterThan(currentBest.amountIn.quotient, amountIn.quotient)) {
return {
bestRoute: routes[i],
amountIn,
amountOut: amountSpecified,
}
}
}
return currentBest
},
{
bestRoute: null,
amountIn: null,
amountOut: null,
}
)
if (!bestRoute || !amountIn || !amountOut) {
return {
state: TradeState.NO_ROUTE_FOUND,
trade: undefined,
}
}
return {
state: TradeState.VALID,
trade: new ClassicTrade({
v2Routes: [],
v3Routes: [
{
routev3: bestRoute,
inputAmount: amountIn,
outputAmount: amountOut,
},
],
tradeType,
quoteMethod: QuoteMethod.CLIENT_SIDE,
// When using SOR, we don't have gas values from routing-api, so we can't calculate approve gas info
approveInfo: { needsApprove: false },
}),
}
}, [amountSpecified, currenciesAreTheSame, currencyIn, currencyOut, quotesResults, routes, routesLoading, tradeType])
}

@ -5,15 +5,12 @@ import {
ENS_REGISTRAR_ADDRESSES,
MULTICALL_ADDRESSES,
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
QUOTER_ADDRESSES,
TICK_LENS_ADDRESSES,
V2_ROUTER_ADDRESS,
V3_MIGRATOR_ADDRESSES,
} from '@uniswap/sdk-core'
import QuoterV2Json from '@uniswap/swap-router-contracts/artifacts/contracts/lens/QuoterV2.sol/QuoterV2.json'
import IUniswapV2PairJson from '@uniswap/v2-core/build/IUniswapV2Pair.json'
import IUniswapV2Router02Json from '@uniswap/v2-periphery/build/IUniswapV2Router02.json'
import QuoterJson from '@uniswap/v3-periphery/artifacts/contracts/lens/Quoter.sol/Quoter.json'
import TickLensJson from '@uniswap/v3-periphery/artifacts/contracts/lens/TickLens.sol/TickLens.json'
import UniswapInterfaceMulticallJson from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json'
import NonfungiblePositionManagerJson from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
@ -32,14 +29,12 @@ import WETH_ABI from 'abis/weth.json'
import { RPC_PROVIDERS } from 'constants/providers'
import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
import { useMemo } from 'react'
import { NonfungiblePositionManager, Quoter, QuoterV2, TickLens, UniswapInterfaceMulticall } from 'types/v3'
import { NonfungiblePositionManager, TickLens, UniswapInterfaceMulticall } from 'types/v3'
import { V3Migrator } from 'types/v3/V3Migrator'
import { getContract } from 'utils'
const { abi: IUniswapV2PairABI } = IUniswapV2PairJson
const { abi: IUniswapV2Router02ABI } = IUniswapV2Router02Json
const { abi: QuoterABI } = QuoterJson
const { abi: QuoterV2ABI } = QuoterV2Json
const { abi: TickLensABI } = TickLensJson
const { abi: MulticallABI } = UniswapInterfaceMulticallJson
const { abi: NFTPositionManagerABI } = NonfungiblePositionManagerJson
@ -157,10 +152,6 @@ export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean):
)
}
export function useQuoter(useQuoterV2: boolean) {
return useContract<Quoter | QuoterV2>(QUOTER_ADDRESSES, useQuoterV2 ? QuoterV2ABI : QuoterABI)
}
export function useTickLens(): TickLens | null {
const { chainId } = useWeb3React()
const address = chainId ? TICK_LENS_ADDRESSES[chainId] : undefined

@ -0,0 +1,72 @@
import { renderHook } from '@testing-library/react'
import { CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { DAI, USDC_MAINNET } from 'constants/tokens'
import { RouterPreference, TradeState } from 'state/routing/types'
import { useRouterPreference } from 'state/user/hooks'
import { mocked } from 'test-utils/mocked'
import { useRoutingAPITrade } from '../state/routing/useRoutingAPITrade'
import useAutoRouterSupported from './useAutoRouterSupported'
import useDebounce from './useDebounce'
import { useDebouncedTrade } from './useDebouncedTrade'
import useIsWindowVisible from './useIsWindowVisible'
const USDCAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, '10000')
const DAIAmount = CurrencyAmount.fromRawAmount(DAI, '10000')
jest.mock('./useAutoRouterSupported')
jest.mock('./useDebounce')
jest.mock('./useIsWindowVisible')
jest.mock('state/routing/useRoutingAPITrade')
jest.mock('state/user/hooks')
// helpers to set mock expectations
const expectRouterMock = (state: TradeState) => {
mocked(useRoutingAPITrade).mockReturnValue({ state, trade: undefined })
}
beforeEach(() => {
// ignore debounced value
mocked(useDebounce).mockImplementation((value) => value)
mocked(useIsWindowVisible).mockReturnValue(true)
mocked(useAutoRouterSupported).mockReturnValue(true)
mocked(useRouterPreference).mockReturnValue([RouterPreference.CLIENT, () => undefined])
})
describe('#useBestV3Trade ExactIn', () => {
it('does not compute routing api trade when window is not focused', async () => {
mocked(useIsWindowVisible).mockReturnValue(false)
expectRouterMock(TradeState.NO_ROUTE_FOUND)
const { result } = renderHook(() => useDebouncedTrade(TradeType.EXACT_INPUT, USDCAmount, DAI))
expect(useRoutingAPITrade).toHaveBeenCalledWith(
TradeType.EXACT_INPUT,
USDCAmount,
DAI,
RouterPreference.CLIENT,
true, // skipFetch
undefined
)
expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined })
})
})
describe('#useDebouncedTrade ExactOut', () => {
it('does not compute routing api trade when window is not focused', () => {
mocked(useIsWindowVisible).mockReturnValue(false)
expectRouterMock(TradeState.NO_ROUTE_FOUND)
const { result } = renderHook(() => useDebouncedTrade(TradeType.EXACT_OUTPUT, DAIAmount, USDC_MAINNET))
expect(useRoutingAPITrade).toHaveBeenCalledWith(
TradeType.EXACT_OUTPUT,
DAIAmount,
USDC_MAINNET,
RouterPreference.CLIENT,
true, // skipFetch
undefined
)
expect(result.current).toEqual({ state: TradeState.NO_ROUTE_FOUND, trade: undefined })
})
})

@ -8,7 +8,6 @@ import { useRoutingAPITrade } from 'state/routing/useRoutingAPITrade'
import { useRouterPreference } from 'state/user/hooks'
import useAutoRouterSupported from './useAutoRouterSupported'
import { useClientSideV3Trade } from './useClientSideV3Trade'
import useDebounce from './useDebounce'
import useIsWindowVisible from './useIsWindowVisible'
@ -18,7 +17,7 @@ const DEBOUNCE_TIME = 350
// Temporary until we remove the feature flag.
const DEBOUNCE_TIME_INCREASED = 650
export function useBestTrade(
export function useDebouncedTrade(
tradeType: TradeType,
amountSpecified?: CurrencyAmount<Currency>,
otherCurrency?: Currency,
@ -29,7 +28,7 @@ export function useBestTrade(
trade?: InterfaceTrade
}
export function useBestTrade(
export function useDebouncedTrade(
tradeType: TradeType,
amountSpecified?: CurrencyAmount<Currency>,
otherCurrency?: Currency,
@ -40,12 +39,15 @@ export function useBestTrade(
trade?: ClassicTrade
}
/**
* Returns the best v2+v3 trade for a desired swap.
* Returns the debounced v2+v3 trade for a desired swap.
* @param tradeType whether the swap is an exact in/out
* @param amountSpecified the exact amount to swap in/out
* @param otherCurrency the desired output/payment currency
* @param routerPreferenceOverride force useRoutingAPITrade to use a specific RouterPreference
* @param account the connected address
*
*/
export function useBestTrade(
export function useDebouncedTrade(
tradeType: TradeType,
amountSpecified?: CurrencyAmount<Currency>,
otherCurrency?: Currency,
@ -90,21 +92,12 @@ export function useBestTrade(
const inDebounce =
(!debouncedAmount && Boolean(amountSpecified)) || (!debouncedOtherCurrency && Boolean(otherCurrency))
const isLoading = routingAPITrade.state === TradeState.LOADING || inDebounce
const useFallback = (!autoRouterSupported || routingAPITrade.state === TradeState.NO_ROUTE_FOUND) && shouldGetTrade
// only use client side router if routing api trade failed or is not supported
const bestV3Trade = useClientSideV3Trade(
tradeType,
useFallback ? debouncedAmount : undefined,
useFallback ? debouncedOtherCurrency : undefined
)
// only return gas estimate from api if routing api trade is used
return useMemo(
() => ({
...(useFallback ? bestV3Trade : routingAPITrade),
...routingAPITrade,
...(isLoading ? { state: TradeState.LOADING } : {}),
}),
[bestV3Trade, isLoading, routingAPITrade, useFallback]
[isLoading, routingAPITrade]
)
}

@ -1,56 +0,0 @@
import { ChainId, Currency, Token } from '@uniswap/sdk-core'
import { FeeAmount, Pool } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core'
import { useMemo } from 'react'
import { useAllCurrencyCombinations } from './useAllCurrencyCombinations'
import { PoolState, usePools } from './usePools'
/**
* Returns all the existing pools that should be considered for swapping between an input currency and an output currency
* @param currencyIn the input currency
* @param currencyOut the output currency
*/
export function useV3SwapPools(
currencyIn?: Currency,
currencyOut?: Currency
): {
pools: Pool[]
loading: boolean
} {
const { chainId } = useWeb3React()
const allCurrencyCombinations = useAllCurrencyCombinations(currencyIn, currencyOut)
const allCurrencyCombinationsWithAllFees: [Token, Token, FeeAmount][] = useMemo(
() =>
allCurrencyCombinations.reduce<[Token, Token, FeeAmount][]>((list, [tokenA, tokenB]) => {
return chainId === ChainId.MAINNET
? list.concat([
[tokenA, tokenB, FeeAmount.LOW],
[tokenA, tokenB, FeeAmount.MEDIUM],
[tokenA, tokenB, FeeAmount.HIGH],
])
: list.concat([
[tokenA, tokenB, FeeAmount.LOWEST],
[tokenA, tokenB, FeeAmount.LOW],
[tokenA, tokenB, FeeAmount.MEDIUM],
[tokenA, tokenB, FeeAmount.HIGH],
])
}, []),
[allCurrencyCombinations, chainId]
)
const pools = usePools(allCurrencyCombinationsWithAllFees)
return useMemo(() => {
return {
pools: pools
.filter((tuple): tuple is [PoolState.EXISTS, Pool] => {
return tuple[0] === PoolState.EXISTS && tuple[1] !== null
})
.map(([, pool]) => pool),
loading: pools.some(([state]) => state === PoolState.LOADING),
}
}, [pools])
}

@ -35,13 +35,6 @@ export function useSingleContractMultipleData(
return multicall.hooks.useSingleContractMultipleData(chainId, latestBlock, ...args)
}
export function useSingleContractWithCallData(
...args: SkipFirstTwoParams<typeof multicall.hooks.useSingleContractWithCallData>
) {
const { chainId, latestBlock } = useCallContext()
return multicall.hooks.useSingleContractWithCallData(chainId, latestBlock, ...args)
}
function useCallContext() {
const { chainId } = useWeb3React()
const latestBlock = useBlockNumber()

@ -1,6 +1,6 @@
import { Currency, CurrencyAmount, NativeCurrency, Percent, Token, TradeType } from '@uniswap/sdk-core'
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
import { useBestTrade } from 'hooks/useBestTrade'
import { useDebouncedTrade } from 'hooks/useDebouncedTrade'
import { useMemo } from 'react'
import { ClassicTrade, RouterPreference, TradeState } from 'state/routing/types'
import { isClassicTrade } from 'state/routing/utils'
@ -14,7 +14,7 @@ export default function useDerivedPayWithAnyTokenSwapInfo(
maximumAmountIn?: CurrencyAmount<Token>
allowedSlippage: Percent
} {
const { state, trade } = useBestTrade(
const { state, trade } = useDebouncedTrade(
TradeType.EXACT_OUTPUT,
parsedOutputAmount,
inputCurrency ?? undefined,

@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
import { ChainId, Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
import { useBestTrade } from 'hooks/useBestTrade'
import { useDebouncedTrade } from 'hooks/useDebouncedTrade'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { ParsedQs } from 'qs'
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
@ -115,7 +115,7 @@ export function useDerivedSwapInfo(state: SwapState, chainId: ChainId | undefine
[inputCurrency, isExactIn, outputCurrency, typedValue]
)
let trade = useBestTrade(
let trade = useDebouncedTrade(
isExactIn ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT,
parsedAmount,
(isExactIn ? outputCurrency : inputCurrency) ?? undefined,

@ -1,5 +1,5 @@
import { CurrencyAmount, Percent, Price } from '@uniswap/sdk-core'
import { renBTC, USDC_MAINNET } from 'constants/tokens'
import { USDC_MAINNET, WBTC } from 'constants/tokens'
import {
currencyAmountToPreciseFloat,
@ -217,19 +217,19 @@ describe('currencyAmountToPreciseFloat', () => {
describe('priceToPreciseFloat', () => {
it('small number', () => {
const price = new Price(renBTC, USDC_MAINNET, 1234, 1)
const price = new Price(WBTC, USDC_MAINNET, 1234, 1)
expect(priceToPreciseFloat(price)).toEqual(0.0810373)
})
it('tiny number', () => {
const price = new Price(renBTC, USDC_MAINNET, 12345600, 1)
const price = new Price(WBTC, USDC_MAINNET, 12345600, 1)
expect(priceToPreciseFloat(price)).toEqual(0.00000810005)
})
it('lots of decimals', () => {
const price = new Price(renBTC, USDC_MAINNET, 123, 7)
const price = new Price(WBTC, USDC_MAINNET, 123, 7)
expect(priceToPreciseFloat(price)).toEqual(5.691056911)
})
it('integer', () => {
const price = new Price(renBTC, USDC_MAINNET, 1, 7)
const price = new Price(WBTC, USDC_MAINNET, 1, 7)
expect(priceToPreciseFloat(price)).toEqual(700)
})
})