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
......
This diff is collapsed.
......@@ -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')
]
This diff is collapsed.
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