Commit b1ffab18 authored by Moody Salem's avatar Moody Salem Committed by GitHub

Refactors (#772)

* Bunch of refactoring

* Add an integration test that gets as far as swapping

* Drop the nonce code that isn't doing anything right now

* Undo one of the accidental changes to the reducer
parent 60582de4
...@@ -3,7 +3,7 @@ import { TEST_ADDRESS_NEVER_USE } from '../support/commands' ...@@ -3,7 +3,7 @@ import { TEST_ADDRESS_NEVER_USE } from '../support/commands'
describe('Landing Page', () => { describe('Landing Page', () => {
beforeEach(() => cy.visit('/')) beforeEach(() => cy.visit('/'))
it('loads exchange page', () => { it('loads exchange page', () => {
cy.get('#exchangePage') cy.get('#exchange-page')
}) })
it('redirects to url /swap', () => { it('redirects to url /swap', () => {
...@@ -11,12 +11,12 @@ describe('Landing Page', () => { ...@@ -11,12 +11,12 @@ describe('Landing Page', () => {
}) })
it('allows navigation to send', () => { it('allows navigation to send', () => {
cy.get('#send-navLink').click() cy.get('#send-nav-link').click()
cy.url().should('include', '/send') cy.url().should('include', '/send')
}) })
it('allows navigation to pool', () => { it('allows navigation to pool', () => {
cy.get('#pool-navLink').click() cy.get('#pool-nav-link').click()
cy.url().should('include', '/pool') cy.url().should('include', '/pool')
}) })
......
describe('Swap', () => { describe('Swap', () => {
beforeEach(() => cy.visit('/swap')) beforeEach(() => cy.visit('/swap'))
it('can enter an amount into input', () => { it('can enter an amount into input', () => {
cy.get('#swapInputField').type('0.001') cy.get('#swap-currency-input .token-amount-input').type('0.001')
}) })
it('zero swap amount', () => { it('zero swap amount', () => {
cy.get('#swapInputField').type('0.0') cy.get('#swap-currency-input .token-amount-input').type('0.0')
}) })
it('can enter an amount into output', () => { it('can enter an amount into output', () => {
cy.get('#swapOutputField').type('0.001') cy.get('#swap-currency-output .token-amount-input').type('0.001')
}) })
it('zero output amount', () => { it('zero output amount', () => {
cy.get('#swapOutputField').type('0.0') cy.get('#swap-currency-output .token-amount-input').type('0.0')
})
it('can swap ETH for DAI', () => {
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').click()
cy.get('#swap-currency-input .token-amount-input').type('0.001')
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
cy.get('#exchange-show-advanced').click()
cy.get('#exchange-swap-button').click()
cy.get('#exchange-page-confirm-swap-or-send').should('contain', 'Confirm Swap')
}) })
}) })
...@@ -12,7 +12,7 @@ import { TYPE, Link } from '../../theme' ...@@ -12,7 +12,7 @@ import { TYPE, Link } from '../../theme'
import { AutoColumn, ColumnCenter } from '../Column' import { AutoColumn, ColumnCenter } from '../Column'
import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button' import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button'
import { useToken } from '../../contexts/Tokens' import { useToken } from '../../hooks/Tokens'
import { useWeb3React } from '../../hooks' import { useWeb3React } from '../../hooks'
import { usePair } from '../../data/Reserves' import { usePair } from '../../data/Reserves'
...@@ -79,7 +79,7 @@ function CreatePool({ history }: RouteComponentProps<{}>) { ...@@ -79,7 +79,7 @@ function CreatePool({ history }: RouteComponentProps<{}>) {
{token0?.symbol}{' '} {token0?.symbol}{' '}
</Text> </Text>
<TYPE.darkGray fontWeight={500} fontSize={16} marginLeft={'8px'}> <TYPE.darkGray fontWeight={500} fontSize={16} marginLeft={'8px'}>
{token0?.symbol === 'ETH' && '(default)'} {token0?.address === 'ETH' && '(default)'}
</TYPE.darkGray> </TYPE.darkGray>
</Row> </Row>
</ButtonDropwdownLight> </ButtonDropwdownLight>
......
...@@ -136,7 +136,7 @@ interface CurrencyInputPanelProps { ...@@ -136,7 +136,7 @@ interface CurrencyInputPanelProps {
showSendWithSwap?: boolean showSendWithSwap?: boolean
otherSelectedTokenAddress?: string | null otherSelectedTokenAddress?: string | null
advanced?: boolean advanced?: boolean
inputId: string id: string
} }
export default function CurrencyInputPanel({ export default function CurrencyInputPanel({
...@@ -157,7 +157,7 @@ export default function CurrencyInputPanel({ ...@@ -157,7 +157,7 @@ export default function CurrencyInputPanel({
showSendWithSwap = false, showSendWithSwap = false,
otherSelectedTokenAddress = null, otherSelectedTokenAddress = null,
advanced = false, advanced = false,
inputId id
}: CurrencyInputPanelProps) { }: CurrencyInputPanelProps) {
const { t } = useTranslation() const { t } = useTranslation()
...@@ -167,7 +167,7 @@ export default function CurrencyInputPanel({ ...@@ -167,7 +167,7 @@ export default function CurrencyInputPanel({
const theme = useContext(ThemeContext) const theme = useContext(ThemeContext)
return ( return (
<InputPanel> <InputPanel id={id}>
<Container hideInput={hideInput}> <Container hideInput={hideInput}>
{!hideInput && ( {!hideInput && (
<LabelRow> <LabelRow>
...@@ -197,8 +197,8 @@ export default function CurrencyInputPanel({ ...@@ -197,8 +197,8 @@ export default function CurrencyInputPanel({
{!hideInput && ( {!hideInput && (
<> <>
<NumericalInput <NumericalInput
className="token-amount-input"
value={value} value={value}
id={inputId}
onUserInput={val => { onUserInput={val => {
onUserInput(field, val) onUserInput(field, val)
}} }}
...@@ -210,6 +210,7 @@ export default function CurrencyInputPanel({ ...@@ -210,6 +210,7 @@ export default function CurrencyInputPanel({
)} )}
<CurrencySelect <CurrencySelect
selected={!!token} selected={!!token}
className="open-currency-select-button"
onClick={() => { onClick={() => {
if (!disableTokenSelect) { if (!disableTokenSelect) {
setModalOpen(true) setModalOpen(true)
...@@ -223,7 +224,7 @@ export default function CurrencyInputPanel({ ...@@ -223,7 +224,7 @@ export default function CurrencyInputPanel({
<TokenLogo address={token?.address} size={'24px'} /> <TokenLogo address={token?.address} size={'24px'} />
) : null} ) : null}
{isExchange ? ( {isExchange ? (
<StyledTokenName> <StyledTokenName className="token-name-container">
{pair?.token0.symbol}:{pair?.token1.symbol} {pair?.token0.symbol}:{pair?.token1.symbol}
</StyledTokenName> </StyledTokenName>
) : ( ) : (
...@@ -248,7 +249,7 @@ export default function CurrencyInputPanel({ ...@@ -248,7 +249,7 @@ export default function CurrencyInputPanel({
showSendWithSwap={showSendWithSwap} showSendWithSwap={showSendWithSwap}
hiddenToken={token?.address} hiddenToken={token?.address}
otherSelectedTokenAddress={otherSelectedTokenAddress} otherSelectedTokenAddress={otherSelectedTokenAddress}
otherSelectedText={field === 0 ? ' Selected as output' : 'Selected as input'} otherSelectedText={field === 0 ? 'Selected as output' : 'Selected as input'}
/> />
)} )}
</InputPanel> </InputPanel>
......
import React, { useState, useCallback, useEffect, useContext } from 'react'
import ReactGA from 'react-ga'
import { ThemeContext } from 'styled-components'
import { parseEther, parseUnits } from '@ethersproject/units'
import { JSBI, Percent, TokenAmount, TradeType, WETH, Fraction } from '@uniswap/sdk'
import { ArrowDown, ChevronDown, ChevronUp, Repeat } from 'react-feather'
import { withRouter, RouteComponentProps } from 'react-router-dom'
import { BigNumber } from '@ethersproject/bignumber' import { BigNumber } from '@ethersproject/bignumber'
import { MaxUint256 } from '@ethersproject/constants' import { MaxUint256 } from '@ethersproject/constants'
import { Contract } from '@ethersproject/contracts' import { Contract } from '@ethersproject/contracts'
import { useUserAdvanced } from '../../state/application/hooks' import { parseEther, parseUnits } from '@ethersproject/units'
import { useTokenBalanceTreatingWETHasETH, useAllTokenBalancesTreatingWETHasETH } from '../../state/wallet/hooks' import { Fraction, JSBI, Percent, TokenAmount, TradeType, WETH } from '@uniswap/sdk'
import { Field, SwapAction, useSwapStateReducer } from './swap-store' import React, { useCallback, useContext, useEffect, useState } from 'react'
import { ArrowDown, ChevronDown, ChevronUp, Repeat } from 'react-feather'
import ReactGA from 'react-ga'
import { RouteComponentProps, withRouter } from 'react-router-dom'
import { Text } from 'rebass' import { Text } from 'rebass'
import { ThemeContext } from 'styled-components'
import Card, { BlueCard, GreyCard, YellowCard } from '../../components/Card' import Card, { BlueCard, GreyCard, YellowCard } from '../../components/Card'
import { AutoColumn, ColumnCenter } from '../../components/Column' import { AutoColumn, ColumnCenter } from '../../components/Column'
import { AutoRow, RowBetween, RowFixed } from '../Row'
import { ROUTER_ADDRESS } from '../../constants' import { ROUTER_ADDRESS } from '../../constants'
import { useTokenAllowance } from '../../data/Allowances' import { useTokenAllowance } from '../../data/Allowances'
import { useAddUserToken, useFetchTokenByAddress } from '../../state/user/hooks' import { useV1TradeLinkIfBetter } from '../../data/V1'
import { useAllTokens, useToken } from '../../contexts/Tokens'
import { useHasPendingApproval, useTransactionAdder } from '../../state/transactions/hooks'
import { useTokenContract, useWeb3React } from '../../hooks' import { useTokenContract, useWeb3React } from '../../hooks'
import { useTokenByAddressAndAutomaticallyAdd } from '../../hooks/Tokens'
import { useTradeExactIn, useTradeExactOut } from '../../hooks/Trades' import { useTradeExactIn, useTradeExactOut } from '../../hooks/Trades'
import { useWalletModalToggle } from '../../state/application/hooks' import { useUserAdvanced, useWalletModalToggle } from '../../state/application/hooks'
import { useHasPendingApproval, useTransactionAdder } from '../../state/transactions/hooks'
import { useAllTokenBalancesTreatingWETHasETH } from '../../state/wallet/hooks'
import { Hover, TYPE } from '../../theme' import { Hover, TYPE } from '../../theme'
import { Link } from '../../theme/components' import { Link } from '../../theme/components'
import { import {
basisPointsToPercent,
calculateGasMargin, calculateGasMargin,
getEtherscanLink, getEtherscanLink,
getRouterContract, getRouterContract,
basisPointsToPercent, getSigner,
QueryParams, QueryParams
getSigner
} from '../../utils' } from '../../utils'
import Copy from '../AccountDetails/Copy' import Copy from '../AccountDetails/Copy'
import AddressInputPanel from '../AddressInputPanel' import AddressInputPanel from '../AddressInputPanel'
...@@ -39,6 +36,7 @@ import { ButtonError, ButtonLight, ButtonPrimary, ButtonSecondary } from '../But ...@@ -39,6 +36,7 @@ import { ButtonError, ButtonLight, ButtonPrimary, ButtonSecondary } from '../But
import ConfirmationModal from '../ConfirmationModal' import ConfirmationModal from '../ConfirmationModal'
import CurrencyInputPanel from '../CurrencyInputPanel' import CurrencyInputPanel from '../CurrencyInputPanel'
import QuestionHelper from '../Question' import QuestionHelper from '../Question'
import { AutoRow, RowBetween, RowFixed } from '../Row'
import SlippageTabs from '../SlippageTabs' import SlippageTabs from '../SlippageTabs'
import TokenLogo from '../TokenLogo' import TokenLogo from '../TokenLogo'
import { import {
...@@ -55,7 +53,7 @@ import { ...@@ -55,7 +53,7 @@ import {
TruncatedText, TruncatedText,
Wrapper Wrapper
} from './styleds' } from './styleds'
import { useV1TradeLinkIfBetter } from '../../data/V1' import { Field, SwapAction, useSwapStateReducer } from './swap-store'
enum SwapType { enum SwapType {
EXACT_TOKENS_FOR_TOKENS, EXACT_TOKENS_FOR_TOKENS,
...@@ -108,35 +106,10 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro ...@@ -108,35 +106,10 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
const [tradeError, setTradeError] = useState<string>('') // error for things like reserve size or route const [tradeError, setTradeError] = useState<string>('') // error for things like reserve size or route
const tokens = { const tokens = {
[Field.INPUT]: useToken(fieldData[Field.INPUT].address), [Field.INPUT]: useTokenByAddressAndAutomaticallyAdd(fieldData[Field.INPUT].address),
[Field.OUTPUT]: useToken(fieldData[Field.OUTPUT].address) [Field.OUTPUT]: useTokenByAddressAndAutomaticallyAdd(fieldData[Field.OUTPUT].address)
} }
// ensure input + output tokens are added to localstorage
const fetchTokenByAddress = useFetchTokenByAddress()
const addToken = useAddUserToken()
const allTokens = useAllTokens()
const inputTokenAddress = fieldData[Field.INPUT].address
useEffect(() => {
if (inputTokenAddress && !Object.keys(allTokens).some(tokenAddress => tokenAddress === inputTokenAddress)) {
fetchTokenByAddress(inputTokenAddress).then(token => {
if (token !== null) {
addToken(token)
}
})
}
}, [inputTokenAddress, allTokens, fetchTokenByAddress, addToken])
const outputTokenAddress = fieldData[Field.OUTPUT].address
useEffect(() => {
if (outputTokenAddress && !Object.keys(allTokens).some(tokenAddress => tokenAddress === outputTokenAddress)) {
fetchTokenByAddress(outputTokenAddress).then(token => {
if (token !== null) {
addToken(token)
}
})
}
}, [outputTokenAddress, allTokens, fetchTokenByAddress, addToken])
// token contracts for approvals and direct sends // token contracts for approvals and direct sends
const tokenContractInput: Contract = useTokenContract(tokens[Field.INPUT]?.address) const tokenContractInput: Contract = useTokenContract(tokens[Field.INPUT]?.address)
const tokenContractOutput: Contract = useTokenContract(tokens[Field.OUTPUT]?.address) const tokenContractOutput: Contract = useTokenContract(tokens[Field.OUTPUT]?.address)
...@@ -157,8 +130,8 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro ...@@ -157,8 +130,8 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
// get user- and token-specific lookup data // get user- and token-specific lookup data
const userBalances = { const userBalances = {
[Field.INPUT]: useTokenBalanceTreatingWETHasETH(account, tokens[Field.INPUT]), [Field.INPUT]: allBalances?.[tokens[Field.INPUT]?.address]?.raw,
[Field.OUTPUT]: useTokenBalanceTreatingWETHasETH(account, tokens[Field.OUTPUT]) [Field.OUTPUT]: allBalances?.[tokens[Field.OUTPUT]?.address]?.raw
} }
// parse the amount that the user typed // parse the amount that the user typed
...@@ -773,7 +746,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro ...@@ -773,7 +746,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
if (sending && !sendingWithSwap) { if (sending && !sendingWithSwap) {
return ( return (
<AutoColumn> <AutoColumn>
<ButtonPrimary onClick={onSend}> <ButtonPrimary onClick={onSend} id="exchange-page-confirm-send">
<Text color="white" fontSize={20}> <Text color="white" fontSize={20}>
Confirm send Confirm send
</Text> </Text>
...@@ -868,7 +841,12 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro ...@@ -868,7 +841,12 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
</AutoColumn> </AutoColumn>
<AutoRow> <AutoRow>
<ButtonError onClick={onSwap} error={!!warningHigh} style={{ margin: '10px 0 0 0' }}> <ButtonError
onClick={onSwap}
error={!!warningHigh}
style={{ margin: '10px 0 0 0' }}
id="exchange-page-confirm-swap-or-send"
>
<Text fontSize={20} fontWeight={500}> <Text fontSize={20} fontWeight={500}>
{warningHigh ? (sending ? 'Send Anyway' : 'Swap Anyway') : sending ? 'Confirm Send' : 'Confirm Swap'} {warningHigh ? (sending ? 'Send Anyway' : 'Swap Anyway') : sending ? 'Confirm Send' : 'Confirm Swap'}
</Text> </Text>
...@@ -951,7 +929,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro ...@@ -951,7 +929,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
} }
return ( return (
<Wrapper id="exchangePage"> <Wrapper id="exchange-page">
<ConfirmationModal <ConfirmationModal
isOpen={showConfirm} isOpen={showConfirm}
title={sendingWithSwap ? 'Confirm swap and send' : sending ? 'Confirm Send' : 'Confirm Swap'} title={sendingWithSwap ? 'Confirm swap and send' : sending ? 'Confirm Send' : 'Confirm Swap'}
...@@ -989,7 +967,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro ...@@ -989,7 +967,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
showSendWithSwap={true} showSendWithSwap={true}
advanced={advanced} advanced={advanced}
label={''} label={''}
inputId="swapInputField" id="swap-currency-input"
otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address} otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address}
/> />
</InputGroup> </InputGroup>
...@@ -1034,7 +1012,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro ...@@ -1034,7 +1012,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
}} }}
onTokenSelection={address => onTokenSelection(Field.INPUT, address)} onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address} otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address}
inputId="swapInputField" id="swap-currency-input"
/> />
{sendingWithSwap ? ( {sendingWithSwap ? (
...@@ -1078,7 +1056,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro ...@@ -1078,7 +1056,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)} onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)}
advanced={advanced} advanced={advanced}
otherSelectedTokenAddress={tokens[Field.INPUT]?.address} otherSelectedTokenAddress={tokens[Field.INPUT]?.address}
inputId="swapOutputField" id="swap-currency-output"
/> />
{sendingWithSwap && ( {sendingWithSwap && (
<RowBetween padding="0 1rem 0 12px"> <RowBetween padding="0 1rem 0 12px">
...@@ -1199,6 +1177,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro ...@@ -1199,6 +1177,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
onClick={() => { onClick={() => {
setShowConfirm(true) setShowConfirm(true)
}} }}
id="exchange-swap-button"
disabled={!isValid} disabled={!isValid}
error={!!warningHigh} error={!!warningHigh}
> >
...@@ -1229,7 +1208,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro ...@@ -1229,7 +1208,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<AdvancedDropwdown> <AdvancedDropwdown>
{!showAdvanced && ( {!showAdvanced && (
<Hover> <Hover>
<RowBetween onClick={() => setShowAdvanced(true)} padding={'8px 20px'}> <RowBetween onClick={() => setShowAdvanced(true)} padding={'8px 20px'} id="exchange-show-advanced">
<Text fontSize={16} fontWeight={500} style={{ userSelect: 'none' }}> <Text fontSize={16} fontWeight={500} style={{ userSelect: 'none' }}>
Show Advanced Show Advanced
</Text> </Text>
......
...@@ -142,7 +142,7 @@ function NavigationTabs({ location: { pathname }, history }: RouteComponentProps ...@@ -142,7 +142,7 @@ function NavigationTabs({ location: { pathname }, history }: RouteComponentProps
<Tabs style={{ marginBottom: '20px' }}> <Tabs style={{ marginBottom: '20px' }}>
{tabOrder.map(({ path, textKey, regex }) => ( {tabOrder.map(({ path, textKey, regex }) => (
<StyledNavLink <StyledNavLink
id={`${textKey}-navLink`} id={`${textKey}-nav-link`}
key={path} key={path}
to={path} to={path}
isActive={(_, { pathname }) => !!pathname.match(regex)} isActive={(_, { pathname }) => !!pathname.match(regex)}
......
...@@ -14,7 +14,7 @@ import { LightCard } from '../Card' ...@@ -14,7 +14,7 @@ import { LightCard } from '../Card'
import { AutoColumn, ColumnCenter } from '../Column' import { AutoColumn, ColumnCenter } from '../Column'
import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button' import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button'
import { useToken } from '../../contexts/Tokens' import { useToken } from '../../hooks/Tokens'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { usePairAdder } from '../../state/user/hooks' import { usePairAdder } from '../../state/user/hooks'
import { usePair } from '../../data/Reserves' import { usePair } from '../../data/Reserves'
......
...@@ -31,7 +31,7 @@ import { ...@@ -31,7 +31,7 @@ import {
useRemoveUserAddedToken useRemoveUserAddedToken
} from '../../state/user/hooks' } from '../../state/user/hooks'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useToken, useAllTokens, ALL_TOKENS } from '../../contexts/Tokens' import { useToken, useAllTokens, ALL_TOKENS } from '../../hooks/Tokens'
import QuestionHelper from '../Question' import QuestionHelper from '../Question'
const TokenModalInfo = styled.div` const TokenModalInfo = styled.div`
...@@ -226,13 +226,12 @@ function SearchModal({ ...@@ -226,13 +226,12 @@ function SearchModal({
const tokenList = useMemo(() => { const tokenList = useMemo(() => {
return Object.keys(allTokens) return Object.keys(allTokens)
.sort((a, b): number => { .sort((a, b): number => {
if (a && allTokens[a]?.equals(WETH[chainId])) return -1
if (b && allTokens[b]?.equals(WETH[chainId])) return 1
if (allTokens[a].symbol && allTokens[b].symbol) { if (allTokens[a].symbol && allTokens[b].symbol) {
const aSymbol = allTokens[a].symbol.toLowerCase() const aSymbol = allTokens[a].symbol.toLowerCase()
const bSymbol = allTokens[b].symbol.toLowerCase() const bSymbol = allTokens[b].symbol.toLowerCase()
// pin ETH to top
if (aSymbol === 'ETH'.toLowerCase() || bSymbol === 'ETH'.toLowerCase()) {
return aSymbol === bSymbol ? 0 : aSymbol === 'ETH'.toLowerCase() ? -1 : 1
}
// sort by balance // sort by balance
const balanceA = allBalances?.[account]?.[a] const balanceA = allBalances?.[account]?.[a]
const balanceB = allBalances?.[account]?.[b] const balanceB = allBalances?.[account]?.[b]
...@@ -256,7 +255,7 @@ function SearchModal({ ...@@ -256,7 +255,7 @@ function SearchModal({
balance: allBalances?.[account]?.[k] balance: allBalances?.[account]?.[k]
} }
}) })
}, [allTokens, allBalances, account, sortDirection]) }, [allTokens, allBalances, account, sortDirection, chainId])
const filteredTokenList = useMemo(() => { const filteredTokenList = useMemo(() => {
return tokenList.filter(tokenEntry => { return tokenList.filter(tokenEntry => {
...@@ -423,6 +422,7 @@ function SearchModal({ ...@@ -423,6 +422,7 @@ function SearchModal({
return ( return (
<MenuItem <MenuItem
key={temporaryToken.address} key={temporaryToken.address}
className={`temporary-token-${temporaryToken}`}
onClick={() => { onClick={() => {
addToken(temporaryToken) addToken(temporaryToken)
_onTokenSelect(temporaryToken.address) _onTokenSelect(temporaryToken.address)
...@@ -467,6 +467,7 @@ function SearchModal({ ...@@ -467,6 +467,7 @@ function SearchModal({
return ( return (
<MenuItem <MenuItem
key={address} key={address}
className={`token-item-${address}`}
onClick={() => (hiddenToken && hiddenToken === address ? null : _onTokenSelect(address))} onClick={() => (hiddenToken && hiddenToken === address ? null : _onTokenSelect(address))}
disabled={hiddenToken && hiddenToken === address} disabled={hiddenToken && hiddenToken === address}
selected={otherSelectedTokenAddress === address} selected={otherSelectedTokenAddress === address}
......
...@@ -3,7 +3,7 @@ import React, { useState } from 'react' ...@@ -3,7 +3,7 @@ import React, { useState } from 'react'
import styled, { keyframes } from 'styled-components' import styled, { keyframes } from 'styled-components'
import { useWeb3React } from '../../hooks' import { useWeb3React } from '../../hooks'
import { useToken } from '../../contexts/Tokens' import { useToken } from '../../hooks/Tokens'
import { getEtherscanLink } from '../../utils' import { getEtherscanLink } from '../../utils'
import { Link } from '../../theme' import { Link } from '../../theme'
......
import { ChainId, Token, WETH } from '@uniswap/sdk' import { ChainId, Token, WETH } from '@uniswap/sdk'
import { useMemo } from 'react' import { useEffect, useMemo } from 'react'
import { useWeb3React } from '../hooks' import { useAddUserToken, useFetchTokenByAddress, useUserAddedTokens } from '../state/user/hooks'
import { useUserAddedTokens } from '../state/user/hooks' import { useWeb3React } from './index'
export const ALL_TOKENS = [ export const ALL_TOKENS = [
// WETH on all chains // WETH on all chains
...@@ -66,3 +66,23 @@ export function useToken(tokenAddress: string): Token { ...@@ -66,3 +66,23 @@ export function useToken(tokenAddress: string): Token {
return tokens?.[tokenAddress] return tokens?.[tokenAddress]
} }
// gets token information by address (typically user input) and
// automatically adds it for the user if the token address is valid
export function useTokenByAddressAndAutomaticallyAdd(tokenAddress?: string): Token | undefined {
const fetchTokenByAddress = useFetchTokenByAddress()
const addToken = useAddUserToken()
const allTokens = useAllTokens()
useEffect(() => {
if (tokenAddress && !allTokens?.[tokenAddress]) {
fetchTokenByAddress(tokenAddress).then(token => {
if (token !== null) {
addToken(token)
}
})
}
}, [tokenAddress, allTokens, fetchTokenByAddress, addToken])
return useMemo(() => allTokens?.[tokenAddress], [allTokens, tokenAddress])
}
import React, { useReducer, useState, useCallback, useEffect, useContext } from 'react' import { BigNumber } from '@ethersproject/bignumber'
import ReactGA from 'react-ga'
import styled, { ThemeContext } from 'styled-components'
import { RouteComponentProps, withRouter } from 'react-router-dom'
import { parseUnits, parseEther } from '@ethersproject/units'
import { MaxUint256 } from '@ethersproject/constants' import { MaxUint256 } from '@ethersproject/constants'
import { Contract } from '@ethersproject/contracts' import { Contract } from '@ethersproject/contracts'
import { WETH, TokenAmount, JSBI, Percent, Route, Token, Price } from '@uniswap/sdk' import { parseEther, parseUnits } from '@ethersproject/units'
import { JSBI, Percent, Price, Route, Token, TokenAmount, WETH } from '@uniswap/sdk'
import TokenLogo from '../../components/TokenLogo' import React, { useCallback, useContext, useEffect, useReducer, useState } from 'react'
import DoubleLogo from '../../components/DoubleLogo'
import SearchModal from '../../components/SearchModal'
import PositionCard from '../../components/PositionCard'
import ConfirmationModal from '../../components/ConfirmationModal'
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import { Text } from 'rebass'
import { useTokenBalanceTreatingWETHasETH } from '../../state/wallet/hooks'
import { TYPE } from '../../theme'
import { Plus } from 'react-feather' import { Plus } from 'react-feather'
import ReactGA from 'react-ga'
import { RouteComponentProps, withRouter } from 'react-router-dom'
import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components'
import { ButtonLight, ButtonPrimary } from '../../components/Button'
import { BlueCard, GreyCard, LightCard } from '../../components/Card' import { BlueCard, GreyCard, LightCard } from '../../components/Card'
import { AutoColumn, ColumnCenter } from '../../components/Column' import { AutoColumn, ColumnCenter } from '../../components/Column'
import { ButtonPrimary, ButtonLight } from '../../components/Button' import ConfirmationModal from '../../components/ConfirmationModal'
import Row, { AutoRow, RowBetween, RowFlat, RowFixed } from '../../components/Row' import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import DoubleLogo from '../../components/DoubleLogo'
import PositionCard from '../../components/PositionCard'
import Row, { AutoRow, RowBetween, RowFixed, RowFlat } from '../../components/Row'
import SearchModal from '../../components/SearchModal'
import { useToken } from '../../contexts/Tokens' import TokenLogo from '../../components/TokenLogo'
import { useTokenAllowance } from '../../data/Allowances'
import { useTotalSupply } from '../../data/TotalSupply'
import { useWeb3React, useTokenContract } from '../../hooks'
import { useTransactionAdder, useHasPendingApproval } from '../../state/transactions/hooks'
import { ROUTER_ADDRESS } from '../../constants' import { ROUTER_ADDRESS } from '../../constants'
import { getRouterContract, calculateGasMargin, calculateSlippageAmount } from '../../utils' import { useTokenAllowance } from '../../data/Allowances'
import { BigNumber } from '@ethersproject/bignumber'
import { usePair } from '../../data/Reserves' import { usePair } from '../../data/Reserves'
import { useAddUserToken, useFetchTokenByAddress } from '../../state/user/hooks' import { useTotalSupply } from '../../data/TotalSupply'
import { useAllTokens } from '../../contexts/Tokens' import { useTokenContract, useWeb3React } from '../../hooks'
import { useTokenByAddressAndAutomaticallyAdd } from '../../hooks/Tokens'
import { useHasPendingApproval, useTransactionAdder } from '../../state/transactions/hooks'
import { useTokenBalanceTreatingWETHasETH } from '../../state/wallet/hooks'
import { TYPE } from '../../theme'
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils'
import { Dots, Wrapper } from './styleds'
// denominated in bips // denominated in bips
const ALLOWED_SLIPPAGE = 50 const ALLOWED_SLIPPAGE = 50
...@@ -41,38 +40,12 @@ const ALLOWED_SLIPPAGE = 50 ...@@ -41,38 +40,12 @@ const ALLOWED_SLIPPAGE = 50
// denominated in seconds // denominated in seconds
const DEADLINE_FROM_NOW = 60 * 20 const DEADLINE_FROM_NOW = 60 * 20
const Wrapper = styled.div`
position: relative;
`
const FixedBottom = styled.div` const FixedBottom = styled.div`
position: absolute; position: absolute;
margin-top: 2rem; margin-top: 2rem;
width: 100%; width: 100%;
` `
// styles
const Dots = styled.span`
&::after {
display: inline-block;
animation: ellipsis 1.25s infinite;
content: '.';
width: 1em;
text-align: left;
}
@keyframes ellipsis {
0% {
content: '.';
}
33% {
content: '..';
}
66% {
content: '...';
}
}
`
enum Field { enum Field {
INPUT, INPUT,
OUTPUT OUTPUT
...@@ -161,7 +134,7 @@ function reducer( ...@@ -161,7 +134,7 @@ function reducer(
} }
} }
interface AddLiquidityProps extends RouteComponentProps<{}> { interface AddLiquidityProps extends RouteComponentProps {
token0: string token0: string
token1: string token1: string
} }
...@@ -183,35 +156,9 @@ function AddLiquidity({ token0, token1 }: AddLiquidityProps) { ...@@ -183,35 +156,9 @@ function AddLiquidity({ token0, token1 }: AddLiquidityProps) {
// get basic SDK entities // get basic SDK entities
const tokens: { [field: number]: Token } = { const tokens: { [field: number]: Token } = {
[Field.INPUT]: useToken(fieldData[Field.INPUT].address), [Field.INPUT]: useTokenByAddressAndAutomaticallyAdd(fieldData[Field.INPUT].address),
[Field.OUTPUT]: useToken(fieldData[Field.OUTPUT].address) [Field.OUTPUT]: useTokenByAddressAndAutomaticallyAdd(fieldData[Field.OUTPUT].address)
}
// ensure input + output tokens are added to localstorage
const fetchTokenByAddress = useFetchTokenByAddress()
const addToken = useAddUserToken()
const allTokens = useAllTokens()
const inputTokenAddress = fieldData[Field.INPUT].address
useEffect(() => {
if (inputTokenAddress && !Object.keys(allTokens).some(tokenAddress => tokenAddress === inputTokenAddress)) {
fetchTokenByAddress(inputTokenAddress).then(token => {
if (token !== null) {
addToken(token)
}
})
}
}, [inputTokenAddress, allTokens, fetchTokenByAddress, addToken])
const outputTokenAddress = fieldData[Field.OUTPUT].address
useEffect(() => {
if (outputTokenAddress && !Object.keys(allTokens).some(tokenAddress => tokenAddress === outputTokenAddress)) {
fetchTokenByAddress(outputTokenAddress).then(token => {
if (token !== null) {
addToken(token)
}
})
} }
}, [outputTokenAddress, allTokens, fetchTokenByAddress, addToken])
// token contracts for approvals and direct sends // token contracts for approvals and direct sends
const tokenContractInput: Contract = useTokenContract(tokens[Field.INPUT]?.address) const tokenContractInput: Contract = useTokenContract(tokens[Field.INPUT]?.address)
...@@ -754,7 +701,7 @@ function AddLiquidity({ token0, token1 }: AddLiquidityProps) { ...@@ -754,7 +701,7 @@ function AddLiquidity({ token0, token1 }: AddLiquidityProps) {
onTokenSelection={address => onTokenSelection(Field.INPUT, address)} onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
pair={pair} pair={pair}
label="Input" label="Input"
inputId="addLiquidityInput" id="add-liquidity-input"
/> />
<ColumnCenter> <ColumnCenter>
<Plus size="16" color={theme.text2} /> <Plus size="16" color={theme.text2} />
...@@ -770,7 +717,7 @@ function AddLiquidity({ token0, token1 }: AddLiquidityProps) { ...@@ -770,7 +717,7 @@ function AddLiquidity({ token0, token1 }: AddLiquidityProps) {
token={tokens[Field.OUTPUT]} token={tokens[Field.OUTPUT]}
onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)} onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)}
pair={pair} pair={pair}
inputId="addLiquidityOutput" id="add-liquidity-output"
/> />
{tokens[Field.OUTPUT] && tokens[Field.INPUT] && ( {tokens[Field.OUTPUT] && tokens[Field.INPUT] && (
<> <>
......
import React, { useReducer, useState, useCallback, useEffect, useContext } from 'react' import { splitSignature } from '@ethersproject/bytes'
import ReactGA from 'react-ga'
import styled, { ThemeContext } from 'styled-components'
import { parseUnits } from '@ethersproject/units'
import { Contract } from '@ethersproject/contracts' import { Contract } from '@ethersproject/contracts'
import { TokenAmount, JSBI, Route, WETH, Percent, Token } from '@uniswap/sdk' import { parseUnits } from '@ethersproject/units'
import { JSBI, Percent, Route, Token, TokenAmount, WETH } from '@uniswap/sdk'
import Slider from '../../components/Slider' import React, { useCallback, useContext, useEffect, useReducer, useState } from 'react'
import TokenLogo from '../../components/TokenLogo' import { ArrowDown, Plus } from 'react-feather'
import DoubleLogo from '../../components/DoubleLogo' import ReactGA from 'react-ga'
import PositionCard from '../../components/PositionCard'
import ConfirmationModal from '../../components/ConfirmationModal'
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import { useTokenBalance } from '../../state/wallet/hooks'
import { TYPE } from '../../theme'
import { Text } from 'rebass' import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components'
import { ButtonConfirmed, ButtonPrimary } from '../../components/Button'
import { LightCard } from '../../components/Card' import { LightCard } from '../../components/Card'
import { ButtonPrimary } from '../../components/Button'
import { ButtonConfirmed } from '../../components/Button'
import { ArrowDown, Plus } from 'react-feather'
import { AutoColumn, ColumnCenter } from '../../components/Column' import { AutoColumn, ColumnCenter } from '../../components/Column'
import ConfirmationModal from '../../components/ConfirmationModal'
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import DoubleLogo from '../../components/DoubleLogo'
import PositionCard from '../../components/PositionCard'
import Row, { RowBetween, RowFixed } from '../../components/Row' import Row, { RowBetween, RowFixed } from '../../components/Row'
import { useToken } from '../../contexts/Tokens' import Slider from '../../components/Slider'
import { useWeb3React } from '../../hooks' import TokenLogo from '../../components/TokenLogo'
import { usePairContract } from '../../hooks'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { useTotalSupply } from '../../data/TotalSupply'
import { splitSignature } from '@ethersproject/bytes'
import { ROUTER_ADDRESS } from '../../constants' import { ROUTER_ADDRESS } from '../../constants'
import { getRouterContract, calculateGasMargin, calculateSlippageAmount } from '../../utils'
import { usePair } from '../../data/Reserves' import { usePair } from '../../data/Reserves'
import { useTotalSupply } from '../../data/TotalSupply'
import { usePairContract, useWeb3React } from '../../hooks'
import { useToken } from '../../hooks/Tokens'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { useTokenBalance } from '../../state/wallet/hooks'
import { TYPE } from '../../theme'
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils'
import { ClickableText, FixedBottom, MaxButton, Wrapper } from './styleds'
// denominated in bips // denominated in bips
const ALLOWED_SLIPPAGE = 50 const ALLOWED_SLIPPAGE = 50
...@@ -38,48 +36,6 @@ const ALLOWED_SLIPPAGE = 50 ...@@ -38,48 +36,6 @@ const ALLOWED_SLIPPAGE = 50
// denominated in seconds // denominated in seconds
const DEADLINE_FROM_NOW = 60 * 20 const DEADLINE_FROM_NOW = 60 * 20
const Wrapper = styled.div`
position: relative;
`
const FixedBottom = styled.div`
position: absolute;
top: 100px;
width: 100%;
margin-bottom: 80px;
`
const ClickableText = styled(Text)`
:hover {
cursor: pointer;
}
color: ${({ theme }) => theme.primary1};
`
// const CustomNumericalInput = styled(NumericalInput)`
// font-size: 72px;
// font-weight: 500;
// `
const MaxButton = styled.button<{ width: string }>`
padding: 0.5rem 1rem;
background-color: ${({ theme }) => theme.primary5};
border: 1px solid ${({ theme }) => theme.primary5};
border-radius: 0.5rem;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
margin-right: 0.5rem;
color: ${({ theme }) => theme.primary1};
:hover {
border: 1px solid ${({ theme }) => theme.primary1};
}
:focus {
border: 1px solid ${({ theme }) => theme.primary1};
outline: none;
}
`
enum Field { enum Field {
LIQUIDITY, LIQUIDITY,
TOKEN0, TOKEN0,
...@@ -718,7 +674,7 @@ export default function RemoveLiquidity({ token0, token1 }: { token0: string; to ...@@ -718,7 +674,7 @@ export default function RemoveLiquidity({ token0, token1 }: { token0: string; to
token={pair?.liquidityToken} token={pair?.liquidityToken}
isExchange={true} isExchange={true}
pair={pair} pair={pair}
inputId="liquidityAmount" id="liquidity-amount"
/> />
<ColumnCenter> <ColumnCenter>
<ArrowDown size="16" color={theme.text2} /> <ArrowDown size="16" color={theme.text2} />
...@@ -732,7 +688,7 @@ export default function RemoveLiquidity({ token0, token1 }: { token0: string; to ...@@ -732,7 +688,7 @@ export default function RemoveLiquidity({ token0, token1 }: { token0: string; to
token={tokens[Field.TOKEN0]} token={tokens[Field.TOKEN0]}
label={'Output'} label={'Output'}
disableTokenSelect disableTokenSelect
inputId="removeLiquidityToken0" id="remove-liquidity-token0"
/> />
<ColumnCenter> <ColumnCenter>
<Plus size="16" color={theme.text2} /> <Plus size="16" color={theme.text2} />
...@@ -746,7 +702,7 @@ export default function RemoveLiquidity({ token0, token1 }: { token0: string; to ...@@ -746,7 +702,7 @@ export default function RemoveLiquidity({ token0, token1 }: { token0: string; to
token={tokens[Field.TOKEN1]} token={tokens[Field.TOKEN1]}
label={'Output'} label={'Output'}
disableTokenSelect disableTokenSelect
inputId="removeLiquidityToken1" id="remove-liquidity-token1"
/> />
</> </>
)} )}
......
import { Text } from 'rebass'
import styled from 'styled-components'
export const Wrapper = styled.div`
position: relative;
`
export const FixedBottom = styled.div`
position: absolute;
top: 100px;
width: 100%;
margin-bottom: 80px;
`
export const ClickableText = styled(Text)`
:hover {
cursor: pointer;
}
color: ${({ theme }) => theme.primary1};
`
export const MaxButton = styled.button<{ width: string }>`
padding: 0.5rem 1rem;
background-color: ${({ theme }) => theme.primary5};
border: 1px solid ${({ theme }) => theme.primary5};
border-radius: 0.5rem;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
margin-right: 0.5rem;
color: ${({ theme }) => theme.primary1};
:hover {
border: 1px solid ${({ theme }) => theme.primary1};
}
:focus {
border: 1px solid ${({ theme }) => theme.primary1};
outline: none;
}
`
export const Dots = styled.span`
&::after {
display: inline-block;
animation: ellipsis 1.25s infinite;
content: '.';
width: 1em;
text-align: left;
}
@keyframes ellipsis {
0% {
content: '.';
}
33% {
content: '..';
}
66% {
content: '...';
}
}
`
...@@ -24,7 +24,3 @@ export const finalizeTransaction = createAction<{ ...@@ -24,7 +24,3 @@ export const finalizeTransaction = createAction<{
hash: string hash: string
receipt: SerializableTransactionReceipt receipt: SerializableTransactionReceipt
}>('finalizeTransaction') }>('finalizeTransaction')
export const updateTransactionCount = createAction<{ address: string; transactionCount: number; chainId: number }>(
'updateTransactionCount'
)
import { createReducer } from '@reduxjs/toolkit' import { createReducer } from '@reduxjs/toolkit'
import { isAddress } from '../../utils' import { addTransaction, checkTransaction, finalizeTransaction, SerializableTransactionReceipt } from './actions'
import {
addTransaction,
checkTransaction,
finalizeTransaction,
SerializableTransactionReceipt,
updateTransactionCount
} from './actions'
const now = () => new Date().getTime() const now = () => new Date().getTime()
...@@ -19,7 +12,6 @@ export interface TransactionDetails { ...@@ -19,7 +12,6 @@ export interface TransactionDetails {
addedTime: number addedTime: number
confirmedTime?: number confirmedTime?: number
from: string from: string
nonce?: number // todo: find a way to populate this
// set to true when we receive a transaction count that exceeds the nonce of this transaction // set to true when we receive a transaction count that exceeds the nonce of this transaction
unknownStatus?: boolean unknownStatus?: boolean
...@@ -58,14 +50,4 @@ export default createReducer(initialState, builder => ...@@ -58,14 +50,4 @@ export default createReducer(initialState, builder =>
state[chainId][hash].unknownStatus = false state[chainId][hash].unknownStatus = false
state[chainId][hash].confirmedTime = now() state[chainId][hash].confirmedTime = now()
}) })
// marks every transaction with a nonce less than the transaction count unknown if it was pending
// this can be overridden by a finalize that comes later
.addCase(updateTransactionCount, (state, { payload: { transactionCount, address, chainId } }) => {
// mark any transactions under the transaction count to be unknown status
Object.values(state?.[chainId] ?? {})
.filter(t => !t.receipt)
.filter(t => t.from === isAddress(address))
.filter(t => typeof t.nonce && t.nonce < transactionCount)
.forEach(t => (t.unknownStatus = t.unknownStatus ?? true))
})
) )
...@@ -3,11 +3,10 @@ import { useDispatch, useSelector } from 'react-redux' ...@@ -3,11 +3,10 @@ import { useDispatch, useSelector } from 'react-redux'
import { useWeb3React } from '../../hooks' import { useWeb3React } from '../../hooks'
import { useAddPopup, useBlockNumber } from '../application/hooks' import { useAddPopup, useBlockNumber } from '../application/hooks'
import { AppDispatch, AppState } from '../index' import { AppDispatch, AppState } from '../index'
import { checkTransaction, finalizeTransaction, updateTransactionCount } from './actions' import { checkTransaction, finalizeTransaction } from './actions'
import useSWR from 'swr'
export default function Updater() { export default function Updater() {
const { chainId, account, library } = useWeb3React() const { chainId, library } = useWeb3React()
const lastBlockNumber = useBlockNumber() const lastBlockNumber = useBlockNumber()
...@@ -19,16 +18,6 @@ export default function Updater() { ...@@ -19,16 +18,6 @@ export default function Updater() {
// show popup on confirm // show popup on confirm
const addPopup = useAddPopup() const addPopup = useAddPopup()
const { data: transactionCount } = useSWR<number | null>(['accountNonce', account, lastBlockNumber], () => {
if (!account) return null
return library.getTransactionCount(account, 'latest')
})
useEffect(() => {
if (transactionCount === null) return
dispatch(updateTransactionCount({ address: account, transactionCount, chainId }))
}, [transactionCount, account, chainId, dispatch])
useEffect(() => { useEffect(() => {
if (typeof chainId === 'number' && library) { if (typeof chainId === 'number' && library) {
Object.keys(allTransactions) Object.keys(allTransactions)
......
...@@ -2,7 +2,7 @@ import { ChainId, JSBI, Pair, Token, TokenAmount, WETH } from '@uniswap/sdk' ...@@ -2,7 +2,7 @@ import { ChainId, JSBI, Pair, Token, TokenAmount, WETH } from '@uniswap/sdk'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
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 '../../contexts/Tokens' import { useAllTokens } from '../../hooks/Tokens'
import { getTokenDecimals, getTokenName, getTokenSymbol } from '../../utils' import { getTokenDecimals, getTokenName, getTokenSymbol } from '../../utils'
import { AppDispatch, AppState } from '../index' import { AppDispatch, AppState } from '../index'
import { import {
......
...@@ -2,7 +2,7 @@ import { getAddress } from '@ethersproject/address' ...@@ -2,7 +2,7 @@ import { getAddress } from '@ethersproject/address'
import { JSBI, Token, TokenAmount, WETH } from '@uniswap/sdk' import { JSBI, Token, TokenAmount, WETH } from '@uniswap/sdk'
import { useEffect, useMemo } from 'react' import { useEffect, useMemo } from 'react'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { useAllTokens } from '../../contexts/Tokens' import { useAllTokens } from '../../hooks/Tokens'
import { usePrevious, useWeb3React } from '../../hooks' import { usePrevious, useWeb3React } from '../../hooks'
import { isAddress } from '../../utils' import { isAddress } from '../../utils'
import { AppDispatch, AppState } from '../index' import { AppDispatch, AppState } from '../index'
......
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