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 @@
conditions = {Country=["BY","CU","IR","IQ","CI","LR","KP","SD","SY","ZW"]}
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
[[redirects]]
from = "/*"
......
......@@ -158,15 +158,15 @@ export default function Header() {
return (
<HeaderFrame>
<MigrateBanner>
{/* <b>Uniswap V2 is live.&nbsp;</b> Move your liquidity now using the&nbsp; */}
<b>Testnet only.</b>&nbsp;Uniswap V2 has not been launched and is coming soon.&nbsp;Read the&nbsp;
{/* <Link href="https://migrate.uniswap.exchange/">
<b>migration helper</b>
</Link>
&nbsp;or read the&nbsp; */}
<Link href="https://uniswap.org/blog/uniswap-v2/">
Uniswap V2 is live! Read the&nbsp;
<Link href="https://uniswap.org/blog/launch-uniswap-v2/">
<b>blog post ↗</b>
</Link>
&nbsp;or&nbsp;
<Link href="https://migrate.uniswap.exchange/">
<b>migrate your liquidity ↗</b>
</Link>
.
</MigrateBanner>
<RowBetween padding="1rem">
<HeaderElement>
......
......@@ -15,7 +15,7 @@ import { AutoColumn, ColumnCenter } from '../Column'
import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button'
import { useToken } from '../../hooks/Tokens'
import { useWeb3React } from '@web3-react/core'
import { useWeb3React } from '../../hooks'
import { usePairAdder } from '../../state/user/hooks'
import { usePair } from '../../data/Reserves'
......
......@@ -4,7 +4,7 @@ import { darken } from 'polished'
import { RouteComponentProps, withRouter } from 'react-router-dom'
import { Percent, Pair, JSBI } from '@uniswap/sdk'
import { useWeb3React } from '@web3-react/core'
import { useWeb3React } from '../../hooks'
import { useTotalSupply } from '../../data/TotalSupply'
import { useTokenBalance } from '../../state/wallet/hooks'
......@@ -80,7 +80,7 @@ function PositionCard({ pair, history, border, minimal = false }: PositionCardPr
<RowFixed>
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} />
<Text fontWeight={500} fontSize={20}>
{token0?.symbol}:{token1?.symbol}
{token0?.symbol}/{token1?.symbol}
</Text>
</RowFixed>
<RowFixed>
......@@ -134,7 +134,7 @@ function PositionCard({ pair, history, border, minimal = false }: PositionCardPr
<RowFixed>
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} />
<Text fontWeight={500} fontSize={20}>
{token0?.symbol}:{token1?.symbol}
{token0?.symbol}/{token1?.symbol}
</Text>
</RowFixed>
<RowFixed>
......@@ -204,7 +204,7 @@ function PositionCard({ pair, history, border, minimal = false }: PositionCardPr
)}
<AutoRow justify="center" marginTop={'10px'}>
<Link>View pool information ↗</Link>
<Link href={`https://uniswap.info/pair/${pair?.liquidityToken.address}`}>View pool information ↗</Link>
</AutoRow>
<RowBetween marginTop="10px">
<ButtonSecondary
......
......@@ -182,7 +182,7 @@ function SearchModal({
const allBalances = useAllTokenBalancesTreatingWETHasETH()
const [searchQuery, setSearchQuery] = useState('')
const [sortDirection, setSortDirection] = useState(true)
const [invertSearchOrder, setInvertSearchOrder] = useState(false)
const userAddedTokens = useUserAddedTokens()
const fetchTokenByAddress = useFetchTokenByAddress()
......@@ -200,7 +200,7 @@ function SearchModal({
const [showTokenImport, setShowTokenImport] = useState(false)
// 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(() => {
const address = isAddress(searchQuery)
......@@ -228,37 +228,37 @@ function SearchModal({
const tokenList = useMemo(() => {
return Object.keys(allTokens)
.sort((tokenAddressA, tokenAddressB): number => {
if (tokenAddressA && allTokens[tokenAddressA]?.equals(WETH[chainId])) return -1
if (tokenAddressB && allTokens[tokenAddressB]?.equals(WETH[chainId])) return 1
if (allTokens[tokenAddressA].symbol && allTokens[tokenAddressB].symbol) {
const aSymbol = allTokens[tokenAddressA].symbol.toLowerCase()
const bSymbol = allTokens[tokenAddressB].symbol.toLowerCase()
// sort by balance
const balanceA = allBalances?.[account]?.[tokenAddressA]
const balanceB = allBalances?.[account]?.[tokenAddressB]
if (balanceA?.greaterThan('0') && !balanceB?.greaterThan('0')) {
return sortDirection ? -1 : 1
}
if (!balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) {
return sortDirection ? 1 : -1
}
return aSymbol < bSymbol ? -1 : aSymbol > bSymbol ? 1 : 0
} else {
return 0
// -1 = a is first
// 1 = b is first
// sort ETH first
const a = allTokens[tokenAddressA]
const b = allTokens[tokenAddressB]
if (a.equals(WETH[chainId])) return -1
if (b.equals(WETH[chainId])) return 1
// sort by balances
const balanceA = allBalances[account]?.[tokenAddressA]
const balanceB = allBalances[account]?.[tokenAddressB]
if (balanceA?.greaterThan('0') && !balanceB?.greaterThan('0')) return !invertSearchOrder ? -1 : 1
if (!balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) return !invertSearchOrder ? 1 : -1
if (balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) {
return balanceA.greaterThan(balanceB) ? (!invertSearchOrder ? -1 : 1) : !invertSearchOrder ? 1 : -1
}
// sort by symbol
return a.symbol.toLowerCase() < b.symbol.toLowerCase() ? -1 : 1
})
.filter(tokenAddress => isAddress(tokenAddress))
.map(tokenAddress => {
const token = allTokens[tokenAddress]
return {
name: allTokens[tokenAddress].name,
symbol: allTokens[tokenAddress].symbol,
address: isAddress(tokenAddress) as string, // always a string after filtering
name: token.name,
symbol: token.symbol,
address: isAddress(tokenAddress) as string,
balance: allBalances?.[account]?.[tokenAddress]
}
})
}, [allTokens, allBalances, account, sortDirection, chainId])
}, [allTokens, chainId, allBalances, account, invertSearchOrder])
const filteredTokenList = useMemo(() => {
return tokenList.filter(tokenEntry => {
......@@ -307,57 +307,69 @@ function SearchModal({
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(() => {
return allPairs.sort((a, b): number => {
// sort by balance
const balanceA = allBalances?.[account]?.[a.liquidityToken.address]
const balanceB = allBalances?.[account]?.[b.liquidityToken.address]
if (balanceA?.greaterThan('0') && !balanceB?.greaterThan('0')) {
return sortDirection ? -1 : 1
}
if (!balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) {
return sortDirection ? 1 : -1
} else {
return 0
const balanceA = allBalances[account]?.[a.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')) return !invertSearchOrder ? 1 : -1
if (balanceA?.greaterThan('0') && balanceB?.greaterThan('0')) {
return balanceA.greaterThan(balanceB) ? (!invertSearchOrder ? -1 : 1) : !invertSearchOrder ? 1 : -1
}
return 0
})
}, [account, allBalances, allPairs, sortDirection])
}, [allPairs, allBalances, account, invertSearchOrder])
const filteredPairList = useMemo(() => {
const isAddress = searchQuery.slice(0, 2) === '0x'
const searchQueryIsAddress = !!isAddress(searchQuery)
return sortedPairList.filter(pair => {
if (searchQuery === '') {
return true
}
// if there's no search query, hide non-ETH pairs
if (searchQuery === '') return pair.token0.equals(WETH[chainId]) || pair.token1.equals(WETH[chainId])
const token0 = pair.token0
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 {
const regexMatches = Object.keys(token0).map(field => {
if (
(field === 'address' && isAddress) ||
(field === 'name' && !isAddress) ||
(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)
const identifier0 = `${token0.symbol}/${token1.symbol}`
const identifier1 = `${token1.symbol}/${token0.symbol}`
if (identifier0.slice(0, searchQuery.length).toLowerCase() === searchQuery.toLowerCase()) return true
if (identifier1.slice(0, searchQuery.length).toLowerCase() === searchQuery.toLowerCase()) return true
}
return false
})
}, [searchQuery, sortedPairList])
}, [searchQuery, sortedPairList, chainId])
function renderPairsList() {
if (filteredPairList?.length === 0) {
......@@ -441,87 +453,73 @@ function SearchModal({
return <TokenModalInfo>{t('noToken')}</TokenModalInfo>
}
} else {
return filteredTokenList
.sort((a, b) => {
if (a.address === WETH[chainId].address) {
return -1
} else if (b.address === WETH[chainId].address) {
return 1
} else if (a.balance?.greaterThan('0') && !b.balance?.greaterThan('0')) {
return sortDirection ? -1 : 1
} else if (!a.balance?.greaterThan('0') && b.balance?.greaterThan('0')) {
return sortDirection ? 1 : -1
} else {
return sortDirection ? -1 : 1
}
})
.map(({ address, symbol, balance }) => {
const urlAdded = urlAddedTokens?.some(token => token.address === address)
const customAdded = userAddedTokens?.some(token => token.address === address) && !urlAdded
const zeroBalance = balance && JSBI.equal(JSBI.BigInt(0), balance.raw)
// if token import page dont show preset list, else show all
return (
<MenuItem
key={address}
className={`token-item-${address}`}
onClick={() => (hiddenToken && hiddenToken === address ? null : _onTokenSelect(address))}
disabled={hiddenToken && hiddenToken === address}
selected={otherSelectedTokenAddress === address}
>
<RowFixed>
<TokenLogo address={address} size={'24px'} style={{ marginRight: '14px' }} />
<Column>
<Text fontWeight={500}>
{symbol}
{otherSelectedTokenAddress === address && <GreySpan> ({otherSelectedText})</GreySpan>}
</Text>
<FadedSpan>
<TYPE.main fontWeight={500}>
{urlAdded && 'Added by URL'}
{customAdded && 'Added by user'}
</TYPE.main>
{customAdded && (
<div
onClick={event => {
event.stopPropagation()
if (searchQuery === address) {
setSearchQuery('')
}
removeTokenByAddress(chainId, address)
}}
>
<StyledLink style={{ marginLeft: '4px', fontWeight: 400 }}>(Remove)</StyledLink>
</div>
)}
</FadedSpan>
</Column>
</RowFixed>
<AutoColumn gap="4px" justify="end">
{balance ? (
<Text>
{zeroBalance && showSendWithSwap ? (
<ButtonSecondary padding={'4px 8px'}>
<Text textAlign="center" fontWeight={500} fontSize={14} color={theme.primary1}>
Send With Swap
</Text>
</ButtonSecondary>
) : balance ? (
balance.toSignificant(6)
) : (
'-'
)}
</Text>
) : account ? (
<SpinnerWrapper src={Circle} alt="loader" />
) : (
'-'
)}
</AutoColumn>
</MenuItem>
)
})
return filteredTokenList.map(({ address, symbol, balance }) => {
const urlAdded = urlAddedTokens?.some(token => token.address === address)
const customAdded = userAddedTokens?.some(token => token.address === address) && !urlAdded
const zeroBalance = balance && JSBI.equal(JSBI.BigInt(0), balance.raw)
// if token import page dont show preset list, else show all
return (
<MenuItem
key={address}
className={`token-item-${address}`}
onClick={() => (hiddenToken && hiddenToken === address ? null : _onTokenSelect(address))}
disabled={hiddenToken && hiddenToken === address}
selected={otherSelectedTokenAddress === address}
>
<RowFixed>
<TokenLogo address={address} size={'24px'} style={{ marginRight: '14px' }} />
<Column>
<Text fontWeight={500}>
{symbol}
{otherSelectedTokenAddress === address && <GreySpan> ({otherSelectedText})</GreySpan>}
</Text>
<FadedSpan>
<TYPE.main fontWeight={500}>
{urlAdded && 'Added by URL'}
{customAdded && 'Added by user'}
</TYPE.main>
{customAdded && (
<div
onClick={event => {
event.stopPropagation()
if (searchQuery === address) {
setSearchQuery('')
}
removeTokenByAddress(chainId, address)
}}
>
<StyledLink style={{ marginLeft: '4px', fontWeight: 400 }}>(Remove)</StyledLink>
</div>
)}
</FadedSpan>
</Column>
</RowFixed>
<AutoColumn gap="4px" justify="end">
{balance ? (
<Text>
{zeroBalance && showSendWithSwap ? (
<ButtonSecondary padding={'4px 8px'}>
<Text textAlign="center" fontWeight={500} fontSize={14} color={theme.primary1}>
Send With Swap
</Text>
</ButtonSecondary>
) : balance ? (
balance.toSignificant(6)
) : (
'-'
)}
</Text>
) : account ? (
<SpinnerWrapper src={Circle} alt="loader" />
) : (
'-'
)}
</AutoColumn>
</MenuItem>
)
})
}
}
......@@ -530,7 +528,7 @@ function SearchModal({
<FilterWrapper
onClick={() => {
setActiveFilter(filter)
setSortDirection(!sortDirection)
setInvertSearchOrder(invertSearchOrder => !invertSearchOrder)
}}
selected={filter === activeFilter}
>
......@@ -539,7 +537,7 @@ function SearchModal({
</Text>
{filter === activeFilter && filterType === 'tokens' && (
<Text fontSize={14} fontWeight={500}>
{sortDirection ? '' : ''}
{!invertSearchOrder ? '' : ''}
</Text>
)}
</FilterWrapper>
......
......@@ -2,7 +2,8 @@ import React, { useState, useEffect } from 'react'
import ReactGA from 'react-ga'
import styled from 'styled-components'
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 { useWalletModalOpen, useWalletModalToggle } from '../../state/application/hooks'
......
import { Trade } from '@uniswap/sdk'
import React, { useContext } from 'react'
import { Text } from 'rebass'
import { ThemeContext } from 'styled-components'
import { V1_TRADE_LINK_THRESHOLD } from '../../constants'
import { useV1TradeLinkIfBetter } from '../../data/V1'
import { Link } from '../../theme'
import { YellowCard } from '../Card'
import { AutoColumn } from '../Column'
export default function V1TradeLink({ bestV2Trade }: { bestV2Trade?: Trade }) {
const v1TradeLinkIfBetter = useV1TradeLinkIfBetter(bestV2Trade, V1_TRADE_LINK_THRESHOLD)
export default function V1TradeLink({ v1TradeLinkIfBetter }: { v1TradeLinkIfBetter: string }) {
const theme = useContext(ThemeContext)
return v1TradeLinkIfBetter ? (
<YellowCard style={{ marginTop: '12px', padding: '8px 4px' }}>
......
......@@ -15,7 +15,7 @@ export const network = new NetworkConnector({
})
export const injected = new InjectedConnector({
supportedChainIds: [3, 4, 5, 42]
supportedChainIds: [1, 3, 4, 5, 42]
})
// 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 { Token, TokenAmount, Pair, Trade, ChainId, WETH, Route, TradeType, Percent } from '@uniswap/sdk'
import useSWR from 'swr'
import { useWeb3React } from '@web3-react/core'
import { useWeb3React } from '../hooks'
import IUniswapV1Factory from '../constants/abis/v1_factory.json'
import { V1_FACTORY_ADDRESS } from '../constants'
......@@ -41,49 +41,63 @@ function useMockV1Pair(token?: Token) {
: undefined
}
export function useV1TradeLinkIfBetter(trade: Trade, minimumDelta: Percent = new Percent('0')): string {
const inputPair = useMockV1Pair(trade?.route?.input)
const outputPair = useMockV1Pair(trade?.route?.output)
export function useV1TradeLinkIfBetter(
isExactIn: boolean,
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
const inputIsWETH = mainnet && trade?.route?.input?.equals(WETH[ChainId.MAINNET])
const outputIsWETH = mainnet && trade?.route?.output?.equals(WETH[ChainId.MAINNET])
const neitherWETH = mainnet && !!trade && !inputIsWETH && !outputIsWETH
// get the mock v1 pairs
const inputPair = useMockV1Pair(input)
const outputPair = useMockV1Pair(output)
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[]
if (inputIsWETH && outputPair) {
pairs = [outputPair]
} else if (outputIsWETH && 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]
}
const route = pairs && new Route(pairs, trade.route.input)
const route = pairs && new Route(pairs, input)
const v1Trade =
route &&
new Trade(
route,
trade.tradeType === TradeType.EXACT_INPUT ? trade.inputAmount : trade.outputAmount,
trade.tradeType
)
route && exactAmount
? new Trade(route, exactAmount, isExactIn ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT)
: undefined
let v1HasBetterTrade = false
if (v1Trade) {
if (trade.tradeType === TradeType.EXACT_INPUT) {
// check if the output amount on v1, discounted by minimumDelta, is greater than on v2
const discountedV1Output = v1Trade.outputAmount.multiply(new Percent('1').subtract(minimumDelta))
v1HasBetterTrade = discountedV1Output.greaterThan(trade.outputAmount)
if (isExactIn) {
// discount the v1 output amount by minimumDelta
const discountedV1Output = v1Trade?.outputAmount.multiply(new Percent('1').subtract(minimumDelta))
// 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 {
// check if the input amount on v1, inflated by minimumDelta, is less than on v2
const inflatedV1Input = v1Trade.inputAmount.multiply(new Percent('1').add(minimumDelta))
v1HasBetterTrade = inflatedV1Input.lessThan(trade.inputAmount)
// inflate the v1 amount by minimumDelta
const inflatedV1Input = v1Trade?.inputAmount.multiply(new Percent('1').add(minimumDelta))
// 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
? `https://v1.uniswap.exchange/swap?inputCurrency=${
inputIsWETH ? 'ETH' : trade.route.input.address
}&outputCurrency=${outputIsWETH ? 'ETH' : trade.route.output.address}`
? `https://v1.uniswap.exchange/swap?inputCurrency=${inputIsWETH ? 'ETH' : input.address}&outputCurrency=${
outputIsWETH ? 'ETH' : output.address
}`
: undefined
}
import { ChainId, Token, WETH } from '@uniswap/sdk'
import { Token, WETH } from '@uniswap/sdk'
import { useEffect, useMemo } from 'react'
import { useAddUserToken, useFetchTokenByAddress, useUserAddedTokens } from '../state/user/hooks'
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 = [
// WETH on all chains
...Object.values(WETH),
// Mainnet Tokens
new Token(ChainId.MAINNET, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'Dai Stablecoin'),
new Token(ChainId.MAINNET, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', 'USD//C'),
new Token(ChainId.MAINNET, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 18, 'MKR', 'Maker'),
// 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
// chain-specific tokens
...MAINNET_TOKENS,
...RINKEBY_TOKENS,
...KOVAN_TOKENS,
...ROPSTEN_TOKENS
]
// remap WETH to ETH
.map(token => {
......
......@@ -23,15 +23,23 @@ function useAllCommonPairs(tokenA?: Token, tokenB?: Token): Pair[] {
const aToUSDC = usePair(tokenA, 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), [
pairBetween,
aToETH,
bToETH,
aToDAI,
bToDAI,
aToUSDC,
bToUSDC
])
// get connecting pairs
const DAIToETH = usePair(chainId === ChainId.MAINNET ? DAI : null, WETH[chainId])
const USDCToETH = usePair(chainId === ChainId.MAINNET ? USDC : null, WETH[chainId])
const DAIToUSDC = usePair(chainId === ChainId.MAINNET ? DAI : null, chainId === ChainId.MAINNET ? USDC : null)
// only pass along valid pairs, non-duplicated pairs
return useMemo(
() =>
[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'
import { ButtonPrimary, ButtonSecondary } from '../../components/Button'
import { AutoColumn, ColumnCenter } from '../../components/Column'
import { useWeb3React } from '@web3-react/core'
import { useWeb3React } from '../../hooks'
import { usePair } from '../../data/Reserves'
import { useAllDummyPairs } from '../../state/user/hooks'
......
......@@ -39,10 +39,9 @@ import { useDefaultsFromURL, useDerivedSwapInfo, useSwapActionHandlers, useSwapS
import { useHasPendingApproval } from '../../state/transactions/hooks'
import { useAllTokenBalancesTreatingWETHasETH } from '../../state/wallet/hooks'
import { CursorPointer, TYPE } from '../../theme'
import { Link } from '../../theme/components'
import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown, warningServerity } from '../../utils/prices'
export default function Send({ history, location: { search } }: RouteComponentProps) {
export default function Send({ location: { search } }: RouteComponentProps) {
useDefaultsFromURL(search)
// text translation
......@@ -61,7 +60,14 @@ export default function Send({ history, location: { search } }: RouteComponentPr
// trade details, check query params for initial state
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 dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT
......@@ -474,13 +480,6 @@ export default function Send({ history, location: { search } }: RouteComponentPr
) : noRoute && userHasSpecifiedInputOutput ? (
<GreyCard style={{ textAlign: 'center' }}>
<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>
) : mustApprove === true ? (
<ButtonLight onClick={approveCallback} disabled={pendingApprovalInput}>
......@@ -507,7 +506,7 @@ export default function Send({ history, location: { search } }: RouteComponentPr
</Text>
</ButtonError>
)}
<V1TradeLink bestV2Trade={bestTrade} />
<V1TradeLink v1TradeLinkIfBetter={v1TradeLinkIfBetter} />
</BottomGrouping>
{bestTrade && (
<AdvancedSwapDetailsDropdown
......
......@@ -25,11 +25,11 @@ import { useWalletModalToggle } from '../../state/application/hooks'
import { Field } from '../../state/swap/actions'
import { useDefaultsFromURL, useDerivedSwapInfo, useSwapActionHandlers, useSwapState } from '../../state/swap/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 SwapModalHeader from '../../components/swap/SwapModalHeader'
export default function Swap({ history, location: { search } }: RouteComponentProps) {
export default function Swap({ location: { search } }: RouteComponentProps) {
useDefaultsFromURL(search)
// text translation
// const { t } = useTranslation()
......@@ -40,7 +40,7 @@ export default function Swap({ history, location: { search } }: RouteComponentPr
const toggleWalletModal = useWalletModalToggle()
const { independentField, typedValue } = useSwapState()
const { bestTrade, tokenBalances, parsedAmounts, tokens, error } = useDerivedSwapInfo()
const { bestTrade, tokenBalances, parsedAmounts, tokens, error, v1TradeLinkIfBetter } = useDerivedSwapInfo()
const isValid = !error
const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT
......@@ -277,14 +277,6 @@ export default function Swap({ history, location: { search } }: RouteComponentPr
) : noRoute && userHasSpecifiedInputOutput ? (
<GreyCard style={{ textAlign: 'center' }}>
<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>
) : mustApprove === true ? (
<ButtonLight onClick={approveCallback} disabled={pendingApprovalInput}>
......@@ -308,7 +300,7 @@ export default function Swap({ history, location: { search } }: RouteComponentPr
</Text>
</ButtonError>
)}
<V1TradeLink bestV2Trade={bestTrade} />
<V1TradeLink v1TradeLinkIfBetter={v1TradeLinkIfBetter} />
</BottomGrouping>
{bestTrade && (
<AdvancedSwapDetailsDropdown
......
......@@ -8,6 +8,8 @@ import { useTradeExactIn, useTradeExactOut } from '../../hooks/Trades'
import { AppDispatch, AppState } from '../index'
import { useTokenBalancesTreatWETHAsETH } from '../wallet/hooks'
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'] {
return useSelector<AppState, AppState['swap']>(state => state.swap)
......@@ -68,6 +70,7 @@ export function useDerivedSwapInfo(): {
parsedAmounts: { [field in Field]?: TokenAmount }
bestTrade?: Trade
error?: string
v1TradeLinkIfBetter?: string
} {
const { account } = useWeb3React()
......@@ -106,6 +109,16 @@ export function useDerivedSwapInfo(): {
[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
if (!account) {
error = 'Connect Wallet'
......@@ -132,7 +145,8 @@ export function useDerivedSwapInfo(): {
tokenBalances,
parsedAmounts,
bestTrade,
error
error,
v1TradeLinkIfBetter
}
}
......
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 { shallowEqual, useDispatch, useSelector } from 'react-redux'
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