Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1b300af70 | ||
|
|
600049bc6e | ||
|
|
6e91311489 | ||
|
|
f6a464cb3b | ||
|
|
e589c751d7 |
@@ -15,6 +15,7 @@ export const USDC = new Token(ChainId.MAINNET, '0xA0b86991c6218b36c1d19D4a2e9Eb0
|
||||
export const USDT = new Token(ChainId.MAINNET, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', 'Tether USD')
|
||||
export const COMP = new Token(ChainId.MAINNET, '0xc00e94Cb662C3520282E6f5717214004A7f26888', 18, 'COMP', 'Compound')
|
||||
export const MKR = new Token(ChainId.MAINNET, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 18, 'MKR', 'Maker')
|
||||
export const AMPL = new Token(ChainId.MAINNET, '0xD46bA6D942050d489DBd938a2C909A5d5039A161', 9, 'AMPL', 'Ampleforth')
|
||||
|
||||
const WETH_ONLY: ChainTokenList = {
|
||||
[ChainId.MAINNET]: [WETH[ChainId.MAINNET]],
|
||||
@@ -30,6 +31,16 @@ export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = {
|
||||
[ChainId.MAINNET]: [...WETH_ONLY[ChainId.MAINNET], DAI, USDC, USDT, COMP, MKR]
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 in ChainId]?: { [tokenAddress: string]: Token[] } } = {
|
||||
[ChainId.MAINNET]: {
|
||||
[AMPL.address]: [DAI, WETH[ChainId.MAINNET]]
|
||||
}
|
||||
}
|
||||
|
||||
// used for display in the default list when adding liquidity
|
||||
export const SUGGESTED_BASES: ChainTokenList = {
|
||||
...WETH_ONLY,
|
||||
@@ -153,5 +164,4 @@ export const MIN_ETH: JSBI = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(16))
|
||||
export const BETTER_TRADE_LINK_THRESHOLD = new Percent(JSBI.BigInt(75), JSBI.BigInt(10000))
|
||||
|
||||
// the Uniswap Default token list lives here
|
||||
export const DEFAULT_TOKEN_LIST_URL =
|
||||
'https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json'
|
||||
export const DEFAULT_TOKEN_LIST_URL = 'https://unpkg.com/@uniswap/default-token-list@latest'
|
||||
|
||||
@@ -2,9 +2,8 @@ import { Currency, CurrencyAmount, Pair, Token, Trade } from '@uniswap/sdk'
|
||||
import flatMap from 'lodash.flatmap'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { BASES_TO_CHECK_TRADES_AGAINST } from '../constants'
|
||||
import { BASES_TO_CHECK_TRADES_AGAINST, CUSTOM_BASES } from '../constants'
|
||||
import { PairState, usePairs } from '../data/Reserves'
|
||||
import { maxHopsFor } from '../utils/maxHopsFor'
|
||||
import { wrappedCurrency } from '../utils/wrappedCurrency'
|
||||
|
||||
import { useActiveWeb3React } from './index'
|
||||
@@ -18,18 +17,35 @@ function useAllCommonPairs(currencyA?: Currency, currencyB?: Currency): Pair[] {
|
||||
? [wrappedCurrency(currencyA, chainId), wrappedCurrency(currencyB, chainId)]
|
||||
: [undefined, undefined]
|
||||
|
||||
const allPairCombinations: [Token | undefined, Token | undefined][] = useMemo(
|
||||
() => [
|
||||
// the direct pair
|
||||
[tokenA, tokenB],
|
||||
// token A against all bases
|
||||
...bases.map((base): [Token | undefined, Token | undefined] => [tokenA, base]),
|
||||
// token B against all bases
|
||||
...bases.map((base): [Token | undefined, Token | undefined] => [tokenB, base]),
|
||||
// each base against all bases
|
||||
...flatMap(bases, (base): [Token, Token][] => bases.map(otherBase => [base, otherBase]))
|
||||
],
|
||||
[tokenA, tokenB, bases]
|
||||
const allPairCombinations: [Token, Token][] = useMemo(
|
||||
() =>
|
||||
[
|
||||
// the direct pair
|
||||
[tokenA, tokenB],
|
||||
// token A against all bases
|
||||
...bases.map((base): [Token | undefined, Token | undefined] => [tokenA, base]),
|
||||
// token B against all bases
|
||||
...bases.map((base): [Token | undefined, Token | undefined] => [tokenB, base]),
|
||||
// each base against all bases
|
||||
...flatMap(bases, (base): [Token, Token][] => bases.map(otherBase => [base, otherBase]))
|
||||
]
|
||||
.filter((tokens): tokens is [Token, Token] => Boolean(tokens[0] && tokens[1]))
|
||||
.filter(([tokenA, tokenB]) => {
|
||||
if (!chainId) return true
|
||||
const customBases = CUSTOM_BASES[chainId]
|
||||
if (!customBases) return true
|
||||
|
||||
const customBasesA: Token[] | undefined = customBases[tokenA.address]
|
||||
const customBasesB: Token[] | undefined = customBases[tokenB.address]
|
||||
|
||||
if (!customBasesA && !customBasesB) return true
|
||||
|
||||
if (customBasesA && customBasesA.findIndex(base => tokenB.equals(base)) === -1) return false
|
||||
if (customBasesB && customBasesB.findIndex(base => tokenA.equals(base)) === -1) return false
|
||||
|
||||
return true
|
||||
}),
|
||||
[tokenA, tokenB, bases, chainId]
|
||||
)
|
||||
|
||||
const allPairs = usePairs(allPairCombinations)
|
||||
@@ -59,9 +75,8 @@ export function useTradeExactIn(currencyAmountIn?: CurrencyAmount, currencyOut?:
|
||||
|
||||
return useMemo(() => {
|
||||
if (currencyAmountIn && currencyOut && allowedPairs.length > 0) {
|
||||
const maxHops = maxHopsFor(currencyAmountIn.currency, currencyOut)
|
||||
return (
|
||||
Trade.bestTradeExactIn(allowedPairs, currencyAmountIn, currencyOut, { maxHops, maxNumResults: 1 })[0] ?? null
|
||||
Trade.bestTradeExactIn(allowedPairs, currencyAmountIn, currencyOut, { maxHops: 3, maxNumResults: 1 })[0] ?? null
|
||||
)
|
||||
}
|
||||
return null
|
||||
@@ -76,9 +91,9 @@ export function useTradeExactOut(currencyIn?: Currency, currencyAmountOut?: Curr
|
||||
|
||||
return useMemo(() => {
|
||||
if (currencyIn && currencyAmountOut && allowedPairs.length > 0) {
|
||||
const maxHops = maxHopsFor(currencyIn, currencyAmountOut.currency)
|
||||
return (
|
||||
Trade.bestTradeExactOut(allowedPairs, currencyIn, currencyAmountOut, { maxHops, maxNumResults: 1 })[0] ?? null
|
||||
Trade.bestTradeExactOut(allowedPairs, currencyIn, currencyAmountOut, { maxHops: 3, maxNumResults: 1 })[0] ??
|
||||
null
|
||||
)
|
||||
}
|
||||
return null
|
||||
|
||||
@@ -104,8 +104,6 @@ function useSwapCallArguments(
|
||||
}, [account, allowedSlippage, chainId, deadline, library, recipient, trade, v1Exchange])
|
||||
}
|
||||
|
||||
const DEFAULT_FAILED_SWAP_ERROR = 'Unexpected error. Please try again or contact support.'
|
||||
|
||||
// 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(
|
||||
@@ -161,17 +159,19 @@ export function useSwapCallback(
|
||||
return contract.callStatic[methodName](...args, options)
|
||||
.then(result => {
|
||||
console.debug('Unexpected successful call after failed estimate gas', call, gasError, result)
|
||||
return { call, error: new Error(DEFAULT_FAILED_SWAP_ERROR) }
|
||||
return { call, error: new Error('Unexpected issue with estimating the gas. Please try again.') }
|
||||
})
|
||||
.catch(callError => {
|
||||
console.debug('Call threw error', call, callError)
|
||||
let errorMessage: string = DEFAULT_FAILED_SWAP_ERROR
|
||||
let errorMessage: string
|
||||
switch (callError.reason) {
|
||||
case 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT':
|
||||
case 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT':
|
||||
errorMessage =
|
||||
'This transaction will not succeed either due to price movement or fee on transfer. Try increasing your slippage tolerance.'
|
||||
break
|
||||
default:
|
||||
errorMessage = `The transaction cannot succeed due to error: ${callError.reason}. This is probably an issue with one of the tokens you are swapping.`
|
||||
}
|
||||
return { call, error: new Error(errorMessage) }
|
||||
})
|
||||
@@ -188,7 +188,7 @@ export function useSwapCallback(
|
||||
if (!successfulEstimation) {
|
||||
const errorCalls = estimatedCalls.filter((call): call is FailedCall => 'error' in call)
|
||||
if (errorCalls.length > 0) throw errorCalls[errorCalls.length - 1].error
|
||||
throw new Error(DEFAULT_FAILED_SWAP_ERROR)
|
||||
throw new Error('Unexpected error. Please contact support: none of the calls threw an error')
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -235,7 +235,7 @@ export function useSwapCallback(
|
||||
} else {
|
||||
// otherwise, the error was unexpected and we need to convey that
|
||||
console.error(`Swap failed`, error, methodName, args, value)
|
||||
throw new Error(DEFAULT_FAILED_SWAP_ERROR)
|
||||
throw new Error(`Swap failed: ${error.message}`)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createReducer } from '@reduxjs/toolkit'
|
||||
import { getVersionUpgrade, VersionUpgrade } from '@uniswap/token-lists'
|
||||
import { TokenList } from '@uniswap/token-lists/dist/types'
|
||||
import { updateVersion } from '../user/actions'
|
||||
import { acceptListUpdate, addList, fetchTokenList } from './actions'
|
||||
|
||||
export interface ListsState {
|
||||
@@ -87,4 +88,7 @@ export default createReducer(initialState, builder =>
|
||||
current: state.byUrl[url].pendingUpdate
|
||||
}
|
||||
})
|
||||
.addCase(updateVersion, state => {
|
||||
delete state.byUrl['https://unpkg.com/@uniswap/default-token-list@latest/uniswap-default.tokenlist.json']
|
||||
})
|
||||
)
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { ChainId, Currency, ETHER, Token, WETH } from '@uniswap/sdk'
|
||||
|
||||
function isEtherish(currency: Currency): boolean {
|
||||
return currency === ETHER || (currency instanceof Token && WETH[currency.chainId].equals(currency))
|
||||
}
|
||||
|
||||
const AMPL_TOKEN_ADDRESS = '0xD46bA6D942050d489DBd938a2C909A5d5039A161'
|
||||
|
||||
/**
|
||||
* Band-aid on maxHops because some tokens seems to always fail with multihop swaps
|
||||
* @param currencyIn currency in
|
||||
* @param currencyOut currency out
|
||||
*/
|
||||
export function maxHopsFor(currencyIn: Currency, currencyOut: Currency): number {
|
||||
if (
|
||||
isEtherish(currencyIn) &&
|
||||
currencyOut instanceof Token &&
|
||||
currencyOut.chainId === ChainId.MAINNET &&
|
||||
currencyOut.address === AMPL_TOKEN_ADDRESS
|
||||
) {
|
||||
return 1
|
||||
} else if (
|
||||
isEtherish(currencyOut) &&
|
||||
currencyIn instanceof Token &&
|
||||
currencyIn.chainId === ChainId.MAINNET &&
|
||||
currencyIn.address === AMPL_TOKEN_ADDRESS
|
||||
) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 3
|
||||
}
|
||||
66
src/utils/useUSDCPrice.ts
Normal file
66
src/utils/useUSDCPrice.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { ChainId, Currency, currencyEquals, JSBI, Price, WETH } from '@uniswap/sdk'
|
||||
import { useMemo } from 'react'
|
||||
import { USDC } from '../constants'
|
||||
import { PairState, usePairs } from '../data/Reserves'
|
||||
import { useActiveWeb3React } from '../hooks'
|
||||
import { wrappedCurrency } from './wrappedCurrency'
|
||||
|
||||
/**
|
||||
* Returns the price in USDC of the input currency
|
||||
* @param currency currency to compute the USDC price of
|
||||
*/
|
||||
export default function useUSDCPrice(currency?: Currency): Price | undefined {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const wrapped = wrappedCurrency(currency, chainId)
|
||||
const tokenPairs: [Currency | undefined, Currency | undefined][] = useMemo(
|
||||
() => [
|
||||
[
|
||||
chainId && wrapped && currencyEquals(WETH[chainId], wrapped) ? undefined : currency,
|
||||
chainId ? WETH[chainId] : undefined
|
||||
],
|
||||
[wrapped?.equals(USDC) ? undefined : wrapped, chainId === ChainId.MAINNET ? USDC : undefined],
|
||||
[chainId ? WETH[chainId] : undefined, chainId === ChainId.MAINNET ? USDC : undefined]
|
||||
],
|
||||
[chainId, currency, wrapped]
|
||||
)
|
||||
const [[ethPairState, ethPair], [usdcPairState, usdcPair], [usdcEthPairState, usdcEthPair]] = usePairs(tokenPairs)
|
||||
|
||||
return useMemo(() => {
|
||||
if (!currency || !wrapped || !chainId) {
|
||||
return
|
||||
}
|
||||
// handle weth/eth
|
||||
if (wrapped.equals(WETH[chainId])) {
|
||||
if (usdcPair) {
|
||||
const price = usdcPair.priceOf(WETH[chainId])
|
||||
return new Price(currency, USDC, price.denominator, price.numerator)
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
// handle usdc
|
||||
if (wrapped.equals(USDC)) {
|
||||
return new Price(USDC, USDC, '1', '1')
|
||||
}
|
||||
|
||||
const ethPairETHAmount = ethPair?.reserveOf(WETH[chainId])
|
||||
const ethPairETHUSDCValue: JSBI =
|
||||
ethPairETHAmount && usdcEthPair ? usdcEthPair.priceOf(WETH[chainId]).quote(ethPairETHAmount).raw : JSBI.BigInt(0)
|
||||
|
||||
// all other tokens
|
||||
// first try the usdc pair
|
||||
if (usdcPairState === PairState.EXISTS && usdcPair && usdcPair.reserveOf(USDC).greaterThan(ethPairETHUSDCValue)) {
|
||||
const price = usdcPair.priceOf(wrapped)
|
||||
return new Price(currency, USDC, price.denominator, price.numerator)
|
||||
}
|
||||
if (ethPairState === PairState.EXISTS && ethPair && usdcEthPairState === PairState.EXISTS && usdcEthPair) {
|
||||
if (usdcEthPair.reserveOf(USDC).greaterThan('0') && ethPair.reserveOf(WETH[chainId]).greaterThan('0')) {
|
||||
const ethUsdcPrice = usdcEthPair.priceOf(USDC)
|
||||
const currencyEthPrice = ethPair.priceOf(WETH[chainId])
|
||||
const usdcPrice = ethUsdcPrice.multiply(currencyEthPrice).invert()
|
||||
return new Price(currency, USDC, usdcPrice.denominator, usdcPrice.numerator)
|
||||
}
|
||||
}
|
||||
return
|
||||
}, [chainId, currency, ethPair, ethPairState, usdcEthPair, usdcEthPairState, usdcPair, usdcPairState, wrapped])
|
||||
}
|
||||
Reference in New Issue
Block a user