Commit e311e2fc authored by Noah Zinsmeister's avatar Noah Zinsmeister Committed by GitHub

mainnet (#782)

* mainnet config

* fix token + pair sorting

* standardize wrapped useWeb3React imports

* add final return to pair sorting

* add connecting pairs

* break tokens out into separate files

revert isAddress change

* filter out duplicate pairs

* remove add liquidity prompts

* link to v1 trades that are invalid on v2

* forward v2 subdomain to apex

* update blog link

* get rid of smart quotes

* link to uniswap info in position card

* improve pair sorting/searching

break out token identification

fix crash on early pool position clicks

change pair token separator from : to /
parent 9c4f63f4
...@@ -7,6 +7,12 @@ ...@@ -7,6 +7,12 @@
conditions = {Country=["BY","CU","IR","IQ","CI","LR","KP","SD","SY","ZW"]} conditions = {Country=["BY","CU","IR","IQ","CI","LR","KP","SD","SY","ZW"]}
headers = {Link="<https://uniswap.exchange>"} headers = {Link="<https://uniswap.exchange>"}
# forward v2 subdomain to apex
[[redirects]]
from = "https://v2.uniswap.exchange/*"
to = "https://uniswap.exchange/:splat"
status = 301
# support SPA setup # support SPA setup
[[redirects]] [[redirects]]
from = "/*" from = "/*"
......
...@@ -158,15 +158,15 @@ export default function Header() { ...@@ -158,15 +158,15 @@ export default function Header() {
return ( return (
<HeaderFrame> <HeaderFrame>
<MigrateBanner> <MigrateBanner>
{/* <b>Uniswap V2 is live.&nbsp;</b> Move your liquidity now using the&nbsp; */} Uniswap V2 is live! Read the&nbsp;
<b>Testnet only.</b>&nbsp;Uniswap V2 has not been launched and is coming soon.&nbsp;Read the&nbsp; <Link href="https://uniswap.org/blog/launch-uniswap-v2/">
{/* <Link href="https://migrate.uniswap.exchange/">
<b>migration helper</b>
</Link>
&nbsp;or read the&nbsp; */}
<Link href="https://uniswap.org/blog/uniswap-v2/">
<b>blog post ↗</b> <b>blog post ↗</b>
</Link> </Link>
&nbsp;or&nbsp;
<Link href="https://migrate.uniswap.exchange/">
<b>migrate your liquidity ↗</b>
</Link>
.
</MigrateBanner> </MigrateBanner>
<RowBetween padding="1rem"> <RowBetween padding="1rem">
<HeaderElement> <HeaderElement>
......
...@@ -15,7 +15,7 @@ import { AutoColumn, ColumnCenter } from '../Column' ...@@ -15,7 +15,7 @@ import { AutoColumn, ColumnCenter } from '../Column'
import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button' import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button'
import { useToken } from '../../hooks/Tokens' import { useToken } from '../../hooks/Tokens'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '../../hooks'
import { usePairAdder } from '../../state/user/hooks' import { usePairAdder } from '../../state/user/hooks'
import { usePair } from '../../data/Reserves' import { usePair } from '../../data/Reserves'
......
...@@ -4,7 +4,7 @@ import { darken } from 'polished' ...@@ -4,7 +4,7 @@ import { darken } from 'polished'
import { RouteComponentProps, withRouter } from 'react-router-dom' import { RouteComponentProps, withRouter } from 'react-router-dom'
import { Percent, Pair, JSBI } from '@uniswap/sdk' import { Percent, Pair, JSBI } from '@uniswap/sdk'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '../../hooks'
import { useTotalSupply } from '../../data/TotalSupply' import { useTotalSupply } from '../../data/TotalSupply'
import { useTokenBalance } from '../../state/wallet/hooks' import { useTokenBalance } from '../../state/wallet/hooks'
...@@ -80,7 +80,7 @@ function PositionCard({ pair, history, border, minimal = false }: PositionCardPr ...@@ -80,7 +80,7 @@ function PositionCard({ pair, history, border, minimal = false }: PositionCardPr
<RowFixed> <RowFixed>
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} /> <DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} />
<Text fontWeight={500} fontSize={20}> <Text fontWeight={500} fontSize={20}>
{token0?.symbol}:{token1?.symbol} {token0?.symbol}/{token1?.symbol}
</Text> </Text>
</RowFixed> </RowFixed>
<RowFixed> <RowFixed>
...@@ -134,7 +134,7 @@ function PositionCard({ pair, history, border, minimal = false }: PositionCardPr ...@@ -134,7 +134,7 @@ function PositionCard({ pair, history, border, minimal = false }: PositionCardPr
<RowFixed> <RowFixed>
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} /> <DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} />
<Text fontWeight={500} fontSize={20}> <Text fontWeight={500} fontSize={20}>
{token0?.symbol}:{token1?.symbol} {token0?.symbol}/{token1?.symbol}
</Text> </Text>
</RowFixed> </RowFixed>
<RowFixed> <RowFixed>
...@@ -204,7 +204,7 @@ function PositionCard({ pair, history, border, minimal = false }: PositionCardPr ...@@ -204,7 +204,7 @@ function PositionCard({ pair, history, border, minimal = false }: PositionCardPr
)} )}
<AutoRow justify="center" marginTop={'10px'}> <AutoRow justify="center" marginTop={'10px'}>
<Link>View pool information ↗</Link> <Link href={`https://uniswap.info/pair/${pair?.liquidityToken.address}`}>View pool information ↗</Link>
</AutoRow> </AutoRow>
<RowBetween marginTop="10px"> <RowBetween marginTop="10px">
<ButtonSecondary <ButtonSecondary
......
...@@ -182,7 +182,7 @@ function SearchModal({ ...@@ -182,7 +182,7 @@ function SearchModal({
const allBalances = useAllTokenBalancesTreatingWETHasETH() const allBalances = useAllTokenBalancesTreatingWETHasETH()
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
const [sortDirection, setSortDirection] = useState(true) const [invertSearchOrder, setInvertSearchOrder] = useState(false)
const userAddedTokens = useUserAddedTokens() const userAddedTokens = useUserAddedTokens()
const fetchTokenByAddress = useFetchTokenByAddress() const fetchTokenByAddress = useFetchTokenByAddress()
...@@ -200,7 +200,7 @@ function SearchModal({ ...@@ -200,7 +200,7 @@ function SearchModal({
const [showTokenImport, setShowTokenImport] = useState(false) const [showTokenImport, setShowTokenImport] = useState(false)
// used to help scanning on results, put token found from input on left // used to help scanning on results, put token found from input on left
const [identifiedToken, setIdentifiedToken] = useState<Token | null>() const [identifiedToken, setIdentifiedToken] = useState<Token>()
useEffect(() => { useEffect(() => {
const address = isAddress(searchQuery) const address = isAddress(searchQuery)
...@@ -228,37 +228,37 @@ function SearchModal({ ...@@ -228,37 +228,37 @@ function SearchModal({
const tokenList = useMemo(() => { const tokenList = useMemo(() => {
return Object.keys(allTokens) return Object.keys(allTokens)
.sort((tokenAddressA, tokenAddressB): number => { .sort((tokenAddressA, tokenAddressB): number => {
if (tokenAddressA && allTokens[tokenAddressA]?.equals(WETH[chainId])) return -1 // -1 = a is first
if (tokenAddressB && allTokens[tokenAddressB]?.equals(WETH[chainId])) return 1 // 1 = b is first
if (allTokens[tokenAddressA].symbol && allTokens[tokenAddressB].symbol) { // sort ETH first
const aSymbol = allTokens[tokenAddressA].symbol.toLowerCase() const a = allTokens[tokenAddressA]
const bSymbol = allTokens[tokenAddressB].symbol.toLowerCase() const b = allTokens[tokenAddressB]
// sort by balance if (a.equals(WETH[chainId])) return -1
const balanceA = allBalances?.[account]?.[tokenAddressA] if (b.equals(WETH[chainId])) return 1
const balanceB = allBalances?.[account]?.[tokenAddressB]
// sort by balances
if (balanceA?.greaterThan('0') && !balanceB?.greaterThan('0')) { const balanceA = allBalances[account]?.[tokenAddressA]
return sortDirection ? -1 : 1 const balanceB = allBalances[account]?.[tokenAddressB]
} if (balanceA?.greaterThan('0') && !balanceB?.greaterThan('0')) return !invertSearchOrder ? -1 : 1
if (!balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) { if (!balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) return !invertSearchOrder ? 1 : -1
return sortDirection ? 1 : -1 if (balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) {
} return balanceA.greaterThan(balanceB) ? (!invertSearchOrder ? -1 : 1) : !invertSearchOrder ? 1 : -1
return aSymbol < bSymbol ? -1 : aSymbol > bSymbol ? 1 : 0
} else {
return 0
} }
// sort by symbol
return a.symbol.toLowerCase() < b.symbol.toLowerCase() ? -1 : 1
}) })
.filter(tokenAddress => isAddress(tokenAddress))
.map(tokenAddress => { .map(tokenAddress => {
const token = allTokens[tokenAddress]
return { return {
name: allTokens[tokenAddress].name, name: token.name,
symbol: allTokens[tokenAddress].symbol, symbol: token.symbol,
address: isAddress(tokenAddress) as string, // always a string after filtering address: isAddress(tokenAddress) as string,
balance: allBalances?.[account]?.[tokenAddress] balance: allBalances?.[account]?.[tokenAddress]
} }
}) })
}, [allTokens, allBalances, account, sortDirection, chainId]) }, [allTokens, chainId, allBalances, account, invertSearchOrder])
const filteredTokenList = useMemo(() => { const filteredTokenList = useMemo(() => {
return tokenList.filter(tokenEntry => { return tokenList.filter(tokenEntry => {
...@@ -307,57 +307,69 @@ function SearchModal({ ...@@ -307,57 +307,69 @@ function SearchModal({
onDismiss() onDismiss()
} }
// make an effort to identify the specific token a user is searching for
useEffect(() => {
const searchQueryIsAddress = !!isAddress(searchQuery)
// try to find an exact match by address
if (searchQueryIsAddress) {
const identifiedTokenByAddress = Object.values(allTokens).filter(token => {
if (searchQueryIsAddress && token.address === isAddress(searchQuery)) {
return true
}
return false
})
if (identifiedTokenByAddress.length > 0) setIdentifiedToken(identifiedTokenByAddress[0])
}
// try to find an exact match by symbol
else {
const identifiedTokenBySymbol = Object.values(allTokens).filter(token => {
if (token.symbol.slice(0, searchQuery.length).toLowerCase() === searchQuery.toLowerCase()) return true
return false
})
if (identifiedTokenBySymbol.length > 0) setIdentifiedToken(identifiedTokenBySymbol[0])
}
return () => {
setIdentifiedToken(undefined)
}
}, [allTokens, searchQuery])
const sortedPairList = useMemo(() => { const sortedPairList = useMemo(() => {
return allPairs.sort((a, b): number => { return allPairs.sort((a, b): number => {
// sort by balance // sort by balance
const balanceA = allBalances?.[account]?.[a.liquidityToken.address] const balanceA = allBalances[account]?.[a.liquidityToken.address]
const balanceB = allBalances?.[account]?.[b.liquidityToken.address] const balanceB = allBalances[account]?.[b.liquidityToken.address]
if (balanceA?.greaterThan('0') && !balanceB?.greaterThan('0')) return !invertSearchOrder ? -1 : 1
if (balanceA?.greaterThan('0') && !balanceB?.greaterThan('0')) { if (!balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) return !invertSearchOrder ? 1 : -1
return sortDirection ? -1 : 1 if (balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) {
} return balanceA.greaterThan(balanceB) ? (!invertSearchOrder ? -1 : 1) : !invertSearchOrder ? 1 : -1
if (!balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) {
return sortDirection ? 1 : -1
} else {
return 0
} }
return 0
}) })
}, [account, allBalances, allPairs, sortDirection]) }, [allPairs, allBalances, account, invertSearchOrder])
const filteredPairList = useMemo(() => { const filteredPairList = useMemo(() => {
const isAddress = searchQuery.slice(0, 2) === '0x' const searchQueryIsAddress = !!isAddress(searchQuery)
return sortedPairList.filter(pair => { return sortedPairList.filter(pair => {
if (searchQuery === '') { // if there's no search query, hide non-ETH pairs
return true if (searchQuery === '') return pair.token0.equals(WETH[chainId]) || pair.token1.equals(WETH[chainId])
}
const token0 = pair.token0 const token0 = pair.token0
const token1 = pair.token1 const token1 = pair.token1
if (!token0 || !token1) {
return false // no token fetched yet if (searchQueryIsAddress) {
if (token0.address === isAddress(searchQuery)) return true
if (token1.address === isAddress(searchQuery)) return true
} else { } else {
const regexMatches = Object.keys(token0).map(field => { const identifier0 = `${token0.symbol}/${token1.symbol}`
if ( const identifier1 = `${token1.symbol}/${token0.symbol}`
(field === 'address' && isAddress) || if (identifier0.slice(0, searchQuery.length).toLowerCase() === searchQuery.toLowerCase()) return true
(field === 'name' && !isAddress) || if (identifier1.slice(0, searchQuery.length).toLowerCase() === searchQuery.toLowerCase()) return true
(field === 'symbol' && !isAddress)
) {
if (token0[field].match(new RegExp(escapeRegExp(searchQuery), 'i'))) {
setIdentifiedToken(token0)
}
if (token1[field].match(new RegExp(escapeRegExp(searchQuery), 'i'))) {
setIdentifiedToken(token1)
}
return (
token0[field].match(new RegExp(escapeRegExp(searchQuery), 'i')) ||
token1[field].match(new RegExp(escapeRegExp(searchQuery), 'i'))
)
}
return false
})
return regexMatches.some(m => m)
} }
return false
}) })
}, [searchQuery, sortedPairList]) }, [searchQuery, sortedPairList, chainId])
function renderPairsList() { function renderPairsList() {
if (filteredPairList?.length === 0) { if (filteredPairList?.length === 0) {
...@@ -441,87 +453,73 @@ function SearchModal({ ...@@ -441,87 +453,73 @@ function SearchModal({
return <TokenModalInfo>{t('noToken')}</TokenModalInfo> return <TokenModalInfo>{t('noToken')}</TokenModalInfo>
} }
} else { } else {
return filteredTokenList return filteredTokenList.map(({ address, symbol, balance }) => {
.sort((a, b) => { const urlAdded = urlAddedTokens?.some(token => token.address === address)
if (a.address === WETH[chainId].address) { const customAdded = userAddedTokens?.some(token => token.address === address) && !urlAdded
return -1
} else if (b.address === WETH[chainId].address) { const zeroBalance = balance && JSBI.equal(JSBI.BigInt(0), balance.raw)
return 1
} else if (a.balance?.greaterThan('0') && !b.balance?.greaterThan('0')) { // if token import page dont show preset list, else show all
return sortDirection ? -1 : 1 return (
} else if (!a.balance?.greaterThan('0') && b.balance?.greaterThan('0')) { <MenuItem
return sortDirection ? 1 : -1 key={address}
} else { className={`token-item-${address}`}
return sortDirection ? -1 : 1 onClick={() => (hiddenToken && hiddenToken === address ? null : _onTokenSelect(address))}
} disabled={hiddenToken && hiddenToken === address}
}) selected={otherSelectedTokenAddress === address}
.map(({ address, symbol, balance }) => { >
const urlAdded = urlAddedTokens?.some(token => token.address === address) <RowFixed>
const customAdded = userAddedTokens?.some(token => token.address === address) && !urlAdded <TokenLogo address={address} size={'24px'} style={{ marginRight: '14px' }} />
<Column>
const zeroBalance = balance && JSBI.equal(JSBI.BigInt(0), balance.raw) <Text fontWeight={500}>
{symbol}
// if token import page dont show preset list, else show all {otherSelectedTokenAddress === address && <GreySpan> ({otherSelectedText})</GreySpan>}
return ( </Text>
<MenuItem <FadedSpan>
key={address} <TYPE.main fontWeight={500}>
className={`token-item-${address}`} {urlAdded && 'Added by URL'}
onClick={() => (hiddenToken && hiddenToken === address ? null : _onTokenSelect(address))} {customAdded && 'Added by user'}
disabled={hiddenToken && hiddenToken === address} </TYPE.main>
selected={otherSelectedTokenAddress === address} {customAdded && (
> <div
<RowFixed> onClick={event => {
<TokenLogo address={address} size={'24px'} style={{ marginRight: '14px' }} /> event.stopPropagation()
<Column> if (searchQuery === address) {
<Text fontWeight={500}> setSearchQuery('')
{symbol} }
{otherSelectedTokenAddress === address && <GreySpan> ({otherSelectedText})</GreySpan>} removeTokenByAddress(chainId, address)
</Text> }}
<FadedSpan> >
<TYPE.main fontWeight={500}> <StyledLink style={{ marginLeft: '4px', fontWeight: 400 }}>(Remove)</StyledLink>
{urlAdded && 'Added by URL'} </div>
{customAdded && 'Added by user'} )}
</TYPE.main> </FadedSpan>
{customAdded && ( </Column>
<div </RowFixed>
onClick={event => { <AutoColumn gap="4px" justify="end">
event.stopPropagation() {balance ? (
if (searchQuery === address) { <Text>
setSearchQuery('') {zeroBalance && showSendWithSwap ? (
} <ButtonSecondary padding={'4px 8px'}>
removeTokenByAddress(chainId, address) <Text textAlign="center" fontWeight={500} fontSize={14} color={theme.primary1}>
}} Send With Swap
> </Text>
<StyledLink style={{ marginLeft: '4px', fontWeight: 400 }}>(Remove)</StyledLink> </ButtonSecondary>
</div> ) : balance ? (
)} balance.toSignificant(6)
</FadedSpan> ) : (
</Column> '-'
</RowFixed> )}
<AutoColumn gap="4px" justify="end"> </Text>
{balance ? ( ) : account ? (
<Text> <SpinnerWrapper src={Circle} alt="loader" />
{zeroBalance && showSendWithSwap ? ( ) : (
<ButtonSecondary padding={'4px 8px'}> '-'
<Text textAlign="center" fontWeight={500} fontSize={14} color={theme.primary1}> )}
Send With Swap </AutoColumn>
</Text> </MenuItem>
</ButtonSecondary> )
) : balance ? ( })
balance.toSignificant(6)
) : (
'-'
)}
</Text>
) : account ? (
<SpinnerWrapper src={Circle} alt="loader" />
) : (
'-'
)}
</AutoColumn>
</MenuItem>
)
})
} }
} }
...@@ -530,7 +528,7 @@ function SearchModal({ ...@@ -530,7 +528,7 @@ function SearchModal({
<FilterWrapper <FilterWrapper
onClick={() => { onClick={() => {
setActiveFilter(filter) setActiveFilter(filter)
setSortDirection(!sortDirection) setInvertSearchOrder(invertSearchOrder => !invertSearchOrder)
}} }}
selected={filter === activeFilter} selected={filter === activeFilter}
> >
...@@ -539,7 +537,7 @@ function SearchModal({ ...@@ -539,7 +537,7 @@ function SearchModal({
</Text> </Text>
{filter === activeFilter && filterType === 'tokens' && ( {filter === activeFilter && filterType === 'tokens' && (
<Text fontSize={14} fontWeight={500}> <Text fontSize={14} fontWeight={500}>
{sortDirection ? '' : ''} {!invertSearchOrder ? '' : ''}
</Text> </Text>
)} )}
</FilterWrapper> </FilterWrapper>
......
...@@ -2,7 +2,8 @@ import React, { useState, useEffect } from 'react' ...@@ -2,7 +2,8 @@ import React, { useState, useEffect } from 'react'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import styled from 'styled-components' import styled from 'styled-components'
import { isMobile } from 'react-device-detect' import { isMobile } from 'react-device-detect'
import { useWeb3React, UnsupportedChainIdError } from '@web3-react/core' import { UnsupportedChainIdError } from '@web3-react/core'
import { useWeb3React } from '../../hooks'
import { URI_AVAILABLE } from '@web3-react/walletconnect-connector' import { URI_AVAILABLE } from '@web3-react/walletconnect-connector'
import { useWalletModalOpen, useWalletModalToggle } from '../../state/application/hooks' import { useWalletModalOpen, useWalletModalToggle } from '../../state/application/hooks'
......
import { Trade } from '@uniswap/sdk'
import React, { useContext } from 'react' import React, { useContext } from 'react'
import { Text } from 'rebass' import { Text } from 'rebass'
import { ThemeContext } from 'styled-components' import { ThemeContext } from 'styled-components'
import { V1_TRADE_LINK_THRESHOLD } from '../../constants'
import { useV1TradeLinkIfBetter } from '../../data/V1'
import { Link } from '../../theme' import { Link } from '../../theme'
import { YellowCard } from '../Card' import { YellowCard } from '../Card'
import { AutoColumn } from '../Column' import { AutoColumn } from '../Column'
export default function V1TradeLink({ bestV2Trade }: { bestV2Trade?: Trade }) { export default function V1TradeLink({ v1TradeLinkIfBetter }: { v1TradeLinkIfBetter: string }) {
const v1TradeLinkIfBetter = useV1TradeLinkIfBetter(bestV2Trade, V1_TRADE_LINK_THRESHOLD)
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
return v1TradeLinkIfBetter ? ( return v1TradeLinkIfBetter ? (
<YellowCard style={{ marginTop: '12px', padding: '8px 4px' }}> <YellowCard style={{ marginTop: '12px', padding: '8px 4px' }}>
......
...@@ -15,7 +15,7 @@ export const network = new NetworkConnector({ ...@@ -15,7 +15,7 @@ export const network = new NetworkConnector({
}) })
export const injected = new InjectedConnector({ export const injected = new InjectedConnector({
supportedChainIds: [3, 4, 5, 42] supportedChainIds: [1, 3, 4, 5, 42]
}) })
// mainnet only // mainnet only
......
import { Token, ChainId } from '@uniswap/sdk'
export default [
new Token(ChainId.KOVAN, '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', 18, 'DAI', 'Dai Stablecoin'),
new Token(ChainId.KOVAN, '0xAaF64BFCC32d0F15873a02163e7E500671a4ffcD', 18, 'MKR', 'Maker')
]
import { Token, ChainId } from '@uniswap/sdk'
export default [
new Token(ChainId.MAINNET, '0xB6eD7644C69416d67B522e20bC294A9a9B405B31', 8, '0xBTC', '0xBitcoin Token'),
new Token(ChainId.MAINNET, '0xfC1E690f61EFd961294b3e1Ce3313fBD8aa4f85d', 18, 'aDAI', 'Aave Interest bearing DAI'),
new Token(ChainId.MAINNET, '0x737F98AC8cA59f2C68aD658E3C3d8C8963E40a4c', 18, 'AMN', 'Amon'),
new Token(ChainId.MAINNET, '0xD46bA6D942050d489DBd938a2C909A5d5039A161', 9, 'AMPL', 'Ampleforth'),
new Token(ChainId.MAINNET, '0xcD62b1C403fa761BAadFC74C525ce2B51780b184', 18, 'ANJ', 'Aragon Network Juror'),
new Token(ChainId.MAINNET, '0x960b236A07cf122663c4303350609A66A7B288C0', 18, 'ANT', 'Aragon Network Token'),
new Token(ChainId.MAINNET, '0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55', 18, 'BAND', 'BandToken'),
new Token(ChainId.MAINNET, '0x0D8775F648430679A709E98d2b0Cb6250d2887EF', 18, 'BAT', 'Basic Attention Token'),
new Token(ChainId.MAINNET, '0x107c4504cd79C5d2696Ea0030a8dD4e92601B82e', 18, 'BLT', 'Bloom Token'),
new Token(ChainId.MAINNET, '0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C', 18, 'BNT', 'Bancor Network Token'),
new Token(ChainId.MAINNET, '0x0327112423F3A68efdF1fcF402F6c5CB9f7C33fd', 18, 'BTC++', 'PieDAO BTC++'),
new Token(ChainId.MAINNET, '0x4F9254C83EB525f9FCf346490bbb3ed28a81C667', 18, 'CELR', 'CelerToken'),
new Token(ChainId.MAINNET, '0xF5DCe57282A584D2746FaF1593d3121Fcac444dC', 8, 'cSAI', 'Compound Dai'),
new Token(ChainId.MAINNET, '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', 8, 'cDAI', 'Compound Dai'),
new Token(ChainId.MAINNET, '0xaaAEBE6Fe48E54f431b0C390CfaF0b017d09D42d', 4, 'CEL', 'Celsius'),
new Token(ChainId.MAINNET, '0x06AF07097C9Eeb7fD685c692751D5C66dB49c215', 18, 'CHAI', 'Chai'),
new Token(ChainId.MAINNET, '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359', 18, 'SAI', 'Dai Stablecoin v1.0 (SAI)'),
new Token(ChainId.MAINNET, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'Dai Stablecoin'),
new Token(ChainId.MAINNET, '0x0Cf0Ee63788A0849fE5297F3407f701E122cC023', 18, 'DATA', 'Streamr DATAcoin'),
new Token(ChainId.MAINNET, '0xE0B7927c4aF23765Cb51314A0E0521A9645F0E2A', 9, 'DGD', 'DigixDAO'),
new Token(ChainId.MAINNET, '0x4f3AfEC4E5a3F2A6a1A411DEF7D7dFe50eE057bF', 9, 'DGX', 'Digix Gold Token'),
new Token(
ChainId.MAINNET,
'0xc719d010B63E5bbF2C0551872CD5316ED26AcD83',
18,
'DIP',
'Decentralized Insurance Protocol'
),
new Token(ChainId.MAINNET, '0xC0F9bD5Fa5698B6505F643900FFA515Ea5dF54A9', 18, 'DONUT', 'Donut'),
new Token(ChainId.MAINNET, '0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c', 18, 'ENJ', 'Enjin Coin'),
new Token(ChainId.MAINNET, '0x06f65b8CfCb13a9FE37d836fE9708dA38Ecb29B2', 18, 'FAME', 'SAINT FAME: Genesis Shirt'),
new Token(ChainId.MAINNET, '0x4946Fcea7C692606e8908002e55A582af44AC121', 18, 'FOAM', 'FOAM Token'),
new Token(ChainId.MAINNET, '0x419D0d8BdD9aF5e606Ae2232ed285Aff190E711b', 8, 'FUN', 'FunFair'),
new Token(ChainId.MAINNET, '0x4a57E687b9126435a9B19E4A802113e266AdeBde', 18, 'FXC', 'Flexacoin'),
new Token(ChainId.MAINNET, '0x543Ff227F64Aa17eA132Bf9886cAb5DB55DCAddf', 18, 'GEN', 'DAOstack'),
new Token(ChainId.MAINNET, '0x6810e776880C02933D47DB1b9fc05908e5386b96', 18, 'GNO', 'Gnosis Token'),
new Token(ChainId.MAINNET, '0x12B19D3e2ccc14Da04FAe33e63652ce469b3F2FD', 12, 'GRID', 'GRID Token'),
new Token(ChainId.MAINNET, '0x0000000000b3F879cb30FE243b4Dfee438691c04', 2, 'GST2', 'Gastoken.io'),
new Token(ChainId.MAINNET, '0xF1290473E210b2108A85237fbCd7b6eb42Cc654F', 18, 'HEDG', 'HedgeTrade'),
new Token(ChainId.MAINNET, '0x6c6EE5e31d828De241282B9606C8e98Ea48526E2', 18, 'HOT', 'HoloToken'),
new Token(ChainId.MAINNET, '0x493C57C4763932315A328269E1ADaD09653B9081', 18, 'iDAI', 'Fulcrum DAI iToken'),
new Token(ChainId.MAINNET, '0x14094949152EDDBFcd073717200DA82fEd8dC960', 18, 'iSAI', 'Fulcrum SAI iToken '),
new Token(ChainId.MAINNET, '0x6fB3e0A217407EFFf7Ca062D46c26E5d60a14d69', 18, 'IOTX', 'IoTeX Network'),
new Token(ChainId.MAINNET, '0x4Cd988AfBad37289BAAf53C13e98E2BD46aAEa8c', 18, 'KEY', 'KEY'),
new Token(ChainId.MAINNET, '0xdd974D5C2e2928deA5F71b9825b8b646686BD200', 18, 'KNC', 'Kyber Network Crystal'),
new Token(ChainId.MAINNET, '0x514910771AF9Ca656af840dff83E8264EcF986CA', 18, 'LINK', 'ChainLink Token'),
new Token(ChainId.MAINNET, '0xBBbbCA6A901c926F240b89EacB641d8Aec7AEafD', 18, 'LRC', 'LoopringCoin V2'),
new Token(ChainId.MAINNET, '0x80fB784B7eD66730e8b1DBd9820aFD29931aab03', 18, 'LEND', 'EthLend Token'),
new Token(ChainId.MAINNET, '0xA4e8C3Ec456107eA67d3075bF9e3DF3A75823DB0', 18, 'LOOM', 'LoomToken'),
new Token(ChainId.MAINNET, '0x58b6A8A3302369DAEc383334672404Ee733aB239', 18, 'LPT', 'Livepeer Token'),
new Token(ChainId.MAINNET, '0xD29F0b5b3F50b07Fe9a9511F7d86F4f4bAc3f8c4', 18, 'LQD', 'Liquidity.Network Token'),
new Token(ChainId.MAINNET, '0x0F5D2fB29fb7d3CFeE444a200298f468908cC942', 18, 'MANA', 'Decentraland MANA'),
new Token(ChainId.MAINNET, '0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0', 18, 'MATIC', 'Matic Token'),
new Token(ChainId.MAINNET, '0x8888889213DD4dA823EbDD1e235b09590633C150', 18, 'MBC', 'Marblecoin'),
new Token(ChainId.MAINNET, '0xd15eCDCF5Ea68e3995b2D0527A0aE0a3258302F8', 18, 'MCX', 'MachiX Token'),
new Token(ChainId.MAINNET, '0xa3d58c4E56fedCae3a7c43A725aeE9A71F0ece4e', 18, 'MET', 'Metronome'),
new Token(ChainId.MAINNET, '0x80f222a749a2e18Eb7f676D371F19ad7EFEEe3b7', 18, 'MGN', 'Magnolia Token'),
new Token(ChainId.MAINNET, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 18, 'MKR', 'Maker'),
new Token(ChainId.MAINNET, '0xec67005c4E498Ec7f55E092bd1d35cbC47C91892', 18, 'MLN', 'Melon Token'),
new Token(ChainId.MAINNET, '0x957c30aB0426e0C93CD8241E2c60392d08c6aC8e', 0, 'MOD', 'Modum Token'),
new Token(ChainId.MAINNET, '0xB62132e35a6c13ee1EE0f84dC5d40bad8d815206', 18, 'NEXO', 'Nexo'),
new Token(ChainId.MAINNET, '0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671', 18, 'NMR', 'Numeraire'),
new Token(ChainId.MAINNET, '0x985dd3D42De1e256d09e1c10F112bCCB8015AD41', 18, 'OCEAN', 'OceanToken'),
new Token(ChainId.MAINNET, '0x4575f41308EC1483f3d399aa9a2826d74Da13Deb', 18, 'OXT', 'Orchid'),
new Token(ChainId.MAINNET, '0xD56daC73A4d6766464b38ec6D91eB45Ce7457c44', 18, 'PAN', 'Panvala pan'),
new Token(ChainId.MAINNET, '0x8E870D67F660D95d5be530380D0eC0bd388289E1', 18, 'PAX', 'PAX'),
new Token(ChainId.MAINNET, '0x45804880De22913dAFE09f4980848ECE6EcbAf78', 18, 'PAXG', 'Paxos Gold'),
new Token(ChainId.MAINNET, '0x93ED3FBe21207Ec2E8f2d3c3de6e058Cb73Bc04d', 18, 'PNK', 'Pinakion'),
new Token(ChainId.MAINNET, '0x6758B7d441a9739b98552B373703d8d3d14f9e62', 18, 'POA20', 'POA ERC20 on Foundation'),
new Token(ChainId.MAINNET, '0x687BfC3E73f6af55F0CccA8450114D107E781a0e', 18, 'QCH', 'QChi'),
new Token(ChainId.MAINNET, '0x4a220E6096B25EADb88358cb44068A3248254675', 18, 'QNT', 'Quant'),
new Token(ChainId.MAINNET, '0x99ea4dB9EE77ACD40B119BD1dC4E33e1C070b80d', 18, 'QSP', 'Quantstamp Token'),
new Token(ChainId.MAINNET, '0xF970b8E36e23F7fC3FD752EeA86f8Be8D83375A6', 18, 'RCN', 'Ripio Credit Network Token'),
new Token(ChainId.MAINNET, '0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6', 18, 'RDN', 'Raiden Token'),
new Token(ChainId.MAINNET, '0x408e41876cCCDC0F92210600ef50372656052a38', 18, 'REN', 'Republic Token'),
new Token(ChainId.MAINNET, '0x1985365e9f78359a9B6AD760e32412f4a445E862', 18, 'REP', 'Reputation'),
new Token(ChainId.MAINNET, '0x9469D013805bFfB7D3DEBe5E7839237e535ec483', 18, 'RING', 'Darwinia Network Native Token'),
new Token(ChainId.MAINNET, '0x607F4C5BB672230e8672085532f7e901544a7375', 9, 'RLC', 'iEx.ec Network Token'),
new Token(ChainId.MAINNET, '0xB4EFd85c19999D84251304bDA99E90B92300Bd93', 18, 'RPL', 'Rocket Pool'),
new Token(ChainId.MAINNET, '0x4156D3342D5c385a87D264F90653733592000581', 8, 'SALT', 'Salt'),
new Token(ChainId.MAINNET, '0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098', 18, 'SAN', 'SANtiment network token'),
new Token(ChainId.MAINNET, '0x5e74C9036fb86BD7eCdcb084a0673EFc32eA31cb', 18, 'sETH', 'Synth sETH'),
new Token(ChainId.MAINNET, '0x3A9FfF453d50D4Ac52A6890647b823379ba36B9E', 18, 'SHUF', 'Shuffle.Monster V3'),
new Token(ChainId.MAINNET, '0x744d70FDBE2Ba4CF95131626614a1763DF805B9E', 18, 'SNT', 'Status Network Token'),
new Token(ChainId.MAINNET, '0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F', 18, 'SNX', 'Synthetix Network Token'),
new Token(ChainId.MAINNET, '0x23B608675a2B2fB1890d3ABBd85c5775c51691d5', 18, 'SOCKS', 'Unisocks Edition 0'),
new Token(ChainId.MAINNET, '0x42d6622deCe394b54999Fbd73D108123806f6a18', 18, 'SPANK', 'SPANK'),
new Token(ChainId.MAINNET, '0xB64ef51C888972c908CFacf59B47C1AfBC0Ab8aC', 8, 'STORJ', 'StorjToken'),
new Token(ChainId.MAINNET, '0x57Ab1ec28D129707052df4dF418D58a2D46d5f51', 18, 'sUSD', 'Synth sUSD'),
new Token(ChainId.MAINNET, '0x8CE9137d39326AD0cD6491fb5CC0CbA0e089b6A9', 18, 'SXP', 'Swipe'),
new Token(ChainId.MAINNET, '0x00006100F7090010005F1bd7aE6122c3C2CF0090', 18, 'TAUD', 'TrueAUD'),
new Token(ChainId.MAINNET, '0x00000100F2A2bd000715001920eB70D229700085', 18, 'TCAD', 'TrueCAD'),
new Token(ChainId.MAINNET, '0x00000000441378008EA67F4284A57932B1c000a5', 18, 'TGBP', 'TrueGBP'),
new Token(ChainId.MAINNET, '0x0000852600CEB001E08e00bC008be620d60031F2', 18, 'THKD', 'TrueHKD'),
new Token(ChainId.MAINNET, '0xaAAf91D9b90dF800Df4F55c205fd6989c977E73a', 8, 'TKN', 'Monolith TKN'),
new Token(ChainId.MAINNET, '0x0Ba45A8b5d5575935B8158a88C631E9F9C95a2e5', 18, 'TRB', 'Tellor Tributes'),
new Token(ChainId.MAINNET, '0xCb94be6f13A1182E4A4B6140cb7bf2025d28e41B', 6, 'TRST', 'Trustcoin'),
new Token(ChainId.MAINNET, '0x2C537E5624e4af88A7ae4060C022609376C8D0EB', 6, 'TRYB', 'BiLira'),
new Token(ChainId.MAINNET, '0x0000000000085d4780B73119b644AE5ecd22b376', 18, 'TUSD', 'TrueUSD'),
new Token(ChainId.MAINNET, '0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e', 8, 'UBT', 'UniBright'),
new Token(ChainId.MAINNET, '0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828', 18, 'UMA', 'UMA Voting Token v1'),
new Token(ChainId.MAINNET, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', 'USD//C'),
new Token(ChainId.MAINNET, '0xA4Bdb11dc0a2bEC88d24A3aa1E6Bb17201112eBe', 6, 'USDS', 'StableUSD'),
new Token(ChainId.MAINNET, '0xeb269732ab75A6fD61Ea60b06fE994cD32a83549', 18, 'USDx', 'dForce'),
new Token(ChainId.MAINNET, '0x8f3470A7388c05eE4e7AF3d01D8C722b0FF52374', 18, 'VERI', 'Veritaseum'),
new Token(ChainId.MAINNET, '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 8, 'WBTC', 'Wrapped BTC'),
new Token(ChainId.MAINNET, '0x09fE5f0236F0Ea5D930197DCE254d77B04128075', 18, 'WCK', 'Wrapped CryptoKitties'),
new Token(ChainId.MAINNET, '0xB4272071eCAdd69d933AdcD19cA99fe80664fc08', 18, 'XCHF', 'CryptoFranc'),
new Token(ChainId.MAINNET, '0x0f7F961648aE6Db43C75663aC7E5414Eb79b5704', 18, 'XIO', 'XIO Network'),
new Token(ChainId.MAINNET, '0xE41d2489571d322189246DaFA5ebDe1F4699F498', 18, 'ZRX', '0x Protocol Token')
]
import { Token, ChainId } from '@uniswap/sdk'
export default [
new Token(ChainId.RINKEBY, '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735', 18, 'DAI', 'Dai Stablecoin'),
new Token(ChainId.RINKEBY, '0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85', 18, 'MKR', 'Maker')
]
import { Token, ChainId } from '@uniswap/sdk'
export default [new Token(ChainId.ROPSTEN, '0xaD6D458402F60fD3Bd25163575031ACDce07538D', 18, 'DAI', 'Dai Stablecoin')]
import { Contract } from '@ethersproject/contracts' import { Contract } from '@ethersproject/contracts'
import { Token, TokenAmount, Pair, Trade, ChainId, WETH, Route, TradeType, Percent } from '@uniswap/sdk' import { Token, TokenAmount, Pair, Trade, ChainId, WETH, Route, TradeType, Percent } from '@uniswap/sdk'
import useSWR from 'swr' import useSWR from 'swr'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '../hooks'
import IUniswapV1Factory from '../constants/abis/v1_factory.json' import IUniswapV1Factory from '../constants/abis/v1_factory.json'
import { V1_FACTORY_ADDRESS } from '../constants' import { V1_FACTORY_ADDRESS } from '../constants'
...@@ -41,49 +41,63 @@ function useMockV1Pair(token?: Token) { ...@@ -41,49 +41,63 @@ function useMockV1Pair(token?: Token) {
: undefined : undefined
} }
export function useV1TradeLinkIfBetter(trade: Trade, minimumDelta: Percent = new Percent('0')): string { export function useV1TradeLinkIfBetter(
const inputPair = useMockV1Pair(trade?.route?.input) isExactIn: boolean,
const outputPair = useMockV1Pair(trade?.route?.output) inputToken: Token,
outputToken: Token,
exactAmount: TokenAmount,
v2Trade: Trade,
minimumDelta: Percent = new Percent('0')
): string {
const { chainId } = useWeb3React()
const input = inputToken
const output = outputToken
const mainnet = chainId === ChainId.MAINNET
const mainnet = trade?.route?.input?.chainId === ChainId.MAINNET // get the mock v1 pairs
const inputIsWETH = mainnet && trade?.route?.input?.equals(WETH[ChainId.MAINNET]) const inputPair = useMockV1Pair(input)
const outputIsWETH = mainnet && trade?.route?.output?.equals(WETH[ChainId.MAINNET]) const outputPair = useMockV1Pair(output)
const neitherWETH = mainnet && !!trade && !inputIsWETH && !outputIsWETH
const inputIsWETH = mainnet && input?.equals(WETH[ChainId.MAINNET])
const outputIsWETH = mainnet && output?.equals(WETH[ChainId.MAINNET])
// construct a direct or through ETH v1 route
let pairs: Pair[] let pairs: Pair[]
if (inputIsWETH && outputPair) { if (inputIsWETH && outputPair) {
pairs = [outputPair] pairs = [outputPair]
} else if (outputIsWETH && inputPair) { } else if (outputIsWETH && inputPair) {
pairs = [inputPair] pairs = [inputPair]
} else if (neitherWETH && inputPair && outputPair) { }
// if neither are WETH, it's token-to-token (if they both exist)
else if (inputPair && outputPair) {
pairs = [inputPair, outputPair] pairs = [inputPair, outputPair]
} }
const route = pairs && new Route(pairs, trade.route.input) const route = pairs && new Route(pairs, input)
const v1Trade = const v1Trade =
route && route && exactAmount
new Trade( ? new Trade(route, exactAmount, isExactIn ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT)
route, : undefined
trade.tradeType === TradeType.EXACT_INPUT ? trade.inputAmount : trade.outputAmount,
trade.tradeType
)
let v1HasBetterTrade = false let v1HasBetterTrade = false
if (v1Trade) { if (v1Trade) {
if (trade.tradeType === TradeType.EXACT_INPUT) { if (isExactIn) {
// check if the output amount on v1, discounted by minimumDelta, is greater than on v2 // discount the v1 output amount by minimumDelta
const discountedV1Output = v1Trade.outputAmount.multiply(new Percent('1').subtract(minimumDelta)) const discountedV1Output = v1Trade?.outputAmount.multiply(new Percent('1').subtract(minimumDelta))
v1HasBetterTrade = discountedV1Output.greaterThan(trade.outputAmount) // check if the discounted v1 amount is still greater than v2, short-circuiting if no v2 trade exists
v1HasBetterTrade = !!!v2Trade || discountedV1Output.greaterThan(v2Trade.outputAmount)
} else { } else {
// check if the input amount on v1, inflated by minimumDelta, is less than on v2 // inflate the v1 amount by minimumDelta
const inflatedV1Input = v1Trade.inputAmount.multiply(new Percent('1').add(minimumDelta)) const inflatedV1Input = v1Trade?.inputAmount.multiply(new Percent('1').add(minimumDelta))
v1HasBetterTrade = inflatedV1Input.lessThan(trade.inputAmount) // check if the inflated v1 amount is still less than v2, short-circuiting if no v2 trade exists
v1HasBetterTrade = !!!v2Trade || inflatedV1Input.lessThan(v2Trade.inputAmount)
} }
} }
return v1HasBetterTrade return v1HasBetterTrade
? `https://v1.uniswap.exchange/swap?inputCurrency=${ ? `https://v1.uniswap.exchange/swap?inputCurrency=${inputIsWETH ? 'ETH' : input.address}&outputCurrency=${
inputIsWETH ? 'ETH' : trade.route.input.address outputIsWETH ? 'ETH' : output.address
}&outputCurrency=${outputIsWETH ? 'ETH' : trade.route.output.address}` }`
: undefined : undefined
} }
import { ChainId, Token, WETH } from '@uniswap/sdk' import { Token, WETH } from '@uniswap/sdk'
import { useEffect, useMemo } from 'react' import { useEffect, useMemo } from 'react'
import { useAddUserToken, useFetchTokenByAddress, useUserAddedTokens } from '../state/user/hooks' import { useAddUserToken, useFetchTokenByAddress, useUserAddedTokens } from '../state/user/hooks'
import { useWeb3React } from './index' import { useWeb3React } from './index'
import MAINNET_TOKENS from '../constants/tokens/mainnet'
import RINKEBY_TOKENS from '../constants/tokens/rinkeby'
import KOVAN_TOKENS from '../constants/tokens/kovan'
import ROPSTEN_TOKENS from '../constants/tokens/ropsten'
export const ALL_TOKENS = [ export const ALL_TOKENS = [
// WETH on all chains // WETH on all chains
...Object.values(WETH), ...Object.values(WETH),
// chain-specific tokens
// Mainnet Tokens ...MAINNET_TOKENS,
new Token(ChainId.MAINNET, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'Dai Stablecoin'), ...RINKEBY_TOKENS,
new Token(ChainId.MAINNET, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', 'USD//C'), ...KOVAN_TOKENS,
new Token(ChainId.MAINNET, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 18, 'MKR', 'Maker'), ...ROPSTEN_TOKENS
// Rinkeby Tokens
new Token(ChainId.RINKEBY, '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735', 18, 'DAI', 'Dai Stablecoin'),
new Token(ChainId.RINKEBY, '0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85', 18, 'MKR', 'Maker'),
// Kovan Tokens
new Token(ChainId.KOVAN, '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', 18, 'DAI', 'Dai Stablecoin'),
new Token(ChainId.KOVAN, '0xAaF64BFCC32d0F15873a02163e7E500671a4ffcD', 18, 'MKR', 'Maker'),
// Ropsten Tokens
new Token(ChainId.ROPSTEN, '0xaD6D458402F60fD3Bd25163575031ACDce07538D', 18, 'DAI', 'Dai Stablecoin')
// Goerli Tokens
] ]
// remap WETH to ETH // remap WETH to ETH
.map(token => { .map(token => {
......
...@@ -23,15 +23,23 @@ function useAllCommonPairs(tokenA?: Token, tokenB?: Token): Pair[] { ...@@ -23,15 +23,23 @@ function useAllCommonPairs(tokenA?: Token, tokenB?: Token): Pair[] {
const aToUSDC = usePair(tokenA, chainId === ChainId.MAINNET ? USDC : null) const aToUSDC = usePair(tokenA, chainId === ChainId.MAINNET ? USDC : null)
const bToUSDC = usePair(tokenB, chainId === ChainId.MAINNET ? USDC : null) const bToUSDC = usePair(tokenB, chainId === ChainId.MAINNET ? USDC : null)
return useMemo(() => [pairBetween, aToETH, bToETH, aToDAI, bToDAI, aToUSDC, bToUSDC].filter(p => !!p), [ // get connecting pairs
pairBetween, const DAIToETH = usePair(chainId === ChainId.MAINNET ? DAI : null, WETH[chainId])
aToETH, const USDCToETH = usePair(chainId === ChainId.MAINNET ? USDC : null, WETH[chainId])
bToETH, const DAIToUSDC = usePair(chainId === ChainId.MAINNET ? DAI : null, chainId === ChainId.MAINNET ? USDC : null)
aToDAI,
bToDAI, // only pass along valid pairs, non-duplicated pairs
aToUSDC, return useMemo(
bToUSDC () =>
]) [pairBetween, aToETH, bToETH, aToDAI, bToDAI, aToUSDC, bToUSDC, DAIToETH, USDCToETH, DAIToUSDC]
// filter out invalid pairs
.filter(p => !!p)
// filter out duplicated pairs
.filter(
(p, i, pairs) => i === pairs.findIndex(pair => pair?.liquidityToken.address === p.liquidityToken.address)
),
[pairBetween, aToETH, bToETH, aToDAI, bToDAI, aToUSDC, bToUSDC, DAIToETH, USDCToETH, DAIToUSDC]
)
} }
/** /**
......
...@@ -14,7 +14,7 @@ import { RowBetween } from '../../components/Row' ...@@ -14,7 +14,7 @@ import { RowBetween } from '../../components/Row'
import { ButtonPrimary, ButtonSecondary } from '../../components/Button' import { ButtonPrimary, ButtonSecondary } from '../../components/Button'
import { AutoColumn, ColumnCenter } from '../../components/Column' import { AutoColumn, ColumnCenter } from '../../components/Column'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '../../hooks'
import { usePair } from '../../data/Reserves' import { usePair } from '../../data/Reserves'
import { useAllDummyPairs } from '../../state/user/hooks' import { useAllDummyPairs } from '../../state/user/hooks'
......
...@@ -39,10 +39,9 @@ import { useDefaultsFromURL, useDerivedSwapInfo, useSwapActionHandlers, useSwapS ...@@ -39,10 +39,9 @@ import { useDefaultsFromURL, useDerivedSwapInfo, useSwapActionHandlers, useSwapS
import { useHasPendingApproval } from '../../state/transactions/hooks' import { useHasPendingApproval } from '../../state/transactions/hooks'
import { useAllTokenBalancesTreatingWETHasETH } from '../../state/wallet/hooks' import { useAllTokenBalancesTreatingWETHasETH } from '../../state/wallet/hooks'
import { CursorPointer, TYPE } from '../../theme' import { CursorPointer, TYPE } from '../../theme'
import { Link } from '../../theme/components'
import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown, warningServerity } from '../../utils/prices' import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown, warningServerity } from '../../utils/prices'
export default function Send({ history, location: { search } }: RouteComponentProps) { export default function Send({ location: { search } }: RouteComponentProps) {
useDefaultsFromURL(search) useDefaultsFromURL(search)
// text translation // text translation
...@@ -61,7 +60,14 @@ export default function Send({ history, location: { search } }: RouteComponentPr ...@@ -61,7 +60,14 @@ export default function Send({ history, location: { search } }: RouteComponentPr
// trade details, check query params for initial state // trade details, check query params for initial state
const { independentField, typedValue } = useSwapState() const { independentField, typedValue } = useSwapState()
const { parsedAmounts, bestTrade, tokenBalances, tokens, error: swapError } = useDerivedSwapInfo() const {
parsedAmounts,
bestTrade,
tokenBalances,
tokens,
error: swapError,
v1TradeLinkIfBetter
} = useDerivedSwapInfo()
const isSwapValid = !swapError && !recipientError && bestTrade const isSwapValid = !swapError && !recipientError && bestTrade
const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT
...@@ -474,13 +480,6 @@ export default function Send({ history, location: { search } }: RouteComponentPr ...@@ -474,13 +480,6 @@ export default function Send({ history, location: { search } }: RouteComponentPr
) : noRoute && userHasSpecifiedInputOutput ? ( ) : noRoute && userHasSpecifiedInputOutput ? (
<GreyCard style={{ textAlign: 'center' }}> <GreyCard style={{ textAlign: 'center' }}>
<TYPE.main mb="4px">Insufficient liquidity for this trade.</TYPE.main> <TYPE.main mb="4px">Insufficient liquidity for this trade.</TYPE.main>
<Link
onClick={() => {
history.push('/add/' + tokens[Field.INPUT]?.address + '-' + tokens[Field.OUTPUT]?.address)
}}
>
Add liquidity now.
</Link>
</GreyCard> </GreyCard>
) : mustApprove === true ? ( ) : mustApprove === true ? (
<ButtonLight onClick={approveCallback} disabled={pendingApprovalInput}> <ButtonLight onClick={approveCallback} disabled={pendingApprovalInput}>
...@@ -507,7 +506,7 @@ export default function Send({ history, location: { search } }: RouteComponentPr ...@@ -507,7 +506,7 @@ export default function Send({ history, location: { search } }: RouteComponentPr
</Text> </Text>
</ButtonError> </ButtonError>
)} )}
<V1TradeLink bestV2Trade={bestTrade} /> <V1TradeLink v1TradeLinkIfBetter={v1TradeLinkIfBetter} />
</BottomGrouping> </BottomGrouping>
{bestTrade && ( {bestTrade && (
<AdvancedSwapDetailsDropdown <AdvancedSwapDetailsDropdown
......
...@@ -25,11 +25,11 @@ import { useWalletModalToggle } from '../../state/application/hooks' ...@@ -25,11 +25,11 @@ import { useWalletModalToggle } from '../../state/application/hooks'
import { Field } from '../../state/swap/actions' import { Field } from '../../state/swap/actions'
import { useDefaultsFromURL, useDerivedSwapInfo, useSwapActionHandlers, useSwapState } from '../../state/swap/hooks' import { useDefaultsFromURL, useDerivedSwapInfo, useSwapActionHandlers, useSwapState } from '../../state/swap/hooks'
import { useHasPendingApproval } from '../../state/transactions/hooks' import { useHasPendingApproval } from '../../state/transactions/hooks'
import { CursorPointer, Link, TYPE } from '../../theme' import { CursorPointer, TYPE } from '../../theme'
import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown, warningServerity } from '../../utils/prices' import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown, warningServerity } from '../../utils/prices'
import SwapModalHeader from '../../components/swap/SwapModalHeader' import SwapModalHeader from '../../components/swap/SwapModalHeader'
export default function Swap({ history, location: { search } }: RouteComponentProps) { export default function Swap({ location: { search } }: RouteComponentProps) {
useDefaultsFromURL(search) useDefaultsFromURL(search)
// text translation // text translation
// const { t } = useTranslation() // const { t } = useTranslation()
...@@ -40,7 +40,7 @@ export default function Swap({ history, location: { search } }: RouteComponentPr ...@@ -40,7 +40,7 @@ export default function Swap({ history, location: { search } }: RouteComponentPr
const toggleWalletModal = useWalletModalToggle() const toggleWalletModal = useWalletModalToggle()
const { independentField, typedValue } = useSwapState() const { independentField, typedValue } = useSwapState()
const { bestTrade, tokenBalances, parsedAmounts, tokens, error } = useDerivedSwapInfo() const { bestTrade, tokenBalances, parsedAmounts, tokens, error, v1TradeLinkIfBetter } = useDerivedSwapInfo()
const isValid = !error const isValid = !error
const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT
...@@ -277,14 +277,6 @@ export default function Swap({ history, location: { search } }: RouteComponentPr ...@@ -277,14 +277,6 @@ export default function Swap({ history, location: { search } }: RouteComponentPr
) : noRoute && userHasSpecifiedInputOutput ? ( ) : noRoute && userHasSpecifiedInputOutput ? (
<GreyCard style={{ textAlign: 'center' }}> <GreyCard style={{ textAlign: 'center' }}>
<TYPE.main mb="4px">Insufficient liquidity for this trade.</TYPE.main> <TYPE.main mb="4px">Insufficient liquidity for this trade.</TYPE.main>
<Link
onClick={() => {
history.push('/add/' + tokens[Field.INPUT]?.address + '-' + tokens[Field.OUTPUT]?.address)
}}
>
{' '}
Add liquidity now.
</Link>
</GreyCard> </GreyCard>
) : mustApprove === true ? ( ) : mustApprove === true ? (
<ButtonLight onClick={approveCallback} disabled={pendingApprovalInput}> <ButtonLight onClick={approveCallback} disabled={pendingApprovalInput}>
...@@ -308,7 +300,7 @@ export default function Swap({ history, location: { search } }: RouteComponentPr ...@@ -308,7 +300,7 @@ export default function Swap({ history, location: { search } }: RouteComponentPr
</Text> </Text>
</ButtonError> </ButtonError>
)} )}
<V1TradeLink bestV2Trade={bestTrade} /> <V1TradeLink v1TradeLinkIfBetter={v1TradeLinkIfBetter} />
</BottomGrouping> </BottomGrouping>
{bestTrade && ( {bestTrade && (
<AdvancedSwapDetailsDropdown <AdvancedSwapDetailsDropdown
......
...@@ -8,6 +8,8 @@ import { useTradeExactIn, useTradeExactOut } from '../../hooks/Trades' ...@@ -8,6 +8,8 @@ import { useTradeExactIn, useTradeExactOut } from '../../hooks/Trades'
import { AppDispatch, AppState } from '../index' import { AppDispatch, AppState } from '../index'
import { useTokenBalancesTreatWETHAsETH } from '../wallet/hooks' import { useTokenBalancesTreatWETHAsETH } from '../wallet/hooks'
import { Field, selectToken, setDefaultsFromURL, switchTokens, typeInput } from './actions' import { Field, selectToken, setDefaultsFromURL, switchTokens, typeInput } from './actions'
import { useV1TradeLinkIfBetter } from '../../data/V1'
import { V1_TRADE_LINK_THRESHOLD } from '../../constants'
export function useSwapState(): AppState['swap'] { export function useSwapState(): AppState['swap'] {
return useSelector<AppState, AppState['swap']>(state => state.swap) return useSelector<AppState, AppState['swap']>(state => state.swap)
...@@ -68,6 +70,7 @@ export function useDerivedSwapInfo(): { ...@@ -68,6 +70,7 @@ export function useDerivedSwapInfo(): {
parsedAmounts: { [field in Field]?: TokenAmount } parsedAmounts: { [field in Field]?: TokenAmount }
bestTrade?: Trade bestTrade?: Trade
error?: string error?: string
v1TradeLinkIfBetter?: string
} { } {
const { account } = useWeb3React() const { account } = useWeb3React()
...@@ -106,6 +109,16 @@ export function useDerivedSwapInfo(): { ...@@ -106,6 +109,16 @@ export function useDerivedSwapInfo(): {
[Field.OUTPUT]: tokenOut [Field.OUTPUT]: tokenOut
} }
// get link to trade on v1, if a better rate exists
const v1TradeLinkIfBetter = useV1TradeLinkIfBetter(
isExactIn,
tokens[Field.INPUT],
tokens[Field.OUTPUT],
isExactIn ? parsedAmounts[Field.INPUT] : parsedAmounts[Field.OUTPUT],
bestTrade,
V1_TRADE_LINK_THRESHOLD
)
let error: string | undefined let error: string | undefined
if (!account) { if (!account) {
error = 'Connect Wallet' error = 'Connect Wallet'
...@@ -132,7 +145,8 @@ export function useDerivedSwapInfo(): { ...@@ -132,7 +145,8 @@ export function useDerivedSwapInfo(): {
tokenBalances, tokenBalances,
parsedAmounts, parsedAmounts,
bestTrade, bestTrade,
error error,
v1TradeLinkIfBetter
} }
} }
......
import { ChainId, JSBI, Pair, Token, TokenAmount, WETH } from '@uniswap/sdk' import { ChainId, JSBI, Pair, Token, TokenAmount, WETH } from '@uniswap/sdk'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '../../hooks'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import { shallowEqual, useDispatch, useSelector } from 'react-redux' import { shallowEqual, useDispatch, useSelector } from 'react-redux'
import { useAllTokens } from '../../hooks/Tokens' import { useAllTokens } from '../../hooks/Tokens'
......
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