Commit 76dbc9fa authored by Ian Lapham's avatar Ian Lapham Committed by GitHub

Multiple UI changes (#700)

* small layout and text changes, bug fixes

* UI changes, add summary to txns

* Remove updates, small changes on send, common bases
parent 006fe9b3
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52 (66869) - http://www.bohemiancoding.com/sketch -->
<title>Path</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g id="check-circle-(1)" transform="translate(1.000000, 0.000000)" stroke="#D9EAFF" stroke-width="2">
<path d="M20,10.08 L20,11 C19.9974678,15.4286859 17.082294,19.328213 12.8353524,20.583901 C8.58841086,21.839589 4.02139355,20.1523121 1.61095509,16.4370663 C-0.799483376,12.7218205 -0.479136554,7.86363898 2.39827419,4.49707214 C5.27568494,1.13050531 10.0247126,0.0575252842 14.07,1.86" id="Path"></path>
</g>
</g>
</svg>
\ No newline at end of file
......@@ -9,14 +9,13 @@ import Copy from './Copy'
import Circle from '../../assets/images/circle.svg'
import { transparentize } from 'polished'
import { useAllTransactions } from '../../contexts/Transactions'
const TransactionStatusWrapper = styled.div`
display: flex;
align-items: center;
min-width: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-break: break-word;
`
const TransactionWrapper = styled.div`
......@@ -25,17 +24,14 @@ const TransactionWrapper = styled.div`
width: 100%;
margin-top: 0.75rem;
a {
/* flex: 1 1 auto; */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
max-width: 250px;
word-break: break-word;
}
`
const TransactionStatusText = styled.span`
margin-left: 0.5rem;
word-break: keep-all;
`
const rotate = keyframes`
......@@ -76,11 +72,14 @@ const ButtonWrapper = styled.div`
export default function Transaction({ hash, pending }) {
const { chainId } = useWeb3React()
const allTransactions = useAllTransactions()
const summary = allTransactions?.[hash]?.response?.summary
return (
<TransactionWrapper key={hash}>
<TransactionStatusWrapper>
<Link href={getEtherscanLink(chainId, hash, 'transaction')}>{hash} </Link>
<Link href={getEtherscanLink(chainId, hash, 'transaction')}>{summary ? summary : hash} </Link>
<Copy toCopy={hash} />
</TransactionStatusWrapper>
{pending ? (
......
......@@ -79,8 +79,8 @@ export default function AddressInputPanel({ initialInput = '', onChange, onError
}, [onChange, data.address, data.name])
useEffect(() => {
onError(error)
}, [onError, error])
onError(error, input)
}, [onError, error, input])
// run parser on debounced input
useEffect(() => {
......
......@@ -31,9 +31,9 @@ export default function AdvancedSettings({ setIsOpen, setDeadline, allowedSlippa
// text translation
const { t } = useTranslation()
const [deadlineInput, setDeadlineInput] = useState(15)
const [deadlineInput, setDeadlineInput] = useState(20)
const [slippageInput, setSlippageInput] = useState()
const [activeIndex, setActiveIndex] = useState(SLIPPAGE_INDEX[3])
const [activeIndex, setActiveIndex] = useState(SLIPPAGE_INDEX[2])
const [slippageInputError, setSlippageInputError] = useState(null) // error
......@@ -65,9 +65,9 @@ export default function AdvancedSettings({ setIsOpen, setDeadline, allowedSlippa
useEffect(() => {
if (allowedSlippage === 10) {
setActiveIndex(1)
} else if (allowedSlippage === 100) {
} else if (allowedSlippage === 50) {
setActiveIndex(2)
} else if (allowedSlippage === 200) {
} else if (allowedSlippage === 100) {
setActiveIndex(3)
} else {
setActiveIndex(4)
......@@ -108,25 +108,25 @@ export default function AdvancedSettings({ setIsOpen, setDeadline, allowedSlippa
padding="4px 6px"
borderRadius="8px"
style={{ marginRight: '16px' }}
width={'60px'}
width={'180px'}
onClick={() => {
setActiveIndex(SLIPPAGE_INDEX[2])
setAllowedSlippage(100)
setAllowedSlippage(50)
}}
>
1%
0.5% (suggested)
</ButtonRadio>
<ButtonRadio
active={SLIPPAGE_INDEX[3] === activeIndex}
padding="4px"
borderRadius="8px"
width={'140px'}
width={'60px'}
onClick={() => {
setActiveIndex(SLIPPAGE_INDEX[3])
setAllowedSlippage(200)
setAllowedSlippage(100)
}}
>
2% (suggested)
1%
</ButtonRadio>
</Row>
<RowFixed>
......
......@@ -115,13 +115,13 @@ export const ButtonEmpty = styled(Base)`
color: black;
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, '#edeef2')};
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg3};
}
&:hover {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, '#edeef2')};
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg3};
}
&:active {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, '#edeef2')};
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg3};
}
&:disabled {
opacity: 50%;
......
......@@ -83,7 +83,7 @@ function ConfirmationModal({
{!pendingConfirmation ? 'Transaction Submitted' : 'Waiting For Confirmation'}
</Text>
<AutoColumn gap="12px" justify={'center'}>
<Text fontWeight={600} fontSize={16} color="#2172E5">
<Text fontWeight={600} fontSize={16} color="#2172E5" textAlign="center">
{pendingText}
</Text>
</AutoColumn>
......@@ -102,7 +102,7 @@ function ConfirmationModal({
</>
)}
{pendingConfirmation && <div style={{ height: '138px' }} />}
<Text fontSize={12} color="#565A69">
<Text fontSize={12} color="#565A69" textAlign="center">
{pendingConfirmation
? 'Confirm this transaction in your wallet'
: `Estimated time until confirmation: 3 min`}
......
......@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'
import { withRouter } from 'react-router-dom'
import { Token, JSBI, WETH } from '@uniswap/sdk'
import Row from '../Row'
import Row, { AutoRow } from '../Row'
import TokenLogo from '../TokenLogo'
import SearchModal from '../SearchModal'
import AddLiquidity from '../../pages/Supply/AddLiquidity'
......@@ -53,7 +53,7 @@ function CreatePool({ history }) {
<BlueCard>
<AutoColumn gap="10px">
<TYPE.blue>{'Step ' + step + '.'} </TYPE.blue>
{step === 1 && <TYPE.blue fontWeight={400}>Select or add your 2nd token to continue.</TYPE.blue>}
{step === 1 && <TYPE.blue fontWeight={400}>Select or add a second token to continue.</TYPE.blue>}
</AutoColumn>
</BlueCard>
<AutoColumn gap="24px">
......@@ -73,11 +73,14 @@ function CreatePool({ history }) {
setActiveField(Fields.TOKEN0)
}}
>
<Row>
<Row align="flex-end">
<TokenLogo address={token0Address} />
<Text fontWeight={500} fontSize={20} marginLeft={'12px'}>
{token0?.symbol}
{token0?.symbol}{' '}
</Text>
<TYPE.darkGray fontWeight={500} fontSize={16} marginLeft={'8px'}>
{token0.symbol === 'ETH' && '(default)'}
</TYPE.darkGray>
</Row>
</ButtonDropwdownLight>
)}
......@@ -109,12 +112,13 @@ function CreatePool({ history }) {
</Row>
</ButtonDropwdownLight>
)}
{pairExists ? (
<TYPE.body>
Pool already exists! Join the pool{' '}
<Link onClick={() => history.push('/add/' + token0Address + '-' + token1Address)}>here.</Link>
</TYPE.body>
<AutoRow padding="10px" justify="center">
<TYPE.body textAlign="center">
Pool already exists!
<Link onClick={() => history.push('/add/' + token0Address + '-' + token1Address)}> Join the pool.</Link>
</TYPE.body>
</AutoRow>
) : (
<ButtonPrimary disabled={step !== 2}>
<Text fontWeight={500} fontSize={20}>
......@@ -133,6 +137,7 @@ function CreatePool({ history }) {
setShowSearch(false)
}}
hiddenToken={activeField === Fields.TOKEN0 ? token1Address : token0Address}
showCommonBases={true}
/>
</AutoColumn>
)
......
......@@ -111,9 +111,9 @@ export default function CurrencyInputPanel({
hideBalance = false,
isExchange = false,
pair = null, // used for double token logo
customBalance = null, // used for LP balances instead of token balance
hideInput = false,
showSendWithSwap = false
showSendWithSwap = false,
otherSelectedTokenAddress = null
}) {
const { t } = useTranslation()
......@@ -134,7 +134,6 @@ export default function CurrencyInputPanel({
{!!token?.address && !atMax && type !== 'OUTPUT' && (
<StyledBalanceMax onClick={onMax}>MAX</StyledBalanceMax>
)}
{/* {renderUnlockButton()} */}
</>
)}
<CurrencySelect
......@@ -165,26 +164,6 @@ export default function CurrencyInputPanel({
</Aligner>
</CurrencySelect>
</InputRow>
{/* {!hideBalance && !!token && (
<LabelRow>
<RowBetween>
<Text fontSize={16} fontWeight={400}>
{'-'}
</Text>
{!!token?.address && type !== 'OUTPUT' && !atMax ? (
<MiniMax onClick={onMax}>MAX</MiniMax>
) : (
<Clear>Clear</Clear>
)}
<ErrorSpan data-tip={'Enter max'} error={!!error} onClick={() => {}}></ErrorSpan>
<ClickableText fontWeight={500} onClick={onMax}>
<TYPE.body>
{customBalance ? customBalance?.toSignificant(4) : userTokenBalance?.toSignificant(4)} {token.symbol}
</TYPE.body>
</ClickableText>
</RowBetween>
</LabelRow>
)} */}
</Container>
{!disableTokenSelect && (
<SearchModal
......@@ -198,6 +177,8 @@ export default function CurrencyInputPanel({
onTokenSelect={onTokenSelection}
showSendWithSwap={showSendWithSwap}
hiddenToken={token?.address}
otherSelectedTokenAddress={otherSelectedTokenAddress}
otherSelectedText={field === 0 ? ' Selected as output' : 'Selected as input'}
/>
)}
</InputPanel>
......
......@@ -13,7 +13,7 @@ import AddressInputPanel from '../AddressInputPanel'
import ConfirmationModal from '../ConfirmationModal'
import CurrencyInputPanel from '../CurrencyInputPanel'
import { Copy } from 'react-feather'
import Copy from '../AccountDetails/Copy'
import { Link } from '../../theme/components'
import { Text } from 'rebass'
import { TYPE } from '../../theme'
......@@ -26,9 +26,7 @@ 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 { useTranslation } from 'react-i18next'
import { useAddressAllowance } from '../../contexts/Allowances'
import { useWeb3React, useTokenContract } from '../../hooks'
import { useAddressBalance, useAllBalances } from '../../contexts/Balances'
......@@ -244,10 +242,10 @@ const SWAP_TYPE = {
const GAS_MARGIN = ethers.utils.bigNumberify(1000)
// default allowed slippage, in bips
const INITIAL_ALLOWED_SLIPPAGE = 200
const INITIAL_ALLOWED_SLIPPAGE = 50
// 15 minutes, denominated in seconds
const DEFAULT_DEADLINE_FROM_NOW = 60 * 15
const DEFAULT_DEADLINE_FROM_NOW = 60 * 20
// used for warning states based on slippage in bips
const ALLOWED_SLIPPAGE_MEDIUM = 100
......@@ -261,13 +259,13 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
const routerAddress: string = ROUTER_ADDRESSES[chainId]
// adding notifications on txns
const [, addPopup] = usePopups()
const addTransaction = useTransactionAdder()
// sending state
const [sending] = useState<boolean>(sendingInput)
const [sendingWithSwap, setSendingWithSwap] = useState<boolean>(false)
const [recipient, setRecipient] = useState<string>('')
const [ENS, setENS] = useState<string>('')
// trade details, check query params for initial state
const [state, dispatch] = useReducer(
......@@ -313,7 +311,6 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
// 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]
......@@ -341,7 +338,6 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
// approvals
const inputApproval: TokenAmount = useAddressAllowance(account, tokens[Field.INPUT], routerAddress)
const outputApproval: TokenAmount = useAddressAllowance(account, tokens[Field.OUTPUT], routerAddress)
// all balances for detecting a swap with send
const allBalances: TokenAmount[] = useAllBalances()
......@@ -502,14 +498,9 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
new TokenAmount(tokens[Field.INPUT], calculateSlippageAmount(parsedAmounts[Field.OUTPUT])?.[0])
}
const showInputUnlock: boolean =
const showInputApprove: boolean =
parsedAmounts[Field.INPUT] && inputApproval && JSBI.greaterThan(parsedAmounts[Field.INPUT].raw, inputApproval.raw)
const showOutputUnlock: boolean =
parsedAmounts[Field.OUTPUT] &&
outputApproval &&
JSBI.greaterThan(parsedAmounts[Field.OUTPUT].raw, outputApproval.raw)
// function for a pure send
async function onSend() {
setAttemptingTxn(true)
......@@ -522,15 +513,18 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
.sendTransaction({ to: recipient.toString(), value: hex(parsedAmounts[Field.INPUT].raw) })
.then(response => {
setTxHash(response.hash)
addTransaction(response)
addTransaction(
response,
'Send ' +
parsedAmounts[Field.INPUT]?.toSignificant(3) +
' ' +
tokens[Field.INPUT]?.symbol +
' to ' +
recipient
)
setPendingConfirmation(false)
})
.catch(e => {
addPopup(
<AutoColumn gap="sm">
<Text>Transaction Failed: try again.</Text>
</AutoColumn>
)
.catch(() => {
resetModal()
setShowConfirm(false)
})
......@@ -548,15 +542,18 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
})
.then(response => {
setTxHash(response.hash)
addTransaction(response)
addTransaction(
response,
'Send ' +
parsedAmounts[Field.INPUT]?.toSignificant(3) +
' ' +
tokens[Field.INPUT]?.symbol +
' to ' +
recipient
)
setPendingConfirmation(false)
})
.catch(e => {
addPopup(
<AutoColumn gap="sm">
<Text>Transaction Failed: try again.</Text>
</AutoColumn>
)
.catch(() => {
resetModal()
setShowConfirm(false)
})
......@@ -659,21 +656,26 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
})
.then(response => {
setTxHash(response.hash)
addTransaction(response)
addTransaction(
response,
'Swap ' +
slippageAdjustedAmounts?.[Field.INPUT]?.toSignificant(3) +
' ' +
tokens[Field.INPUT]?.symbol +
' for ' +
slippageAdjustedAmounts?.[Field.OUTPUT]?.toSignificant(3) +
' ' +
tokens[Field.OUTPUT]?.symbol
)
setPendingConfirmation(false)
})
.catch(() => {
addPopup(
<AutoColumn gap="sm">
<Text>Transaction Failed: try again.</Text>
</AutoColumn>
)
resetModal()
setShowConfirm(false)
})
}
async function approveAmount(field) {
async function approveAmount(field: Field) {
let estimatedGas
let useUserBalance = false
const tokenContract = field === Field.INPUT ? tokenContractInput : tokenContractOutput
......@@ -691,7 +693,7 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
gasLimit: calculateGasMargin(estimatedGas, GAS_MARGIN)
})
.then(response => {
addTransaction(response, { approval: tokens[field]?.address })
addTransaction(response, 'Approve ' + tokens[field]?.symbol, { approval: tokens[field]?.address })
})
}
......@@ -717,12 +719,12 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
}
if (!parsedAmounts[Field.INPUT]) {
setGeneralError('Enter an amount')
setInputError('Enter an amount')
setIsValid(false)
}
if (!parsedAmounts[Field.OUTPUT] && !ignoreOutput) {
setGeneralError('Enter an amount')
setOutputError('Enter an amount')
setIsValid(false)
}
......@@ -755,22 +757,35 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
parsedAmounts[Field.INPUT] &&
JSBI.lessThan(userBalances[Field.INPUT].raw, parsedAmounts[Field.INPUT]?.raw)
) {
setInputError('Insufficient balance.')
setInputError('Insufficient ' + tokens[Field.INPUT]?.symbol + ' balance')
setIsValid(false)
}
// check for null trade entitiy if not enough balance for trade
if (
(!sending || sendingWithSwap) &&
userBalances[Field.INPUT] &&
!trade &&
parsedAmounts[independentField] &&
!parsedAmounts[dependentField] &&
tokens[dependentField]
) {
setInputError('Insufficient ' + tokens[Field.INPUT]?.symbol + ' balance')
setIsValid(false)
}
}, [
pair,
sending,
sendingWithSwap,
dependentField,
ignoreOutput,
independentField,
pair,
parsedAmounts,
recipient,
recipientError,
sending,
sendingWithSwap,
showInputUnlock,
showOutputUnlock,
route,
tokens,
userBalances,
route
trade,
userBalances
])
// warnings on slippage
......@@ -785,6 +800,10 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
// reset modal state when closed
function resetModal() {
// clear input if txn submitted
if (!pendingConfirmation) {
onUserInput(Field.INPUT, '')
}
setPendingConfirmation(true)
setAttemptingTxn(false)
setShowAdvanced(false)
......@@ -796,14 +815,33 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
<AutoColumn gap="lg" style={{ marginTop: '40px' }}>
<RowBetween>
<Text fontSize={36} fontWeight={500}>
{parsedAmounts[Field.INPUT]?.toFixed(8)} {tokens[Field.INPUT]?.symbol}
{parsedAmounts[Field.INPUT]?.toSignificant(6)} {tokens[Field.INPUT]?.symbol}
</Text>
<TokenLogo address={tokens[Field.INPUT]?.address} size={'30px'} />
</RowBetween>
<TYPE.darkGray fontSize={20}>To</TYPE.darkGray>
<TYPE.blue fontSize={36}>
{recipient?.slice(0, 6)}...{recipient?.slice(36, 42)}
</TYPE.blue>
{ENS ? (
<AutoColumn gap="lg">
<TYPE.blue fontSize={36}>{ENS}</TYPE.blue>
<AutoRow gap="10px">
<Link href={getEtherscanLink(chainId, ENS, 'address')}>
<TYPE.blue fontSize={18}>
{recipient?.slice(0, 8)}...{recipient?.slice(34, 42)}
</TYPE.blue>
</Link>
<Copy toCopy={recipient} />
</AutoRow>
</AutoColumn>
) : (
<AutoRow gap="10px">
<Link href={getEtherscanLink(chainId, ENS, 'address')}>
<TYPE.blue fontSize={36}>
{recipient?.slice(0, 6)}...{recipient?.slice(36, 42)}
</TYPE.blue>
</Link>
<Copy toCopy={recipient} />
</AutoRow>
)}
</AutoColumn>
)
}
......@@ -847,7 +885,7 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
</RowFixed>
</RowBetween>
<RowFixed>
<ArrowDown size="16" color="#888D9B" />
<ArrowDown size="16" color={'#888D9B'} />
</RowFixed>
<RowBetween align="flex-end">
<Text fontSize={36} fontWeight={500} color={warningHigh ? '#FF6871' : '#2172E5'}>
......@@ -898,11 +936,19 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
</Text>
</ButtonError>
<AutoColumn justify="center" gap="lg">
<TYPE.italic textAlign="center" style={{ width: '80%' }}>
{`Output is estimated. You will receive at least ${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(
6
)} ${tokens[Field.OUTPUT]?.symbol} or the transaction will revert.`}
</TYPE.italic>
{independentField === Field.INPUT ? (
<TYPE.italic textAlign="center" style={{ width: '80%' }}>
{`Output is estimated. You will receive at least ${slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(
6
)} ${tokens[Field.OUTPUT]?.symbol} or the transaction will revert.`}
</TYPE.italic>
) : (
<TYPE.italic textAlign="center" style={{ width: '80%' }}>
{`Input is estimated. You will sell at most ${slippageAdjustedAmounts[Field.INPUT]?.toSignificant(6)} ${
tokens[Field.INPUT]?.symbol
} or the transaction will revert.`}
</TYPE.italic>
)}
<Link
onClick={() => {
setShowAdvanced(true)
......@@ -918,7 +964,6 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
const PriceBar = function() {
return (
// <GreyCard>
<AutoRow justify="space-between">
<AutoColumn justify="center">
<Text fontWeight={500} fontSize={16} color="#000000">
......@@ -951,7 +996,6 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
</Text>
</AutoColumn>
</AutoRow>
// </GreyCard>
)
}
......@@ -960,7 +1004,7 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
? sendingWithSwap
? `Sending ${parsedAmounts[Field.OUTPUT]?.toSignificant(6)} ${tokens[Field.OUTPUT]?.symbol} to ${recipient}`
: `Sending ${parsedAmounts[Field.INPUT]?.toSignificant(6)} ${tokens[Field.INPUT]?.symbol} to ${recipient}`
: ` Swapped ${parsedAmounts[Field.INPUT]?.toSignificant(6)} ${tokens[Field.INPUT]?.symbol} for ${parsedAmounts[
: ` Swapping ${parsedAmounts[Field.INPUT]?.toSignificant(6)} ${tokens[Field.INPUT]?.symbol} for ${parsedAmounts[
Field.OUTPUT
]?.toSignificant(6)} ${tokens[Field.OUTPUT]?.symbol}`
......@@ -983,6 +1027,9 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
} else {
setRecipient('')
}
if (result.name) {
setENS(result.name)
}
}
return (
......@@ -1015,7 +1062,6 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
</MaxButton>
)}
<StyledNumerical value={formattedAmounts[Field.INPUT]} onUserInput={val => onUserInput(Field.INPUT, val)} />
{/* {!parsedAmounts[Field.INPUT] && <TYPE.gray>Enter an amount.</TYPE.gray>} */}
<CurrencyInputPanel
field={Field.INPUT}
value={formattedAmounts[Field.INPUT]}
......@@ -1031,6 +1077,7 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
hideBalance={true}
hideInput={true}
showSendWithSwap={true}
otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address}
/>
</InputGroup>
</>
......@@ -1051,6 +1098,7 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
maxAmountInput && onMaxInput(maxAmountInput.toExact())
}}
onTokenSelection={address => onTokenSelection(Field.INPUT, address)}
otherSelectedTokenAddress={tokens[Field.OUTPUT]?.address}
/>
{sendingWithSwap ? (
<ColumnCenter>
......@@ -1066,7 +1114,11 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
) : (
<Hover>
<ColumnCenter>
<ArrowDown size="16" onClick={onSwapTokens} />
<ArrowDown
size="16"
onClick={onSwapTokens}
color={tokens[Field.INPUT] && tokens[Field.OUTPUT] ? '#2172E5' : '#888D9B'}
/>
</ColumnCenter>
</Hover>
)}
......@@ -1083,6 +1135,7 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
onTokenSelection={address => onTokenSelection(Field.OUTPUT, address)}
error={outputError}
pair={pair}
otherSelectedTokenAddress={tokens[Field.INPUT]?.address}
/>
{sendingWithSwap && (
<RowBetween padding="0 8px">
......@@ -1094,12 +1147,19 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
)}
{sending && (
<AutoColumn gap="sm">
<AutoColumn gap="lg">
{!sendingWithSwap && (
<Hover onClick={() => setSendingWithSwap(true)}>
<TYPE.blue textAlign="center">Add a swap +</TYPE.blue>
</Hover>
)}
<AddressInputPanel
onChange={_onRecipient}
onError={(error: boolean) => {
if (error) {
onError={(error: boolean, input) => {
if (error && input !== '') {
setRecipientError('Invalid Recipient')
} else if (error && input === '') {
setRecipientError('Enter a Recipient')
} else {
setRecipientError(null)
}
......@@ -1111,7 +1171,6 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
<BottomGrouping>
{noRoute ? (
<GreyCard style={{ textAlign: 'center' }}>
{/* <RowBetween style={{ margin: '10px 0' }}> */}
<TYPE.main>No exchange for this pair.</TYPE.main>
<Link
......@@ -1122,22 +1181,8 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
{' '}
Create one now
</Link>
{/* </RowBetween> */}
</GreyCard>
) : showOutputUnlock ? (
<ButtonLight
onClick={() => {
!pendingApprovalOutput && approveAmount(Field.OUTPUT)
}}
disabled={pendingApprovalOutput}
>
{pendingApprovalOutput ? (
<Dots>Unlocking {tokens[Field.OUTPUT]?.symbol}</Dots>
) : (
'Unlock ' + tokens[Field.OUTPUT]?.symbol
)}
</ButtonLight>
) : showInputUnlock ? (
) : showInputApprove && !inputError ? (
<ButtonLight
onClick={() => {
approveAmount(Field.INPUT)
......@@ -1145,9 +1190,9 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
disabled={pendingApprovalInput}
>
{pendingApprovalInput ? (
<Dots>Unlocking {tokens[Field.INPUT]?.symbol}</Dots>
<Dots>Approving {tokens[Field.INPUT]?.symbol}</Dots>
) : (
'Unlock ' + tokens[Field.INPUT]?.symbol
'Approve ' + tokens[Field.INPUT]?.symbol
)}
</ButtonLight>
) : (
......@@ -1181,7 +1226,7 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
)}
</BottomGrouping>
<FixedBottom>
{!noRoute && tokens[Field.OUTPUT] && (
{!noRoute && tokens[Field.OUTPUT] && tokens[Field.INPUT] && (
<GreyCard pt={2} mb={2}>
<PriceBar />
</GreyCard>
......@@ -1197,7 +1242,7 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
<Link href={getEtherscanLink(chainId, tokens[Field.INPUT]?.address, 'address')}>
(View on Etherscan)
</Link>
<Copy size={'16'} />
<Copy toCopy={tokens[Field.INPUT]?.address} />
</AutoRow>
<TYPE.subHeader>
Please verify the legitimacy of this token before making any transactions.
......@@ -1231,7 +1276,7 @@ function ExchangePage({ sendingInput = false, history, initialCurrency, params }
</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?
enough liquidity to support this trade. Are you sure you want to continue this trade?
</Text>
</AutoColumn>
</GreyCard>
......
......@@ -3,13 +3,13 @@ import styled from 'styled-components'
import Row from '../Row'
import Menu from '../Menu'
import { Link } from '../../theme'
import { Text } from 'rebass'
import { YellowCard } from '../Card'
import Web3Status from '../Web3Status'
import { Link } from '../../theme'
import { Text } from 'rebass'
import { WETH } from '@uniswap/sdk'
import { isMobile } from 'react-device-detect'
import { YellowCard } from '../Card'
import { useWeb3React } from '../../hooks'
import { useAddressBalance } from '../../contexts/Balances'
import { useWalletModalToggle } from '../../contexts/Application'
......
......@@ -119,7 +119,7 @@ export default function Menu() {
Code
</MenuItem>
<MenuItem id="link" href="https://uniswap.info/">
Stats
Analytics
</MenuItem>
</MenuFlyout>
) : (
......
......@@ -5,6 +5,7 @@ const StyledInput = styled.input`
color: ${({ error, theme }) => error && theme.red1};
color: ${({ theme }) => theme.text1};
width: 0;
position: relative;
font-size: 24px;
font-weight: 500;
font-family: 'Inter', sans-serif;
......
......@@ -155,15 +155,15 @@ function PoolFinder({ history }) {
border="1px solid #EDEEF2"
/>
) : (
<LightCard padding="45px">
<LightCard padding="45px 10px">
<AutoColumn gap="sm" justify="center">
<Text color="">No position found.</Text>
<Text textAlign="center">Pool found, you don’t have liquidity on this pair yet.</Text>
<Link
onClick={() => {
history.push('/add/' + token0Address + '-' + token1Address)
}}
>
Add liquidity to this pair instead.
<Text textAlign="center">Add liquidity to this pair instead.</Text>
</Link>
</AutoColumn>
</LightCard>
......
......@@ -33,7 +33,6 @@ const MobilePopupWrapper = styled.div`
const MobilePopupInner = styled.div`
height: 99%;
box-sizing: border-box;
white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
display: flex;
......@@ -58,7 +57,7 @@ const FixedPopupColumn = styled(AutoColumn)`
const Popup = styled.div`
display: inline-block;
width: 100%;
height: 120px;
min-height: 120px;
padding: 1em;
box-sizing: border-box;
background-color: white;
......@@ -67,7 +66,7 @@ const Popup = styled.div`
border-radius: 10px;
padding: 20px;
padding-right: 35px;
whitespace: normal;
z-index: 2;
${({ theme }) => theme.mediaWidth.upToSmall`
min-width: 290px;
......@@ -104,12 +103,12 @@ export default function App() {
{showMessage && (
<PinkCard padding="20px" style={{ zIndex: '2' }}>
<AutoColumn justify={'center'} gap={'20px'}>
<Hover onClick={() => hideMigrationMessage()}>
<StyledClose />
</Hover>
<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={() => hideMigrationMessage()}>
<Text textAlign="center">Dismiss</Text>
</Hover>
</AutoColumn>
</PinkCard>
)}
......@@ -135,12 +134,12 @@ export default function App() {
{showMessage && (
<MobileCardPink>
<AutoColumn justify={'center'} gap={'20px'}>
<Hover onClick={() => hideMigrationMessage()}>
<StyledClose />
</Hover>
<Text>Uniswap has upgraded.</Text>
<Text textAlign="center">Are you a liquidity provider? Upgrade now using the migration helper.</Text>
<ButtonPink width={'265px'}>Migrate your liquidity </ButtonPink>
<Hover onClick={() => hideMigrationMessage()}>
<Text textAlign="center">Dismiss</Text>
</Hover>
</AutoColumn>
</MobileCardPink>
)}
......
......@@ -12,11 +12,12 @@ import Card from '../Card'
import TokenLogo from '../TokenLogo'
import DoubleLogo from '../DoubleLogo'
import { Text } from 'rebass'
import { Link } from '../../theme/components'
import { GreyCard } from '../../components/Card'
import { AutoColumn } from '../Column'
import { ChevronDown, ChevronUp } from 'react-feather'
import { ButtonSecondary } from '../Button'
import { RowBetween, RowFixed } from '../Row'
import { RowBetween, RowFixed, AutoRow } from '../Row'
const FixedHeightRow = styled(RowBetween)`
height: 24px;
......@@ -138,9 +139,9 @@ function PositionCard({ pairAddress, token0, token1, history, minimal = false, .
{userPoolBalance ? userPoolBalance.toFixed(6) : '-'}
</Text>
{showMore ? (
<ChevronUp size="30" style={{ marginLeft: '10px' }} />
<ChevronUp size="20" style={{ marginLeft: '10px' }} />
) : (
<ChevronDown size="30" style={{ marginLeft: '10px' }} />
<ChevronDown size="20" style={{ marginLeft: '10px' }} />
)}
</RowFixed>
</FixedHeightRow>
......@@ -149,9 +150,8 @@ function PositionCard({ pairAddress, token0, token1, history, minimal = false, .
<FixedHeightRow>
<RowFixed>
{!minimal && <TokenLogo size="16px" style={{ marginRight: '4px' }} address={token0?.address || ''} />}
<Text color="#888D9B" fontSize={14} fontWeight={500}>
{token0?.symbol}:
Your {token0?.symbol}:
</Text>
</RowFixed>
{token0Deposited ? (
......@@ -168,7 +168,7 @@ function PositionCard({ pairAddress, token0, token1, history, minimal = false, .
<RowFixed>
{!minimal && <TokenLogo size="16px" style={{ marginRight: '4px' }} address={token1?.address || ''} />}
<Text color="#888D9B" fontSize={14} fontWeight={500}>
{token1?.symbol}:
Your {token1?.symbol}:
</Text>
</RowFixed>
{token1Deposited ? (
......@@ -189,28 +189,29 @@ function PositionCard({ pairAddress, token0, token1, history, minimal = false, .
</Text>
</FixedHeightRow>
)}
<RowBetween marginTop="10px">
<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>
<AutoRow justify="center" marginTop={'10px'}>
<Link>View analytics</Link>
</AutoRow>
</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>
</HoverCard>
)
......
......@@ -2,27 +2,28 @@ import React, { useState, useRef, useMemo, useEffect } from 'react'
import '@reach/tooltip/styles.css'
import styled from 'styled-components'
import escapeStringRegex from 'escape-string-regexp'
import { JSBI } from '@uniswap/sdk'
import { JSBI, WETH } from '@uniswap/sdk'
import { Link } from 'react-router-dom'
import { ethers } from 'ethers'
import { isMobile } from 'react-device-detect'
import { withRouter } from 'react-router-dom'
import { COMMON_BASES } from '../../constants'
import { Link as StyledLink } from '../../theme/components'
import { Hover } from '../../theme'
import Modal from '../Modal'
import Circle from '../../assets/images/circle.svg'
import TokenLogo from '../TokenLogo'
import DoubleTokenLogo from '../DoubleLogo'
import Column, { AutoColumn } from '../Column'
import { Text } from 'rebass'
import { Hover } from '../../theme'
import { LightCard } from '../Card'
import { ArrowLeft } from 'react-feather'
import { CloseIcon } from '../../theme/components'
import { ColumnCenter } from '../../components/Column'
import { Spinner, TYPE } from '../../theme'
import { ButtonSecondary } from '../Button'
import { RowBetween, RowFixed } from '../Row'
import { RowBetween, RowFixed, AutoRow } from '../Row'
import { isAddress } from '../../utils'
import { useAllPairs } from '../../contexts/Pairs'
......@@ -31,6 +32,7 @@ import { useSavedTokens } from '../../contexts/LocalStorage'
import { useAllBalances } from '../../contexts/Balances'
import { useTranslation } from 'react-i18next'
import { useToken, useAllTokens, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
import QuestionHelper from '../Question'
const TokenModalInfo = styled.div`
${({ theme }) => theme.flexRowNoWrap}
......@@ -52,6 +54,11 @@ const FadedSpan = styled.span`
color: ${({ theme }) => theme.blue1};
`
const GreySpan = styled.span`
color: ${({ theme }) => theme.text3};
font-weight: 400;
`
const SpinnerWrapper = styled(Spinner)`
margin: 0 0.25rem 0 0.25rem;
color: ${({ theme }) => theme.text4};
......@@ -79,11 +86,6 @@ const Input = styled.input`
}
`
const TokenModal = styled.div`
${({ theme }) => theme.flexColumnNoWrap}
width: 100%;
`
const FilterWrapper = styled(RowFixed)`
padding: 8px;
background-color: ${({ selected, theme }) => selected && theme.bg2};
......@@ -105,7 +107,6 @@ const PaddedColumn = styled(AutoColumn)`
const PaddedItem = styled(RowBetween)`
padding: 4px 24px;
/* width: calc(100% - 48px); */
height: 56px;
`
......@@ -116,6 +117,22 @@ const MenuItem = styled(PaddedItem)`
}
opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
`
const BaseWrapper = styled(AutoRow)`
border: 1px solid ${({ theme, disable }) => (disable ? 'transparent' : theme.bg3)};
padding: 0 6px;
border-radius: 10px;
width: 120px;
:hover {
cursor: ${({ disable }) => !disable && 'pointer'};
background-color: ${({ theme, disable }) => !disable && theme.bg2};
}
background-color: ${({ theme, disable }) => disable && theme.bg3};
opacity: ${({ disable }) => disable && '0.4'};
`
// filters on results
const FILTERS = {
VOLUME: 'VOLUME',
......@@ -131,7 +148,10 @@ function SearchModal({
urlAddedTokens,
filterType,
hiddenToken,
showSendWithSwap
showSendWithSwap,
otherSelectedTokenAddress,
otherSelectedText,
showCommonBases = false
}) {
const { t } = useTranslation()
const { account, chainId } = useWeb3React()
......@@ -367,59 +387,75 @@ function SearchModal({
return <TokenModalInfo>{t('noToken')}</TokenModalInfo>
}
return filteredTokenList.map(({ address, symbol, balance }) => {
const urlAdded = urlAddedTokens && urlAddedTokens.hasOwnProperty(address)
const customAdded =
address !== 'ETH' &&
INITIAL_TOKENS_CONTEXT[chainId] &&
!INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(address) &&
!urlAdded
return filteredTokenList
.sort((a, b) => {
if (b?.address === WETH[chainId]?.address) {
return 1
} else
return parseFloat(a?.balance?.toExact()) > parseFloat(b?.balance?.toExact())
? sortDirection
? -1
: 1
: sortDirection
? 1
: -1
})
.map(({ address, symbol, balance }) => {
const urlAdded = urlAddedTokens && urlAddedTokens.hasOwnProperty(address)
const customAdded =
address !== 'ETH' &&
INITIAL_TOKENS_CONTEXT[chainId] &&
!INITIAL_TOKENS_CONTEXT[chainId].hasOwnProperty(address) &&
!urlAdded
const zeroBalance = balance && JSBI.equal(JSBI.BigInt(0), balance.raw)
const zeroBalance = balance && JSBI.equal(JSBI.BigInt(0), balance.raw)
// if token import page dont show preset list, else show all
return (
<MenuItem
key={address}
onClick={() => (hiddenToken && hiddenToken === address ? () => {} : _onTokenSelect(address))}
disabled={hiddenToken && hiddenToken === address}
>
<RowFixed>
<TokenLogo address={address} size={'24px'} style={{ marginRight: '14px' }} />
<Column>
<Text fontWeight={500}>{symbol}</Text>
<FadedSpan>
{urlAdded && '(Added by URL)'} {customAdded && '(Added by user)'}
</FadedSpan>
</Column>
</RowFixed>
<AutoColumn gap="4px" justify="end">
{balance ? (
<Text>
{zeroBalance && showSendWithSwap ? (
<ColumnCenter
justify="center"
style={{ backgroundColor: '#EBF4FF', padding: '8px', borderRadius: '12px' }}
>
<Text textAlign="center" fontWeight={500} color="#2172E5">
Send With Swap
</Text>
</ColumnCenter>
) : balance ? (
balance.toSignificant(6)
) : (
'-'
)}
</Text>
) : account ? (
<SpinnerWrapper src={Circle} alt="loader" />
) : (
'-'
)}
</AutoColumn>
</MenuItem>
)
})
// if token import page dont show preset list, else show all
return (
<MenuItem
key={address}
onClick={() => (hiddenToken && hiddenToken === address ? () => {} : _onTokenSelect(address))}
disabled={hiddenToken && hiddenToken === address}
>
<RowFixed>
<TokenLogo address={address} size={'24px'} style={{ marginRight: '14px' }} />
<Column>
<Text fontWeight={500}>
{symbol}
{otherSelectedTokenAddress === address && <GreySpan> ({otherSelectedText})</GreySpan>}
</Text>
<FadedSpan>
{urlAdded && '(Added by URL)'} {customAdded && '(Added by user)'}
</FadedSpan>
</Column>
</RowFixed>
<AutoColumn gap="4px" justify="end">
{balance ? (
<Text>
{zeroBalance && showSendWithSwap ? (
<ColumnCenter
justify="center"
style={{ backgroundColor: '#EBF4FF', padding: '8px', borderRadius: '12px' }}
>
<Text textAlign="center" fontWeight={500} color="#2172E5">
Send With Swap
</Text>
</ColumnCenter>
) : balance ? (
balance.toSignificant(6)
) : (
'-'
)}
</Text>
) : account ? (
<SpinnerWrapper src={Circle} alt="loader" />
) : (
'-'
)}
</AutoColumn>
</MenuItem>
)
})
}
const Filter = ({ title, filter }) => {
......@@ -451,9 +487,9 @@ function SearchModal({
maxHeight={50}
initialFocusRef={isMobile ? undefined : inputRef}
>
<TokenModal>
<Column style={{ width: '100%' }}>
{showTokenImport ? (
<PaddedColumn gap="20px">
<PaddedColumn gap="lg">
<RowBetween>
<RowFixed>
<Hover>
......@@ -470,11 +506,11 @@ function SearchModal({
<CloseIcon onClick={onDismiss} />
</RowBetween>
<TYPE.body style={{ marginTop: '40px' }}>
To import a custom token, paste the token address in the search bar.
To import a custom token, paste token address in the search bar.
</TYPE.body>
<Input
type={'text'}
placeholder={t('tokenSearchPlaceholder')}
placeholder={'0x0000000000...'}
value={searchQuery}
ref={inputRef}
onChange={onInput}
......@@ -520,6 +556,33 @@ function SearchModal({
ref={inputRef}
onChange={onInput}
/>
{showCommonBases && (
<AutoColumn gap="md">
<AutoRow>
<Text fontWeight={500} fontSize={16}>
Common Bases
</Text>
<QuestionHelper text="These tokens are commonly used in pairs." />
</AutoRow>
<AutoRow gap="10px">
{COMMON_BASES[chainId].map(token => {
return (
<BaseWrapper
gap="6px"
onClick={() => hiddenToken !== token.address && _onTokenSelect(token.address)}
disable={hiddenToken === token.address}
key={token.address}
>
<TokenLogo address={token.address} />
<Text fontWeight={500} fontSize={16}>
{token.symbol}
</Text>
</BaseWrapper>
)
})}
</AutoRow>
</AutoColumn>
)}
<RowBetween>
<div>
{filterType !== 'tokens' && (
......@@ -554,7 +617,7 @@ function SearchModal({
)}
{!showTokenImport && <div style={{ width: '100%', height: '1px', backgroundColor: '#E1E1E1' }} />}
{!showTokenImport && <TokenList>{filterType === 'tokens' ? renderTokenList() : renderPairsList()}</TokenList>}
</TokenModal>
</Column>
</Modal>
)
}
......
import React from 'react'
import React, { useState, useEffect } from 'react'
import Slider from '@material-ui/core/Slider'
import { withStyles } from '@material-ui/core/styles'
import { useDebounce } from '../../hooks'
// const marks = [
// {
......@@ -65,11 +66,29 @@ const StyledSlider = withStyles({
}
})(Slider)
export default function InputSlider({ value, onChange }) {
export default function InputSlider({ value, onChange, override }) {
const [internalVal, setInternalVal] = useState(0)
const debouncedInternalValue = useDebounce(internalVal, 10)
function handleChange(e, val) {
setInternalVal(val)
console.log(val)
console.log(debouncedInternalValue)
if (val === debouncedInternalValue) {
onChange(e, val)
}
}
useEffect(() => {
if (override && value !== internalVal) {
setInternalVal(value)
}
}, [internalVal, override, value])
return (
<StyledSlider
value={typeof value === 'number' ? value : 0}
onChange={onChange}
value={typeof internalVal === 'number' ? internalVal : 0}
onChange={handleChange}
aria-labelledby="input-slider"
// marks={marks}
/>
......
......@@ -7,23 +7,14 @@ import { AutoColumn } from '../Column'
import { useWeb3React } from '../../hooks'
import { getEtherscanLink } from '../../utils'
export default function TxnPopup({ hash, success }) {
export default function TxnPopup({ hash, success, summary }) {
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>
)
}
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>
)
}
......@@ -5,18 +5,37 @@ import { useWeb3React, UnsupportedChainIdError } from '@web3-react/core'
import { darken, lighten } from 'polished'
import { Activity } from 'react-feather'
import { shortenAddress } from '../../utils'
import { useENSName } from '../../hooks'
import Identicon from '../Identicon'
import PortisIcon from '../../assets/images/portisIcon.png'
import WalletModal from '../WalletModal'
import { useAllTransactions } from '../../contexts/Transactions'
import { useWalletModalToggle } from '../../contexts/Application'
import { injected, walletconnect, walletlink, fortmatic, portis } from '../../connectors'
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
import PortisIcon from '../../assets/images/portisIcon.png'
import { Spinner } from '../../theme'
import LightCircle from '../../assets/svg/lightcircle.svg'
import { RowBetween } from '../Row'
import { useENSName } from '../../hooks'
import { shortenAddress } from '../../utils'
import { useAllTransactions } from '../../contexts/Transactions'
import { NetworkContextName } from '../../constants'
import Identicon from '../Identicon'
import { useWalletModalToggle } from '../../contexts/Application'
import { injected, walletconnect, walletlink, fortmatic, portis } from '../../connectors'
const SpinnerWrapper = styled(Spinner)`
margin: 0 0.25rem 0 0.25rem;
`
const IconWrapper = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
align-items: center;
justify-content: center;
& > * {
height: ${({ size }) => (size ? size + 'px' : '32px')};
width: ${({ size }) => (size ? size + 'px' : '32px')};
}
`
const Web3StatusGeneric = styled.button`
${({ theme }) => theme.flexRowNoWrap}
......@@ -103,16 +122,6 @@ const NetworkIcon = styled(Activity)`
height: 16px;
`
const IconWrapper = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
align-items: center;
justify-content: center;
& > * {
height: ${({ size }) => (size ? size + 'px' : '32px')};
width: ${({ size }) => (size ? size + 'px' : '32px')};
}
`
export default function Web3Status() {
const { t } = useTranslation()
const { active, account, connector, error } = useWeb3React()
......@@ -164,7 +173,9 @@ export default function Web3Status() {
return (
<Web3StatusConnected onClick={toggleWalletModal} pending={hasPendingTransactions}>
{hasPendingTransactions ? (
<Text>{pending?.length} Pending</Text>
<RowBetween>
<Text>{pending?.length} Pending</Text> <SpinnerWrapper src={LightCircle} alt="loader" />
</RowBetween>
) : (
<Text>{ENSName || shortenAddress(account)}</Text>
)}
......
import { injected, walletconnect, walletlink, fortmatic, portis } from '../connectors'
import { ChainId, WETH, Token } from '@uniswap/sdk'
export const FACTORY_ADDRESSES = {
1: '',
......@@ -16,6 +17,14 @@ export const ROUTER_ADDRESSES = {
42: '0xcDbE04934d89e97a24BCc07c3562DC8CF17d8167'
}
export const COMMON_BASES = {
1: [WETH[ChainId.MAINNET]],
4: [
WETH[ChainId.RINKEBY],
new Token(ChainId.RINKEBY, '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735', 18, 'DAI', 'Dai Stablecoin')
]
}
export const SUPPORTED_THEMES = {
DARK: 'DARK',
LIGHT: 'LIGHT'
......
......@@ -10,6 +10,7 @@ const RESPONSE = 'response'
const CUSTOM_DATA = 'CUSTOM_DATA'
const BLOCK_NUMBER_CHECKED = 'BLOCK_NUMBER_CHECKED'
const RECEIPT = 'receipt'
const SUMMARY = 'summary'
const ADD = 'ADD'
const CHECK = 'CHECK'
......@@ -131,7 +132,14 @@ export function Updater() {
check(chainId, hash, globalBlockNumber)
} else {
finalize(chainId, hash, receipt)
addPopup(<TxnPopup hash={hash} success={true} />)
// add success or failure popup
if (receipt.status === 1) {
addPopup(<TxnPopup hash={hash} success={true} summary={allTransactions[hash]?.response?.summary} />)
} else {
addPopup(
<TxnPopup hash={hash} success={false} summary={allTransactions[hash]?.response?.summary} />
)
}
}
}
})
......@@ -155,17 +163,15 @@ export function useTransactionAdder() {
const [, { add }] = useTransactionsContext()
return useCallback(
(response, customData = {}) => {
(response, summary = '', customData = {}) => {
if (!(chainId || chainId === 0)) {
throw Error(`Invalid networkId '${chainId}`)
}
const hash = safeAccess(response, ['hash'])
if (!hash) {
throw Error('No transaction hash found.')
}
add(chainId, hash, { ...response, [CUSTOM_DATA]: customData })
add(chainId, hash, { ...response, [CUSTOM_DATA]: customData, [SUMMARY]: summary })
},
[chainId, add]
)
......@@ -181,7 +187,6 @@ export function useAllTransactions() {
export function usePendingApproval(tokenAddress) {
const allTransactions = useAllTransactions()
return (
Object.keys(allTransactions).filter(hash => {
if (allTransactions[hash][RECEIPT]) {
......
......@@ -51,6 +51,7 @@ const BodyWrapper = styled.div`
const Body = styled.div`
max-width: 355px;
width: 100%;
min-height: 340px;
background: ${({ theme }) => theme.bg1};
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);
......
......@@ -14,14 +14,12 @@ 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 { GreyCard } from '../../components/Card'
import { useToken } from '../../contexts/Tokens'
import { usePopups } from '../../contexts/Application'
import { useAddressBalance } from '../../contexts/Balances'
import { useAddressAllowance } from '../../contexts/Allowances'
import { usePair, useTotalSupply } from '../../contexts/Pairs'
......@@ -33,10 +31,10 @@ import { ROUTER_ADDRESSES } from '../../constants'
import { getRouterContract, calculateGasMargin } from '../../utils'
// denominated in bips
const ALLOWED_SLIPPAGE = 200
const ALLOWED_SLIPPAGE = 50
// denominated in seconds
const DEADLINE_FROM_NOW = 60 * 15
const DEADLINE_FROM_NOW = 60 * 20
const GAS_MARGIN: BigNumber = ethers.utils.bigNumberify(1000)
......@@ -140,7 +138,6 @@ function reducer(
function AddLiquidity({ token0, token1, step = false }) {
const { account, chainId, library } = useWeb3React()
const [, addPopup] = usePopups()
const routerAddress: string = ROUTER_ADDRESSES[chainId]
......@@ -179,8 +176,8 @@ function AddLiquidity({ token0, token1, step = false }) {
// state for amount approvals
const inputApproval: TokenAmount = useAddressAllowance(account, tokens[Field.INPUT], routerAddress)
const outputApproval: TokenAmount = useAddressAllowance(account, tokens[Field.OUTPUT], routerAddress)
const [showInputUnlock, setShowInputUnlock] = useState<boolean>(false)
const [showOutputUnlock, setShowOutputUnlock] = useState<boolean>(false)
const [showInputApprove, setShowInputApprove] = useState<boolean>(false)
const [showOutputApprove, setShowOutputApprove] = useState<boolean>(false)
// get user-pecific and token-specific lookup data
const userBalances: { [field: number]: TokenAmount } = {
......@@ -314,12 +311,12 @@ function AddLiquidity({ token0, token1, step = false }) {
: undefined
})
// monitor parsed amounts and update unlocked buttons
// monitor parsed amounts and update approve buttons
useEffect(() => {
setShowInputUnlock(
setShowInputApprove(
parsedAmounts[Field.INPUT] && inputApproval && JSBI.greaterThan(parsedAmounts[Field.INPUT].raw, inputApproval.raw)
)
setShowOutputUnlock(
setShowOutputApprove(
parsedAmounts[Field.OUTPUT] &&
outputApproval &&
JSBI.greaterThan(parsedAmounts[Field.OUTPUT].raw, outputApproval.raw)
......@@ -362,7 +359,7 @@ function AddLiquidity({ token0, token1, step = false }) {
userBalances?.[Field.INPUT] &&
JSBI.greaterThan(parsedAmounts?.[Field.INPUT]?.raw, userBalances?.[Field.INPUT]?.raw)
) {
setInputError('Insufficient balance.')
setInputError('Insufficient ' + tokens[Field.INPUT]?.symbol + ' balance')
setIsValid(false)
}
if (
......@@ -370,10 +367,10 @@ function AddLiquidity({ token0, token1, step = false }) {
userBalances?.[Field.OUTPUT] &&
JSBI.greaterThan(parsedAmounts?.[Field.OUTPUT]?.raw, userBalances?.[Field.OUTPUT]?.raw)
) {
setOutputError('Insufficient balance.')
setOutputError('Insufficient ' + tokens[Field.OUTPUT]?.symbol + ' balance')
setIsValid(false)
}
}, [noLiquidity, parsedAmounts, showInputUnlock, showOutputUnlock, userBalances])
}, [noLiquidity, parsedAmounts, showInputApprove, showOutputApprove, tokens, userBalances])
// state for txn
const addTransaction = useTransactionAdder()
......@@ -450,16 +447,21 @@ function AddLiquidity({ token0, token1, step = false }) {
})
.then(response => {
setTxHash(response.hash)
addTransaction(response)
addTransaction(
response,
'Add ' +
parsedAmounts[Field.INPUT]?.toSignificant(3) +
' ' +
tokens[Field.INPUT]?.symbol +
' and ' +
parsedAmounts[Field.OUTPUT]?.toSignificant(3) +
' ' +
tokens[Field.OUTPUT]?.symbol
)
setPendingConfirmation(false)
})
.catch((e: Error) => {
console.log(e)
addPopup(
<AutoColumn gap="10px">
<Text>Transaction Failed: try again.</Text>
</AutoColumn>
)
setPendingConfirmation(true)
setAttemptingTxn(false)
setShowConfirm(false)
......@@ -484,7 +486,7 @@ function AddLiquidity({ token0, token1, step = false }) {
gasLimit: calculateGasMargin(estimatedGas, GAS_MARGIN)
})
.then(response => {
addTransaction(response, { approval: tokens[field]?.address })
addTransaction(response, 'Approve ' + tokens[field]?.symbol, { approval: tokens[field]?.address })
})
}
......@@ -584,9 +586,8 @@ function AddLiquidity({ token0, token1, step = false }) {
</>
)
}
const PriceBar = function(props) {
const PriceBar = () => {
return (
// <GreyCard>
<AutoRow justify="space-between">
<AutoColumn justify="center">
<Text fontWeight={500} fontSize={16} color="#000000">
......@@ -609,17 +610,15 @@ function AddLiquidity({ token0, token1, step = false }) {
{poolTokenPercentage ? poolTokenPercentage?.toFixed(2) : '0.0'}
{'%'}
</Text>
<Text fontWeight={500} fontSize={14} color="#888D9B" pt={1}>
Pool Share
</Text>
</AutoColumn>
</AutoRow>
// </GreyCard>
)
}
const pendingText: string = `Supplied ${parsedAmounts[Field.INPUT]?.toSignificant(6)} ${
const pendingText: string = `Supplying ${parsedAmounts[Field.INPUT]?.toSignificant(6)} ${
tokens[Field.INPUT]?.symbol
} ${'and'} ${parsedAmounts[Field.OUTPUT]?.toSignificant(6)} ${tokens[Field.OUTPUT]?.symbol}`
......@@ -655,7 +654,7 @@ function AddLiquidity({ token0, token1, step = false }) {
<TYPE.blue fontWeight={400}>
You are the first liquidity provider. The ratio of tokens you add will set the price of this pool.
</TYPE.blue>
<TYPE.blue fontWeight={400}>Once you are happy with the rate. Click create pool to review.</TYPE.blue>
<TYPE.blue fontWeight={400}>Once you are happy with the rate click supply to review.</TYPE.blue>
</AutoColumn>
</BlueCard>
</ColumnCenter>
......@@ -689,30 +688,21 @@ function AddLiquidity({ token0, token1, step = false }) {
error={outputError}
pair={pair}
/>
{/* {!noLiquidity && (
<RowBetween>
Rate:
<div>
1 {tokens[independentField]?.symbol} = {route?.midPrice?.toSignificant(6)}
{tokens[dependentField]?.symbol}
</div>
</RowBetween>
)} */}
{showOutputUnlock ? (
{showOutputApprove ? (
<ButtonLight
onClick={() => {
approveAmount(Field.OUTPUT)
}}
>
{pendingApprovalOutput ? 'Waiting for unlock' : 'Unlock ' + tokens[Field.OUTPUT]?.symbol}
{pendingApprovalOutput ? 'Waiting for approve' : 'Approve ' + tokens[Field.OUTPUT]?.symbol}
</ButtonLight>
) : showInputUnlock ? (
) : showInputApprove ? (
<ButtonLight
onClick={() => {
approveAmount(Field.INPUT)
}}
>
{pendingApprovalInput ? 'Waiting for unlock' : 'Unlock ' + tokens[Field.INPUT]?.symbol}
{pendingApprovalInput ? 'Waiting for approve' : 'Approve ' + tokens[Field.INPUT]?.symbol}
</ButtonLight>
) : (
<ButtonPrimary
......@@ -734,11 +724,6 @@ function AddLiquidity({ token0, token1, step = false }) {
{tokens[Field.OUTPUT] && (
<GreyCard pt={2} mb={2}>
<PriceBar />
{/* <AutoRow justify="center" gap="8px">
<Link fontSize="14px" fontWeight={400} href="">
Show Advanced
</Link>
</AutoRow> */}
</GreyCard>
)}
<PositionCard
......
......@@ -4,10 +4,10 @@ import { ethers } from 'ethers'
import { parseUnits } from '@ethersproject/units'
import { TokenAmount, JSBI, Route, WETH, Percent, Token, Pair } from '@uniswap/sdk'
import Slider from '../../components/Slider'
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'
......@@ -32,7 +32,7 @@ import { ROUTER_ADDRESSES } from '../../constants'
import { getRouterContract, calculateGasMargin } from '../../utils'
// denominated in seconds
const DEADLINE_FROM_NOW = 60 * 15
const DEADLINE_FROM_NOW = 60 * 20
const GAS_MARGIN: BigNumber = ethers.utils.bigNumberify(1000)
......@@ -44,6 +44,7 @@ const FixedBottom = styled.div`
position: absolute;
top: 100px;
width: 100%;
margin-bottom: 80px;
`
const ClickableText = styled(Text)`
......@@ -52,6 +53,11 @@ const ClickableText = styled(Text)`
}
`
// const CustomNumericalInput = styled(NumericalInput)`
// font-size: 72px;
// font-weight: 500;
// `
const MaxButton = styled.button`
padding: 0.5rem 1rem;
background-color: ${({ theme }) => theme.blue5};
......@@ -195,16 +201,6 @@ export default function RemoveLiquidity({ token0, token1 }) {
dispatch({ type: RemoveAction.TYPE, payload: { field, typedValue } })
}, [])
const handleSliderChange = (event, newPercent) => {
onUserInput(
Field.LIQUIDITY,
new TokenAmount(
pair?.liquidityToken,
JSBI.divide(JSBI.multiply(userLiquidity.raw, JSBI.BigInt(newPercent)), JSBI.BigInt(100))
).toExact()
)
}
const parsedAmounts: { [field: number]: TokenAmount } = {}
let poolTokenAmount
try {
......@@ -270,12 +266,34 @@ 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 &&
parsedAmounts[Field.LIQUIDITY] &&
new Percent(parsedAmounts[Field.LIQUIDITY]?.raw, userLiquidity.raw).toFixed(0)
const handlePresetPercentage = newPercent => {
onUserInput(
Field.LIQUIDITY,
new TokenAmount(
pair?.liquidityToken,
JSBI.divide(JSBI.multiply(userLiquidity.raw, JSBI.BigInt(newPercent)), JSBI.BigInt(100))
).toExact()
)
}
// update controlled perctenage when derived is updated
// useEffect(() => {
// if (derivedPerecent) {
// setPercentageInput(parseFloat(derivedPerecent))
// } else {
// setPercentageInput(0)
// }
// }, [derivedPerecent])
// get formatted amounts
const formattedAmounts = {
[Field.LIQUIDITY]:
......@@ -467,7 +485,17 @@ export default function RemoveLiquidity({ token0, token1 }) {
.then(response => {
setPendingConfirmation(false)
setTxHash(response.hash)
addTransaction(response)
addTransaction(
response,
'Remove ' +
parsedAmounts[Field.TOKEN0]?.toSignificant(3) +
' ' +
tokens[Field.TOKEN0]?.symbol +
' and ' +
parsedAmounts[Field.TOKEN1]?.toSignificant(3) +
' ' +
tokens[Field.TOKEN1]?.symbol
)
})
.catch(e => {
console.log(e)
......@@ -560,7 +588,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
</>
)
}
const pendingText: string = `Removed ${parsedAmounts[Field.TOKEN0]?.toSignificant(6)} ${
const pendingText: string = `Removing ${parsedAmounts[Field.TOKEN0]?.toSignificant(6)} ${
tokens[Field.TOKEN0]?.symbol
} and ${parsedAmounts[Field.TOKEN1]?.toSignificant(6)} ${tokens[Field.TOKEN1]?.symbol}`
......@@ -578,7 +606,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
topContent={modalHeader}
bottomContent={modalBottom}
pendingText={pendingText}
title="You will remove"
title="You will recieve"
/>
<AutoColumn gap="20px">
<LightCard>
......@@ -595,13 +623,28 @@ export default function RemoveLiquidity({ token0, token1 }) {
{showAdvanced ? 'Hide Advanced' : 'Show Advanced'}
</ClickableText>
</RowBetween>
<RowBetween style={{ alignItems: 'flex-end' }}>
<Row style={{ alignItems: 'flex-end' }}>
{/* <CustomNumericalInput value={percentageInput} onUserInput={input => handlePresetPercentage(input)} /> */}
<Text fontSize={72} fontWeight={500}>
{derivedPerecent ? (parseInt(derivedPerecent) < 1 ? '<1' : derivedPerecent) : '0'}%
</Text>
{!showAdvanced && <MaxButton onClick={e => handleSliderChange(e, 100)}>Max</MaxButton>}
</RowBetween>
{!showAdvanced && <Slider value={parseFloat(derivedPerecent)} onChange={handleSliderChange} />}
</Row>
{!showAdvanced && (
<RowBetween>
<MaxButton onClick={e => handlePresetPercentage(25)} width="20%">
25%
</MaxButton>
<MaxButton onClick={e => handlePresetPercentage(50)} width="20%">
50%
</MaxButton>
<MaxButton onClick={e => handlePresetPercentage(75)} width="20%">
75%
</MaxButton>
<MaxButton onClick={e => handlePresetPercentage(100)} width="20%">
Max
</MaxButton>
</RowBetween>
)}
</AutoColumn>
</LightCard>
{!showAdvanced && (
......@@ -664,7 +707,6 @@ export default function RemoveLiquidity({ token0, token1 }) {
token={tokens[Field.TOKEN0]}
error={inputError}
disableTokenSelect
customBalance={TokensDeposited[Field.TOKEN0]}
/>
<ColumnCenter>
<Plus size="16" color="#888D9B" />
......@@ -678,7 +720,6 @@ export default function RemoveLiquidity({ token0, token1 }) {
token={tokens[Field.TOKEN1]}
error={outputError}
disableTokenSelect
customBalance={TokensDeposited[Field.TOKEN1]}
/>
</>
)}
......
......@@ -6,11 +6,11 @@ import { withRouter } from 'react-router-dom'
import Question from '../../components/Question'
import SearchModal from '../../components/SearchModal'
import PositionCard from '../../components/PositionCard'
import { Link } from '../../theme'
import { Link, TYPE } from '../../theme'
import { Text } from 'rebass'
import { LightCard } from '../../components/Card'
import { RowBetween } from '../../components/Row'
import { ButtonPrimary } from '../../components/Button'
import { GreyCard } from '../../components/Card'
import { AutoColumn, ColumnCenter } from '../../components/Column'
import { useAllPairs } from '../../contexts/Pairs'
......@@ -71,25 +71,29 @@ function Supply({ history }) {
</ButtonPrimary>
<Positions>
<AutoColumn gap="20px">
{filteredExchangeList?.length !== 0 && (
<RowBetween padding={'0 8px'}>
<Text fontWeight={500}>Your Pooled Liquidity</Text>
<Question text="filler text" />
</RowBetween>
<RowBetween padding={'0 8px'}>
<Text fontWeight={500}>Your Pooled Liquidity</Text>
<Question text="filler text" />
</RowBetween>
{filteredExchangeList?.length === 0 && (
<LightCard
padding="40px
"
>
<TYPE.body textAlign="center">No liquidity found.</TYPE.body>
</LightCard>
)}
{filteredExchangeList}
<GreyCard style={{ textAlign: 'center', padding: '0.5rem 1.25rem 1rem 1.25rem' }}>
<Text color="#AEAEAE">
{filteredExchangeList?.length !== 0 ? `Don't see a pool you joined? ` : 'Already joined a pool?'}{' '}
<Link
onClick={() => {
history.push('/find')
}}
>
Import it.
</Link>
</Text>
</GreyCard>
<Text color="#AEAEAE" textAlign="center">
{filteredExchangeList?.length !== 0 ? `Don't see a pool you joined? ` : 'Already joined a pool? '}{' '}
<Link
onClick={() => {
history.push('/find')
}}
>
Import it.
</Link>
</Text>
</AutoColumn>
<FixedBottom>
<ColumnCenter>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment