add basic structure for advanced mode (#703)
* add basic structure for advanced mode * auto dismiss popup * Remove test code * remove shadow * Update advanced section, ui tweaks, balances back inline * fix memory leak Co-authored-by: Callil Capuozzo <callil.capuozzo@gmail.com>
This commit is contained in:
parent
60d7bc4532
commit
3f1d7ab310
@ -34,7 +34,7 @@
|
||||
"react": "^16.8.6",
|
||||
"react-device-detect": "^1.6.2",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-feather": "^1.1.6",
|
||||
"react-feather": "^2.0.8",
|
||||
"react-ga": "^2.5.7",
|
||||
"react-i18next": "^10.7.0",
|
||||
"react-router-dom": "^5.0.0",
|
||||
|
95
src/components/BalanceCard/index.tsx
Normal file
95
src/components/BalanceCard/index.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import Copy from '../AccountDetails/Copy'
|
||||
import TokenLogo from '../TokenLogo'
|
||||
import { Link } from '../../theme/components'
|
||||
import { TYPE } from '../../theme'
|
||||
import { Hover } from '../../theme'
|
||||
import { GreyCard } from '../Card'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import { ChevronDown, ChevronUp } from 'react-feather'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { getEtherscanLink } from '../../utils'
|
||||
|
||||
export default function BalanceCard({ token0, balance0, import0, token1, balance1, import1 }) {
|
||||
const [details0, setDetails0] = useState(false)
|
||||
const [details1, setDetails1] = useState(false)
|
||||
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
return (
|
||||
<AutoColumn gap="lg">
|
||||
<GreyCard>
|
||||
<AutoColumn gap="md">
|
||||
<TYPE.black>Selected Tokens</TYPE.black>
|
||||
{token0 && balance0 && (
|
||||
<Hover onClick={() => setDetails0(!details0)}>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TokenLogo address={token0?.address || ''} />
|
||||
<TYPE.black marginLeft="10px">
|
||||
{token0?.name} ({token0?.symbol})
|
||||
</TYPE.black>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<TYPE.black>{balance0?.toSignificant(6)}</TYPE.black>
|
||||
{details0 ? (
|
||||
<ChevronUp size="20" style={{ marginLeft: '10px' }} color="black" />
|
||||
) : (
|
||||
<ChevronDown size="20" style={{ marginLeft: '10px' }} color="black" />
|
||||
)}
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
{import0 && <TYPE.yellow style={{ paddingLeft: '32px' }}>Token imported by user</TYPE.yellow>}
|
||||
</Hover>
|
||||
)}
|
||||
{details0 && (
|
||||
<AutoColumn gap="sm" style={{ marginTop: '2px', marginBottom: '6px' }}>
|
||||
<RowFixed>
|
||||
<TYPE.blue style={{ paddingLeft: '32px' }}>Copy token address</TYPE.blue>
|
||||
<Copy toCopy={token0?.address} />
|
||||
</RowFixed>
|
||||
<Link href={getEtherscanLink(chainId, token0?.address, 'address')} style={{ paddingLeft: '32px' }}>
|
||||
View on etherscan
|
||||
</Link>
|
||||
</AutoColumn>
|
||||
)}
|
||||
{token1 && balance1 && (
|
||||
<Hover onClick={() => setDetails1(!details1)}>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TokenLogo address={token1?.address || ''} />
|
||||
<TYPE.black marginLeft="10px">
|
||||
{token1?.name} ({token1?.symbol})
|
||||
</TYPE.black>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<TYPE.black>{balance1?.toSignificant(6)}</TYPE.black>
|
||||
{details1 ? (
|
||||
<ChevronUp size="20" style={{ marginLeft: '10px' }} color="black" />
|
||||
) : (
|
||||
<ChevronDown size="20" style={{ marginLeft: '10px' }} color="black" />
|
||||
)}
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
{import0 && <TYPE.yellow style={{ paddingLeft: '32px' }}>Token imported by user</TYPE.yellow>}
|
||||
</Hover>
|
||||
)}
|
||||
{details1 && (
|
||||
<AutoColumn gap="sm" style={{ marginTop: '2px', marginBottom: '6px' }}>
|
||||
<RowFixed>
|
||||
<TYPE.blue style={{ paddingLeft: '32px' }}>Copy token address</TYPE.blue>
|
||||
<Copy toCopy={token1?.address} />
|
||||
</RowFixed>
|
||||
<Link href={getEtherscanLink(chainId, token1?.address, 'address')} style={{ paddingLeft: '32px' }}>
|
||||
View on etherscan
|
||||
</Link>
|
||||
</AutoColumn>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</GreyCard>
|
||||
</AutoColumn>
|
||||
)
|
||||
}
|
@ -20,6 +20,10 @@ const Base = styled(RebassButton)`
|
||||
&:disabled {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
> * {
|
||||
user-select: none;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonPrimary = styled(Base)`
|
||||
|
@ -22,7 +22,7 @@ export const GreyCard = styled(Card)`
|
||||
`
|
||||
|
||||
export const YellowCard = styled(Card)`
|
||||
background-color: rgba(243, 190, 30, 0.3);
|
||||
background-color: rgba(243, 132, 30, 0.05);
|
||||
color: ${({ theme }) => theme.yellow2};
|
||||
font-weight: 500;
|
||||
`
|
||||
|
@ -6,10 +6,14 @@ import { darken } from 'polished'
|
||||
import TokenLogo from '../TokenLogo'
|
||||
import DoubleLogo from '../DoubleLogo'
|
||||
import SearchModal from '../SearchModal'
|
||||
import { RowBetween } from '../Row'
|
||||
import { TYPE, Hover } from '../../theme'
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useAddressBalance } from '../../contexts/Balances'
|
||||
|
||||
const InputRow = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
@ -39,6 +43,27 @@ const CurrencySelect = styled.button`
|
||||
}
|
||||
`
|
||||
|
||||
const LabelRow = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
padding: 0 1.25rem 1rem 1rem;
|
||||
span:hover {
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => darken(0.2, theme.text2)};
|
||||
}
|
||||
`
|
||||
|
||||
const ErrorSpan = styled.span`
|
||||
color: ${({ error, theme }) => error && theme.red1};
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
color: ${({ error, theme }) => error && darken(0.1, theme.red1)};
|
||||
}
|
||||
`
|
||||
|
||||
const Aligner = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -65,9 +90,7 @@ const InputPanel = styled.div`
|
||||
|
||||
const Container = styled.div`
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')};
|
||||
/* border: 1px solid ${({ error, theme }) => (error ? theme.red1 : theme.bg2)}; */
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
`
|
||||
|
||||
@ -118,6 +141,8 @@ export default function CurrencyInputPanel({
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const { account } = useWeb3React()
|
||||
const userTokenBalance = useAddressBalance(account, token)
|
||||
|
||||
return (
|
||||
<InputPanel>
|
||||
@ -164,6 +189,16 @@ export default function CurrencyInputPanel({
|
||||
</Aligner>
|
||||
</CurrencySelect>
|
||||
</InputRow>
|
||||
{!hideBalance && !!token && (
|
||||
<LabelRow>
|
||||
<RowBetween>
|
||||
<ErrorSpan data-tip={'Enter max'} error={!!error} onClick={() => {}}></ErrorSpan>
|
||||
<Hover onClick={onMax}>
|
||||
<TYPE.body fontWeight={500}>Balance: {userTokenBalance?.toSignificant(6)}</TYPE.body>
|
||||
</Hover>
|
||||
</RowBetween>
|
||||
</LabelRow>
|
||||
)}
|
||||
</Container>
|
||||
{!disableTokenSelect && (
|
||||
<SearchModal
|
||||
|
@ -5,24 +5,24 @@ import { withRouter } from 'react-router-dom'
|
||||
import { parseUnits, parseEther } from '@ethersproject/units'
|
||||
import { WETH, TradeType, Pair, Trade, TokenAmount, JSBI, Percent } from '@uniswap/sdk'
|
||||
|
||||
import Copy from '../AccountDetails/Copy'
|
||||
import TokenLogo from '../TokenLogo'
|
||||
import SlippageTabs from '../SlippageTabs'
|
||||
import QuestionHelper from '../Question'
|
||||
import NumericalInput from '../NumericalInput'
|
||||
import AdvancedSettings from '../AdvancedSettings'
|
||||
import AddressInputPanel from '../AddressInputPanel'
|
||||
import ConfirmationModal from '../ConfirmationModal'
|
||||
import CurrencyInputPanel from '../CurrencyInputPanel'
|
||||
|
||||
import Copy from '../AccountDetails/Copy'
|
||||
import { Link } from '../../theme/components'
|
||||
import { Text } from 'rebass'
|
||||
import { TYPE } from '../../theme'
|
||||
import { Hover } from '../../theme'
|
||||
import { ArrowDown } from 'react-feather'
|
||||
import { AutoColumn, ColumnCenter } from '../../components/Column'
|
||||
import { RowBetween, RowFixed, AutoRow } from '../../components/Row'
|
||||
import { GreyCard, BlueCard, YellowCard } from '../../components/Card'
|
||||
import { ArrowDown, ChevronDown, ChevronUp } from 'react-feather'
|
||||
import { ButtonPrimary, ButtonError, ButtonLight } from '../Button'
|
||||
import { GreyCard, BlueCard, YellowCard, LightCard } from '../../components/Card'
|
||||
|
||||
import { usePair } from '../../contexts/Pairs'
|
||||
import { useToken } from '../../contexts/Tokens'
|
||||
@ -33,7 +33,7 @@ import { useAddressBalance, useAllBalances } from '../../contexts/Balances'
|
||||
import { useTransactionAdder, usePendingApproval } from '../../contexts/Transactions'
|
||||
|
||||
import { ROUTER_ADDRESSES } from '../../constants'
|
||||
import { INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
|
||||
// import { INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
|
||||
import { getRouterContract, calculateGasMargin, getProviderOrSigner, getEtherscanLink } from '../../utils'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
@ -60,6 +60,41 @@ const FixedBottom = styled.div`
|
||||
margin-bottom: 40px;
|
||||
`
|
||||
|
||||
const AdvancedDropwdown = styled.div`
|
||||
position: absolute;
|
||||
margin-top: 2px;
|
||||
left: -8px;
|
||||
width: 339px;
|
||||
margin-bottom: 40px;
|
||||
padding: 10px 0;
|
||||
padding-top: 24px;
|
||||
border-bottom-left-radius: 20px;
|
||||
border-bottom-right-radius: 20px;
|
||||
color: #565a69;
|
||||
background-color: rgba(237, 238, 242, 0.8);
|
||||
color: ${({ theme }) => theme.text2};
|
||||
z-index: -1;
|
||||
`
|
||||
|
||||
// const PseudoBottom = styled.div`
|
||||
// height: 14px;
|
||||
// width: 340px;
|
||||
// background: white;
|
||||
// position: absolute;
|
||||
// border-bottom-left-radius: 41px;
|
||||
// top: 0px;
|
||||
// border-bottom-right-radius: 40px;
|
||||
// left: -1px;
|
||||
// /* box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
||||
// 0px 24px 32px rgba(0, 0, 0, 0.01); */
|
||||
// `
|
||||
|
||||
const SectionBreak = styled.div`
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
`
|
||||
|
||||
const BottomGrouping = styled.div`
|
||||
margin-top: 20px;
|
||||
position: relative;
|
||||
@ -67,7 +102,7 @@ const BottomGrouping = styled.div`
|
||||
|
||||
const ErrorText = styled(Text)`
|
||||
color: ${({ theme, warningLow, warningMedium, warningHigh }) =>
|
||||
warningHigh ? theme.red1 : warningMedium ? theme.yellow2 : warningLow ? theme.green1 : theme.text3};
|
||||
warningHigh ? theme.red1 : warningMedium ? theme.yellow2 : warningLow ? theme.green1 : theme.text1};
|
||||
`
|
||||
|
||||
const InputGroup = styled(AutoColumn)`
|
||||
@ -313,9 +348,9 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
|
||||
const pendingApprovalInput = usePendingApproval(tokens[Field.INPUT]?.address)
|
||||
|
||||
// check for imported tokens to show warning
|
||||
const importedTokenInput = tokens[Field.INPUT] && !!!INITIAL_TOKENS_CONTEXT?.[chainId]?.[tokens[Field.INPUT]?.address]
|
||||
const importedTokenOutput =
|
||||
tokens[Field.OUTPUT] && !!!INITIAL_TOKENS_CONTEXT?.[chainId]?.[tokens[Field.OUTPUT]?.address]
|
||||
// const importedTokenInput = tokens[Field.INPUT] && !!!INITIAL_TOKENS_CONTEXT?.[chainId]?.[tokens[Field.INPUT]?.address]
|
||||
// const importedTokenOutput =
|
||||
// tokens[Field.OUTPUT] && !!!INITIAL_TOKENS_CONTEXT?.[chainId]?.[tokens[Field.OUTPUT]?.address]
|
||||
|
||||
// entities for swap
|
||||
const pair: Pair = usePair(tokens[Field.INPUT], tokens[Field.OUTPUT])
|
||||
@ -378,6 +413,16 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
|
||||
[dependentField]: parsedAmounts[dependentField] ? parsedAmounts[dependentField].toSignificant(8) : ''
|
||||
}
|
||||
|
||||
const priceSlippage =
|
||||
slippageFromTrade &&
|
||||
new Percent(
|
||||
JSBI.subtract(
|
||||
JSBI.multiply(slippageFromTrade.numerator, JSBI.BigInt('1000')),
|
||||
JSBI.multiply(JSBI.BigInt('3'), slippageFromTrade.denominator)
|
||||
),
|
||||
JSBI.multiply(slippageFromTrade.denominator, JSBI.BigInt('1000'))
|
||||
)
|
||||
|
||||
const onTokenSelection = useCallback((field: Field, address: string) => {
|
||||
dispatch({
|
||||
type: SwapAction.SELECT_TOKEN,
|
||||
@ -915,16 +960,6 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
|
||||
</AutoColumn>
|
||||
)
|
||||
}
|
||||
if (showAdvanced) {
|
||||
return (
|
||||
<AdvancedSettings
|
||||
setIsOpen={setShowAdvanced}
|
||||
setDeadline={setDeadline}
|
||||
setAllowedSlippage={setAllowedSlippage}
|
||||
allowedSlippage={allowedSlippage}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (!sending || (sending && sendingWithSwap)) {
|
||||
return (
|
||||
@ -949,13 +984,6 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
|
||||
} or the transaction will revert.`}
|
||||
</TYPE.italic>
|
||||
)}
|
||||
<Link
|
||||
onClick={() => {
|
||||
setShowAdvanced(true)
|
||||
}}
|
||||
>
|
||||
Advanced
|
||||
</Link>
|
||||
</AutoColumn>
|
||||
</>
|
||||
)
|
||||
@ -989,10 +1017,14 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
|
||||
warningMedium={warningMedium}
|
||||
warningHigh={warningHigh}
|
||||
>
|
||||
{slippageFromTrade ? slippageFromTrade.toFixed(4) : '0'}%
|
||||
{priceSlippage
|
||||
? priceSlippage.toFixed(4) === '0.0000'
|
||||
? '<0.0001%'
|
||||
: priceSlippage.toFixed(4) + '%'
|
||||
: '-'}
|
||||
</ErrorText>
|
||||
<Text fontWeight={500} fontSize={14} color="#888D9B" pt={1}>
|
||||
Slippage
|
||||
Price Slippage
|
||||
</Text>
|
||||
</AutoColumn>
|
||||
</AutoRow>
|
||||
@ -1167,6 +1199,11 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
|
||||
/>
|
||||
</AutoColumn>
|
||||
)}
|
||||
{!noRoute && tokens[Field.OUTPUT] && tokens[Field.INPUT] && (
|
||||
<LightCard padding="1rem" borderRadius={'20px'}>
|
||||
<PriceBar />
|
||||
</LightCard>
|
||||
)}
|
||||
</AutoColumn>
|
||||
<BottomGrouping>
|
||||
{noRoute ? (
|
||||
@ -1225,64 +1262,144 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
|
||||
</ButtonError>
|
||||
)}
|
||||
</BottomGrouping>
|
||||
<FixedBottom>
|
||||
{!noRoute && tokens[Field.OUTPUT] && tokens[Field.INPUT] && (
|
||||
<GreyCard pt={2} mb={2}>
|
||||
<PriceBar />
|
||||
</GreyCard>
|
||||
)}
|
||||
<AutoColumn gap="lg">
|
||||
{importedTokenInput && (
|
||||
<YellowCard style={{ paddingTop: '1rem' }}>
|
||||
<AutoColumn gap="sm">
|
||||
<TYPE.mediumHeader>Token imported via address</TYPE.mediumHeader>
|
||||
<AutoRow gap="4px">
|
||||
<TokenLogo address={tokens[Field.INPUT]?.address || ''} />
|
||||
<TYPE.body>({tokens[Field.INPUT]?.symbol})</TYPE.body>
|
||||
<Link href={getEtherscanLink(chainId, tokens[Field.INPUT]?.address, 'address')}>
|
||||
(View on Etherscan)
|
||||
</Link>
|
||||
<Copy toCopy={tokens[Field.INPUT]?.address} />
|
||||
</AutoRow>
|
||||
<TYPE.subHeader>
|
||||
Please verify the legitimacy of this token before making any transactions.
|
||||
</TYPE.subHeader>
|
||||
</AutoColumn>
|
||||
</YellowCard>
|
||||
)}
|
||||
{importedTokenOutput && (
|
||||
<YellowCard style={{ paddingTop: '1rem' }}>
|
||||
<AutoColumn gap="sm">
|
||||
<TYPE.mediumHeader>Token imported via address</TYPE.mediumHeader>
|
||||
<AutoRow gap="4px">
|
||||
<TokenLogo address={tokens[Field.OUTPUT]?.address || ''} />
|
||||
<TYPE.body>({tokens[Field.OUTPUT]?.symbol})</TYPE.body>
|
||||
<Link href={getEtherscanLink(chainId, tokens[Field.OUTPUT]?.address, 'address')}>
|
||||
(View on Etherscan)
|
||||
</Link>
|
||||
</AutoRow>
|
||||
<TYPE.subHeader>
|
||||
Please verify the legitimacy of this token before making any transactions.
|
||||
</TYPE.subHeader>
|
||||
</AutoColumn>
|
||||
</YellowCard>
|
||||
)}
|
||||
{warningHigh && (
|
||||
<GreyCard style={{ paddingTop: '1rem' }}>
|
||||
<AutoColumn gap="md" mt={2}>
|
||||
<RowBetween>
|
||||
<Text fontWeight={500}>Slippage Warning</Text>
|
||||
<QuestionHelper text="" />
|
||||
</RowBetween>
|
||||
<Text color="#565A69" lineHeight="145.23%;">
|
||||
This trade will move the price by {slippageFromTrade.toFixed(2)}%. This pool probably doesn’t have
|
||||
enough liquidity to support this trade. Are you sure you want to continue this trade?
|
||||
{tokens[Field.INPUT] && tokens[Field.OUTPUT] && !noRoute && (
|
||||
<AdvancedDropwdown>
|
||||
{!showAdvanced && (
|
||||
<Hover>
|
||||
<RowBetween onClick={() => setShowAdvanced(true)} padding={'0 20px'}>
|
||||
<Text fontSize={14} fontWeight={500} style={{ userSelect: 'none' }}>
|
||||
Show Advanced
|
||||
</Text>
|
||||
</AutoColumn>
|
||||
</GreyCard>
|
||||
<ChevronDown color={'#565A69'} />
|
||||
</RowBetween>
|
||||
</Hover>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</FixedBottom>
|
||||
{showAdvanced && (
|
||||
<AutoColumn gap="md">
|
||||
<Hover>
|
||||
<RowBetween onClick={() => setShowAdvanced(false)} padding={'0 20px'}>
|
||||
<Text fontSize={14} color="#565A69" fontWeight={500} style={{ userSelect: 'none' }}>
|
||||
Hide Advanced
|
||||
</Text>
|
||||
<ChevronUp color="#565A69" />
|
||||
</RowBetween>
|
||||
</Hover>
|
||||
<SectionBreak />
|
||||
<AutoColumn style={{ padding: '0 20px' }}>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14} fontWeight={400}>
|
||||
{independentField === Field.INPUT
|
||||
? sending
|
||||
? 'Maximum amount sent'
|
||||
: 'Maximum amount received'
|
||||
: 'Minimum amount sold'}
|
||||
</TYPE.black>
|
||||
<QuestionHelper text="" />
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14}>
|
||||
{independentField === Field.INPUT
|
||||
? slippageAdjustedAmounts[Field.OUTPUT]
|
||||
? slippageAdjustedAmounts[Field.OUTPUT]?.toFixed(5) === '0.00000'
|
||||
? '<0.00001'
|
||||
: slippageAdjustedAmounts[Field.OUTPUT]?.toFixed(5)
|
||||
: '-'
|
||||
: slippageAdjustedAmounts[Field.INPUT]
|
||||
? slippageAdjustedAmounts[Field.INPUT]?.toFixed(5) === '0.00000'
|
||||
? '<0.00001'
|
||||
: slippageAdjustedAmounts[Field.INPUT]?.toFixed(5)
|
||||
: '-'}
|
||||
</TYPE.black>
|
||||
{parsedAmounts[Field.OUTPUT] && parsedAmounts[Field.INPUT] && (
|
||||
<TYPE.black fontSize={14} marginLeft={'4px'}>
|
||||
{independentField === Field.INPUT
|
||||
? parsedAmounts[Field.OUTPUT] && tokens[Field.OUTPUT]?.symbol
|
||||
: parsedAmounts[Field.INPUT] && tokens[Field.INPUT]?.symbol}
|
||||
</TYPE.black>
|
||||
)}
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14} fontWeight={400}>
|
||||
Price Slippage
|
||||
</TYPE.black>
|
||||
<QuestionHelper text="" />
|
||||
</RowFixed>
|
||||
<ErrorText
|
||||
fontWeight={500}
|
||||
fontSize={14}
|
||||
warningLow={warningLow}
|
||||
warningMedium={warningMedium}
|
||||
warningHigh={warningHigh}
|
||||
>
|
||||
{priceSlippage
|
||||
? priceSlippage.toFixed(4) === '0.0000'
|
||||
? '<0.0001%'
|
||||
: priceSlippage.toFixed(4) + '%'
|
||||
: '-'}
|
||||
</ErrorText>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14} fontWeight={400}>
|
||||
Total Slippage
|
||||
</TYPE.black>
|
||||
<QuestionHelper text="" />
|
||||
</RowFixed>
|
||||
<TYPE.black fontSize={14}>
|
||||
{slippageFromTrade ? slippageFromTrade.toSignificant(4) + '%' : '-'}
|
||||
</TYPE.black>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
<SectionBreak />
|
||||
<RowFixed padding={'0 20px'}>
|
||||
<TYPE.black fontSize={14}>Set front running resistance</TYPE.black>
|
||||
<QuestionHelper text="" />
|
||||
</RowFixed>
|
||||
<SlippageTabs
|
||||
rawSlippage={allowedSlippage}
|
||||
setRawSlippage={setAllowedSlippage}
|
||||
deadline={deadline}
|
||||
setDeadline={setDeadline}
|
||||
/>
|
||||
</AutoColumn>
|
||||
)}
|
||||
<FixedBottom>
|
||||
<AutoColumn gap="lg">
|
||||
{warningHigh && (
|
||||
<YellowCard style={{ padding: '20px', paddingTop: '10px', marginBottom: '10px' }}>
|
||||
<AutoColumn gap="md" mt={2}>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<span role="img" aria-label="warning">
|
||||
⚠️
|
||||
</span>{' '}
|
||||
<Text fontWeight={500} marginLeft="4px">
|
||||
Slippage Warning
|
||||
</Text>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<Text lineHeight="145.23%;" fontSize={14} fontWeight={400}>
|
||||
This trade will move the price by {slippageFromTrade.toFixed(2)}%. This pool probably doesn’t have
|
||||
enough liquidity to support this trade.
|
||||
</Text>
|
||||
</AutoColumn>
|
||||
</YellowCard>
|
||||
)}
|
||||
{/* <BalanceCard
|
||||
token0={tokens[Field.INPUT]}
|
||||
token1={tokens[Field.OUTPUT]}
|
||||
import0={importedTokenInput}
|
||||
balance0={userBalances[Field.INPUT]}
|
||||
balance1={userBalances[Field.OUTPUT]}
|
||||
import1={importedTokenOutput}
|
||||
/> */}
|
||||
</AutoColumn>
|
||||
</FixedBottom>
|
||||
</AdvancedDropwdown>
|
||||
)}
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ const HeaderFrame = styled.div`
|
||||
padding: 10px;
|
||||
width: calc(100% - 20px);
|
||||
`};
|
||||
z-index: 2;
|
||||
`
|
||||
|
||||
const HeaderElement = styled.div`
|
||||
|
@ -45,9 +45,9 @@ const MobilePopupInner = styled.div`
|
||||
|
||||
const FixedPopupColumn = styled(AutoColumn)`
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
right: 20px
|
||||
width: 380px;
|
||||
top: 56px;
|
||||
right: 24px;
|
||||
width: 355px;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
display: none;
|
||||
@ -57,7 +57,6 @@ const FixedPopupColumn = styled(AutoColumn)`
|
||||
const Popup = styled.div`
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
min-height: 120px;
|
||||
padding: 1em;
|
||||
box-sizing: border-box;
|
||||
background-color: white;
|
||||
@ -67,6 +66,7 @@ const Popup = styled.div`
|
||||
padding: 20px;
|
||||
padding-right: 35px;
|
||||
z-index: 2;
|
||||
overflow: hidden;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
min-width: 290px;
|
||||
@ -96,7 +96,7 @@ export default function App() {
|
||||
return (
|
||||
<Popup key={item.key}>
|
||||
<StyledClose color="#888D9B" onClick={() => removePopup(item.key)} />
|
||||
{item.content}
|
||||
{React.cloneElement(item.content, { popKey: item.key })}
|
||||
</Popup>
|
||||
)
|
||||
})}
|
||||
@ -127,7 +127,7 @@ export default function App() {
|
||||
return (
|
||||
<Popup key={item.key}>
|
||||
<StyledClose color="#888D9B" onClick={() => removePopup(item.key)} />
|
||||
{item.content}
|
||||
{React.cloneElement(item.content, { popKey: item.key })}
|
||||
</Popup>
|
||||
)
|
||||
})}
|
||||
|
@ -26,8 +26,8 @@ const QuestionWrapper = styled.div`
|
||||
`
|
||||
|
||||
const HelpCircleStyled = styled.img`
|
||||
height: 24px;
|
||||
width: 23px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
`
|
||||
|
||||
const fadeIn = keyframes`
|
||||
|
@ -72,8 +72,6 @@ export default function InputSlider({ value, onChange, override }) {
|
||||
|
||||
function handleChange(e, val) {
|
||||
setInternalVal(val)
|
||||
console.log(val)
|
||||
console.log(debouncedInternalValue)
|
||||
if (val === debouncedInternalValue) {
|
||||
onChange(e, val)
|
||||
}
|
||||
|
379
src/components/SlippageTabs/index.js
Normal file
379
src/components/SlippageTabs/index.js
Normal file
@ -0,0 +1,379 @@
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react'
|
||||
import styled, { css } from 'styled-components'
|
||||
|
||||
import QuestionHelper from '../Question'
|
||||
import { Text } from 'rebass'
|
||||
import { TYPE } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
|
||||
import { darken } from 'polished'
|
||||
import { useDebounce } from '../../hooks'
|
||||
|
||||
const WARNING_TYPE = Object.freeze({
|
||||
none: 'none',
|
||||
emptyInput: 'emptyInput',
|
||||
invalidEntryBound: 'invalidEntryBound',
|
||||
riskyEntryHigh: 'riskyEntryHigh',
|
||||
riskyEntryLow: 'riskyEntryLow'
|
||||
})
|
||||
|
||||
const FancyButton = styled.button`
|
||||
color: ${({ theme }) => theme.textColor};
|
||||
align-items: center;
|
||||
min-width: 55px;
|
||||
height: 2rem;
|
||||
border-radius: 36px;
|
||||
font-size: 12px;
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
outline: none;
|
||||
background: ${({ theme }) => theme.bg1};
|
||||
:hover {
|
||||
cursor: inherit;
|
||||
border: 1px solid ${({ theme }) => theme.bg4};
|
||||
}
|
||||
:focus {
|
||||
border: 1px solid ${({ theme }) => theme.blue1};
|
||||
}
|
||||
`
|
||||
|
||||
const Option = styled(FancyButton)`
|
||||
margin-right: 8px;
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
background-color: ${({ active, theme }) => active && theme.blue1};
|
||||
color: ${({ active, theme }) => (active ? theme.white : theme.text1)};
|
||||
`
|
||||
|
||||
const Input = styled.input`
|
||||
background: ${({ theme }) => theme.bg1};
|
||||
flex-grow: 1;
|
||||
font-size: 12px;
|
||||
min-width: 20px;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
&::-webkit-outer-spin-button,
|
||||
&::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
cursor: inherit;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
text-align: left;
|
||||
${({ active }) =>
|
||||
active &&
|
||||
css`
|
||||
color: initial;
|
||||
cursor: initial;
|
||||
text-align: right;
|
||||
`}
|
||||
${({ placeholder }) =>
|
||||
placeholder !== 'Custom' &&
|
||||
css`
|
||||
text-align: right;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
`}
|
||||
${({ color }) =>
|
||||
color === 'red' &&
|
||||
css`
|
||||
color: ${({ theme }) => theme.red1};
|
||||
`}
|
||||
`
|
||||
|
||||
const BottomError = styled(Text)`
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
${({ show }) =>
|
||||
show &&
|
||||
css`
|
||||
padding-top: 12px;
|
||||
`}
|
||||
`
|
||||
|
||||
const OptionCustom = styled(FancyButton)`
|
||||
height: 2rem;
|
||||
position: relative;
|
||||
padding: 0 0.75rem;
|
||||
${({ active }) =>
|
||||
active &&
|
||||
css`
|
||||
border: 1px solid ${({ theme, warning }) => (warning ? theme.red1 : theme.blue1)};
|
||||
:hover {
|
||||
border: 1px solid ${({ theme, warning }) => (warning ? darken(0.1, theme.red1) : darken(0.1, theme.blue1))};
|
||||
}
|
||||
`}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0px;
|
||||
border-radius: 2rem;
|
||||
}
|
||||
`
|
||||
|
||||
const SlippageSelector = styled.div`
|
||||
padding: 0 20px;
|
||||
`
|
||||
|
||||
const Percent = styled.div`
|
||||
color: inherit;
|
||||
font-size: 0, 8rem;
|
||||
flex-grow: 0;
|
||||
${({ color, theme }) =>
|
||||
(color === 'faded' &&
|
||||
css`
|
||||
color: ${theme.bg1};
|
||||
`) ||
|
||||
(color === 'red' &&
|
||||
css`
|
||||
color: ${theme.red1};
|
||||
`)};
|
||||
`
|
||||
|
||||
export default function TransactionDetails({ setRawSlippage, rawSlippage, deadline, setDeadline }) {
|
||||
const [activeIndex, setActiveIndex] = useState(2)
|
||||
|
||||
const [warningType, setWarningType] = useState(WARNING_TYPE.none)
|
||||
|
||||
const inputRef = useRef()
|
||||
|
||||
const [userInput, setUserInput] = useState('')
|
||||
const debouncedInput = useDebounce(userInput, 150)
|
||||
|
||||
const [initialSlippage] = useState(rawSlippage)
|
||||
|
||||
const [deadlineInput, setDeadlineInput] = useState(deadline / 60)
|
||||
|
||||
function parseCustomDeadline(e) {
|
||||
let val = e.target.value
|
||||
const acceptableValues = [/^$/, /^\d+$/]
|
||||
if (acceptableValues.some(re => re.test(val))) {
|
||||
setDeadlineInput(val)
|
||||
setDeadline(val * 60)
|
||||
}
|
||||
}
|
||||
|
||||
const setFromCustom = () => {
|
||||
setActiveIndex(4)
|
||||
inputRef.current.focus()
|
||||
// if there's a value, evaluate the bounds
|
||||
checkBounds(debouncedInput)
|
||||
}
|
||||
|
||||
const updateSlippage = useCallback(
|
||||
newSlippage => {
|
||||
// round to 2 decimals to prevent ethers error
|
||||
let numParsed = parseInt(newSlippage * 100)
|
||||
|
||||
// set both slippage values in parents
|
||||
setRawSlippage(numParsed)
|
||||
},
|
||||
[setRawSlippage]
|
||||
)
|
||||
|
||||
// used for slippage presets
|
||||
const setFromFixed = useCallback(
|
||||
(index, slippage) => {
|
||||
// update slippage in parent, reset errors and input state
|
||||
updateSlippage(slippage)
|
||||
setWarningType(WARNING_TYPE.none)
|
||||
setActiveIndex(index)
|
||||
},
|
||||
[updateSlippage]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
switch (Number.parseInt(initialSlippage)) {
|
||||
case 10:
|
||||
setFromFixed(1, 0.1)
|
||||
break
|
||||
case 50:
|
||||
setFromFixed(2, 0.5)
|
||||
break
|
||||
case 100:
|
||||
setFromFixed(3, 1)
|
||||
break
|
||||
default:
|
||||
// restrict to 2 decimal places
|
||||
let acceptableValues = [/^$/, /^\d{1,2}$/, /^\d{0,2}\.\d{0,2}$/]
|
||||
// if its within accepted decimal limit, update the input state
|
||||
if (acceptableValues.some(val => val.test(initialSlippage / 100))) {
|
||||
setUserInput(initialSlippage / 100)
|
||||
setActiveIndex(4)
|
||||
}
|
||||
}
|
||||
}, [initialSlippage, setFromFixed])
|
||||
|
||||
const checkBounds = useCallback(
|
||||
slippageValue => {
|
||||
setWarningType(WARNING_TYPE.none)
|
||||
|
||||
if (slippageValue === '' || slippageValue === '.') {
|
||||
return setWarningType(WARNING_TYPE.emptyInput)
|
||||
}
|
||||
|
||||
// check bounds and set errors
|
||||
if (Number(slippageValue) < 0 || Number(slippageValue) > 50) {
|
||||
return setWarningType(WARNING_TYPE.invalidEntryBound)
|
||||
}
|
||||
if (Number(slippageValue) >= 0 && Number(slippageValue) < 0.1) {
|
||||
setWarningType(WARNING_TYPE.riskyEntryLow)
|
||||
}
|
||||
if (Number(slippageValue) > 5) {
|
||||
setWarningType(WARNING_TYPE.riskyEntryHigh)
|
||||
}
|
||||
//update the actual slippage value in parent
|
||||
updateSlippage(Number(slippageValue))
|
||||
},
|
||||
[updateSlippage]
|
||||
)
|
||||
|
||||
// check that the theyve entered number and correct decimal
|
||||
const parseInput = e => {
|
||||
let input = e.target.value
|
||||
|
||||
// restrict to 2 decimal places
|
||||
let acceptableValues = [/^$/, /^\d{1,2}$/, /^\d{0,2}\.\d{0,2}$/]
|
||||
// if its within accepted decimal limit, update the input state
|
||||
if (acceptableValues.some(a => a.test(input))) {
|
||||
setUserInput(input)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (activeIndex === 4) {
|
||||
checkBounds(debouncedInput)
|
||||
}
|
||||
})
|
||||
|
||||
const dropDownContent = () => {
|
||||
return (
|
||||
<>
|
||||
<SlippageSelector>
|
||||
<RowBetween>
|
||||
<Option
|
||||
onClick={() => {
|
||||
setFromFixed(1, 0.1)
|
||||
}}
|
||||
active={activeIndex === 1}
|
||||
>
|
||||
0.1%
|
||||
</Option>
|
||||
<Option
|
||||
onClick={() => {
|
||||
setFromFixed(2, 0.5)
|
||||
}}
|
||||
active={activeIndex === 2}
|
||||
>
|
||||
0.5%
|
||||
</Option>
|
||||
<Option
|
||||
onClick={() => {
|
||||
setFromFixed(3, 1)
|
||||
}}
|
||||
active={activeIndex === 3}
|
||||
>
|
||||
1%
|
||||
</Option>
|
||||
<OptionCustom
|
||||
active={activeIndex === 4}
|
||||
warning={
|
||||
warningType !== WARNING_TYPE.none &&
|
||||
warningType !== WARNING_TYPE.emptyInput &&
|
||||
warningType !== WARNING_TYPE.riskyEntryLow
|
||||
}
|
||||
onClick={() => {
|
||||
setFromCustom()
|
||||
}}
|
||||
>
|
||||
<RowBetween>
|
||||
{!(warningType === WARNING_TYPE.none || warningType === WARNING_TYPE.emptyInput) && (
|
||||
<span
|
||||
role="img"
|
||||
aria-label="warning"
|
||||
style={{
|
||||
color:
|
||||
warningType !== WARNING_TYPE.none && warningType !== WARNING_TYPE.riskyEntryLow
|
||||
? 'red'
|
||||
: warningType === WARNING_TYPE.riskyEntryLow
|
||||
? '#F3841E'
|
||||
: ''
|
||||
}}
|
||||
>
|
||||
⚠️
|
||||
</span>
|
||||
)}
|
||||
<Input
|
||||
tabIndex={-1}
|
||||
ref={inputRef}
|
||||
active={activeIndex === 4}
|
||||
placeholder={
|
||||
activeIndex === 4
|
||||
? !!userInput
|
||||
? ''
|
||||
: '0'
|
||||
: activeIndex !== 4 && userInput !== ''
|
||||
? userInput
|
||||
: 'Custom'
|
||||
}
|
||||
value={activeIndex === 4 ? userInput : ''}
|
||||
onChange={parseInput}
|
||||
color={
|
||||
warningType === WARNING_TYPE.emptyInput
|
||||
? ''
|
||||
: warningType !== WARNING_TYPE.none && warningType !== WARNING_TYPE.riskyEntryLow
|
||||
? 'red'
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
<Percent
|
||||
color={
|
||||
activeIndex !== 4
|
||||
? 'faded'
|
||||
: warningType === WARNING_TYPE.riskyEntryHigh || warningType === WARNING_TYPE.invalidEntryBound
|
||||
? 'red'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
%
|
||||
</Percent>
|
||||
</RowBetween>
|
||||
</OptionCustom>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<BottomError
|
||||
show={activeIndex === 4}
|
||||
color={
|
||||
warningType === WARNING_TYPE.emptyInput
|
||||
? '#565A69'
|
||||
: warningType !== WARNING_TYPE.none && warningType !== WARNING_TYPE.riskyEntryLow
|
||||
? 'red'
|
||||
: warningType === WARNING_TYPE.riskyEntryLow
|
||||
? '#F3841E'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{warningType === WARNING_TYPE.emptyInput && 'Enter a slippage percentage'}
|
||||
{warningType === WARNING_TYPE.invalidEntryBound && 'Please select a value no greater than 50%'}
|
||||
{warningType === WARNING_TYPE.riskyEntryHigh && 'Your transaction may be frontrun'}
|
||||
{warningType === WARNING_TYPE.riskyEntryLow && 'Your transaction may fail'}
|
||||
</BottomError>
|
||||
</RowBetween>
|
||||
</SlippageSelector>
|
||||
<AutoColumn gap="md">
|
||||
<RowFixed padding={'0 20px'}>
|
||||
<TYPE.black fontSize={14}>Deadline</TYPE.black>
|
||||
<QuestionHelper text="Deadline in minutes. If your transaction takes longer than this it will revert." />
|
||||
</RowFixed>
|
||||
<RowBetween padding={'0 20px'}>
|
||||
<OptionCustom style={{ width: '80px' }}>
|
||||
<Input tabIndex={-1} placeholder={deadlineInput} value={deadlineInput} onChange={parseCustomDeadline} />
|
||||
</OptionCustom>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return dropDownContent()
|
||||
}
|
@ -1,20 +1,81 @@
|
||||
import React from 'react'
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
|
||||
import { Link } from '../../theme/components'
|
||||
import { TYPE } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { getEtherscanLink } from '../../utils'
|
||||
import { usePopups } from '../../contexts/Application'
|
||||
|
||||
export default function TxnPopup({ hash, success, summary }) {
|
||||
import { CheckCircle, AlertCircle } from 'react-feather'
|
||||
|
||||
import styled from 'styled-components'
|
||||
|
||||
const Fader = styled.div`
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: ${({ count }) => `calc(100% - (100% / ${150 / count}))`};
|
||||
height: 2px;
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
transition: width 100ms linear;
|
||||
`
|
||||
|
||||
function useInterval(callback, delay) {
|
||||
const savedCallback = useRef()
|
||||
|
||||
// Remember the latest callback.
|
||||
useEffect(() => {
|
||||
savedCallback.current = callback
|
||||
return () => {}
|
||||
}, [callback])
|
||||
|
||||
// Set up the interval.
|
||||
useEffect(() => {
|
||||
function tick() {
|
||||
savedCallback.current()
|
||||
}
|
||||
if (delay !== null) {
|
||||
let id = setInterval(tick, delay)
|
||||
return () => clearInterval(id)
|
||||
}
|
||||
return () => {}
|
||||
}, [delay])
|
||||
}
|
||||
|
||||
const delay = 100
|
||||
|
||||
export default function TxnPopup({ hash, success, summary, popKey }) {
|
||||
const { chainId } = useWeb3React()
|
||||
let [count, setCount] = useState(1)
|
||||
|
||||
const [isRunning, setIsRunning] = useState(true)
|
||||
const [, , removePopup] = usePopups()
|
||||
|
||||
useInterval(
|
||||
() => {
|
||||
count > 150 && removePopup(popKey)
|
||||
setCount(count + 1)
|
||||
},
|
||||
isRunning ? delay : null
|
||||
)
|
||||
|
||||
return (
|
||||
<AutoColumn gap="12px">
|
||||
<TYPE.body>Transaction {success ? 'confirmed.' : 'failed.'}</TYPE.body>
|
||||
<TYPE.green>{summary ? summary : 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)}</TYPE.green>
|
||||
<Link href={getEtherscanLink(chainId, hash, 'transaction')}>View on Etherscan</Link>
|
||||
</AutoColumn>
|
||||
<AutoRow onMouseEnter={() => setIsRunning(false)} onMouseLeave={() => setIsRunning(true)}>
|
||||
{success ? (
|
||||
<CheckCircle color={'#27AE60'} size={24} style={{ paddingRight: '24px' }} />
|
||||
) : (
|
||||
<AlertCircle color={'#FF6871'} size={24} style={{ paddingRight: '24px' }} />
|
||||
)}
|
||||
<AutoColumn gap="8px">
|
||||
<TYPE.body fontWeight={500}>
|
||||
{summary ? summary : 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)}
|
||||
</TYPE.body>
|
||||
<Link href={getEtherscanLink(chainId, hash, 'transaction')}>View on Etherscan</Link>
|
||||
</AutoColumn>
|
||||
<Fader count={count} />
|
||||
</AutoRow>
|
||||
)
|
||||
}
|
||||
|
@ -414,7 +414,6 @@ export function useAllBalances(): Array<TokenAmount> {
|
||||
let newBalances = {}
|
||||
Object.keys(state[chainId]).map(address => {
|
||||
return Object.keys(state[chainId][address]).map(tokenAddress => {
|
||||
// console.log(allTokens[tokenAddress])
|
||||
if (state[chainId][address][tokenAddress].value) {
|
||||
// fix if ETH found in local storage from old storage
|
||||
if (tokenAddress === 'ETH') {
|
||||
|
@ -46,6 +46,8 @@ const BodyWrapper = styled.div`
|
||||
max-width: calc(355px + 4rem);
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
z-index: 1;
|
||||
`
|
||||
|
||||
const Body = styled.div`
|
||||
@ -72,7 +74,6 @@ export default function App() {
|
||||
</HeaderWrapper>
|
||||
<BodyWrapper>
|
||||
<Popups />
|
||||
|
||||
<Body>
|
||||
<Web3ReactManager>
|
||||
<BrowserRouter>
|
||||
|
@ -14,9 +14,9 @@ import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||
import { Text } from 'rebass'
|
||||
import { TYPE } from '../../theme'
|
||||
import { Plus } from 'react-feather'
|
||||
import { BlueCard, LightCard } from '../../components/Card'
|
||||
import { AutoColumn, ColumnCenter } from '../../components/Column'
|
||||
import { ButtonPrimary, ButtonLight } from '../../components/Button'
|
||||
import { BlueCard, LightCard, GreyCard } from '../../components/Card'
|
||||
import Row, { AutoRow, RowBetween, RowFlat, RowFixed } from '../../components/Row'
|
||||
|
||||
import { useToken } from '../../contexts/Tokens'
|
||||
@ -688,6 +688,11 @@ function AddLiquidity({ token0, token1, step = false }) {
|
||||
error={outputError}
|
||||
pair={pair}
|
||||
/>
|
||||
{tokens[Field.OUTPUT] && tokens[Field.INPUT] && (
|
||||
<LightCard padding="1rem" borderRadius={'20px'}>
|
||||
<PriceBar />
|
||||
</LightCard>
|
||||
)}
|
||||
{showOutputApprove ? (
|
||||
<ButtonLight
|
||||
onClick={() => {
|
||||
@ -721,11 +726,6 @@ function AddLiquidity({ token0, token1, step = false }) {
|
||||
{!noLiquidity && (
|
||||
<FixedBottom>
|
||||
<AutoColumn>
|
||||
{tokens[Field.OUTPUT] && (
|
||||
<GreyCard pt={2} mb={2}>
|
||||
<PriceBar />
|
||||
</GreyCard>
|
||||
)}
|
||||
<PositionCard
|
||||
pairAddress={pair?.liquidityToken?.address}
|
||||
token0={tokens[Field.INPUT]}
|
||||
|
@ -7,7 +7,6 @@ import { TokenAmount, JSBI, Route, WETH, Percent, Token, Pair } from '@uniswap/s
|
||||
import TokenLogo from '../../components/TokenLogo'
|
||||
import DoubleLogo from '../../components/DoubleLogo'
|
||||
import PositionCard from '../../components/PositionCard'
|
||||
// import NumericalInput from '../../components/NumericalInput'
|
||||
import ConfirmationModal from '../../components/ConfirmationModal'
|
||||
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||
import { TYPE } from '../../theme'
|
||||
@ -266,9 +265,6 @@ export default function RemoveLiquidity({ token0, token1 }) {
|
||||
parsedAmounts[Field.LIQUIDITY] &&
|
||||
pair.getLiquidityValue(tokens[Field.TOKEN1], totalPoolTokens, parsedAmounts[Field.LIQUIDITY], false)
|
||||
|
||||
// controlled input for percetange input
|
||||
// const [percentageInput, setPercentageInput] = useState(0)
|
||||
|
||||
// derived percent for advanced mode
|
||||
const derivedPerecent =
|
||||
userLiquidity &&
|
||||
@ -285,15 +281,6 @@ export default function RemoveLiquidity({ token0, token1 }) {
|
||||
)
|
||||
}
|
||||
|
||||
// update controlled perctenage when derived is updated
|
||||
// useEffect(() => {
|
||||
// if (derivedPerecent) {
|
||||
// setPercentageInput(parseFloat(derivedPerecent))
|
||||
// } else {
|
||||
// setPercentageInput(0)
|
||||
// }
|
||||
// }, [derivedPerecent])
|
||||
|
||||
// get formatted amounts
|
||||
const formattedAmounts = {
|
||||
[Field.LIQUIDITY]:
|
||||
@ -608,7 +595,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
|
||||
pendingText={pendingText}
|
||||
title="You will recieve"
|
||||
/>
|
||||
<AutoColumn gap="20px">
|
||||
<AutoColumn gap="md">
|
||||
<LightCard>
|
||||
<AutoColumn gap="20px">
|
||||
<RowBetween>
|
||||
|
@ -106,6 +106,11 @@ export const TYPE = {
|
||||
{children}
|
||||
</Text>
|
||||
),
|
||||
black: ({ children, ...rest }) => (
|
||||
<Text fontWeight={500} color={theme().text1} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
),
|
||||
largeHeader: ({ children, ...rest }) => (
|
||||
<Text fontWeight={600} fontSize={24} {...rest}>
|
||||
{children}
|
||||
@ -122,7 +127,7 @@ export const TYPE = {
|
||||
</Text>
|
||||
),
|
||||
body: ({ children, ...rest }) => (
|
||||
<Text fontWeight={400} fontSize={16} color={'#888D9B'} {...rest}>
|
||||
<Text fontWeight={400} fontSize={16} color={'#191B1F'} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
),
|
||||
@ -131,6 +136,11 @@ export const TYPE = {
|
||||
{children}
|
||||
</Text>
|
||||
),
|
||||
yellow: ({ children, ...rest }) => (
|
||||
<Text fontWeight={500} color={theme().yellow2} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
),
|
||||
green: ({ children, ...rest }) => (
|
||||
<Text fontWeight={500} color={theme().green1} {...rest}>
|
||||
{children}
|
||||
|
10
yarn.lock
10
yarn.lock
@ -12927,10 +12927,12 @@ react-error-overlay@^6.0.4:
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.4.tgz#0d165d6d27488e660bc08e57bdabaad741366f7a"
|
||||
integrity sha512-ueZzLmHltszTshDMwyfELDq8zOA803wQ1ZuzCccXa1m57k1PxSHfflPD5W9YIiTXLs0JTLzoj6o1LuM5N6zzNA==
|
||||
|
||||
react-feather@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-1.1.6.tgz#2a547e3d5cd5e383d3da0128d593cbdb3c1b32f7"
|
||||
integrity sha512-iCofWhTjX+vQwvDmg7o6vg0XrUg1c41yBDZG+l83nz1FiCsleJoUgd3O+kHpOeWMXuPrRIFfCixvcqyOLGOgIg==
|
||||
react-feather@^2.0.8:
|
||||
version "2.0.8"
|
||||
resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-2.0.8.tgz#455baf1470f756a57e2ad6c72545444ce5925781"
|
||||
integrity sha512-J0dCEOvOxpovHeOVj3+8mAhN3/UERTfX6rSxnV6x4E+0s+STY536jhSjRfpSvTQA0SSFjYr4KrpPfdsLmK+zZg==
|
||||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
|
||||
react-focus-lock@^1.17.7:
|
||||
version "1.19.1"
|
||||
|
Loading…
Reference in New Issue
Block a user