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
This commit is contained in:
parent
60582de4d6
commit
b1ffab1890
@ -3,7 +3,7 @@ import { TEST_ADDRESS_NEVER_USE } from '../support/commands'
|
||||
describe('Landing Page', () => {
|
||||
beforeEach(() => cy.visit('/'))
|
||||
it('loads exchange page', () => {
|
||||
cy.get('#exchangePage')
|
||||
cy.get('#exchange-page')
|
||||
})
|
||||
|
||||
it('redirects to url /swap', () => {
|
||||
@ -11,12 +11,12 @@ describe('Landing Page', () => {
|
||||
})
|
||||
|
||||
it('allows navigation to send', () => {
|
||||
cy.get('#send-navLink').click()
|
||||
cy.get('#send-nav-link').click()
|
||||
cy.url().should('include', '/send')
|
||||
})
|
||||
|
||||
it('allows navigation to pool', () => {
|
||||
cy.get('#pool-navLink').click()
|
||||
cy.get('#pool-nav-link').click()
|
||||
cy.url().should('include', '/pool')
|
||||
})
|
||||
|
||||
|
@ -1,18 +1,28 @@
|
||||
describe('Swap', () => {
|
||||
beforeEach(() => cy.visit('/swap'))
|
||||
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', () => {
|
||||
cy.get('#swapInputField').type('0.0')
|
||||
cy.get('#swap-currency-input .token-amount-input').type('0.0')
|
||||
})
|
||||
|
||||
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', () => {
|
||||
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'
|
||||
import { AutoColumn, ColumnCenter } from '../Column'
|
||||
import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button'
|
||||
|
||||
import { useToken } from '../../contexts/Tokens'
|
||||
import { useToken } from '../../hooks/Tokens'
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { usePair } from '../../data/Reserves'
|
||||
|
||||
@ -79,7 +79,7 @@ function CreatePool({ history }: RouteComponentProps<{}>) {
|
||||
{token0?.symbol}{' '}
|
||||
</Text>
|
||||
<TYPE.darkGray fontWeight={500} fontSize={16} marginLeft={'8px'}>
|
||||
{token0?.symbol === 'ETH' && '(default)'}
|
||||
{token0?.address === 'ETH' && '(default)'}
|
||||
</TYPE.darkGray>
|
||||
</Row>
|
||||
</ButtonDropwdownLight>
|
||||
|
@ -136,7 +136,7 @@ interface CurrencyInputPanelProps {
|
||||
showSendWithSwap?: boolean
|
||||
otherSelectedTokenAddress?: string | null
|
||||
advanced?: boolean
|
||||
inputId: string
|
||||
id: string
|
||||
}
|
||||
|
||||
export default function CurrencyInputPanel({
|
||||
@ -157,7 +157,7 @@ export default function CurrencyInputPanel({
|
||||
showSendWithSwap = false,
|
||||
otherSelectedTokenAddress = null,
|
||||
advanced = false,
|
||||
inputId
|
||||
id
|
||||
}: CurrencyInputPanelProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@ -167,7 +167,7 @@ export default function CurrencyInputPanel({
|
||||
const theme = useContext(ThemeContext)
|
||||
|
||||
return (
|
||||
<InputPanel>
|
||||
<InputPanel id={id}>
|
||||
<Container hideInput={hideInput}>
|
||||
{!hideInput && (
|
||||
<LabelRow>
|
||||
@ -197,8 +197,8 @@ export default function CurrencyInputPanel({
|
||||
{!hideInput && (
|
||||
<>
|
||||
<NumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
id={inputId}
|
||||
onUserInput={val => {
|
||||
onUserInput(field, val)
|
||||
}}
|
||||
@ -210,6 +210,7 @@ export default function CurrencyInputPanel({
|
||||
)}
|
||||
<CurrencySelect
|
||||
selected={!!token}
|
||||
className="open-currency-select-button"
|
||||
onClick={() => {
|
||||
if (!disableTokenSelect) {
|
||||
setModalOpen(true)
|
||||
@ -223,7 +224,7 @@ export default function CurrencyInputPanel({
|
||||
<TokenLogo address={token?.address} size={'24px'} />
|
||||
) : null}
|
||||
{isExchange ? (
|
||||
<StyledTokenName>
|
||||
<StyledTokenName className="token-name-container">
|
||||
{pair?.token0.symbol}:{pair?.token1.symbol}
|
||||
</StyledTokenName>
|
||||
) : (
|
||||
@ -248,7 +249,7 @@ export default function CurrencyInputPanel({
|
||||
showSendWithSwap={showSendWithSwap}
|
||||
hiddenToken={token?.address}
|
||||
otherSelectedTokenAddress={otherSelectedTokenAddress}
|
||||
otherSelectedText={field === 0 ? ' Selected as output' : 'Selected as input'}
|
||||
otherSelectedText={field === 0 ? 'Selected as output' : 'Selected as input'}
|
||||
/>
|
||||
)}
|
||||
</InputPanel>
|
||||
|
@ -1,37 +1,34 @@
|
||||
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 { MaxUint256 } from '@ethersproject/constants'
|
||||
import { Contract } from '@ethersproject/contracts'
|
||||
import { useUserAdvanced } from '../../state/application/hooks'
|
||||
import { useTokenBalanceTreatingWETHasETH, useAllTokenBalancesTreatingWETHasETH } from '../../state/wallet/hooks'
|
||||
import { Field, SwapAction, useSwapStateReducer } from './swap-store'
|
||||
import { parseEther, parseUnits } from '@ethersproject/units'
|
||||
import { Fraction, JSBI, Percent, TokenAmount, TradeType, WETH } from '@uniswap/sdk'
|
||||
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 { ThemeContext } from 'styled-components'
|
||||
import Card, { BlueCard, GreyCard, YellowCard } from '../../components/Card'
|
||||
import { AutoColumn, ColumnCenter } from '../../components/Column'
|
||||
import { AutoRow, RowBetween, RowFixed } from '../Row'
|
||||
import { ROUTER_ADDRESS } from '../../constants'
|
||||
import { useTokenAllowance } from '../../data/Allowances'
|
||||
import { useAddUserToken, useFetchTokenByAddress } from '../../state/user/hooks'
|
||||
import { useAllTokens, useToken } from '../../contexts/Tokens'
|
||||
import { useHasPendingApproval, useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { useV1TradeLinkIfBetter } from '../../data/V1'
|
||||
import { useTokenContract, useWeb3React } from '../../hooks'
|
||||
import { useTokenByAddressAndAutomaticallyAdd } from '../../hooks/Tokens'
|
||||
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 { Link } from '../../theme/components'
|
||||
import {
|
||||
basisPointsToPercent,
|
||||
calculateGasMargin,
|
||||
getEtherscanLink,
|
||||
getRouterContract,
|
||||
basisPointsToPercent,
|
||||
QueryParams,
|
||||
getSigner
|
||||
getSigner,
|
||||
QueryParams
|
||||
} from '../../utils'
|
||||
import Copy from '../AccountDetails/Copy'
|
||||
import AddressInputPanel from '../AddressInputPanel'
|
||||
@ -39,6 +36,7 @@ import { ButtonError, ButtonLight, ButtonPrimary, ButtonSecondary } from '../But
|
||||
import ConfirmationModal from '../ConfirmationModal'
|
||||
import CurrencyInputPanel from '../CurrencyInputPanel'
|
||||
import QuestionHelper from '../Question'
|
||||
import { AutoRow, RowBetween, RowFixed } from '../Row'
|
||||
import SlippageTabs from '../SlippageTabs'
|
||||
import TokenLogo from '../TokenLogo'
|
||||
import {
|
||||
@ -55,7 +53,7 @@ import {
|
||||
TruncatedText,
|
||||
Wrapper
|
||||
} from './styleds'
|
||||
import { useV1TradeLinkIfBetter } from '../../data/V1'
|
||||
import { Field, SwapAction, useSwapStateReducer } from './swap-store'
|
||||
|
||||
enum SwapType {
|
||||
EXACT_TOKENS_FOR_TOKENS,
|
||||
@ -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 tokens = {
|
||||
[Field.INPUT]: useToken(fieldData[Field.INPUT].address),
|
||||
[Field.OUTPUT]: useToken(fieldData[Field.OUTPUT].address)
|
||||
[Field.INPUT]: useTokenByAddressAndAutomaticallyAdd(fieldData[Field.INPUT].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
|
||||
const tokenContractInput: Contract = useTokenContract(tokens[Field.INPUT]?.address)
|
||||
const tokenContractOutput: Contract = useTokenContract(tokens[Field.OUTPUT]?.address)
|
||||
@ -157,8 +130,8 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
|
||||
|
||||
// get user- and token-specific lookup data
|
||||
const userBalances = {
|
||||
[Field.INPUT]: useTokenBalanceTreatingWETHasETH(account, tokens[Field.INPUT]),
|
||||
[Field.OUTPUT]: useTokenBalanceTreatingWETHasETH(account, tokens[Field.OUTPUT])
|
||||
[Field.INPUT]: allBalances?.[tokens[Field.INPUT]?.address]?.raw,
|
||||
[Field.OUTPUT]: allBalances?.[tokens[Field.OUTPUT]?.address]?.raw
|
||||
}
|
||||
|
||||
// parse the amount that the user typed
|
||||
@ -773,7 +746,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
|
||||
if (sending && !sendingWithSwap) {
|
||||
return (
|
||||
<AutoColumn>
|
||||
<ButtonPrimary onClick={onSend}>
|
||||
<ButtonPrimary onClick={onSend} id="exchange-page-confirm-send">
|
||||
<Text color="white" fontSize={20}>
|
||||
Confirm send
|
||||
</Text>
|
||||
@ -868,7 +841,12 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
|
||||
</AutoColumn>
|
||||
|
||||
<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}>
|
||||
{warningHigh ? (sending ? 'Send Anyway' : 'Swap Anyway') : sending ? 'Confirm Send' : 'Confirm Swap'}
|
||||
</Text>
|
||||
@ -951,7 +929,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
|
||||
}
|
||||
|
||||
return (
|
||||
<Wrapper id="exchangePage">
|
||||
<Wrapper id="exchange-page">
|
||||
<ConfirmationModal
|
||||
isOpen={showConfirm}
|
||||
title={sendingWithSwap ? 'Confirm swap and send' : sending ? 'Confirm Send' : 'Confirm Swap'}
|
||||
@ -989,7 +967,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
|
||||
showSendWithSwap={true}
|
||||
advanced={advanced}
|
||||
label={''}
|
||||
inputId="swapInputField"
|
||||
id="swap-currency-input"
|
||||
otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address}
|
||||
/>
|
||||
</InputGroup>
|
||||
@ -1034,7 +1012,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
|
||||
}}
|
||||
onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
|
||||
otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address}
|
||||
inputId="swapInputField"
|
||||
id="swap-currency-input"
|
||||
/>
|
||||
|
||||
{sendingWithSwap ? (
|
||||
@ -1078,7 +1056,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
|
||||
onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)}
|
||||
advanced={advanced}
|
||||
otherSelectedTokenAddress={tokens[Field.INPUT]?.address}
|
||||
inputId="swapOutputField"
|
||||
id="swap-currency-output"
|
||||
/>
|
||||
{sendingWithSwap && (
|
||||
<RowBetween padding="0 1rem 0 12px">
|
||||
@ -1199,6 +1177,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
|
||||
onClick={() => {
|
||||
setShowConfirm(true)
|
||||
}}
|
||||
id="exchange-swap-button"
|
||||
disabled={!isValid}
|
||||
error={!!warningHigh}
|
||||
>
|
||||
@ -1229,7 +1208,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
|
||||
<AdvancedDropwdown>
|
||||
{!showAdvanced && (
|
||||
<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' }}>
|
||||
Show Advanced
|
||||
</Text>
|
||||
|
@ -142,7 +142,7 @@ function NavigationTabs({ location: { pathname }, history }: RouteComponentProps
|
||||
<Tabs style={{ marginBottom: '20px' }}>
|
||||
{tabOrder.map(({ path, textKey, regex }) => (
|
||||
<StyledNavLink
|
||||
id={`${textKey}-navLink`}
|
||||
id={`${textKey}-nav-link`}
|
||||
key={path}
|
||||
to={path}
|
||||
isActive={(_, { pathname }) => !!pathname.match(regex)}
|
||||
|
@ -14,7 +14,7 @@ import { LightCard } from '../Card'
|
||||
import { AutoColumn, ColumnCenter } from '../Column'
|
||||
import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button'
|
||||
|
||||
import { useToken } from '../../contexts/Tokens'
|
||||
import { useToken } from '../../hooks/Tokens'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { usePairAdder } from '../../state/user/hooks'
|
||||
import { usePair } from '../../data/Reserves'
|
||||
|
@ -31,7 +31,7 @@ import {
|
||||
useRemoveUserAddedToken
|
||||
} from '../../state/user/hooks'
|
||||
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'
|
||||
|
||||
const TokenModalInfo = styled.div`
|
||||
@ -226,13 +226,12 @@ function SearchModal({
|
||||
const tokenList = useMemo(() => {
|
||||
return Object.keys(allTokens)
|
||||
.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) {
|
||||
const aSymbol = allTokens[a].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
|
||||
const balanceA = allBalances?.[account]?.[a]
|
||||
const balanceB = allBalances?.[account]?.[b]
|
||||
@ -256,7 +255,7 @@ function SearchModal({
|
||||
balance: allBalances?.[account]?.[k]
|
||||
}
|
||||
})
|
||||
}, [allTokens, allBalances, account, sortDirection])
|
||||
}, [allTokens, allBalances, account, sortDirection, chainId])
|
||||
|
||||
const filteredTokenList = useMemo(() => {
|
||||
return tokenList.filter(tokenEntry => {
|
||||
@ -423,6 +422,7 @@ function SearchModal({
|
||||
return (
|
||||
<MenuItem
|
||||
key={temporaryToken.address}
|
||||
className={`temporary-token-${temporaryToken}`}
|
||||
onClick={() => {
|
||||
addToken(temporaryToken)
|
||||
_onTokenSelect(temporaryToken.address)
|
||||
@ -467,6 +467,7 @@ function SearchModal({
|
||||
return (
|
||||
<MenuItem
|
||||
key={address}
|
||||
className={`token-item-${address}`}
|
||||
onClick={() => (hiddenToken && hiddenToken === address ? null : _onTokenSelect(address))}
|
||||
disabled={hiddenToken && hiddenToken === address}
|
||||
selected={otherSelectedTokenAddress === address}
|
||||
|
@ -3,7 +3,7 @@ import React, { useState } from 'react'
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { useToken } from '../../contexts/Tokens'
|
||||
import { useToken } from '../../hooks/Tokens'
|
||||
import { getEtherscanLink } from '../../utils'
|
||||
|
||||
import { Link } from '../../theme'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ChainId, Token, WETH } from '@uniswap/sdk'
|
||||
import { useMemo } from 'react'
|
||||
import { useWeb3React } from '../hooks'
|
||||
import { useUserAddedTokens } from '../state/user/hooks'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { useAddUserToken, useFetchTokenByAddress, useUserAddedTokens } from '../state/user/hooks'
|
||||
import { useWeb3React } from './index'
|
||||
|
||||
export const ALL_TOKENS = [
|
||||
// WETH on all chains
|
||||
@ -66,3 +66,23 @@ export function useToken(tokenAddress: string): Token {
|
||||
|
||||
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])
|
||||
}
|
@ -1,39 +1,38 @@
|
||||
import React, { useReducer, useState, useCallback, useEffect, useContext } from 'react'
|
||||
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 { BigNumber } from '@ethersproject/bignumber'
|
||||
import { MaxUint256 } from '@ethersproject/constants'
|
||||
import { Contract } from '@ethersproject/contracts'
|
||||
import { WETH, TokenAmount, JSBI, Percent, Route, Token, Price } from '@uniswap/sdk'
|
||||
|
||||
import TokenLogo from '../../components/TokenLogo'
|
||||
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 { parseEther, parseUnits } from '@ethersproject/units'
|
||||
import { JSBI, Percent, Price, Route, Token, TokenAmount, WETH } from '@uniswap/sdk'
|
||||
import React, { useCallback, useContext, useEffect, useReducer, useState } from 'react'
|
||||
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 { AutoColumn, ColumnCenter } from '../../components/Column'
|
||||
import { ButtonPrimary, ButtonLight } from '../../components/Button'
|
||||
import Row, { AutoRow, RowBetween, RowFlat, RowFixed } from '../../components/Row'
|
||||
import ConfirmationModal from '../../components/ConfirmationModal'
|
||||
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 { useTokenAllowance } from '../../data/Allowances'
|
||||
import { useTotalSupply } from '../../data/TotalSupply'
|
||||
import { useWeb3React, useTokenContract } from '../../hooks'
|
||||
import { useTransactionAdder, useHasPendingApproval } from '../../state/transactions/hooks'
|
||||
import TokenLogo from '../../components/TokenLogo'
|
||||
|
||||
import { ROUTER_ADDRESS } from '../../constants'
|
||||
import { getRouterContract, calculateGasMargin, calculateSlippageAmount } from '../../utils'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { useTokenAllowance } from '../../data/Allowances'
|
||||
import { usePair } from '../../data/Reserves'
|
||||
import { useAddUserToken, useFetchTokenByAddress } from '../../state/user/hooks'
|
||||
import { useAllTokens } from '../../contexts/Tokens'
|
||||
import { useTotalSupply } from '../../data/TotalSupply'
|
||||
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
|
||||
const ALLOWED_SLIPPAGE = 50
|
||||
@ -41,38 +40,12 @@ const ALLOWED_SLIPPAGE = 50
|
||||
// denominated in seconds
|
||||
const DEADLINE_FROM_NOW = 60 * 20
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
`
|
||||
|
||||
const FixedBottom = styled.div`
|
||||
position: absolute;
|
||||
margin-top: 2rem;
|
||||
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 {
|
||||
INPUT,
|
||||
OUTPUT
|
||||
@ -161,7 +134,7 @@ function reducer(
|
||||
}
|
||||
}
|
||||
|
||||
interface AddLiquidityProps extends RouteComponentProps<{}> {
|
||||
interface AddLiquidityProps extends RouteComponentProps {
|
||||
token0: string
|
||||
token1: string
|
||||
}
|
||||
@ -183,36 +156,10 @@ function AddLiquidity({ token0, token1 }: AddLiquidityProps) {
|
||||
|
||||
// get basic SDK entities
|
||||
const tokens: { [field: number]: Token } = {
|
||||
[Field.INPUT]: useToken(fieldData[Field.INPUT].address),
|
||||
[Field.OUTPUT]: useToken(fieldData[Field.OUTPUT].address)
|
||||
[Field.INPUT]: useTokenByAddressAndAutomaticallyAdd(fieldData[Field.INPUT].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
|
||||
const tokenContractInput: Contract = useTokenContract(tokens[Field.INPUT]?.address)
|
||||
const tokenContractOutput: Contract = useTokenContract(tokens[Field.OUTPUT]?.address)
|
||||
@ -754,7 +701,7 @@ function AddLiquidity({ token0, token1 }: AddLiquidityProps) {
|
||||
onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
|
||||
pair={pair}
|
||||
label="Input"
|
||||
inputId="addLiquidityInput"
|
||||
id="add-liquidity-input"
|
||||
/>
|
||||
<ColumnCenter>
|
||||
<Plus size="16" color={theme.text2} />
|
||||
@ -770,7 +717,7 @@ function AddLiquidity({ token0, token1 }: AddLiquidityProps) {
|
||||
token={tokens[Field.OUTPUT]}
|
||||
onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)}
|
||||
pair={pair}
|
||||
inputId="addLiquidityOutput"
|
||||
id="add-liquidity-output"
|
||||
/>
|
||||
{tokens[Field.OUTPUT] && tokens[Field.INPUT] && (
|
||||
<>
|
||||
|
@ -1,36 +1,34 @@
|
||||
import React, { useReducer, useState, useCallback, useEffect, useContext } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import styled, { ThemeContext } from 'styled-components'
|
||||
import { parseUnits } from '@ethersproject/units'
|
||||
import { splitSignature } from '@ethersproject/bytes'
|
||||
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 React, { useCallback, useContext, useEffect, useReducer, useState } from 'react'
|
||||
import { ArrowDown, Plus } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import { Text } from 'rebass'
|
||||
import styled, { ThemeContext } from 'styled-components'
|
||||
import { ButtonConfirmed, ButtonPrimary } from '../../components/Button'
|
||||
import { LightCard } from '../../components/Card'
|
||||
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 Slider from '../../components/Slider'
|
||||
import TokenLogo from '../../components/TokenLogo'
|
||||
import DoubleLogo from '../../components/DoubleLogo'
|
||||
import PositionCard from '../../components/PositionCard'
|
||||
import ConfirmationModal from '../../components/ConfirmationModal'
|
||||
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||
import { ROUTER_ADDRESS } from '../../constants'
|
||||
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 { Text } from 'rebass'
|
||||
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 Row, { RowBetween, RowFixed } from '../../components/Row'
|
||||
|
||||
import { useToken } from '../../contexts/Tokens'
|
||||
import { useWeb3React } from '../../hooks'
|
||||
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 { getRouterContract, calculateGasMargin, calculateSlippageAmount } from '../../utils'
|
||||
import { usePair } from '../../data/Reserves'
|
||||
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils'
|
||||
import { ClickableText, FixedBottom, MaxButton, Wrapper } from './styleds'
|
||||
|
||||
// denominated in bips
|
||||
const ALLOWED_SLIPPAGE = 50
|
||||
@ -38,48 +36,6 @@ const ALLOWED_SLIPPAGE = 50
|
||||
// denominated in seconds
|
||||
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 {
|
||||
LIQUIDITY,
|
||||
TOKEN0,
|
||||
@ -718,7 +674,7 @@ export default function RemoveLiquidity({ token0, token1 }: { token0: string; to
|
||||
token={pair?.liquidityToken}
|
||||
isExchange={true}
|
||||
pair={pair}
|
||||
inputId="liquidityAmount"
|
||||
id="liquidity-amount"
|
||||
/>
|
||||
<ColumnCenter>
|
||||
<ArrowDown size="16" color={theme.text2} />
|
||||
@ -732,7 +688,7 @@ export default function RemoveLiquidity({ token0, token1 }: { token0: string; to
|
||||
token={tokens[Field.TOKEN0]}
|
||||
label={'Output'}
|
||||
disableTokenSelect
|
||||
inputId="removeLiquidityToken0"
|
||||
id="remove-liquidity-token0"
|
||||
/>
|
||||
<ColumnCenter>
|
||||
<Plus size="16" color={theme.text2} />
|
||||
@ -746,7 +702,7 @@ export default function RemoveLiquidity({ token0, token1 }: { token0: string; to
|
||||
token={tokens[Field.TOKEN1]}
|
||||
label={'Output'}
|
||||
disableTokenSelect
|
||||
inputId="removeLiquidityToken1"
|
||||
id="remove-liquidity-token1"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
57
src/pages/Pool/styleds.tsx
Normal file
57
src/pages/Pool/styleds.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
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<{
|
||||
hash: string
|
||||
receipt: SerializableTransactionReceipt
|
||||
}>('finalizeTransaction')
|
||||
|
||||
export const updateTransactionCount = createAction<{ address: string; transactionCount: number; chainId: number }>(
|
||||
'updateTransactionCount'
|
||||
)
|
||||
|
@ -1,12 +1,5 @@
|
||||
import { createReducer } from '@reduxjs/toolkit'
|
||||
import { isAddress } from '../../utils'
|
||||
import {
|
||||
addTransaction,
|
||||
checkTransaction,
|
||||
finalizeTransaction,
|
||||
SerializableTransactionReceipt,
|
||||
updateTransactionCount
|
||||
} from './actions'
|
||||
import { addTransaction, checkTransaction, finalizeTransaction, SerializableTransactionReceipt } from './actions'
|
||||
|
||||
const now = () => new Date().getTime()
|
||||
|
||||
@ -19,7 +12,6 @@ export interface TransactionDetails {
|
||||
addedTime: number
|
||||
confirmedTime?: number
|
||||
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
|
||||
unknownStatus?: boolean
|
||||
@ -58,14 +50,4 @@ export default createReducer(initialState, builder =>
|
||||
state[chainId][hash].unknownStatus = false
|
||||
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'
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { useAddPopup, useBlockNumber } from '../application/hooks'
|
||||
import { AppDispatch, AppState } from '../index'
|
||||
import { checkTransaction, finalizeTransaction, updateTransactionCount } from './actions'
|
||||
import useSWR from 'swr'
|
||||
import { checkTransaction, finalizeTransaction } from './actions'
|
||||
|
||||
export default function Updater() {
|
||||
const { chainId, account, library } = useWeb3React()
|
||||
const { chainId, library } = useWeb3React()
|
||||
|
||||
const lastBlockNumber = useBlockNumber()
|
||||
|
||||
@ -19,16 +18,6 @@ export default function Updater() {
|
||||
// show popup on confirm
|
||||
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(() => {
|
||||
if (typeof chainId === 'number' && library) {
|
||||
Object.keys(allTransactions)
|
||||
|
@ -2,7 +2,7 @@ import { ChainId, JSBI, Pair, Token, TokenAmount, WETH } from '@uniswap/sdk'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
|
||||
import { useAllTokens } from '../../contexts/Tokens'
|
||||
import { useAllTokens } from '../../hooks/Tokens'
|
||||
import { getTokenDecimals, getTokenName, getTokenSymbol } from '../../utils'
|
||||
import { AppDispatch, AppState } from '../index'
|
||||
import {
|
||||
|
@ -2,7 +2,7 @@ import { getAddress } from '@ethersproject/address'
|
||||
import { JSBI, Token, TokenAmount, WETH } from '@uniswap/sdk'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useAllTokens } from '../../contexts/Tokens'
|
||||
import { useAllTokens } from '../../hooks/Tokens'
|
||||
import { usePrevious, useWeb3React } from '../../hooks'
|
||||
import { isAddress } from '../../utils'
|
||||
import { AppDispatch, AppState } from '../index'
|
||||
|
Loading…
Reference in New Issue
Block a user