UI Updates, Route Context Updates (#679)
* UI updates, big fixes on route hook * fix bug on all pair list * logout fix and add mainnet WETH
This commit is contained in:
parent
af6add09a0
commit
50c9d9973a
@ -9,7 +9,7 @@
|
||||
"swapAnyway": "Swap Anyway",
|
||||
"send": "Send",
|
||||
"sendAnyway": "Send Anyway",
|
||||
"pool": "Supply",
|
||||
"pool": "Pool",
|
||||
"betaWarning": "This project is in beta. Use at your own risk.",
|
||||
"input": "Input",
|
||||
"output": "Output",
|
||||
@ -85,5 +85,7 @@
|
||||
"enterTokenCont": "Enter a token address to continue",
|
||||
"priceChange": "Expected price slippage",
|
||||
"forAtLeast": "for at least ",
|
||||
"brokenToken": "The selected token is not compatible with Uniswap V1. Adding liquidity will result in locked funds."
|
||||
"brokenToken": "The selected token is not compatible with Uniswap V1. Adding liquidity will result in locked funds.",
|
||||
"toleranceExplanation": "Lowering this limit decreases your risk of frontrunning. However, this makes more likely that your transaction will fail due to normal price movements.",
|
||||
"tokenSearchPlaceholder": "Search name or paste token address"
|
||||
}
|
||||
|
@ -2,8 +2,7 @@ import React, { useState, useEffect } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { transparentize } from 'polished'
|
||||
|
||||
import QR from '../../assets/svg/QR.svg'
|
||||
|
||||
// import QR from '../../assets/svg/QR.svg'
|
||||
import { isAddress } from '../../utils'
|
||||
import { useWeb3React, useDebounce } from '../../hooks'
|
||||
|
||||
@ -15,6 +14,8 @@ const InputPanel = styled.div`
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
|
||||
`
|
||||
|
||||
const ContainerRow = styled.div`
|
||||
@ -22,6 +23,7 @@ const ContainerRow = styled.div`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 1.25rem;
|
||||
height: 60px;
|
||||
border: 1px solid ${({ error, theme }) => (error ? theme.red1 : theme.bg3)};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
`
|
||||
@ -43,25 +45,26 @@ const Input = styled.input`
|
||||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
|
||||
font-size: 20px;
|
||||
color: ${({ error, theme }) => (error ? theme.red1 : theme.blue1)};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
|
||||
::placeholder {
|
||||
color: ${({ theme }) => theme.text4};
|
||||
}
|
||||
`
|
||||
|
||||
const QRWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
background: #fbfbfb;
|
||||
padding: 4px;
|
||||
border-radius: 8px;
|
||||
`
|
||||
// const QRWrapper = styled.div`
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// justify-content: center;
|
||||
// border: 1px solid ${({ theme }) => theme.bg3};
|
||||
// background: #fbfbfb;
|
||||
// padding: 4px;
|
||||
// border-radius: 8px;
|
||||
// `
|
||||
|
||||
export default function AddressInputPanel({ initialInput = '', onChange, onError }) {
|
||||
const { library } = useWeb3React()
|
||||
@ -172,14 +175,14 @@ export default function AddressInputPanel({ initialInput = '', onChange, onError
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
placeholder="0x1234..."
|
||||
placeholder="Recipient Address"
|
||||
error={input !== '' && error}
|
||||
onChange={onInput}
|
||||
value={input}
|
||||
/>
|
||||
<QRWrapper>
|
||||
{/* <QRWrapper>
|
||||
<img src={QR} alt="" />
|
||||
</QRWrapper>
|
||||
</QRWrapper> */}
|
||||
</InputRow>
|
||||
</InputContainer>
|
||||
</ContainerRow>
|
||||
|
@ -7,6 +7,7 @@ import { Link } from '../../theme/components'
|
||||
import { TYPE } from '../../theme'
|
||||
import { AutoColumn } from '../../components/Column'
|
||||
import { ButtonRadio } from '../Button'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Row, { RowBetween, RowFixed } from '../../components/Row'
|
||||
|
||||
const InputWrapper = styled(RowBetween)`
|
||||
@ -27,6 +28,9 @@ const SLIPPAGE_INDEX = {
|
||||
}
|
||||
|
||||
export default function AdvancedSettings({ setIsOpen, setDeadline, allowedSlippage, setAllowedSlippage }) {
|
||||
// text translation
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [deadlineInput, setDeadlineInput] = useState(15)
|
||||
const [slippageInput, setSlippageInput] = useState()
|
||||
const [activeIndex, setActiveIndex] = useState(SLIPPAGE_INDEX[3])
|
||||
@ -82,8 +86,8 @@ export default function AdvancedSettings({ setIsOpen, setDeadline, allowedSlippa
|
||||
back
|
||||
</Link>
|
||||
<RowBetween>
|
||||
<TYPE.main>Limit additional price impact</TYPE.main>
|
||||
<QuestionHelper text="" />
|
||||
<TYPE.main>Limit front-running tolerance</TYPE.main>
|
||||
<QuestionHelper text={t('toleranceExplanation')} />
|
||||
</RowBetween>
|
||||
<Row>
|
||||
<ButtonRadio
|
||||
|
@ -44,6 +44,24 @@ export const ButtonPrimary = styled(Base)`
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonLight = styled(Base)`
|
||||
background-color: ${({ theme }) => theme.blue5};
|
||||
color: ${({ theme }) => theme.blue1};
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.05, theme.blue5)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.blue5)};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.blue5)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.1, theme.blue5)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.blue5)};
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonSecondary = styled(Base)`
|
||||
background-color: #ebf4ff;
|
||||
color: #2172e5;
|
||||
@ -73,8 +91,6 @@ export const ButtonPink = styled(Base)`
|
||||
background-color: ${({ theme }) => theme.pink2};
|
||||
color: white;
|
||||
|
||||
padding: 10px;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.pink2)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.pink2)};
|
||||
|
@ -18,12 +18,24 @@ export const LightCard = styled(Card)`
|
||||
`
|
||||
|
||||
export const GreyCard = styled(Card)`
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
`
|
||||
|
||||
export const YellowCard = styled(Card)`
|
||||
background-color: rgba(243, 190, 30, 0.3);
|
||||
color: ${({ theme }) => theme.yellow2};
|
||||
fontweight: 500;
|
||||
`
|
||||
|
||||
export const PinkCard = styled(Card)`
|
||||
background-color: rgba(255, 0, 122, 0.03);
|
||||
color: ${({ theme }) => theme.pink2};
|
||||
fontweight: 500;
|
||||
`
|
||||
|
||||
const BlueCardStyled = styled(Card)`
|
||||
background-color: #ebf4ff;
|
||||
color: #2172e5;
|
||||
background-color: ${({ theme }) => theme.blue5};
|
||||
color: ${({ theme }) => theme.blue1};
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
width: fit-content;
|
||||
|
@ -23,6 +23,8 @@ const Section = styled(AutoColumn)`
|
||||
|
||||
const BottomSection = styled(Section)`
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
`
|
||||
|
||||
const ConfirmedIcon = styled(ColumnCenter)`
|
||||
|
@ -1,9 +1,7 @@
|
||||
import React, { useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import '@reach/tooltip/styles.css'
|
||||
import { ethers } from 'ethers'
|
||||
import { darken } from 'polished'
|
||||
import { WETH } from '@uniswap/sdk'
|
||||
|
||||
import TokenLogo from '../TokenLogo'
|
||||
import DoubleLogo from '../DoubleLogo'
|
||||
@ -16,30 +14,7 @@ import { Input as NumericalInput } from '../NumericalInput'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useTokenContract } from '../../hooks'
|
||||
import { calculateGasMargin } from '../../utils'
|
||||
import { useAddressBalance } from '../../contexts/Balances'
|
||||
import { useTransactionAdder, usePendingApproval } from '../../contexts/Transactions'
|
||||
|
||||
import { ROUTER_ADDRESSES } from '../../constants'
|
||||
|
||||
const GAS_MARGIN = ethers.utils.bigNumberify(1000)
|
||||
|
||||
const SubCurrencySelect = styled.button`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
padding: 4px 50px 4px 15px;
|
||||
margin-right: -40px;
|
||||
line-height: 0;
|
||||
align-items: center;
|
||||
border-radius: 2.5rem;
|
||||
height: 2rem;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
background: ${({ theme }) => theme.blue5};
|
||||
border: 1px solid ${({ theme }) => theme.blue1};
|
||||
color: ${({ theme }) => theme.blue1};
|
||||
`
|
||||
|
||||
const InputRow = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
@ -156,8 +131,6 @@ export default function CurrencyInputPanel({
|
||||
urlAddedTokens = [], // used
|
||||
onTokenSelection = null,
|
||||
token = null,
|
||||
showUnlock = false, // used to show unlock if approval needed
|
||||
disableUnlock = false,
|
||||
disableTokenSelect = false,
|
||||
hideBalance = false,
|
||||
isExchange = false,
|
||||
@ -167,59 +140,11 @@ export default function CurrencyInputPanel({
|
||||
showSendWithSwap = false
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const { account, chainId } = useWeb3React()
|
||||
const routerAddress = ROUTER_ADDRESSES[chainId]
|
||||
const { account } = useWeb3React()
|
||||
|
||||
const addTransaction = useTransactionAdder()
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
|
||||
const userTokenBalance = useAddressBalance(account, token)
|
||||
const tokenContract = useTokenContract(token?.address)
|
||||
const pendingApproval = usePendingApproval(token?.address)
|
||||
|
||||
function renderUnlockButton() {
|
||||
if (
|
||||
disableUnlock ||
|
||||
!showUnlock ||
|
||||
token?.address === 'ETH' ||
|
||||
token?.address === WETH[chainId].address ||
|
||||
!token?.address
|
||||
) {
|
||||
return null
|
||||
} else {
|
||||
if (!pendingApproval) {
|
||||
return (
|
||||
<SubCurrencySelect
|
||||
onClick={async () => {
|
||||
let estimatedGas
|
||||
let useUserBalance = false
|
||||
estimatedGas = await tokenContract.estimate
|
||||
.approve(routerAddress, ethers.constants.MaxUint256)
|
||||
.catch(e => {
|
||||
console.log('Error setting max token approval.')
|
||||
})
|
||||
if (!estimatedGas) {
|
||||
// general fallback for tokens who restrict approval amounts
|
||||
estimatedGas = await tokenContract.estimate.approve(routerAddress, userTokenBalance)
|
||||
useUserBalance = true
|
||||
}
|
||||
tokenContract
|
||||
.approve(routerAddress, useUserBalance ? userTokenBalance : ethers.constants.MaxUint256, {
|
||||
gasLimit: calculateGasMargin(estimatedGas, GAS_MARGIN)
|
||||
})
|
||||
.then(response => {
|
||||
addTransaction(response, { approval: token?.address })
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t('unlock')}
|
||||
</SubCurrencySelect>
|
||||
)
|
||||
} else {
|
||||
return <SubCurrencySelect>{t('pending')}</SubCurrencySelect>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<InputPanel>
|
||||
@ -234,7 +159,7 @@ export default function CurrencyInputPanel({
|
||||
}}
|
||||
/>
|
||||
{!!token?.address && !atMax && <StyledBalanceMax onClick={onMax}>MAX</StyledBalanceMax>}
|
||||
{renderUnlockButton()}
|
||||
{/* {renderUnlockButton()} */}
|
||||
</>
|
||||
)}
|
||||
<CurrencySelect
|
||||
@ -288,6 +213,7 @@ export default function CurrencyInputPanel({
|
||||
field={field}
|
||||
onTokenSelect={onTokenSelection}
|
||||
showSendWithSwap={showSendWithSwap}
|
||||
hiddenToken={token?.address}
|
||||
/>
|
||||
)}
|
||||
</InputPanel>
|
||||
|
@ -16,29 +16,31 @@ import { Link } from '../../theme/components'
|
||||
import { Text } from 'rebass'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ArrowDown, ArrowUp } from 'react-feather'
|
||||
import { GreyCard, BlueCard } from '../../components/Card'
|
||||
import { GreyCard, BlueCard, YellowCard } from '../../components/Card'
|
||||
import { AutoColumn, ColumnCenter } from '../../components/Column'
|
||||
import { ButtonPrimary, ButtonError } from '../Button'
|
||||
import { RowBetween, RowFixed, AutoRow } from '../../components/Row'
|
||||
import { ButtonPrimary, ButtonError, ButtonLight } from '../Button'
|
||||
|
||||
import { usePair } from '../../contexts/Pairs'
|
||||
import { useToken } from '../../contexts/Tokens'
|
||||
import { usePopups } from '../../contexts/Application'
|
||||
import { useRoute } from '../../contexts/Routes'
|
||||
import { useTransactionAdder } from '../../contexts/Transactions'
|
||||
// import { useTranslation } from 'react-i18next'
|
||||
import { useTransactionAdder, usePendingApproval } from '../../contexts/Transactions'
|
||||
import { useAddressAllowance } from '../../contexts/Allowances'
|
||||
import { useWeb3React, useTokenContract } from '../../hooks'
|
||||
import { useAddressBalance, useAllBalances } from '../../contexts/Balances'
|
||||
|
||||
import { INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
|
||||
import { ROUTER_ADDRESSES } from '../../constants'
|
||||
import { getRouterContract, calculateGasMargin, getProviderOrSigner } from '../../utils'
|
||||
import { getRouterContract, calculateGasMargin, getProviderOrSigner, getEtherscanLink } from '../../utils'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
`
|
||||
|
||||
const ArrowWrapper = styled.div`
|
||||
padding: 4px;
|
||||
padding: 6px;
|
||||
border: 1px solid ${({ theme }) => theme.blue4};
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
@ -221,6 +223,9 @@ const ALLOWED_SLIPPAGE_MEDIUM = 100
|
||||
const ALLOWED_SLIPPAGE_HIGH = 500
|
||||
|
||||
function ExchangePage({ sendingInput = false, history }) {
|
||||
// text translation
|
||||
// const { t } = useTranslation()
|
||||
|
||||
const { chainId, account, library } = useWeb3React()
|
||||
const routerAddress: string = ROUTER_ADDRESSES[chainId]
|
||||
|
||||
@ -245,8 +250,27 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
[Field.OUTPUT]: useToken(fieldData[Field.OUTPUT].address)
|
||||
}
|
||||
|
||||
// token contracts for approvals and direct sends
|
||||
const tokenContractInput: ethers.Contract = useTokenContract(tokens[Field.INPUT]?.address)
|
||||
const tokenContractOutput: ethers.Contract = useTokenContract(tokens[Field.OUTPUT]?.address)
|
||||
|
||||
// check on pending approvals for token amounts
|
||||
const pendingApprovalInput = usePendingApproval(tokens[Field.INPUT]?.address)
|
||||
const pendingApprovalOutput = usePendingApproval(tokens[Field.OUTPUT]?.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 pair: Pair = usePair(tokens[Field.INPUT], tokens[Field.OUTPUT])
|
||||
|
||||
// console.log(pair?.token0?.symbol)
|
||||
// console.log(pair?.token1?.symbol)
|
||||
// console.log('--------------')
|
||||
|
||||
const route = useRoute(tokens[Field.INPUT], tokens[Field.OUTPUT])
|
||||
// const route = useRoute(pair)
|
||||
const noRoute: boolean = !route && !!tokens[Field.INPUT] && !!tokens[Field.OUTPUT]
|
||||
const emptyReserves = pair && JSBI.equal(JSBI.BigInt(0), pair.reserve0.raw)
|
||||
|
||||
@ -274,8 +298,6 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
[Field.OUTPUT]: useAddressBalance(account, tokens[Field.OUTPUT])
|
||||
}
|
||||
|
||||
// console.log(userBalances[Field.OUTPUT]?.raw.toString())
|
||||
|
||||
const parsedAmounts: { [field: number]: TokenAmount } = {}
|
||||
if (typedValue !== '' && typedValue !== '.' && tokens[independentField]) {
|
||||
try {
|
||||
@ -344,6 +366,13 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
})
|
||||
}, [])
|
||||
|
||||
// reset field if sending with with swap is cancled
|
||||
useEffect(() => {
|
||||
if (sending && !sendingWithSwap) {
|
||||
onTokenSelection(Field.OUTPUT, null)
|
||||
}
|
||||
}, [onTokenSelection, sending, sendingWithSwap])
|
||||
|
||||
const MIN_ETHER: TokenAmount = chainId && new TokenAmount(WETH[chainId], JSBI.BigInt(parseEther('.01')))
|
||||
|
||||
let maxAmountInput: TokenAmount
|
||||
@ -427,8 +456,6 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
outputApproval &&
|
||||
JSBI.greaterThan(parsedAmounts[Field.OUTPUT].raw, outputApproval.raw)
|
||||
|
||||
const tokenContract: ethers.Contract = useTokenContract(tokens[Field.INPUT]?.address)
|
||||
|
||||
// function for a pure send
|
||||
async function onSend() {
|
||||
setAttemptingTxn(true)
|
||||
@ -454,8 +481,8 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
setShowConfirm(false)
|
||||
})
|
||||
} else {
|
||||
estimate = tokenContract.estimate.transfer
|
||||
method = tokenContract.transfer
|
||||
estimate = tokenContractInput.estimate.transfer
|
||||
method = tokenContractInput.transfer
|
||||
args = [recipient, parsedAmounts[Field.INPUT].raw.toString()]
|
||||
value = ethers.constants.Zero
|
||||
const estimatedGasLimit = await estimate(...args, { value }).catch(e => {
|
||||
@ -592,6 +619,28 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
})
|
||||
}
|
||||
|
||||
async function approveAmount(field) {
|
||||
let estimatedGas
|
||||
let useUserBalance = false
|
||||
const tokenContract = field === Field.INPUT ? tokenContractInput : tokenContractOutput
|
||||
|
||||
estimatedGas = await tokenContract.estimate.approve(routerAddress, ethers.constants.MaxUint256).catch(e => {
|
||||
console.log('Error setting max token approval.')
|
||||
})
|
||||
if (!estimatedGas) {
|
||||
// general fallback for tokens who restrict approval amounts
|
||||
estimatedGas = await tokenContract.estimate.approve(routerAddress, userBalances[field])
|
||||
useUserBalance = true
|
||||
}
|
||||
tokenContract
|
||||
.approve(routerAddress, useUserBalance ? userBalances[field] : ethers.constants.MaxUint256, {
|
||||
gasLimit: calculateGasMargin(estimatedGas, GAS_MARGIN)
|
||||
})
|
||||
.then(response => {
|
||||
addTransaction(response, { approval: tokens[field]?.address })
|
||||
})
|
||||
}
|
||||
|
||||
// errors
|
||||
const [generalError, setGeneralError] = useState<string>('')
|
||||
const [inputError, setInputError] = useState<string>('')
|
||||
@ -806,7 +855,7 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
)}
|
||||
<RowBetween>
|
||||
<Text color="#565A69" fontWeight={500} fontSize={16}>
|
||||
Slippage <Link onClick={() => setShowAdvanced(true)}>(edit limits)</Link>
|
||||
Slippage
|
||||
</Text>
|
||||
<ErrorText warningHigh={warningHigh} fontWeight={500}>
|
||||
{slippageFromTrade && slippageFromTrade.toFixed(4)}%
|
||||
@ -909,7 +958,6 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
onTokenSelection={address => _onTokenSelect(address)}
|
||||
error={inputError}
|
||||
pair={pair}
|
||||
showUnlock={showInputUnlock}
|
||||
hideBalance={true}
|
||||
hideInput={true}
|
||||
showSendWithSwap={true}
|
||||
@ -928,19 +976,30 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
token={tokens[Field.INPUT]}
|
||||
error={inputError}
|
||||
pair={pair}
|
||||
showUnlock={showInputUnlock}
|
||||
onUserInput={onUserInput}
|
||||
onMax={() => {
|
||||
maxAmountInput && onMaxInput(maxAmountInput.toExact())
|
||||
}}
|
||||
onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
|
||||
/>
|
||||
<ColumnCenter>
|
||||
<ArrowWrapper onClick={onSwapTokens}>
|
||||
<ArrowDown size="16" color="#2F80ED" />
|
||||
<ArrowUp size="16" color="#2F80ED" />
|
||||
</ArrowWrapper>
|
||||
</ColumnCenter>
|
||||
{sendingWithSwap ? (
|
||||
<RowBetween>
|
||||
<ArrowWrapper onClick={onSwapTokens}>
|
||||
<ArrowDown size="16" color="#2F80ED" />
|
||||
<ArrowUp size="16" color="#2F80ED" />
|
||||
</ArrowWrapper>
|
||||
<ArrowWrapper onClick={() => setSendingWithSwap(false)} style={{ marginRight: '20px' }}>
|
||||
<TYPE.blue>Remove Swap</TYPE.blue>
|
||||
</ArrowWrapper>
|
||||
</RowBetween>
|
||||
) : (
|
||||
<ColumnCenter>
|
||||
<ArrowWrapper onClick={onSwapTokens}>
|
||||
<ArrowDown size="16" color="#2F80ED" />
|
||||
<ArrowUp size="16" color="#2F80ED" />
|
||||
</ArrowWrapper>
|
||||
</ColumnCenter>
|
||||
)}
|
||||
<CurrencyInputPanel
|
||||
field={Field.OUTPUT}
|
||||
value={formattedAmounts[Field.OUTPUT]}
|
||||
@ -953,7 +1012,6 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)}
|
||||
error={outputError}
|
||||
pair={pair}
|
||||
showUnlock={showOutputUnlock}
|
||||
/>
|
||||
{!noRoute && ( // hide price if new exchange
|
||||
<RowBetween>
|
||||
@ -962,8 +1020,8 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
</Text>
|
||||
<Text fontWeight={500} color="#565A69">
|
||||
{pair
|
||||
? `1 ${tokens[Field.INPUT].symbol} = ${route?.midPrice.toSignificant(6)} ${
|
||||
tokens[Field.OUTPUT].symbol
|
||||
? `1 ${tokens[Field.INPUT]?.symbol} = ${route?.midPrice.toSignificant(6)} ${
|
||||
tokens[Field.OUTPUT]?.symbol
|
||||
}`
|
||||
: '-'}
|
||||
</Text>
|
||||
@ -1009,6 +1067,26 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
Create one now
|
||||
</Link>
|
||||
</RowBetween>
|
||||
) : showOutputUnlock ? (
|
||||
<ButtonLight
|
||||
onClick={() => {
|
||||
!pendingApprovalOutput && approveAmount(Field.OUTPUT)
|
||||
}}
|
||||
disabled={pendingApprovalOutput}
|
||||
>
|
||||
{pendingApprovalOutput ? 'Waiting for unlock' : 'Unlock ' + tokens[Field.OUTPUT]?.symbol}
|
||||
</ButtonLight>
|
||||
) : showInputUnlock ? (
|
||||
<ButtonLight
|
||||
onClick={() => {
|
||||
approveAmount(Field.INPUT)
|
||||
}}
|
||||
disabled={pendingApprovalInput}
|
||||
>
|
||||
{!pendingApprovalInput && pendingApprovalInput
|
||||
? 'Waiting for unlock'
|
||||
: 'Unlock ' + tokens[Field.INPUT]?.symbol}
|
||||
</ButtonLight>
|
||||
) : (
|
||||
<ButtonError
|
||||
onClick={() => {
|
||||
@ -1039,23 +1117,58 @@ function ExchangePage({ sendingInput = false, history }) {
|
||||
</ButtonError>
|
||||
)}
|
||||
</AutoColumn>
|
||||
|
||||
{warningHigh && (
|
||||
<FixedBottom>
|
||||
<GreyCard>
|
||||
<AutoColumn gap="12px">
|
||||
<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. Are you sure you want to continue this trade?
|
||||
</Text>
|
||||
</AutoColumn>
|
||||
</GreyCard>
|
||||
</FixedBottom>
|
||||
)}
|
||||
<FixedBottom>
|
||||
<AutoColumn gap="20px">
|
||||
{importedTokenInput && (
|
||||
<YellowCard>
|
||||
<AutoColumn gap="10px">
|
||||
<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>
|
||||
</AutoRow>
|
||||
<TYPE.subHeader>
|
||||
Please verify the legitimacy of this token before making any transactions.
|
||||
</TYPE.subHeader>
|
||||
</AutoColumn>
|
||||
</YellowCard>
|
||||
)}
|
||||
{importedTokenOutput && (
|
||||
<YellowCard>
|
||||
<AutoColumn gap="10px">
|
||||
<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>
|
||||
<AutoColumn gap="12px">
|
||||
<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. Are you sure you want to continue this trade?
|
||||
</Text>
|
||||
</AutoColumn>
|
||||
</GreyCard>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</FixedBottom>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import styled from 'styled-components'
|
||||
import Row from '../Row'
|
||||
import Menu from '../Menu'
|
||||
import Logo from '../../assets/svg/logo.svg'
|
||||
import Card from '../Card'
|
||||
import Card, { YellowCard } from '../Card'
|
||||
import Web3Status from '../Web3Status'
|
||||
import { X } from 'react-feather'
|
||||
import { Link } from '../../theme'
|
||||
@ -59,6 +59,7 @@ const AccountElement = styled.div`
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
border-radius: 8px;
|
||||
padding-left: ${({ active }) => (active ? '8px' : 0)};
|
||||
white-space: nowrap;
|
||||
|
||||
:focus {
|
||||
border: 1px solid blue;
|
||||
@ -119,6 +120,26 @@ export default function Header() {
|
||||
)}
|
||||
<Web3Status onClick={toggleWalletModal} />
|
||||
</AccountElement>
|
||||
{chainId === 4 && (
|
||||
<YellowCard style={{ width: 'fit-content', marginLeft: '10px' }} padding={'6px'}>
|
||||
Rinkeby Testnet
|
||||
</YellowCard>
|
||||
)}
|
||||
{chainId === 3 && (
|
||||
<YellowCard style={{ width: 'fit-content', marginLeft: '10px' }} padding={'6px'}>
|
||||
Ropsten Testnet
|
||||
</YellowCard>
|
||||
)}
|
||||
{chainId === 5 && (
|
||||
<YellowCard style={{ width: 'fit-content', marginLeft: '10px' }} padding={'6px'}>
|
||||
Goerli Testnet
|
||||
</YellowCard>
|
||||
)}
|
||||
{chainId === 42 && (
|
||||
<YellowCard style={{ width: 'fit-content', marginLeft: '10px' }} padding={'6px'}>
|
||||
Kovan Testnet
|
||||
</YellowCard>
|
||||
)}
|
||||
<Menu />
|
||||
</HeaderElement>
|
||||
<FixedPopupColumn gap="20px">
|
||||
|
@ -66,7 +66,7 @@ const StyledDialogContent = styled(FilteredDialogContent)`
|
||||
min-height: ${minHeight}vh;
|
||||
`}
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
/* overflow: hidden; */
|
||||
border-radius: 10px;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
width: 65vw;
|
||||
|
@ -22,9 +22,9 @@ const tabOrder = [
|
||||
regex: /\/send/
|
||||
},
|
||||
{
|
||||
path: '/supply',
|
||||
path: '/pool',
|
||||
textKey: 'pool',
|
||||
regex: /\/supply/
|
||||
regex: /\/pool/
|
||||
}
|
||||
]
|
||||
|
||||
@ -105,7 +105,7 @@ function NavigationTabs({ location: { pathname }, history }) {
|
||||
{adding || removing ? (
|
||||
<Tabs>
|
||||
<RowBetween style={{ padding: '1rem' }}>
|
||||
<HistoryLink to="/supply">
|
||||
<HistoryLink to="/pool">
|
||||
<ArrowLink />
|
||||
</HistoryLink>
|
||||
<ActiveText>{adding ? 'Add' : 'Remove'} Liquidity</ActiveText>
|
||||
@ -115,7 +115,7 @@ function NavigationTabs({ location: { pathname }, history }) {
|
||||
) : finding ? (
|
||||
<Tabs>
|
||||
<RowBetween style={{ padding: '1rem' }}>
|
||||
<HistoryLink to="/supply">
|
||||
<HistoryLink to="/pool">
|
||||
<ArrowLink />
|
||||
</HistoryLink>
|
||||
<ActiveText>Find a Pool</ActiveText>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { darken } from 'polished'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
import { Percent, Pair } from '@uniswap/sdk'
|
||||
|
||||
@ -13,6 +14,7 @@ import DoubleLogo from '../DoubleLogo'
|
||||
import { Text } from 'rebass'
|
||||
import { GreyCard } from '../../components/Card'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { ChevronDown, ChevronUp } from 'react-feather'
|
||||
import { ButtonSecondary } from '../Button'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
|
||||
@ -20,12 +22,22 @@ const FixedHeightRow = styled(RowBetween)`
|
||||
height: 24px;
|
||||
`
|
||||
|
||||
const HoverCard = styled(Card)`
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
border: 1px solid ${({ theme }) => darken(0.06, theme.bg3)};
|
||||
}
|
||||
`
|
||||
|
||||
function PositionCard({ pairAddress, token0, token1, history, minimal = false, ...rest }) {
|
||||
const { account } = useWeb3React()
|
||||
const allBalances = useAllBalances()
|
||||
|
||||
const tokenAmount0 = allBalances?.[pairAddress]?.[token0.address]
|
||||
const tokenAmount1 = allBalances?.[pairAddress]?.[token1.address]
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
|
||||
const tokenAmount0 = allBalances?.[pairAddress]?.[token0?.address]
|
||||
const tokenAmount1 = allBalances?.[pairAddress]?.[token1?.address]
|
||||
|
||||
const pair = tokenAmount0 && tokenAmount1 && new Pair(tokenAmount0, tokenAmount1)
|
||||
|
||||
@ -46,97 +58,154 @@ function PositionCard({ pairAddress, token0, token1, history, minimal = false, .
|
||||
userPoolBalance &&
|
||||
pair.getLiquidityValue(token1, totalPoolTokens, userPoolBalance, false)
|
||||
|
||||
function DynamicCard({ children, ...rest }) {
|
||||
if (!minimal) {
|
||||
return (
|
||||
<Card border="1px solid #EDEEF2" {...rest}>
|
||||
{children}
|
||||
</Card>
|
||||
)
|
||||
} else {
|
||||
return <GreyCard {...rest}>{children}</GreyCard>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<DynamicCard {...rest}>
|
||||
<AutoColumn gap="20px">
|
||||
<FixedHeightRow>
|
||||
<RowFixed>
|
||||
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={24} />
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{token0?.symbol}:{token1?.symbol}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{userPoolBalance ? userPoolBalance.toFixed(6) : '-'}
|
||||
</Text>
|
||||
</FixedHeightRow>
|
||||
<AutoColumn gap="12px">
|
||||
if (minimal) {
|
||||
return (
|
||||
<GreyCard {...rest}>
|
||||
<AutoColumn gap="20px">
|
||||
<FixedHeightRow>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{token0?.symbol} Deposited:
|
||||
</Text>
|
||||
{token0Deposited ? (
|
||||
<RowFixed>
|
||||
<TokenLogo address={token0?.address || ''} />
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{token0Deposited?.toFixed(8)}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
<RowFixed>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
Current Position
|
||||
</Text>
|
||||
</RowFixed>
|
||||
</FixedHeightRow>
|
||||
<FixedHeightRow>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{token1?.symbol} Deposited:
|
||||
</Text>
|
||||
{token1Deposited ? (
|
||||
<RowFixed>
|
||||
<TokenLogo address={token1.address || ''} />
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{token1Deposited?.toFixed(8)}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
<FixedHeightRow onClick={() => setShowMore(!showMore)}>
|
||||
<RowFixed>
|
||||
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={24} />
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{token0?.symbol}:{token1?.symbol}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{userPoolBalance ? userPoolBalance.toFixed(6) : '-'}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
</FixedHeightRow>
|
||||
{!minimal && (
|
||||
<AutoColumn gap="12px">
|
||||
<FixedHeightRow>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
Your pool share:
|
||||
</Text>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{poolTokenPercentage ? poolTokenPercentage.toFixed(2) + '%' : '-'}
|
||||
{token0?.symbol}:
|
||||
</Text>
|
||||
{token0Deposited ? (
|
||||
<RowFixed>
|
||||
{!minimal && <TokenLogo address={token0?.address || ''} />}
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{token0Deposited?.toFixed(8)}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</FixedHeightRow>
|
||||
<FixedHeightRow>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{token1?.symbol}:
|
||||
</Text>
|
||||
{token1Deposited ? (
|
||||
<RowFixed>
|
||||
{!minimal && <TokenLogo address={token1?.address || ''} />}
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{token1Deposited?.toFixed(8)}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</FixedHeightRow>
|
||||
</AutoColumn>
|
||||
</AutoColumn>
|
||||
</GreyCard>
|
||||
)
|
||||
} else
|
||||
return (
|
||||
<HoverCard {...rest} onClick={() => setShowMore(!showMore)}>
|
||||
<AutoColumn gap="20px">
|
||||
<FixedHeightRow>
|
||||
<RowFixed>
|
||||
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={24} />
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{token0?.symbol}:{token1?.symbol}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{userPoolBalance ? userPoolBalance.toFixed(6) : '-'}
|
||||
</Text>
|
||||
{showMore ? (
|
||||
<ChevronUp size="30" style={{ marginLeft: '10px' }} />
|
||||
) : (
|
||||
<ChevronDown size="30" style={{ marginLeft: '10px' }} />
|
||||
)}
|
||||
</RowFixed>
|
||||
</FixedHeightRow>
|
||||
{showMore && (
|
||||
<AutoColumn gap="12px">
|
||||
<FixedHeightRow>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{token0?.symbol}:
|
||||
</Text>
|
||||
{token0Deposited ? (
|
||||
<RowFixed>
|
||||
{!minimal && <TokenLogo address={token0?.address || ''} />}
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{token0Deposited?.toFixed(8)}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</FixedHeightRow>
|
||||
<FixedHeightRow>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{token1?.symbol}:
|
||||
</Text>
|
||||
{token1Deposited ? (
|
||||
<RowFixed>
|
||||
{!minimal && <TokenLogo address={token1?.address || ''} />}
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{token1Deposited?.toFixed(8)}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</FixedHeightRow>
|
||||
{!minimal && (
|
||||
<FixedHeightRow>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
Your pool share:
|
||||
</Text>
|
||||
<Text color="#888D9B" fontSize={16} fontWeight={500}>
|
||||
{poolTokenPercentage ? poolTokenPercentage.toFixed(2) + '%' : '-'}
|
||||
</Text>
|
||||
</FixedHeightRow>
|
||||
)}
|
||||
</AutoColumn>
|
||||
)}
|
||||
{showMore && (
|
||||
<RowBetween>
|
||||
<ButtonSecondary
|
||||
width="48%"
|
||||
onClick={() => {
|
||||
history.push('/add/' + token0?.address + '-' + token1?.address)
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</ButtonSecondary>
|
||||
<ButtonSecondary
|
||||
width="48%"
|
||||
onClick={() => {
|
||||
history.push('/remove/' + token0?.address + '-' + token1?.address)
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</ButtonSecondary>
|
||||
</RowBetween>
|
||||
)}
|
||||
</AutoColumn>
|
||||
{!minimal && (
|
||||
<RowBetween>
|
||||
<ButtonSecondary
|
||||
width="48%"
|
||||
onClick={() => {
|
||||
history.push('/add/' + token0?.address + '-' + token1?.address)
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</ButtonSecondary>
|
||||
<ButtonSecondary
|
||||
width="48%"
|
||||
onClick={() => {
|
||||
history.push('/remove/' + token0?.address + '-' + token1?.address)
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</ButtonSecondary>
|
||||
</RowBetween>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</DynamicCard>
|
||||
)
|
||||
</HoverCard>
|
||||
)
|
||||
}
|
||||
|
||||
export default withRouter(PositionCard)
|
||||
|
@ -105,10 +105,11 @@ const PaddedItem = styled(RowBetween)`
|
||||
`
|
||||
|
||||
const MenuItem = styled(PaddedItem)`
|
||||
cursor: pointer;
|
||||
cursor: ${({ disabled }) => !disabled && 'pointer'};
|
||||
:hover {
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme, disabled }) => !disabled && theme.bg2};
|
||||
}
|
||||
opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
|
||||
`
|
||||
// filters on results
|
||||
const FILTERS = {
|
||||
@ -132,6 +133,7 @@ function SearchModal({
|
||||
|
||||
const allTokens = useAllTokens()
|
||||
const allPairs = useAllPairs()
|
||||
|
||||
const allBalances = useAllBalances()
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
@ -175,9 +177,6 @@ function SearchModal({
|
||||
}
|
||||
})
|
||||
.map(k => {
|
||||
if (k === hiddenToken) {
|
||||
return false
|
||||
}
|
||||
return {
|
||||
name: allTokens[k].name,
|
||||
symbol: allTokens[k].symbol,
|
||||
@ -185,7 +184,7 @@ function SearchModal({
|
||||
balance: allBalances?.[account]?.[k]
|
||||
}
|
||||
})
|
||||
}, [allTokens, allBalances, account, sortDirection, hiddenToken])
|
||||
}, [allTokens, allBalances, account, sortDirection])
|
||||
|
||||
const filteredTokenList = useMemo(() => {
|
||||
return tokenList.filter(tokenEntry => {
|
||||
@ -350,7 +349,17 @@ function SearchModal({
|
||||
const zeroBalance = balance && JSBI.equal(JSBI.BigInt(0), balance.raw)
|
||||
|
||||
return (
|
||||
<MenuItem key={address} onClick={() => (zeroBalance ? _onTokenSelect(address, true) : _onTokenSelect(address))}>
|
||||
<MenuItem
|
||||
key={address}
|
||||
onClick={() =>
|
||||
hiddenToken && hiddenToken === address
|
||||
? () => {}
|
||||
: zeroBalance
|
||||
? _onTokenSelect(address, true)
|
||||
: _onTokenSelect(address)
|
||||
}
|
||||
disabled={hiddenToken && hiddenToken === address}
|
||||
>
|
||||
<RowFixed>
|
||||
<TokenLogo address={address} size={'24px'} style={{ marginRight: '14px' }} />
|
||||
<Column>
|
||||
@ -428,7 +437,7 @@ function SearchModal({
|
||||
</RowBetween>
|
||||
<Input
|
||||
type={'text'}
|
||||
placeholder={'Search name or address'}
|
||||
placeholder={t('tokenSearchPlaceholder')}
|
||||
value={searchQuery}
|
||||
ref={inputRef}
|
||||
onChange={onInput}
|
||||
@ -447,6 +456,7 @@ function SearchModal({
|
||||
</StyledLink>
|
||||
</Text>
|
||||
)}
|
||||
{filterType === 'tokens' && <Text>Token Symbol</Text>}
|
||||
</div>
|
||||
<div />
|
||||
<Filter title="Your Balances" filter={FILTERS.BALANCES} />
|
||||
|
29
src/components/TxnPopup/index.js
Normal file
29
src/components/TxnPopup/index.js
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react'
|
||||
|
||||
import { Link } from '../../theme/components'
|
||||
import { TYPE } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { getEtherscanLink } from '../../utils'
|
||||
|
||||
export default function TxnPopup({ hash, success }) {
|
||||
const { chainId } = useWeb3React()
|
||||
if (success) {
|
||||
return (
|
||||
<AutoColumn gap="12px">
|
||||
<TYPE.body>Transaction Confirmed</TYPE.body>
|
||||
<TYPE.green>Hash: {hash.slice(0, 8) + '...' + hash.slice(58, 65)}</TYPE.green>
|
||||
<Link href={getEtherscanLink(chainId, hash, 'transaction')}>View on Etherscan</Link>
|
||||
</AutoColumn>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<AutoColumn gap="12px">
|
||||
<TYPE.body>Transaction Failed</TYPE.body>
|
||||
<TYPE.green>Hash: {hash.slice(0, 8) + '...' + hash.slice(58, 65)}</TYPE.green>
|
||||
<Link href={getEtherscanLink(chainId, hash, 'transaction')}>View on Etherscan</Link>
|
||||
</AutoColumn>
|
||||
)
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||
import styled, { css } from 'styled-components'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useWeb3React, UnsupportedChainIdError } from '@web3-react/core'
|
||||
import { darken, transparentize } from 'polished'
|
||||
import { darken } from 'polished'
|
||||
import { Activity } from 'react-feather'
|
||||
|
||||
import { shortenAddress } from '../../utils'
|
||||
@ -10,8 +10,6 @@ import { useENSName } from '../../hooks'
|
||||
import WalletModal from '../WalletModal'
|
||||
import { useAllTransactions } from '../../contexts/Transactions'
|
||||
import { useWalletModalToggle } from '../../contexts/Application'
|
||||
import { Spinner } from '../../theme'
|
||||
import Circle from '../../assets/images/circle.svg'
|
||||
import { injected, walletconnect, walletlink, fortmatic, portis } from '../../connectors'
|
||||
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
|
||||
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
|
||||
@ -46,39 +44,40 @@ const Web3StatusError = styled(Web3StatusGeneric)`
|
||||
`
|
||||
|
||||
const Web3StatusConnect = styled(Web3StatusGeneric)`
|
||||
background-color: transparent;
|
||||
border: 1px solid ${({ theme }) => theme.blue1};
|
||||
background-color: ${({ theme }) => theme.blue4};
|
||||
border: 1px solid ${({ theme }) => theme.blue4};
|
||||
border: none;
|
||||
color: ${({ theme }) => theme.blue1};
|
||||
font-weight: 500;
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
border: 1px solid ${({ theme }) => darken(0.1, theme.blue1)};
|
||||
border: 1px solid ${({ theme }) => darken(0.1, theme.blue4)};
|
||||
color: ${({ theme }) => darken(0.1, theme.blue1)};
|
||||
}
|
||||
|
||||
${({ faded }) =>
|
||||
faded &&
|
||||
css`
|
||||
background-color: transparent;
|
||||
border: 1px solid ${({ theme }) => theme.blue1};
|
||||
background-color: ${({ theme }) => theme.blue5};
|
||||
border: 1px solid ${({ theme }) => theme.blue5};
|
||||
color: ${({ theme }) => theme.blue1};
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
border: 1px solid ${({ theme }) => darken(0.1, theme.blue1)};
|
||||
border: 1px solid ${({ theme }) => darken(0.1, theme.blue4)};
|
||||
color: ${({ theme }) => darken(0.1, theme.blue1)};
|
||||
}
|
||||
`}
|
||||
`
|
||||
|
||||
const Web3StatusConnected = styled(Web3StatusGeneric)`
|
||||
background-color: ${({ pending, theme }) => (pending ? theme.blue5 : theme.bg1)};
|
||||
background-color: ${({ pending, theme }) => (pending ? theme.blue1 : theme.bg1)};
|
||||
border: 1px solid ${({ pending, theme }) => (pending ? theme.blue1 : theme.bg3)};
|
||||
color: ${({ pending, theme }) => (pending ? theme.blue1 : theme.text3)};
|
||||
color: ${({ pending, theme }) => (pending ? theme.white : theme.text3)};
|
||||
font-weight: 400;
|
||||
:hover {
|
||||
background-color: ${({ pending, theme }) => (pending ? transparentize(0.9, theme.blue1) : darken(0.05, theme.bg1))};
|
||||
background-color: ${({ pending, theme }) => (pending ? darken(0.05, theme.blue1) : darken(0.05, theme.bg1))};
|
||||
|
||||
:focus {
|
||||
border: 1px solid ${({ pending, theme }) => (pending ? darken(0.1, theme.blue1) : darken(0.1, theme.bg3))};
|
||||
@ -93,6 +92,8 @@ const Text = styled.p`
|
||||
white-space: nowrap;
|
||||
margin: 0 0.5rem 0 0.25rem;
|
||||
font-size: 0.83rem;
|
||||
width: fit-content;
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
const NetworkIcon = styled(Activity)`
|
||||
@ -102,10 +103,6 @@ const NetworkIcon = styled(Activity)`
|
||||
height: 16px;
|
||||
`
|
||||
|
||||
const SpinnerWrapper = styled(Spinner)`
|
||||
margin: 0 0.25rem 0 0.25rem;
|
||||
`
|
||||
|
||||
const IconWrapper = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap};
|
||||
align-items: center;
|
||||
@ -166,9 +163,12 @@ export default function Web3Status() {
|
||||
if (account) {
|
||||
return (
|
||||
<Web3StatusConnected onClick={toggleWalletModal} pending={hasPendingTransactions}>
|
||||
{hasPendingTransactions && <SpinnerWrapper src={Circle} alt="loader" />}
|
||||
<Text>{ENSName || shortenAddress(account)}</Text>
|
||||
{getStatusIcon()}
|
||||
{hasPendingTransactions ? (
|
||||
<Text>{pending?.length} Pending</Text>
|
||||
) : (
|
||||
<Text>{ENSName || shortenAddress(account)}</Text>
|
||||
)}
|
||||
{!hasPendingTransactions && getStatusIcon()}
|
||||
</Web3StatusConnected>
|
||||
)
|
||||
} else if (error) {
|
||||
|
@ -5,6 +5,7 @@ import { INITIAL_TOKENS_CONTEXT } from './Tokens'
|
||||
import { ChainId, WETH, Token, TokenAmount, Pair, JSBI } from '@uniswap/sdk'
|
||||
|
||||
const UPDATE = 'UPDATE'
|
||||
const UPDATE_PAIR_ENTITY = 'UPDATE_PAIR_ENTITY'
|
||||
|
||||
const ALL_PAIRS: [Token, Token][] = [
|
||||
[
|
||||
@ -12,8 +13,8 @@ const ALL_PAIRS: [Token, Token][] = [
|
||||
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735'] //dai
|
||||
],
|
||||
[
|
||||
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735'],
|
||||
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0x8ab15C890E5C03B5F240f2D146e3DF54bEf3Df44']
|
||||
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735'], // dai
|
||||
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0x8ab15C890E5C03B5F240f2D146e3DF54bEf3Df44'] // mkr
|
||||
]
|
||||
]
|
||||
|
||||
@ -22,16 +23,20 @@ const PAIR_MAP: {
|
||||
} = ALL_PAIRS.reduce((pairMap, [tokenA, tokenB]) => {
|
||||
const tokens: [Token, Token] = tokenA?.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]
|
||||
// ensure exchanges are unique
|
||||
if (pairMap?.[tokens[0].chainId]?.[tokens[0].address]?.[tokens[1].address] !== undefined)
|
||||
if (pairMap?.[tokens[0].chainId]?.[tokens[0].address]?.[tokens[1].address]?.address !== undefined)
|
||||
throw Error(`Duplicate exchange: ${tokenA} ${tokenB}`)
|
||||
return {
|
||||
...pairMap,
|
||||
[tokens[0].chainId]: {
|
||||
...pairMap?.[tokens[0].chainId],
|
||||
[tokens[0].address]: {
|
||||
...pairMap?.[tokens[0].chainId]?.[tokens[0].address],
|
||||
[tokens[1].address]: Pair.getAddress(...tokens)
|
||||
}
|
||||
addresses: {
|
||||
...pairMap?.[tokens[0].chainId]?.['addresses'],
|
||||
[tokens[0].address]: {
|
||||
...pairMap?.[tokens[0].chainId]?.[tokens[0].address],
|
||||
[tokens[1].address]: Pair.getAddress(...tokens)
|
||||
}
|
||||
},
|
||||
entities: {}
|
||||
}
|
||||
}
|
||||
}, {})
|
||||
@ -53,9 +58,25 @@ function reducer(state, { type, payload }) {
|
||||
...state,
|
||||
[tokensSorted[0].chainId]: {
|
||||
...state?.[tokensSorted[0].chainId],
|
||||
[tokensSorted[0].address]: {
|
||||
...state?.[tokensSorted[0].chainId]?.[tokensSorted[0].address],
|
||||
[tokensSorted[1].address]: Pair.getAddress(tokensSorted[0], tokensSorted[1])
|
||||
addresses: {
|
||||
...state?.[tokensSorted[0].chainId]['addresses'],
|
||||
[tokensSorted[0].address]: {
|
||||
...state?.[tokensSorted[0].chainId]?.[tokensSorted[0].address],
|
||||
[tokensSorted[1].address]: Pair.getAddress(tokensSorted[0], tokensSorted[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case UPDATE_PAIR_ENTITY: {
|
||||
const { pairAddress, pair, chainId } = payload
|
||||
return {
|
||||
...state,
|
||||
[chainId]: {
|
||||
...state?.[chainId],
|
||||
entities: {
|
||||
...state?.[chainId]?.['entities'],
|
||||
[pairAddress]: pair
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,8 +94,16 @@ export default function Provider({ children }) {
|
||||
dispatch({ type: UPDATE, payload: { chainId, tokens } })
|
||||
}, [])
|
||||
|
||||
const updatePairEntity = useCallback((pairAddress, pair, chainId) => {
|
||||
dispatch({ type: UPDATE_PAIR_ENTITY, payload: { pairAddress, pair, chainId } })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<PairContext.Provider value={useMemo(() => [state, { update }], [state, update])}>{children}</PairContext.Provider>
|
||||
<PairContext.Provider
|
||||
value={useMemo(() => [state, { update, updatePairEntity }], [state, update, updatePairEntity])}
|
||||
>
|
||||
{children}
|
||||
</PairContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@ -84,7 +113,7 @@ export function usePairAddress(tokenA?: Token, tokenB?: Token): string | undefin
|
||||
|
||||
const tokens: [Token, Token] = tokenA && tokenB && tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]
|
||||
|
||||
const address = state?.[chainId]?.[tokens[0]?.address]?.[tokens[1]?.address]
|
||||
const address = state?.[chainId]?.['addresses']?.[tokens[0]?.address]?.[tokens[1]?.address]
|
||||
|
||||
useEffect(() => {
|
||||
if (address === undefined && tokenA && tokenB) {
|
||||
@ -97,36 +126,29 @@ export function usePairAddress(tokenA?: Token, tokenB?: Token): string | undefin
|
||||
}
|
||||
|
||||
export function usePair(tokenA?: Token, tokenB?: Token): Pair | undefined {
|
||||
const { chainId } = useWeb3React()
|
||||
const [state, { updatePairEntity }] = usePairContext()
|
||||
|
||||
const address = usePairAddress(tokenA, tokenB)
|
||||
const pair = state?.[chainId]?.['entities']?.[address]
|
||||
|
||||
const tokenAmountA = useAddressBalance(address, tokenA)
|
||||
const tokenAmountB = useAddressBalance(address, tokenB)
|
||||
const [pair, setPair] = useState<Pair>()
|
||||
|
||||
useEffect(() => {
|
||||
if (!pair && tokenAmountA && tokenAmountB) {
|
||||
setPair(new Pair(tokenAmountA, tokenAmountB))
|
||||
updatePairEntity(address, new Pair(tokenAmountA, tokenAmountB), chainId)
|
||||
}
|
||||
}, [pair, tokenAmountA, tokenAmountB])
|
||||
}, [pair, tokenAmountA, tokenAmountB, address, updatePairEntity, chainId])
|
||||
|
||||
return useMemo(() => {
|
||||
return pair
|
||||
}, [pair])
|
||||
}
|
||||
|
||||
export function useAllPairsRaw() {
|
||||
const { chainId } = useWeb3React()
|
||||
const [state] = usePairContext()
|
||||
|
||||
const allExchangeDetails = state?.[chainId]
|
||||
|
||||
return allExchangeDetails
|
||||
return pair
|
||||
}
|
||||
|
||||
export function useAllPairs() {
|
||||
const { chainId } = useWeb3React()
|
||||
const [state] = usePairContext()
|
||||
|
||||
const allPairDetails = state?.[chainId]
|
||||
const allPairDetails = state?.[chainId]?.['addresses']
|
||||
|
||||
const allPairs = useMemo(() => {
|
||||
if (!allPairDetails) {
|
||||
@ -136,10 +158,14 @@ export function useAllPairs() {
|
||||
Object.keys(allPairDetails).map(token0Address => {
|
||||
return Object.keys(allPairDetails[token0Address]).map(token1Address => {
|
||||
const pairAddress = allPairDetails[token0Address][token1Address]
|
||||
return (formattedExchanges[pairAddress] = {
|
||||
token0: token0Address,
|
||||
token1: token1Address
|
||||
})
|
||||
if (pairAddress) {
|
||||
return (formattedExchanges[pairAddress] = {
|
||||
token0: token0Address,
|
||||
token1: token1Address
|
||||
})
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
})
|
||||
return formattedExchanges
|
||||
|
@ -30,7 +30,7 @@ function reducer(state: RouteState, { type, payload }) {
|
||||
[chainId]: {
|
||||
...state[chainId],
|
||||
[tokens[0]]: {
|
||||
...state[tokens[0]],
|
||||
...state[chainId]?.[tokens[0]],
|
||||
[tokens[1]]: {
|
||||
route
|
||||
}
|
||||
@ -77,14 +77,14 @@ export function useRoute(tokenA: Token, tokenB: Token) {
|
||||
const defaultPair = usePair(tokenA, tokenB)
|
||||
|
||||
// get token<->WETH pairs
|
||||
const aToETH = usePair(tokenA && !tokenA.equals(WETH[chainId]) ? tokenA : null, WETH[chainId])
|
||||
const bToETH = usePair(tokenB && !tokenB.equals(WETH[chainId]) ? tokenB : null, WETH[chainId])
|
||||
const aToETH = usePair(tokenA && chainId && !tokenA.equals(WETH[chainId]) ? tokenA : null, WETH[chainId])
|
||||
const bToETH = usePair(tokenB && chainId && !tokenB.equals(WETH[chainId]) ? tokenB : null, WETH[chainId])
|
||||
|
||||
// needs to route through WETH
|
||||
const requiresHop =
|
||||
defaultPair &&
|
||||
JSBI.equal(defaultPair.reserve0.raw, JSBI.BigInt(0)) &&
|
||||
JSBI.equal(defaultPair.reserve1.raw, JSBI.BigInt(0))
|
||||
JSBI.equal(defaultPair?.reserve0?.raw, JSBI.BigInt(0)) &&
|
||||
JSBI.equal(defaultPair?.reserve1?.raw, JSBI.BigInt(0))
|
||||
|
||||
useEffect(() => {
|
||||
if (!route && tokenA && tokenB) {
|
||||
|
@ -6,6 +6,9 @@ import { isAddress, getTokenName, getTokenSymbol, getTokenDecimals, safeAccess }
|
||||
const UPDATE = 'UPDATE'
|
||||
|
||||
export const ALL_TOKENS = [
|
||||
//Mainnet Tokens
|
||||
WETH[ChainId.MAINNET],
|
||||
|
||||
// Rinkeby Tokens
|
||||
WETH[ChainId.RINKEBY],
|
||||
new Token(ChainId.RINKEBY, '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735', 18, 'DAI', 'Dai Stablecoin'),
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
|
||||
|
||||
import TxnPopup from '../components/TxnPopup'
|
||||
|
||||
import { useWeb3React } from '../hooks'
|
||||
import { safeAccess } from '../utils'
|
||||
import { useBlockNumber } from './Application'
|
||||
import { useBlockNumber, usePopups } from './Application'
|
||||
|
||||
const RESPONSE = 'response'
|
||||
const CUSTOM_DATA = 'CUSTOM_DATA'
|
||||
@ -110,6 +112,9 @@ export function Updater() {
|
||||
const [state, { check, finalize }] = useTransactionsContext()
|
||||
const allTransactions = safeAccess(state, [chainId]) || {}
|
||||
|
||||
// show popup on confirm
|
||||
const [, addPopup] = usePopups()
|
||||
|
||||
useEffect(() => {
|
||||
if ((chainId || chainId === 0) && library) {
|
||||
let stale = false
|
||||
@ -126,6 +131,7 @@ export function Updater() {
|
||||
check(chainId, hash, globalBlockNumber)
|
||||
} else {
|
||||
finalize(chainId, hash, receipt)
|
||||
addPopup(<TxnPopup hash={hash} success={true} />)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -138,7 +144,7 @@ export function Updater() {
|
||||
stale = true
|
||||
}
|
||||
}
|
||||
}, [chainId, library, allTransactions, globalBlockNumber, check, finalize])
|
||||
}, [chainId, library, allTransactions, globalBlockNumber, check, finalize, addPopup])
|
||||
|
||||
return null
|
||||
}
|
||||
|
@ -1,12 +1,16 @@
|
||||
import React, { Suspense, lazy } from 'react'
|
||||
import React, { Suspense, lazy, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { transparentize } from 'polished'
|
||||
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'
|
||||
|
||||
import { Text } from 'rebass'
|
||||
import Header from '../components/Header'
|
||||
import NavigationTabs from '../components/NavigationTabs'
|
||||
import Web3ReactManager from '../components/Web3ReactManager'
|
||||
import { useWeb3React } from '../hooks'
|
||||
import { TYPE } from '../theme'
|
||||
import { Hover } from '../theme/components'
|
||||
import { AutoColumn } from '../components/Column'
|
||||
import { PinkCard } from '../components/Card'
|
||||
import { ButtonPink } from '../components/Button'
|
||||
import { isAddress, getAllQueryParams } from '../utils'
|
||||
|
||||
const Swap = lazy(() => import('./Swap'))
|
||||
@ -29,29 +33,6 @@ const HeaderWrapper = styled.div`
|
||||
justify-content: space-between;
|
||||
`
|
||||
|
||||
const BetaMessage = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
cursor: pointer;
|
||||
max-height: 40px;
|
||||
flex: 1 0 auto;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 0.5rem 1rem;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid ${({ theme }) => transparentize(0.6, theme.pink1)};
|
||||
background-color: ${({ theme }) => transparentize(0.9, theme.pink1)};
|
||||
border-radius: 1rem;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: ${({ theme }) => theme.pink1};
|
||||
min-width: 380px;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
`
|
||||
|
||||
const BodyWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -61,6 +42,12 @@ const BodyWrapper = styled.div`
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding-top: 40px;
|
||||
|
||||
& > * {
|
||||
max-width: calc(355px + 4rem);
|
||||
width: 90%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
`
|
||||
|
||||
const Body = styled.div`
|
||||
@ -76,7 +63,7 @@ const Body = styled.div`
|
||||
export default function App() {
|
||||
const params = getAllQueryParams()
|
||||
|
||||
const { chainId } = useWeb3React()
|
||||
const [showMigrationMessage, toggleShowMigrationMessage] = useState(true)
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -86,17 +73,18 @@ export default function App() {
|
||||
<Header />
|
||||
</HeaderWrapper>
|
||||
<BodyWrapper>
|
||||
{chainId === 1 && (
|
||||
<BetaMessage>Incorrect network. This site is intended to be used on Ethereum testnets only.</BetaMessage>
|
||||
{showMigrationMessage && (
|
||||
<PinkCard padding="20px">
|
||||
<AutoColumn justify={'center'} gap={'20px'}>
|
||||
<TYPE.largeHeader>Uniswap has upgraded.</TYPE.largeHeader>
|
||||
<Text textAlign="center">Are you a liquidity provider? Upgrade now using the migration helper.</Text>
|
||||
<ButtonPink width={'265px'}>Migrate your liquidity </ButtonPink>
|
||||
<Hover onClick={() => toggleShowMigrationMessage(false)}>
|
||||
<Text textAlign="center">Dismiss</Text>
|
||||
</Hover>
|
||||
</AutoColumn>
|
||||
</PinkCard>
|
||||
)}
|
||||
|
||||
{(chainId === 3 || chainId === 4 || chainId === 5 || chainId === 42) && (
|
||||
<BetaMessage>
|
||||
Connected to{' '}
|
||||
{chainId === 3 ? 'Ropsten ' : chainId === 4 ? 'Rinkeby' : chainId === 5 ? 'Goerli' : 'Kovan'} testnet.
|
||||
</BetaMessage>
|
||||
)}
|
||||
|
||||
<Body>
|
||||
<Web3ReactManager>
|
||||
<BrowserRouter>
|
||||
@ -138,7 +126,7 @@ export default function App() {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Route exaxct path={'/supply'} component={() => <Pool params={params} />} />
|
||||
<Route exaxct path={'/pool'} component={() => <Pool params={params} />} />
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
@ -154,7 +142,7 @@ export default function App() {
|
||||
if (t0 && t1) {
|
||||
return <Add params={params} token0={t0} token1={t1} />
|
||||
} else {
|
||||
return <Redirect to={{ pathname: '/supply' }} />
|
||||
return <Redirect to={{ pathname: '/pool' }} />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@ -173,7 +161,7 @@ export default function App() {
|
||||
if (t0 && t1) {
|
||||
return <Remove params={params} token0={t0} token1={t1} />
|
||||
} else {
|
||||
return <Redirect to={{ pathname: '/supply' }} />
|
||||
return <Redirect to={{ pathname: '/pool' }} />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -11,23 +11,23 @@ import PositionCard from '../../components/PositionCard'
|
||||
import ConfirmationModal from '../../components/ConfirmationModal'
|
||||
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||
import { Text } from 'rebass'
|
||||
import { TYPE } from '../../theme'
|
||||
import { Plus } from 'react-feather'
|
||||
import { ButtonPrimary } from '../../components/Button'
|
||||
import { AutoColumn, ColumnCenter } from '../../components/Column'
|
||||
import { ButtonPrimary, ButtonLight } from '../../components/Button'
|
||||
import Row, { RowBetween, RowFlat, RowFixed } from '../../components/Row'
|
||||
|
||||
import { useToken } from '../../contexts/Tokens'
|
||||
import { usePopups } from '../../contexts/Application'
|
||||
import { useWeb3React } from '../../hooks'
|
||||
import { useAddressBalance } from '../../contexts/Balances'
|
||||
import { useAddressAllowance } from '../../contexts/Allowances'
|
||||
import { useTransactionAdder } from '../../contexts/Transactions'
|
||||
import { usePair, useTotalSupply } from '../../contexts/Pairs'
|
||||
import { useWeb3React, useTokenContract } from '../../hooks'
|
||||
import { useTransactionAdder, usePendingApproval } from '../../contexts/Transactions'
|
||||
|
||||
import { BigNumber } from 'ethers/utils'
|
||||
import { ROUTER_ADDRESSES } from '../../constants'
|
||||
import { getRouterContract, calculateGasMargin } from '../../utils'
|
||||
import { TYPE } from '../../theme'
|
||||
|
||||
// denominated in bips
|
||||
const ALLOWED_SLIPPAGE = 200
|
||||
@ -43,7 +43,7 @@ const Wrapper = styled.div`
|
||||
|
||||
const FixedBottom = styled.div`
|
||||
position: absolute;
|
||||
bottom: -200px;
|
||||
bottom: -220px;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
@ -158,6 +158,14 @@ export default function AddLiquidity({ token0, token1 }) {
|
||||
[Field.OUTPUT]: useToken(fieldData[Field.OUTPUT].address)
|
||||
}
|
||||
|
||||
// token contracts for approvals and direct sends
|
||||
const tokenContractInput: ethers.Contract = useTokenContract(tokens[Field.INPUT]?.address)
|
||||
const tokenContractOutput: ethers.Contract = useTokenContract(tokens[Field.OUTPUT]?.address)
|
||||
|
||||
// check on pending approvals for token amounts
|
||||
const pendingApprovalInput = usePendingApproval(tokens[Field.INPUT]?.address)
|
||||
const pendingApprovalOutput = usePendingApproval(tokens[Field.OUTPUT]?.address)
|
||||
|
||||
// exhchange data
|
||||
const pair: Pair = usePair(tokens[Field.INPUT], tokens[Field.OUTPUT])
|
||||
const route: Route = pair ? new Route([pair], tokens[independentField]) : undefined
|
||||
@ -456,6 +464,28 @@ export default function AddLiquidity({ token0, token1 }) {
|
||||
})
|
||||
}
|
||||
|
||||
async function approveAmount(field) {
|
||||
let estimatedGas
|
||||
let useUserBalance = false
|
||||
const tokenContract = field === Field.INPUT ? tokenContractInput : tokenContractOutput
|
||||
|
||||
estimatedGas = await tokenContract.estimate.approve(routerAddress, ethers.constants.MaxUint256).catch(e => {
|
||||
console.log('Error setting max token approval.')
|
||||
})
|
||||
if (!estimatedGas) {
|
||||
// general fallback for tokens who restrict approval amounts
|
||||
estimatedGas = await tokenContract.estimate.approve(routerAddress, userBalances[field])
|
||||
useUserBalance = true
|
||||
}
|
||||
tokenContract
|
||||
.approve(routerAddress, useUserBalance ? userBalances[field] : ethers.constants.MaxUint256, {
|
||||
gasLimit: calculateGasMargin(estimatedGas, GAS_MARGIN)
|
||||
})
|
||||
.then(response => {
|
||||
addTransaction(response, { approval: tokens[field]?.address })
|
||||
})
|
||||
}
|
||||
|
||||
const modalHeader = () => {
|
||||
return (
|
||||
<AutoColumn gap="20px">
|
||||
@ -569,7 +599,6 @@ export default function AddLiquidity({ token0, token1 }) {
|
||||
onTokenSelection={onTokenSelection}
|
||||
error={inputError}
|
||||
pair={pair}
|
||||
showUnlock={showInputUnlock}
|
||||
disableTokenSelect
|
||||
/>
|
||||
<ColumnCenter>
|
||||
@ -587,7 +616,6 @@ export default function AddLiquidity({ token0, token1 }) {
|
||||
onTokenSelection={onTokenSelection}
|
||||
error={outputError}
|
||||
pair={pair}
|
||||
showUnlock={showOutputUnlock}
|
||||
disableTokenSelect
|
||||
/>
|
||||
{!noLiquidity && (
|
||||
@ -599,16 +627,34 @@ export default function AddLiquidity({ token0, token1 }) {
|
||||
</div>
|
||||
</RowBetween>
|
||||
)}
|
||||
<ButtonPrimary
|
||||
onClick={() => {
|
||||
setShowConfirm(true)
|
||||
}}
|
||||
disabled={!isValid}
|
||||
>
|
||||
<Text fontSize={20} fontWeight={500}>
|
||||
{generalError ? generalError : inputError ? inputError : outputError ? outputError : 'Supply'}
|
||||
</Text>
|
||||
</ButtonPrimary>
|
||||
{showOutputUnlock ? (
|
||||
<ButtonLight
|
||||
onClick={() => {
|
||||
approveAmount(Field.OUTPUT)
|
||||
}}
|
||||
>
|
||||
{pendingApprovalOutput ? 'Waiting for unlock' : 'Unlock ' + tokens[Field.OUTPUT]?.symbol}
|
||||
</ButtonLight>
|
||||
) : showInputUnlock ? (
|
||||
<ButtonLight
|
||||
onClick={() => {
|
||||
approveAmount(Field.INPUT)
|
||||
}}
|
||||
>
|
||||
{pendingApprovalInput ? 'Waiting for unlock' : 'Unlock ' + tokens[Field.INPUT]?.symbol}
|
||||
</ButtonLight>
|
||||
) : (
|
||||
<ButtonPrimary
|
||||
onClick={() => {
|
||||
setShowConfirm(true)
|
||||
}}
|
||||
disabled={!isValid}
|
||||
>
|
||||
<Text fontSize={20} fontWeight={500}>
|
||||
{generalError ? generalError : inputError ? inputError : outputError ? outputError : 'Supply'}
|
||||
</Text>
|
||||
</ButtonPrimary>
|
||||
)}
|
||||
{!noLiquidity && (
|
||||
<FixedBottom>
|
||||
<PositionCard
|
||||
|
@ -10,6 +10,7 @@ import DoubleLogo from '../../components/DoubleLogo'
|
||||
import PositionCard from '../../components/PositionCard'
|
||||
import ConfirmationModal from '../../components/ConfirmationModal'
|
||||
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||
import { TYPE } from '../../theme'
|
||||
import { Text } from 'rebass'
|
||||
import { LightCard } from '../../components/Card'
|
||||
import { ButtonPrimary } from '../../components/Button'
|
||||
@ -29,7 +30,6 @@ import { BigNumber } from 'ethers/utils'
|
||||
import { splitSignature } from '@ethersproject/bytes'
|
||||
import { ROUTER_ADDRESSES } from '../../constants'
|
||||
import { getRouterContract, calculateGasMargin } from '../../utils'
|
||||
import { TYPE } from '../../theme'
|
||||
|
||||
// denominated in seconds
|
||||
const DEADLINE_FROM_NOW = 60 * 15
|
||||
@ -42,7 +42,7 @@ const Wrapper = styled.div`
|
||||
|
||||
const FixedBottom = styled.div`
|
||||
position: absolute;
|
||||
bottom: -200px;
|
||||
bottom: -220px;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
|
@ -3,15 +3,13 @@ import styled from 'styled-components'
|
||||
import { JSBI } from '@uniswap/sdk'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
|
||||
import Card from '../../components/Card'
|
||||
import Question from '../../components/Question'
|
||||
import SearchModal from '../../components/SearchModal'
|
||||
import PositionCard from '../../components/PositionCard'
|
||||
import Row, { RowBetween } from '../../components/Row'
|
||||
import { Link } from '../../theme'
|
||||
import { Text } from 'rebass'
|
||||
import { AutoColumn } from '../../components/Column'
|
||||
import { ArrowRight } from 'react-feather'
|
||||
import { RowBetween } from '../../components/Row'
|
||||
import { ButtonPrimary } from '../../components/Button'
|
||||
|
||||
import { useAllPairs } from '../../contexts/Pairs'
|
||||
@ -23,11 +21,6 @@ const Positions = styled.div`
|
||||
position: relative;
|
||||
margin-top: 38px;
|
||||
`
|
||||
const FixedBottom = styled.div`
|
||||
position: absolute;
|
||||
bottom: -260px;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
function Supply({ history }) {
|
||||
const { account } = useWeb3React()
|
||||
@ -40,6 +33,8 @@ function Supply({ history }) {
|
||||
// initiate listener for LP balances
|
||||
useAccountLPBalances(account)
|
||||
|
||||
// console.log(allPairs)
|
||||
|
||||
const filteredExchangeList = Object.keys(allPairs)
|
||||
.filter((pairAddress, i) => {
|
||||
return (
|
||||
@ -67,7 +62,7 @@ function Supply({ history }) {
|
||||
setShowPoolSearch(true)
|
||||
}}
|
||||
>
|
||||
<Text fontSize={20}>Join a pool</Text>
|
||||
<Text fontSize={20}>Join {filteredExchangeList?.length > 0 ? 'another' : 'a'} pool</Text>
|
||||
</ButtonPrimary>
|
||||
<Positions>
|
||||
<AutoColumn gap="20px">
|
||||
@ -80,34 +75,17 @@ function Supply({ history }) {
|
||||
{filteredExchangeList}
|
||||
<AutoColumn justify="center">
|
||||
<Text color="#AEAEAE">
|
||||
{filteredExchangeList?.length !== 0 ? `Don't see your ` : 'Already have '} liquidity?{' '}
|
||||
{filteredExchangeList?.length !== 0 ? `Don't see a pool you joined? ` : 'Already joined a pool?'}{' '}
|
||||
<Link
|
||||
onClick={() => {
|
||||
history.push('/find')
|
||||
}}
|
||||
>
|
||||
Find it now.
|
||||
Find it.
|
||||
</Link>
|
||||
</Text>
|
||||
</AutoColumn>
|
||||
</AutoColumn>
|
||||
<FixedBottom>
|
||||
<Card bg="rgba(255, 255, 255, 0.6)" padding={'24px'}>
|
||||
<AutoColumn gap="30px">
|
||||
<Text fontSize="20px" fontWeight={500}>
|
||||
Earn fees with pooled market making.
|
||||
</Text>
|
||||
<Text fontSize="12px">
|
||||
<Link>Provide liquidity </Link>to earn .3% spread fees for providing market depth.
|
||||
</Text>
|
||||
<Link>
|
||||
<Row>
|
||||
Learn More <ArrowRight size="16" />
|
||||
</Row>
|
||||
</Link>
|
||||
</AutoColumn>
|
||||
</Card>
|
||||
</FixedBottom>
|
||||
</Positions>
|
||||
<SearchModal isOpen={showPoolSearch} onDismiss={() => setShowPoolSearch(false)} />
|
||||
</>
|
||||
|
@ -71,3 +71,9 @@ export const Spinner = styled.img`
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
`
|
||||
|
||||
export const Hover = styled.div`
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
|
@ -75,6 +75,7 @@ const theme = darkMode => ({
|
||||
red1: '#FF6871',
|
||||
green1: '#27AE60',
|
||||
yellow1: '#FFE270',
|
||||
yellow2: '#F3841E',
|
||||
|
||||
//shadows
|
||||
shadow1: darkMode ? '#000' : '#2F80ED',
|
||||
@ -99,7 +100,17 @@ export const TYPE = {
|
||||
</Text>
|
||||
),
|
||||
largeHeader: ({ children, ...rest }) => (
|
||||
<Text fontWeight={600} fontSize={24} color={theme().black} {...rest}>
|
||||
<Text fontWeight={600} fontSize={24} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
),
|
||||
mediumHeader: ({ children, ...rest }) => (
|
||||
<Text fontWeight={500} fontSize={20} color={theme().text1} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
),
|
||||
subHeader: ({ children, ...rest }) => (
|
||||
<Text fontWeight={400} fontSize={14} color={theme().text1} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
),
|
||||
@ -113,6 +124,11 @@ export const TYPE = {
|
||||
{children}
|
||||
</Text>
|
||||
),
|
||||
green: ({ children, ...rest }) => (
|
||||
<Text fontWeight={500} color={theme().green1} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
),
|
||||
gray: ({ children, ...rest }) => (
|
||||
<Text fontWeight={500} color={theme().bg3} {...rest}>
|
||||
{children}
|
||||
|
Loading…
Reference in New Issue
Block a user