Commit 6211dff0 authored by ianlapham's avatar ianlapham

typed pages

parent 655b7956
...@@ -3,6 +3,8 @@ import styled from 'styled-components' ...@@ -3,6 +3,8 @@ import styled from 'styled-components'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { transparentize } from 'polished' import { transparentize } from 'polished'
import QR from '../../assets/svg/QR.svg'
import { isAddress } from '../../utils' import { isAddress } from '../../utils'
import { useWeb3React, useDebounce } from '../../hooks' import { useWeb3React, useDebounce } from '../../hooks'
...@@ -13,6 +15,7 @@ const InputPanel = styled.div` ...@@ -13,6 +15,7 @@ const InputPanel = styled.div`
border-radius: 1.25rem; border-radius: 1.25rem;
background-color: ${({ theme }) => theme.inputBackground}; background-color: ${({ theme }) => theme.inputBackground};
z-index: 1; z-index: 1;
width: 100%;
` `
const ContainerRow = styled.div` const ContainerRow = styled.div`
...@@ -49,7 +52,7 @@ const LabelContainer = styled.div` ...@@ -49,7 +52,7 @@ const LabelContainer = styled.div`
const InputRow = styled.div` const InputRow = styled.div`
${({ theme }) => theme.flexRowNoWrap} ${({ theme }) => theme.flexRowNoWrap}
align-items: center; align-items: center;
padding: 0.25rem 0.85rem 0.75rem; padding: 0.75rem;
` `
const Input = styled.input` const Input = styled.input`
...@@ -69,7 +72,17 @@ const Input = styled.input` ...@@ -69,7 +72,17 @@ const Input = styled.input`
} }
` `
export default function AddressInputPanel({ title, initialInput = '', onChange = () => {}, onError = () => {} }) { const QRWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
border: 1px solid ${({ theme }) => theme.outlineGrey};
background: #fbfbfb;
padding: 4px;
border-radius: 8px;
`
export default function AddressInputPanel({ title, initialInput = '', onChange, onError}) {
const { t } = useTranslation() const { t } = useTranslation()
const { library } = useWeb3React() const { library } = useWeb3React()
...@@ -166,11 +179,6 @@ export default function AddressInputPanel({ title, initialInput = '', onChange = ...@@ -166,11 +179,6 @@ export default function AddressInputPanel({ title, initialInput = '', onChange =
<InputPanel> <InputPanel>
<ContainerRow error={input !== '' && error}> <ContainerRow error={input !== '' && error}>
<InputContainer> <InputContainer>
<LabelRow>
<LabelContainer>
<span>{title || t('recipientAddress')}</span>
</LabelContainer>
</LabelRow>
<InputRow> <InputRow>
<Input <Input
type="text" type="text"
...@@ -183,6 +191,9 @@ export default function AddressInputPanel({ title, initialInput = '', onChange = ...@@ -183,6 +191,9 @@ export default function AddressInputPanel({ title, initialInput = '', onChange =
onChange={onInput} onChange={onInput}
value={input} value={input}
/> />
<QRWrapper>
<img src={QR} alt="" />
</QRWrapper>
</InputRow> </InputRow>
</InputContainer> </InputContainer>
</ContainerRow> </ContainerRow>
......
import React, { useState } from 'react'
import styled from 'styled-components'
import QuestionHelper from '../Question'
import NumericalInput from '../NumericalInput'
import { Link } from '../../theme/components'
import { TYPE } from '../../theme'
import { AutoColumn } from '../../components/Column'
import Row, { RowBetween, RowFixed } from '../../components/Row'
import { ButtonRadio } from '../Button'
const InputWrapper = styled(RowBetween)`
width: 200px;
background-color: ${({ theme }) => theme.inputBackground};
border-radius: 8px;
padding: 4px 8px;
border: 1px solid transparent;
border: ${({ active, error, theme }) =>
error ? '1px solid ' + theme.salmonRed : active ? '1px solid ' + theme.royalBlue : ''};
`
const SLIPPAGE_INDEX = {
1: 1,
2: 2,
3: 3,
4: 4
}
export default function AdvancedSettings({ setIsOpen, setDeadline, setAllowedSlippage }) {
const [deadlineInput, setDeadlineInput] = useState(15)
const [slippageInput, setSlippageInput] = useState()
const [activeIndex, setActiveIndex] = useState(SLIPPAGE_INDEX[3])
const [slippageInputError, setSlippageInputError] = useState(null) // error
function parseCustomInput(val) {
const acceptableValues = [/^$/, /^\d{1,2}$/, /^\d{0,2}\.\d{0,2}$/]
if (val > 5) {
setSlippageInputError('Your transaction may be front-run.')
} else {
setSlippageInputError(null)
}
if (acceptableValues.some(a => a.test(val))) {
setSlippageInput(val)
setAllowedSlippage(val * 100)
}
}
function parseCustomDeadline(val) {
const acceptableValues = [/^$/, /^\d+$/]
if (acceptableValues.some(re => re.test(val))) {
setDeadlineInput(val)
setDeadline(val * 60)
}
}
return (
<AutoColumn gap="20px">
<Link
onClick={() => {
setIsOpen(false)
}}
>
back
</Link>
<RowBetween>
<TYPE.main>Limit additional price impact</TYPE.main>
<QuestionHelper text="" />
</RowBetween>
<Row>
<ButtonRadio
active={SLIPPAGE_INDEX[1] === activeIndex}
padding="4px 6px"
borderRadius="8px"
style={{ marginRight: '16px' }}
width={'60px'}
onClick={() => {
setActiveIndex(SLIPPAGE_INDEX[1])
setAllowedSlippage(10)
}}
>
0.1%
</ButtonRadio>
<ButtonRadio
active={SLIPPAGE_INDEX[2] === activeIndex}
padding="4px 6px"
borderRadius="8px"
style={{ marginRight: '16px' }}
width={'60px'}
onClick={() => {
setActiveIndex(SLIPPAGE_INDEX[2])
setAllowedSlippage(100)
}}
>
1%
</ButtonRadio>
<ButtonRadio
active={SLIPPAGE_INDEX[3] === activeIndex}
padding="4px"
borderRadius="8px"
width={'140px'}
onClick={() => {
setActiveIndex(SLIPPAGE_INDEX[3])
setAllowedSlippage(200)
}}
>
2% (suggested)
</ButtonRadio>
</Row>
<RowFixed>
<InputWrapper active={SLIPPAGE_INDEX[4] === activeIndex} error={slippageInputError}>
<NumericalInput
align={slippageInput ? 'right' : 'left'}
value={slippageInput || ''}
onUserInput={val => {
parseCustomInput(val)
setActiveIndex(SLIPPAGE_INDEX[4])
}}
placeHolder="Custom"
onClick={() => {
setActiveIndex(SLIPPAGE_INDEX[4])
if (slippageInput) {
parseCustomInput(slippageInput)
}
}}
/>
%
</InputWrapper>
{slippageInputError && (
<TYPE.error error={true} fontSize={12} style={{ marginLeft: '10px' }}>
Your transaction may be front-run
</TYPE.error>
)}
</RowFixed>
<RowBetween>
<TYPE.main>Adjust deadline (minutes from now)</TYPE.main>
</RowBetween>
<RowFixed>
<InputWrapper>
<NumericalInput
value={deadlineInput}
onUserInput={val => {
parseCustomDeadline(val)
}}
/>
</InputWrapper>
</RowFixed>
</AutoColumn>
)
}
import React from 'react' import React from 'react'
import { Button as RebassButton } from 'rebass/styled-components'
import styled from 'styled-components' import styled from 'styled-components'
import { darken, lighten } from 'polished' import { darken, lighten } from 'polished'
import { RowBetween } from '../Row' import { RowBetween } from '../Row'
import { ChevronDown } from 'react-feather' import { ChevronDown } from 'react-feather'
import { Button as RebassButton } from 'rebass/styled-components'
const Base = styled(RebassButton)` const Base = styled(RebassButton)`
padding: ${({ padding }) => (padding ? padding : '18px')}; padding: ${({ padding }) => (padding ? padding : '18px')};
width: ${({ width }) => (width ? width : '100%')}; width: ${({ width }) => (width ? width : '100%')};
font-size: 1rem;
font-weight: 500; font-weight: 500;
text-align: center; text-align: center;
border-radius: 20px; border-radius: 20px;
...@@ -39,7 +38,7 @@ export const ButtonPrimary = styled(Base)` ...@@ -39,7 +38,7 @@ export const ButtonPrimary = styled(Base)`
} }
&:disabled { &:disabled {
background-color: ${({ theme }) => theme.outlineGrey}; background-color: ${({ theme }) => theme.outlineGrey};
color: ${({ theme }) => theme.darkGrey} color: ${({ theme }) => theme.darkGray}
cursor: auto; cursor: auto;
box-shadow: none; box-shadow: none;
} }
...@@ -48,6 +47,7 @@ export const ButtonPrimary = styled(Base)` ...@@ -48,6 +47,7 @@ export const ButtonPrimary = styled(Base)`
export const ButtonSecondary = styled(Base)` export const ButtonSecondary = styled(Base)`
background-color: #ebf4ff; background-color: #ebf4ff;
color: #2172e5; color: #2172e5;
font-size: 16px;
border-radius: 8px; border-radius: 8px;
padding: 10px; padding: 10px;
...@@ -69,6 +69,30 @@ export const ButtonSecondary = styled(Base)` ...@@ -69,6 +69,30 @@ export const ButtonSecondary = styled(Base)`
} }
` `
export const ButtonPink = styled(Base)`
background-color: ${({ theme }) => theme.darkPink};
color: white;
padding: 10px;
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.darkPink)};
background-color: ${({ theme }) => darken(0.05, theme.darkPink)};
}
&:hover {
background-color: ${({ theme }) => darken(0.05, theme.darkPink)};
}
&:active {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.darkPink)};
background-color: ${({ theme }) => darken(0.1, theme.darkPink)};
}
&:disabled {
background-color: ${({ theme }) => theme.darkPink};
opacity: 50%;
cursor: auto;
}
`
export const ButtonEmpty = styled(Base)` export const ButtonEmpty = styled(Base)`
border: 1px solid #edeef2; border: 1px solid #edeef2;
background-color: transparent; background-color: transparent;
...@@ -157,7 +181,7 @@ export function ButtonError({ children, error, ...rest }) { ...@@ -157,7 +181,7 @@ export function ButtonError({ children, error, ...rest }) {
} }
} }
export function ButtonDropwdown({ disabled, children, ...rest }) { export function ButtonDropwdown({ disabled = false, children, ...rest }) {
return ( return (
<ButtonPrimary {...rest}> <ButtonPrimary {...rest}>
<RowBetween> <RowBetween>
...@@ -168,7 +192,7 @@ export function ButtonDropwdown({ disabled, children, ...rest }) { ...@@ -168,7 +192,7 @@ export function ButtonDropwdown({ disabled, children, ...rest }) {
) )
} }
export function ButtonDropwdownLight({ disabled, children, ...rest }) { export function ButtonDropwdownLight({ disabled = false, children, ...rest }) {
return ( return (
<ButtonEmpty {...rest}> <ButtonEmpty {...rest}>
<RowBetween> <RowBetween>
......
import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { Text } from 'rebass'
import { Box } from 'rebass/styled-components' import { Box } from 'rebass/styled-components'
const Card = styled(Box)` const Card = styled(Box)`
...@@ -18,3 +20,21 @@ export const LightCard = styled(Card)` ...@@ -18,3 +20,21 @@ export const LightCard = styled(Card)`
export const GreyCard = styled(Card)` export const GreyCard = styled(Card)`
background-color: rgba(255, 255, 255, 0.9); background-color: rgba(255, 255, 255, 0.9);
` `
const BlueCardStyled = styled(Card)`
background-color: #ebf4ff;
color: #2172e5;
border-radius: 12px;
padding: 8px;
width: fit-content;
`
export const BlueCard = ({ children }) => {
return (
<BlueCardStyled>
<Text textAlign="center" fontWeight={500} color="#2172E5">
{children}
</Text>
</BlueCardStyled>
)
}
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import ReactGA from 'react-ga'
import { ReactComponent as Dropup } from '../../assets/images/dropup-blue.svg'
import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg'
const SummaryWrapper = styled.div`
color: ${({ error, theme }) => (error ? theme.salmonRed : theme.doveGray)};
font-size: 0.75rem;
text-align: center;
margin-top: 1rem;
padding-top: 1rem;
`
const Details = styled.div`
background-color: ${({ theme }) => theme.concreteGray};
padding: 1.5rem;
border-radius: 1rem;
font-size: 0.75rem;
margin-top: 1rem;
`
const SummaryWrapperContainer = styled.div`
${({ theme }) => theme.flexRowNoWrap};
color: ${({ theme }) => theme.royalBlue};
text-align: center;
margin-top: 1rem;
padding-top: 1rem;
cursor: pointer;
align-items: center;
justify-content: center;
font-size: 0.75rem;
span {
margin-right: 12px;
}
img {
height: 0.75rem;
width: 0.75rem;
}
`
const WrappedDropup = ({ isError, highSlippageWarning, ...rest }) => <Dropup {...rest} />
const ColoredDropup = styled(WrappedDropup)`
path {
stroke: ${({ theme }) => theme.royalBlue};
}
`
const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => <Dropdown {...rest} />
const ColoredDropdown = styled(WrappedDropdown)`
path {
stroke: ${({ theme }) => theme.royalBlue};
}
`
class ContextualInfo extends Component {
static propTypes = {
openDetailsText: PropTypes.string,
renderTransactionDetails: PropTypes.func,
contextualInfo: PropTypes.string,
isError: PropTypes.bool
}
static defaultProps = {
openDetailsText: 'Advanced Details',
closeDetailsText: 'Hide Advanced',
renderTransactionDetails() {},
contextualInfo: '',
isError: false
}
state = {
showDetails: false
}
renderDetails() {
if (!this.state.showDetails) {
return null
}
return <Details>{this.props.renderTransactionDetails()}</Details>
}
render() {
const { openDetailsText, closeDetailsText, contextualInfo, isError } = this.props
if (contextualInfo) {
return <SummaryWrapper error={isError}>{contextualInfo}</SummaryWrapper>
}
return (
<>
<SummaryWrapperContainer
onClick={() => {
!this.state.showDetails &&
ReactGA.event({
category: 'Advanced Interaction',
action: 'Open Advanced Details',
label: 'Pool Page Details'
})
this.setState(prevState => {
return { showDetails: !prevState.showDetails }
})
}}
>
{!this.state.showDetails ? (
<>
<span>{openDetailsText}</span>
<ColoredDropup />
</>
) : (
<>
<span>{closeDetailsText}</span>
<ColoredDropdown />
</>
)}
</SummaryWrapperContainer>
{this.renderDetails()}
</>
)
}
}
export default ContextualInfo
import React, { useState } from 'react'
import styled, { css } from 'styled-components'
import { transparentize } from 'polished'
import ReactGA from 'react-ga'
import { ReactComponent as Dropup } from '../../assets/images/dropup-blue.svg'
import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg'
const SummaryWrapper = styled.div`
color: ${({ error, brokenTokenWarning, theme }) => (error || brokenTokenWarning ? theme.salmonRed : theme.doveGray)};
font-size: 0.75rem;
text-align: center;
margin-top: 1rem;
padding-top: 1rem;
`
const SummaryWrapperContainer = styled.div`
${({ theme }) => theme.flexRowNoWrap};
color: ${({ theme }) => theme.royalBlue};
text-align: center;
margin-top: 1rem;
padding-top: 1rem;
cursor: pointer;
align-items: center;
justify-content: center;
font-size: 0.75rem;
img {
height: 0.75rem;
width: 0.75rem;
}
`
const Details = styled.div`
background-color: ${({ theme }) => theme.concreteGray};
/* padding: 1.25rem 1.25rem 1rem 1.25rem; */
border-radius: 1rem;
font-size: 0.75rem;
margin: 1rem 0.5rem 0 0.5rem;
`
const ErrorSpan = styled.span`
margin-right: 12px;
font-size: 0.75rem;
line-height: 0.75rem;
color: ${({ isError, theme }) => isError && theme.salmonRed};
${({ slippageWarning, highSlippageWarning, theme }) =>
highSlippageWarning
? css`
color: ${theme.salmonRed};
font-weight: 600;
`
: slippageWarning &&
css`
background-color: ${transparentize(0.6, theme.warningYellow)};
font-weight: 600;
padding: 0.25rem;
`}
`
const WrappedDropup = ({ isError, highSlippageWarning, ...rest }) => <Dropup {...rest} />
const ColoredDropup = styled(WrappedDropup)`
path {
stroke: ${({ isError, theme }) => (isError ? theme.salmonRed : theme.royalBlue)};
${({ highSlippageWarning, theme }) =>
highSlippageWarning &&
css`
stroke: ${theme.salmonRed};
`}
}
`
const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => <Dropdown {...rest} />
const ColoredDropdown = styled(WrappedDropdown)`
path {
stroke: ${({ isError, theme }) => (isError ? theme.salmonRed : theme.royalBlue)};
${({ highSlippageWarning, theme }) =>
highSlippageWarning &&
css`
stroke: ${theme.salmonRed};
`}
}
`
export default function ContextualInfo({
openDetailsText = 'Advanced Details',
closeDetailsText = 'Hide Advanced',
contextualInfo = '',
allowExpand = false,
isError = false,
slippageWarning,
highSlippageWarning,
brokenTokenWarning,
dropDownContent
}) {
const [showDetails, setShowDetails] = useState(false)
return !allowExpand ? (
<SummaryWrapper brokenTokenWarning={brokenTokenWarning}>{contextualInfo}</SummaryWrapper>
) : (
<>
<SummaryWrapperContainer
onClick={() => {
!showDetails &&
ReactGA.event({
category: 'Advanced Interaction',
action: 'Open Advanced Details',
label: 'Swap/Send Page Details'
})
setShowDetails(s => !s)
}}
>
<>
<ErrorSpan isError={isError} slippageWarning={slippageWarning} highSlippageWarning={highSlippageWarning}>
{(slippageWarning || highSlippageWarning) && (
<span role="img" aria-label="warning">
⚠️
</span>
)}
{contextualInfo ? contextualInfo : showDetails ? closeDetailsText : openDetailsText}
</ErrorSpan>
{showDetails ? (
<ColoredDropup isError={isError} highSlippageWarning={highSlippageWarning} />
) : (
<ColoredDropdown isError={isError} highSlippageWarning={highSlippageWarning} />
)}
</>
</SummaryWrapperContainer>
{showDetails && <Details>{dropDownContent()}</Details>}
</>
)
}
...@@ -8,6 +8,7 @@ import { WETH } from '@uniswap/sdk' ...@@ -8,6 +8,7 @@ import { WETH } from '@uniswap/sdk'
import TokenLogo from '../TokenLogo' import TokenLogo from '../TokenLogo'
import DoubleLogo from '../DoubleLogo' import DoubleLogo from '../DoubleLogo'
import SearchModal from '../SearchModal' import SearchModal from '../SearchModal'
import { TYPE } from '../../theme'
import { Text } from 'rebass' import { Text } from 'rebass'
import { RowBetween } from '../Row' import { RowBetween } from '../Row'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg' import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
...@@ -20,6 +21,8 @@ import { calculateGasMargin } from '../../utils' ...@@ -20,6 +21,8 @@ import { calculateGasMargin } from '../../utils'
import { useAddressBalance } from '../../contexts/Balances' import { useAddressBalance } from '../../contexts/Balances'
import { useTransactionAdder, usePendingApproval } from '../../contexts/Transactions' import { useTransactionAdder, usePendingApproval } from '../../contexts/Transactions'
import { ROUTER_ADDRESSES } from '../../constants'
const GAS_MARGIN = ethers.utils.bigNumberify(1000) const GAS_MARGIN = ethers.utils.bigNumberify(1000)
const SubCurrencySelect = styled.button` const SubCurrencySelect = styled.button`
...@@ -51,26 +54,18 @@ const CurrencySelect = styled.button` ...@@ -51,26 +54,18 @@ const CurrencySelect = styled.button`
font-size: 20px; font-size: 20px;
background-color: ${({ selected, theme }) => (selected ? theme.buttonBackgroundPlain : theme.royalBlue)}; background-color: ${({ selected, theme }) => (selected ? theme.buttonBackgroundPlain : theme.royalBlue)};
color: ${({ selected, theme }) => (selected ? theme.textColor : theme.white)}; color: ${({ selected, theme }) => (selected ? theme.textColor : theme.white)};
border: 1px solid
${({ selected, theme, disableTokenSelect }) =>
disableTokenSelect ? theme.buttonBackgroundPlain : selected ? theme.buttonOutlinePlain : theme.royalBlue};
border-radius: 8px; border-radius: 8px;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
border: 1px solid
${({ selected, theme }) => (selected ? darken(0.1, theme.outlineGrey) : darken(0.1, theme.royalBlue))};
:focus,
:hover { :hover {
border: 1px solid border: 1px solid
${({ selected, theme }) => (selected ? darken(0.1, theme.outlineGrey) : darken(0.1, theme.royalBlue))}; ${({ selected, theme }) => (selected ? darken(0.2, theme.outlineGrey) : darken(0.2, theme.royalBlue))};
}
:focus {
border: 1px solid ${({ theme }) => darken(0.1, theme.royalBlue)};
}
:active {
background-color: ${({ selected, theme }) =>
selected ? darken(0.1, theme.zumthorBlue) : darken(0.1, theme.royalBlue)};
} }
` `
...@@ -99,12 +94,8 @@ const InputPanel = styled.div` ...@@ -99,12 +94,8 @@ const InputPanel = styled.div`
const Container = styled.div` const Container = styled.div`
border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')}; border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')};
border: 1px solid ${({ error, theme }) => (error ? theme.salmonRed : theme.mercuryGray)}; border: 1px solid ${({ error, theme }) => (error ? theme.salmonRed : theme.backgroundColor)};
background-color: ${({ theme }) => theme.inputBackground}; background-color: ${({ theme }) => theme.inputBackground};
:focus-within {
border: 1px solid ${({ error, theme }) => (error ? theme.salmonRed : theme.malibuBlue)};
}
` `
const LabelRow = styled.div` const LabelRow = styled.div`
...@@ -113,7 +104,7 @@ const LabelRow = styled.div` ...@@ -113,7 +104,7 @@ const LabelRow = styled.div`
color: ${({ theme }) => theme.doveGray}; color: ${({ theme }) => theme.doveGray};
font-size: 0.75rem; font-size: 0.75rem;
line-height: 1rem; line-height: 1rem;
padding: 0.75rem 1rem 0; padding: 0.5rem 1rem 1rem 1rem;
span:hover { span:hover {
cursor: pointer; cursor: pointer;
color: ${({ theme }) => darken(0.2, theme.doveGray)}; color: ${({ theme }) => darken(0.2, theme.doveGray)};
...@@ -161,12 +152,12 @@ export default function CurrencyInputPanel({ ...@@ -161,12 +152,12 @@ export default function CurrencyInputPanel({
value, value,
field, field,
onUserInput, onUserInput,
onTokenSelection = null,
title, title,
onMax, onMax,
atMax, atMax,
error, error,
urlAddedTokens = [], // used urlAddedTokens = [], // used
onTokenSelection = null,
token = null, token = null,
showUnlock = false, // used to show unlock if approval needed showUnlock = false, // used to show unlock if approval needed
disableUnlock = false, disableUnlock = false,
...@@ -176,23 +167,19 @@ export default function CurrencyInputPanel({ ...@@ -176,23 +167,19 @@ export default function CurrencyInputPanel({
exchange = null, // used for double token logo exchange = null, // used for double token logo
customBalance = null, // used for LP balances instead of token balance customBalance = null, // used for LP balances instead of token balance
hideInput = false, hideInput = false,
showSendWithSwap = false, showSendWithSwap = false
onTokenSelectSendWithSwap = null
}) { }) {
const { account, chainId } = useWeb3React()
const { t } = useTranslation() const { t } = useTranslation()
const addTransaction = useTransactionAdder() const { account, chainId } = useWeb3React()
const routerAddress = ROUTER_ADDRESSES[chainId]
const addTransaction = useTransactionAdder()
const [modalOpen, setModalOpen] = useState(false) const [modalOpen, setModalOpen] = useState(false)
// this one causes the infinite loop
const userTokenBalance = useAddressBalance(account, token) const userTokenBalance = useAddressBalance(account, token)
const tokenContract = useTokenContract(token?.address) const tokenContract = useTokenContract(token?.address)
const pendingApproval = usePendingApproval(token?.address) const pendingApproval = usePendingApproval(token?.address)
const routerAddress = '0xd9210Ff5A0780E083BB40e30d005d93a2DcFA4EF'
function renderUnlockButton() { function renderUnlockButton() {
if ( if (
disableUnlock || disableUnlock ||
...@@ -240,19 +227,6 @@ export default function CurrencyInputPanel({ ...@@ -240,19 +227,6 @@ export default function CurrencyInputPanel({
return ( return (
<InputPanel> <InputPanel>
<Container error={!!error} hideInput={hideInput}> <Container error={!!error} hideInput={hideInput}>
{!hideBalance && (
<LabelRow>
<RowBetween>
<Text>{title}</Text>
<ErrorSpan data-tip={'Enter max'} error={!!error} onClick={() => {}}></ErrorSpan>
<ClickableText onClick={onMax}>
<Text>
Balance: {customBalance ? customBalance?.toSignificant(4) : userTokenBalance?.toSignificant(4)}
</Text>
</ClickableText>
</RowBetween>
</LabelRow>
)}
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} hideInput={hideInput}> <InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} hideInput={hideInput}>
{!hideInput && ( {!hideInput && (
<> <>
...@@ -267,7 +241,7 @@ export default function CurrencyInputPanel({ ...@@ -267,7 +241,7 @@ export default function CurrencyInputPanel({
</> </>
)} )}
<CurrencySelect <CurrencySelect
selected={!!token?.address} selected={!!token}
onClick={() => { onClick={() => {
if (!disableTokenSelect) { if (!disableTokenSelect) {
setModalOpen(true) setModalOpen(true)
...@@ -292,6 +266,19 @@ export default function CurrencyInputPanel({ ...@@ -292,6 +266,19 @@ export default function CurrencyInputPanel({
</Aligner> </Aligner>
</CurrencySelect> </CurrencySelect>
</InputRow> </InputRow>
{!hideBalance && !!token && (
<LabelRow>
<RowBetween>
<Text>{'-'}</Text>
<ErrorSpan data-tip={'Enter max'} error={!!error} onClick={() => {}}></ErrorSpan>
<ClickableText onClick={onMax}>
<TYPE.body>
Balance: {customBalance ? customBalance?.toSignificant(4) : userTokenBalance?.toSignificant(4)}
</TYPE.body>
</ClickableText>
</RowBetween>
</LabelRow>
)}
</Container> </Container>
{!disableTokenSelect && ( {!disableTokenSelect && (
<SearchModal <SearchModal
...@@ -304,7 +291,6 @@ export default function CurrencyInputPanel({ ...@@ -304,7 +291,6 @@ export default function CurrencyInputPanel({
field={field} field={field}
onTokenSelect={onTokenSelection} onTokenSelect={onTokenSelection}
showSendWithSwap={showSendWithSwap} showSendWithSwap={showSendWithSwap}
onTokenSelectSendWithSwap={onTokenSelectSendWithSwap}
/> />
)} )}
</InputPanel> </InputPanel>
......
This diff is collapsed.
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import Row from '../Row'
import Menu from '../Menu' import Menu from '../Menu'
import Logo from '../../assets/svg/logo.svg' import Logo from '../../assets/svg/logo.svg'
import Row from '../Row' import Card from '../Card'
import Web3Status from '../Web3Status' import Web3Status from '../Web3Status'
import { CloseIcon } from '../../theme/components' import { X } from 'react-feather'
import { Link } from '../../theme' import { Link } from '../../theme'
import { Text } from 'rebass' import { Text } from 'rebass'
import Card from '../Card'
import { X } from 'react-feather'
import { WETH } from '@uniswap/sdk' import { WETH } from '@uniswap/sdk'
import { isMobile } from 'react-device-detect' import { isMobile } from 'react-device-detect'
...@@ -46,9 +45,7 @@ const Title = styled.div` ...@@ -46,9 +45,7 @@ const Title = styled.div`
const TitleText = styled.div` const TitleText = styled.div`
font-size: 24px; font-size: 24px;
font-weight: 700; font-weight: 700;
background: linear-gradient(119.64deg, #fb1868 -5.55%, #ff00f3 154.46%); color: ${({ theme }) => theme.black};
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-left: 12px; margin-left: 12px;
` `
...@@ -58,22 +55,21 @@ const AccountElement = styled.div` ...@@ -58,22 +55,21 @@ const AccountElement = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
background-color: ${({ theme }) => theme.outlineGrey}; background-color: ${({ theme, active }) => (!active ? theme.white : theme.outlineGrey)};
border: 1px solid ${({ theme }) => theme.outlineGrey}; border: 1px solid ${({ theme }) => theme.outlineGrey};
border-radius: 8px; border-radius: 8px;
padding-left: 8px; padding-left: ${({ active }) => (active ? '8px' : 0)};
:focus { :focus {
border: 1px solid blue; border: 1px solid blue;
} }
/* width: 100%; */
` `
const FixedPopupColumn = styled(AutoColumn)` const FixedPopupColumn = styled(AutoColumn)`
position: absolute; position: absolute;
top: 80px; top: 80px;
right: 20px right: 20px
width: 340px; width: 380px;
` `
const StyledClose = styled(X)` const StyledClose = styled(X)`
...@@ -86,12 +82,10 @@ const StyledClose = styled(X)` ...@@ -86,12 +82,10 @@ const StyledClose = styled(X)`
` `
const Popup = styled(Card)` const Popup = styled(Card)`
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.04), 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.04);
z-index: 9999; z-index: 9999;
border-radius: 8px; border-radius: 8px;
padding: 1rem; padding: 1rem;
background: ${theme => theme.white}; background-color: white;
` `
export default function Header() { export default function Header() {
...@@ -115,8 +109,8 @@ export default function Header() { ...@@ -115,8 +109,8 @@ export default function Header() {
</Title> </Title>
</HeaderElement> </HeaderElement>
<HeaderElement> <HeaderElement>
<AccountElement> <AccountElement active={!!account}>
{!isMobile ? ( {!isMobile && account ? (
<Row style={{ marginRight: '-1.25rem', paddingRight: '1.75rem' }}> <Row style={{ marginRight: '-1.25rem', paddingRight: '1.75rem' }}>
<Text fontWeight={500}> {userEthBalance && userEthBalance?.toFixed(4) + ' ETH'}</Text> <Text fontWeight={500}> {userEthBalance && userEthBalance?.toFixed(4) + ' ETH'}</Text>
</Row> </Row>
......
import React, { useState } from 'react' import React, { useState } from 'react'
import { JSBI } from '@uniswap/sdk'
import { withRouter } from 'react-router-dom' import { withRouter } from 'react-router-dom'
import { TokenAmount, JSBI, Token, Exchange } from '@uniswap/sdk'
import { useToken } from '../../contexts/Tokens'
import { useExchange } from '../../contexts/Exchanges'
import { useWeb3React } from '@web3-react/core'
import { useAddressBalance } from '../../contexts/Balances'
import { usePopups } from '../../contexts/Application'
import Row from '../Row' import Row from '../Row'
import TokenLogo from '../TokenLogo' import TokenLogo from '../TokenLogo'
import SearchModal from '../SearchModal' import SearchModal from '../SearchModal'
import PositionCard from '../PositionCard' import PositionCard from '../PositionCard'
import DoubleTokenLogo from '../DoubleLogo'
import { Link } from '../../theme' import { Link } from '../../theme'
import { Text } from 'rebass' import { Text } from 'rebass'
import { Plus } from 'react-feather' import { Plus } from 'react-feather'
import { LightCard } from '../Card' import { LightCard } from '../Card'
import Column, { AutoColumn, ColumnCenter } from '../Column' import { AutoColumn, ColumnCenter } from '../Column'
import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button' import { ButtonPrimary, ButtonDropwdown, ButtonDropwdownLight } from '../Button'
import DoubleTokenLogo from '../DoubleLogo'
import { useToken } from '../../contexts/Tokens'
import { usePopups } from '../../contexts/Application'
import { useExchange } from '../../contexts/Exchanges'
import { useWeb3React } from '@web3-react/core'
import { useAddressBalance } from '../../contexts/Balances'
function PoolFinder({ history }) { function PoolFinder({ history }) {
const Fields = { const Fields = {
...@@ -27,27 +27,25 @@ function PoolFinder({ history }) { ...@@ -27,27 +27,25 @@ function PoolFinder({ history }) {
} }
const { account } = useWeb3React() const { account } = useWeb3React()
const [showSearch, setShowSearch] = useState(false) const [showSearch, setShowSearch] = useState<boolean>(false)
const [activeField, setActiveField] = useState(Fields.TOKEN0) const [activeField, setActiveField] = useState<number>(Fields.TOKEN0)
const [, addPopup] = usePopups() const [, addPopup] = usePopups()
const [token0Address, setToken0Address] = useState() const [token0Address, setToken0Address] = useState<string>()
const [token1Address, setToken1Address] = useState() const [token1Address, setToken1Address] = useState<string>()
const token0 = useToken(token0Address)
const token1 = useToken(token1Address)
const exchange = useExchange(token0, token1)
const position = useAddressBalance(account, exchange?.liquidityToken) const token0: Token = useToken(token0Address)
const token1: Token = useToken(token1Address)
const newExchange = exchange && JSBI.equal(exchange.reserve0.raw, JSBI.BigInt(0)) const exchange: Exchange = useExchange(token0, token1)
const position: TokenAmount = useAddressBalance(account, exchange?.liquidityToken)
const allowImport = position && JSBI.greaterThan(position.raw, JSBI.BigInt(0)) const newExchange: boolean = exchange && JSBI.equal(exchange.reserve0.raw, JSBI.BigInt(0))
const allowImport: boolean = position && JSBI.greaterThan(position.raw, JSBI.BigInt(0))
function endSearch() { function endSearch() {
history.goBack() history.goBack() // return to previous page
addPopup( addPopup(
<Row> <Row>
<DoubleTokenLogo a0={token0Address || ''} a1={token1Address || ''} margin={true} /> <DoubleTokenLogo a0={token0Address || ''} a1={token1Address || ''} margin={true} />
......
...@@ -2,12 +2,11 @@ import React, { useState, useRef, useMemo, useEffect } from 'react' ...@@ -2,12 +2,11 @@ import React, { useState, useRef, useMemo, useEffect } from 'react'
import '@reach/tooltip/styles.css' import '@reach/tooltip/styles.css'
import styled from 'styled-components' import styled from 'styled-components'
import escapeStringRegex from 'escape-string-regexp' import escapeStringRegex from 'escape-string-regexp'
import { JSBI } from '@uniswap/sdk'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { isMobile } from 'react-device-detect' import { isMobile } from 'react-device-detect'
import { withRouter } from 'react-router-dom' import { withRouter } from 'react-router-dom'
import { JSBI } from '@uniswap/sdk'
import { Link as StyledLink } from '../../theme/components' import { Link as StyledLink } from '../../theme/components'
import Modal from '../Modal' import Modal from '../Modal'
...@@ -107,11 +106,17 @@ const PaddedItem = styled(RowBetween)` ...@@ -107,11 +106,17 @@ const PaddedItem = styled(RowBetween)`
const MenuItem = styled(PaddedItem)` const MenuItem = styled(PaddedItem)`
cursor: pointer; cursor: pointer;
:hover { :hover {
background-color: ${({ theme }) => theme.tokenRowHover}; background-color: ${({ theme }) => theme.tokenRowHover};
} }
` `
// filters on results
const FILTERS = {
VOLUME: 'VOLUME',
LIQUIDITY: 'LIQUIDITY',
BALANCES: 'BALANCES'
}
function SearchModal({ function SearchModal({
history, history,
isOpen, isOpen,
...@@ -120,28 +125,26 @@ function SearchModal({ ...@@ -120,28 +125,26 @@ function SearchModal({
urlAddedTokens, urlAddedTokens,
filterType, filterType,
hiddenToken, hiddenToken,
showSendWithSwap, showSendWithSwap
onTokenSelectSendWithSwap
}) { }) {
const { t } = useTranslation() const { t } = useTranslation()
const { account, chainId } = useWeb3React() const { account, chainId } = useWeb3React()
const allTokens = useAllTokens()
const allExchanges = useAllExchanges()
const allBalances = useAllBalances()
const [searchQuery, setSearchQuery] = useState('') const [searchQuery, setSearchQuery] = useState('')
const [sortDirection, setSortDirection] = useState(true)
// get all exchanges
const allExchanges = useAllExchanges()
const token = useToken(searchQuery) const token = useToken(searchQuery)
const tokenAddress = token && token.address const tokenAddress = token && token.address
// get all tokens // amount of tokens to display at once
const allTokens = useAllTokens() const [, setTokensShown] = useState(0)
const [, setPairsShown] = useState(0)
// all balances for both account and exchanges
let allBalances = useAllBalances()
const [sortDirection, setSortDirection] = useState(true) const [activeFilter, setActiveFilter] = useState(FILTERS.BALANCES)
const tokenList = useMemo(() => { const tokenList = useMemo(() => {
return Object.keys(allTokens) return Object.keys(allTokens)
...@@ -149,12 +152,10 @@ function SearchModal({ ...@@ -149,12 +152,10 @@ function SearchModal({
if (allTokens[a].symbol && allTokens[b].symbol) { if (allTokens[a].symbol && allTokens[b].symbol) {
const aSymbol = allTokens[a].symbol.toLowerCase() const aSymbol = allTokens[a].symbol.toLowerCase()
const bSymbol = allTokens[b].symbol.toLowerCase() const bSymbol = allTokens[b].symbol.toLowerCase()
// pin ETH to top // pin ETH to top
if (aSymbol === 'ETH'.toLowerCase() || bSymbol === 'ETH'.toLowerCase()) { if (aSymbol === 'ETH'.toLowerCase() || bSymbol === 'ETH'.toLowerCase()) {
return aSymbol === bSymbol ? 0 : aSymbol === 'ETH'.toLowerCase() ? -1 : 1 return aSymbol === bSymbol ? 0 : aSymbol === 'ETH'.toLowerCase() ? -1 : 1
} }
// sort by balance // sort by balance
const balanceA = allBalances?.[account]?.[a] const balanceA = allBalances?.[account]?.[a]
const balanceB = allBalances?.[account]?.[b] const balanceB = allBalances?.[account]?.[b]
...@@ -162,16 +163,12 @@ function SearchModal({ ...@@ -162,16 +163,12 @@ function SearchModal({
if (balanceA && !balanceB) { if (balanceA && !balanceB) {
return sortDirection return sortDirection
} }
if (!balanceA && balanceB) { if (!balanceA && balanceB) {
return sortDirection * -1 return sortDirection * -1
} }
if (balanceA && balanceB) { if (balanceA && balanceB) {
return sortDirection * parseFloat(balanceA.toExact()) > parseFloat(balanceB.toExact()) ? -1 : 1 return sortDirection * parseFloat(balanceA.toExact()) > parseFloat(balanceB.toExact()) ? -1 : 1
} }
// sort alphabetically
return aSymbol < bSymbol ? -1 : aSymbol > bSymbol ? 1 : 0 return aSymbol < bSymbol ? -1 : aSymbol > bSymbol ? 1 : 0
} else { } else {
return 0 return 0
...@@ -181,16 +178,11 @@ function SearchModal({ ...@@ -181,16 +178,11 @@ function SearchModal({
if (k === hiddenToken) { if (k === hiddenToken) {
return false return false
} }
let balance
// only update if we have data
balance = allBalances?.[account]?.[k]
return { return {
name: allTokens[k].name, name: allTokens[k].name,
symbol: allTokens[k].symbol, symbol: allTokens[k].symbol,
address: k, address: k,
balance: balance balance: allBalances?.[account]?.[k]
} }
}) })
}, [allTokens, allBalances, account, sortDirection, hiddenToken]) }, [allTokens, allBalances, account, sortDirection, hiddenToken])
...@@ -198,10 +190,7 @@ function SearchModal({ ...@@ -198,10 +190,7 @@ function SearchModal({
const filteredTokenList = useMemo(() => { const filteredTokenList = useMemo(() => {
return tokenList.filter(tokenEntry => { return tokenList.filter(tokenEntry => {
const inputIsAddress = searchQuery.slice(0, 2) === '0x' const inputIsAddress = searchQuery.slice(0, 2) === '0x'
// check the regex for each field
const regexMatches = Object.keys(tokenEntry).map(tokenEntryKey => { const regexMatches = Object.keys(tokenEntry).map(tokenEntryKey => {
// if address field only search if input starts with 0x
if (tokenEntryKey === 'address') { if (tokenEntryKey === 'address') {
return ( return (
inputIsAddress && inputIsAddress &&
...@@ -218,21 +207,14 @@ function SearchModal({ ...@@ -218,21 +207,14 @@ function SearchModal({
}) })
}, [tokenList, searchQuery]) }, [tokenList, searchQuery])
function _onTokenSelect(address, sendWithSwap = false) { function _onTokenSelect(address) {
if (sendWithSwap) { setSearchQuery('')
setSearchQuery('') onTokenSelect(address)
onTokenSelectSendWithSwap(address) onDismiss()
onDismiss()
} else {
setSearchQuery('')
onTokenSelect(address)
onDismiss()
}
} }
// manage focus on modal show // manage focus on modal show
const inputRef = useRef() const inputRef = useRef()
function onInput(event) { function onInput(event) {
const input = event.target.value const input = event.target.value
const checksummedInput = isAddress(input) const checksummedInput = isAddress(input)
...@@ -244,34 +226,39 @@ function SearchModal({ ...@@ -244,34 +226,39 @@ function SearchModal({
onDismiss() onDismiss()
} }
// amount of tokens to display at once
const [, setTokensShown] = useState(0)
const [, setPairsShown] = useState(0)
// filters on results
const FILTERS = {
VOLUME: 'VOLUME',
LIQUIDITY: 'LIQUIDITY',
BALANCES: 'BALANCES'
}
const [activeFilter, setActiveFilter] = useState(FILTERS.BALANCES)
// sort tokens // sort tokens
const escapeStringRegexp = string => string const escapeStringRegexp = string => string
// sort pairs const sortedPairList = useMemo(() => {
const filteredPairList = useMemo(() => { return Object.keys(allExchanges).sort((a, b) => {
// check if the search is an address // sort by balance
const balanceA = allBalances?.[account]?.[a]
const balanceB = allBalances?.[account]?.[b]
if (balanceA && !balanceB) {
return sortDirection
}
if (!balanceA && balanceB) {
return sortDirection * -1
}
if (balanceA && balanceB) {
const order = sortDirection * (parseFloat(balanceA.toExact()) > parseFloat(balanceB.toExact()) ? -1 : 1)
return order ? 1 : -1
} else {
return 0
}
})
}, [account, allBalances, allExchanges, sortDirection])
const filteredPairList = useMemo(() => {
const isAddress = searchQuery.slice(0, 2) === '0x' const isAddress = searchQuery.slice(0, 2) === '0x'
return Object.keys(allExchanges).filter(exchangeAddress => { return sortedPairList.filter(exchangeAddress => {
const exchange = allExchanges[exchangeAddress] const exchange = allExchanges[exchangeAddress]
if (searchQuery === '') { if (searchQuery === '') {
return true return true
} }
const token0 = allTokens[exchange.token0] const token0 = allTokens[exchange.token0]
const token1 = allTokens[exchange.token1] const token1 = allTokens[exchange.token1]
const regexMatches = Object.keys(token0).map(field => { const regexMatches = Object.keys(token0).map(field => {
if ( if (
(field === 'address' && isAddress) || (field === 'address' && isAddress) ||
...@@ -288,7 +275,7 @@ function SearchModal({ ...@@ -288,7 +275,7 @@ function SearchModal({
return regexMatches.some(m => m) return regexMatches.some(m => m)
}) })
}, [allExchanges, allTokens, searchQuery]) }, [account, allBalances, allExchanges, allTokens, searchQuery, sortDirection])
// update the amount shown as filtered list changes // update the amount shown as filtered list changes
useEffect(() => { useEffect(() => {
...@@ -312,9 +299,7 @@ function SearchModal({ ...@@ -312,9 +299,7 @@ function SearchModal({
filteredPairList.map((exchangeAddress, i) => { filteredPairList.map((exchangeAddress, i) => {
const token0 = allTokens[allExchanges[exchangeAddress].token0] const token0 = allTokens[allExchanges[exchangeAddress].token0]
const token1 = allTokens[allExchanges[exchangeAddress].token1] const token1 = allTokens[allExchanges[exchangeAddress].token1]
const balance = allBalances?.[account]?.[exchangeAddress]?.toSignificant(6) const balance = allBalances?.[account]?.[exchangeAddress]?.toSignificant(6)
return ( return (
<MenuItem <MenuItem
key={i} key={i}
......
...@@ -2,23 +2,23 @@ import React from 'react' ...@@ -2,23 +2,23 @@ import React from 'react'
import Slider from '@material-ui/core/Slider' import Slider from '@material-ui/core/Slider'
import { withStyles } from '@material-ui/core/styles' import { withStyles } from '@material-ui/core/styles'
const marks = [ // const marks = [
{ // {
value: 0 // value: 0
}, // },
{ // {
value: 25 // value: 25
}, // },
{ // {
value: 50 // value: 50
}, // },
{ // {
value: 75 // value: 75
}, // },
{ // {
value: 100 // value: 100
} // }
] // ]
const StyledSlider = withStyles({ const StyledSlider = withStyles({
root: { root: {
......
This diff is collapsed.
...@@ -39,7 +39,6 @@ export default function Web3ReactManager({ children }) { ...@@ -39,7 +39,6 @@ export default function Web3ReactManager({ children }) {
const triedEager = useEagerConnect() const triedEager = useEagerConnect()
// after eagerly trying injected, if the network connect ever isn't active or in an error state, activate itd // after eagerly trying injected, if the network connect ever isn't active or in an error state, activate itd
// TODO think about not doing this at all
useEffect(() => { useEffect(() => {
if (triedEager && !networkActive && !networkError && !active) { if (triedEager && !networkActive && !networkError && !active) {
activateNetwork(network) activateNetwork(network)
......
[ [
{
"inputs": [
{
"internalType": "address",
"name": "_WETH",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"payable": true,
"stateMutability": "payable",
"type": "fallback"
},
{ {
"constant": true, "constant": true,
"inputs": [], "inputs": [],
...@@ -153,32 +136,6 @@ ...@@ -153,32 +136,6 @@
"stateMutability": "payable", "stateMutability": "payable",
"type": "function" "type": "function"
}, },
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "tokenA",
"type": "address"
},
{
"internalType": "address",
"name": "tokenB",
"type": "address"
}
],
"name": "exchangeFor",
"outputs": [
{
"internalType": "address",
"name": "exchange",
"type": "address"
}
],
"payable": false,
"stateMutability": "pure",
"type": "function"
},
{ {
"constant": true, "constant": true,
"inputs": [], "inputs": [],
...@@ -191,7 +148,7 @@ ...@@ -191,7 +148,7 @@
} }
], ],
"payable": false, "payable": false,
"stateMutability": "view", "stateMutability": "pure",
"type": "function" "type": "function"
}, },
{ {
...@@ -308,52 +265,6 @@ ...@@ -308,52 +265,6 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "tokenA",
"type": "address"
},
{
"internalType": "address",
"name": "tokenB",
"type": "address"
}
],
"name": "getReserves",
"outputs": [
{
"internalType": "uint256",
"name": "reserveA",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "reserveB",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "initCodeHash",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{ {
"constant": true, "constant": true,
"inputs": [ "inputs": [
...@@ -525,6 +436,11 @@ ...@@ -525,6 +436,11 @@
"name": "deadline", "name": "deadline",
"type": "uint256" "type": "uint256"
}, },
{
"internalType": "bool",
"name": "approveMax",
"type": "bool"
},
{ {
"internalType": "uint8", "internalType": "uint8",
"name": "v", "name": "v",
...@@ -596,6 +512,11 @@ ...@@ -596,6 +512,11 @@
"name": "deadline", "name": "deadline",
"type": "uint256" "type": "uint256"
}, },
{
"internalType": "bool",
"name": "approveMax",
"type": "bool"
},
{ {
"internalType": "uint8", "internalType": "uint8",
"name": "v", "name": "v",
...@@ -629,37 +550,6 @@ ...@@ -629,37 +550,6 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "tokenA",
"type": "address"
},
{
"internalType": "address",
"name": "tokenB",
"type": "address"
}
],
"name": "sortTokens",
"outputs": [
{
"internalType": "address",
"name": "token0",
"type": "address"
},
{
"internalType": "address",
"name": "token1",
"type": "address"
}
],
"payable": false,
"stateMutability": "pure",
"type": "function"
},
{ {
"constant": false, "constant": false,
"inputs": [ "inputs": [
...@@ -895,35 +785,5 @@ ...@@ -895,35 +785,5 @@
"payable": false, "payable": false,
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
},
{
"constant": true,
"inputs": [],
"name": "transferFromSelector",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "transferSelector",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
} }
] ]
import { injected, walletconnect, walletlink, fortmatic, portis } from '../connectors' import { injected, walletconnect, walletlink, fortmatic, portis } from '../connectors'
export const FACTORY_ADDRESSES = { export const FACTORY_ADDRESSES = {
1: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95', 1: '',
3: '0x9c83dCE8CA20E9aAF9D3efc003b2ea62aBC08351', 3: '',
4: '0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36', 4: '0xe2f197885abe8ec7c866cFf76605FD06d4576218',
42: '0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30' 42: ''
} }
export const ROUTER_ADDRESSES = { export const ROUTER_ADDRESSES = {
1: '', 1: '',
3: '', 3: '',
4: '0xd9210Ff5A0780E083BB40e30d005d93a2DcFA4EF', 4: '0xcDbE04934d89e97a24BCc07c3562DC8CF17d8167',
42: '' 42: ''
} }
...@@ -100,12 +100,4 @@ export const SUPPORTED_WALLETS = ...@@ -100,12 +100,4 @@ export const SUPPORTED_WALLETS =
} }
} }
// list of tokens that lock fund on adding liquidity - used to disable button
export const brokenTokens = [
'0xB8c77482e45F1F44dE1745F52C74426C631bDD52',
'0x95dAaaB98046846bF4B2853e23cba236fa394A31',
'0x55296f69f40Ea6d20E478533C15A6B08B654E758',
'0xc3761EB917CD790B30dAD99f6Cc5b4Ff93C4F9eA'
]
export const NetworkContextName = 'NETWORK' export const NetworkContextName = 'NETWORK'
...@@ -162,6 +162,7 @@ export function usePopups() { ...@@ -162,6 +162,7 @@ export function usePopups() {
if (key === item.key) { if (key === item.key) {
item.show = false item.show = false
} }
return true
}) })
setPopups(currentPopups) setPopups(currentPopups)
} }
......
...@@ -427,6 +427,7 @@ export function useAllBalances(): Array<TokenAmount> { ...@@ -427,6 +427,7 @@ export function useAllBalances(): Array<TokenAmount> {
} }
} }
} }
return true
}) })
}) })
return newBalances return newBalances
...@@ -443,6 +444,9 @@ export function useAddressBalance(address: string, token: Token): TokenAmount | ...@@ -443,6 +444,9 @@ export function useAddressBalance(address: string, token: Token): TokenAmount |
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
const [state, { startListening, stopListening }] = useBalancesContext() const [state, { startListening, stopListening }] = useBalancesContext()
const value = typeof chainId === 'number' ? state?.[chainId]?.[address]?.[token?.address]?.value : undefined
const formattedValue = value && token && new TokenAmount(token, value)
/** /**
* @todo * @todo
* when catching for token, causes infinite rerender * when catching for token, causes infinite rerender
...@@ -457,9 +461,6 @@ export function useAddressBalance(address: string, token: Token): TokenAmount | ...@@ -457,9 +461,6 @@ export function useAddressBalance(address: string, token: Token): TokenAmount |
} }
}, [chainId, address, startListening, stopListening]) }, [chainId, address, startListening, stopListening])
const value = typeof chainId === 'number' ? state?.[chainId]?.[address]?.[token?.address]?.value : undefined
const formattedValue = value && token && new TokenAmount(token, value)
return useMemo(() => formattedValue, [formattedValue]) return useMemo(() => formattedValue, [formattedValue])
} }
...@@ -476,6 +477,7 @@ export function useAccountLPBalances(account: string) { ...@@ -476,6 +477,7 @@ export function useAccountLPBalances(account: string) {
stopListening(chainId, account, exchangeAddress) stopListening(chainId, account, exchangeAddress)
} }
} }
return true
}) })
}, [account, allExchanges, chainId, startListening, stopListening]) }, [account, allExchanges, chainId, startListening, stopListening])
} }
...@@ -11,7 +11,7 @@ const UPDATE = 'UPDATE' ...@@ -11,7 +11,7 @@ const UPDATE = 'UPDATE'
const ALL_EXCHANGES: [Token, Token][] = [ const ALL_EXCHANGES: [Token, Token][] = [
[ [
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY][WETH[ChainId.RINKEBY].address], INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY][WETH[ChainId.RINKEBY].address],
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735'] INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735'] //dai
], ],
[ [
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735'], INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735'],
......
...@@ -8,7 +8,7 @@ const UPDATE = 'UPDATE' ...@@ -8,7 +8,7 @@ const UPDATE = 'UPDATE'
export const ALL_TOKENS = [ export const ALL_TOKENS = [
WETH[ChainId.RINKEBY], WETH[ChainId.RINKEBY],
new Token(ChainId.RINKEBY, '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735', 18, 'DAI', 'Dai Stablecoin'), new Token(ChainId.RINKEBY, '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735', 18, 'DAI', 'Dai Stablecoin'),
new Token(ChainId.RINKEBY, '0x8ab15C890E5C03B5F240f2D146e3DF54bEf3Df44', 18, 'IANV2', 'IAn V2 Coin') new Token(ChainId.RINKEBY, '0x8ab15C890E5C03B5F240f2D146e3DF54bEf3Df44', 18, 'IANV2', 'IAn V2 /Coin')
] ]
// only meant to be used in exchanges.ts! // only meant to be used in exchanges.ts!
...@@ -112,6 +112,11 @@ export function useAllTokens(): string[] { ...@@ -112,6 +112,11 @@ export function useAllTokens(): string[] {
const [state] = useTokensContext() const [state] = useTokensContext()
return useMemo(() => { return useMemo(() => {
// hardcode overide weth as ETH
if (state && state[chainId]) {
state[chainId][WETH[chainId].address].symbol = 'ETH'
state[chainId][WETH[chainId].address].name = 'ETH'
}
return state?.[chainId] || {} return state?.[chainId] || {}
}, [state, chainId]) }, [state, chainId])
} }
import { useState, useMemo, useCallback, useEffect, useRef } from 'react' import { useState, useMemo, useCallback, useEffect, useRef } from 'react'
import { useWeb3React as useWeb3ReactCore } from '@web3-react/core' import { useWeb3React as useWeb3ReactCore } from '@web3-react/core'
import copy from 'copy-to-clipboard'
import { isMobile } from 'react-device-detect' import { isMobile } from 'react-device-detect'
import copy from 'copy-to-clipboard'
import { NetworkContextName } from '../constants'
import ERC20_ABI from '../constants/abis/erc20' import ERC20_ABI from '../constants/abis/erc20'
import { getContract, getFactoryContract, getExchangeContract, isAddress } from '../utils'
import { injected } from '../connectors' import { injected } from '../connectors'
import { NetworkContextName } from '../constants'
import { getContract, getFactoryContract, getExchangeContract, isAddress } from '../utils'
export function useWeb3React() { export function useWeb3React() {
const context = useWeb3ReactCore() const context = useWeb3ReactCore()
const contextNetwork = useWeb3ReactCore(NetworkContextName) const contextNetwork = useWeb3ReactCore(NetworkContextName)
return context.active ? context : contextNetwork return context.active ? context : contextNetwork
} }
export function useEagerConnect() { export function useEagerConnect() {
const { activate, active } = useWeb3ReactCore() // specifically using useWeb3ReactCore because of what this hook does const { activate, active } = useWeb3ReactCore() // specifically using useWeb3ReactCore because of what this hook does
const [tried, setTried] = useState(false) const [tried, setTried] = useState(false)
useEffect(() => { useEffect(() => {
...@@ -265,6 +263,5 @@ export function usePrevious(value) { ...@@ -265,6 +263,5 @@ export function usePrevious(value) {
export function useToggle(initialState = false) { export function useToggle(initialState = false) {
const [state, setState] = useState(initialState) const [state, setState] = useState(initialState)
const toggle = useCallback(() => setState(state => !state), []) const toggle = useCallback(() => setState(state => !state), [])
return [state, toggle] return [state, toggle]
} }
...@@ -2,10 +2,9 @@ import React, { Suspense, lazy } from 'react' ...@@ -2,10 +2,9 @@ import React, { Suspense, lazy } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom' import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'
import Web3ReactManager from '../components/Web3ReactManager'
import Header from '../components/Header' import Header from '../components/Header'
import NavigationTabs from '../components/NavigationTabs' import NavigationTabs from '../components/NavigationTabs'
import Web3ReactManager from '../components/Web3ReactManager'
import { isAddress, getAllQueryParams } from '../utils' import { isAddress, getAllQueryParams } from '../utils'
const Swap = lazy(() => import('./Swap')) const Swap = lazy(() => import('./Swap'))
...@@ -65,6 +64,7 @@ export default function App() { ...@@ -65,6 +64,7 @@ export default function App() {
{/* this Suspense is for route code-splitting */} {/* this Suspense is for route code-splitting */}
<Suspense fallback={null}> <Suspense fallback={null}>
<Switch> <Switch>
<Route exact strict path="/" render={() => <Redirect to={{ pathname: '/swap' }} />} />
<Route exact strict path="/find" component={() => <Find params={params} />} /> <Route exact strict path="/find" component={() => <Find params={params} />} />
<Route exact strict path="/swap" component={() => <Swap params={params} />} /> <Route exact strict path="/swap" component={() => <Swap params={params} />} />
<Route <Route
......
import React, { useState, useEffect } from 'react' import React from 'react'
import styled from 'styled-components'
import { darken } from 'polished'
import { TokenAmount, JSBI } from '@uniswap/sdk'
import QR from '../../assets/svg/QR.svg'
import TokenLogo from '../../components/TokenLogo'
import SearchModal from '../../components/SearchModal'
import ExchangePage from '../../components/ExchangePage' import ExchangePage from '../../components/ExchangePage'
import NumericalInput from '../../components/NumericalInput'
import ConfirmationModal from '../../components/ConfirmationModal'
import { Text } from 'rebass'
import { TYPE } from '../../theme'
import { LightCard } from '../../components/Card'
import { ArrowDown } from 'react-feather'
import { AutoColumn } from '../../components/Column'
import { ButtonPrimary } from '../../components/Button'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useToken } from '../../contexts/Tokens'
import { RowBetween } from '../../components/Row'
import { useENSName } from '../../hooks'
import { useWeb3React } from '@web3-react/core'
import { useAddressBalance } from '../../contexts/Balances'
import { parseUnits } from '@ethersproject/units'
import { isAddress } from '../../utils'
const CurrencySelect = styled.button`
display: flex;
align-items: center;
justify-content: space-between;
font-size: 20px;
width: ${({ selected }) => (selected ? '128px' : '180px')}
padding: 8px 12px;
background-color: ${({ selected, theme }) => (selected ? theme.buttonBackgroundPlain : theme.royalBlue)};
color: ${({ selected, theme }) => (selected ? theme.textColor : theme.white)};
border: 1px solid
${({ selected, theme }) => (selected ? theme.outlineGrey : theme.royalBlue)};
border-radius: 8px;
outline: none;
cursor: pointer;
user-select: none;
:hover {
border: 1px solid
${({ selected, theme }) => (selected ? darken(0.1, theme.outlineGrey) : darken(0.1, theme.royalBlue))};
}
:focus {
border: 1px solid ${({ selected, theme }) =>
selected ? darken(0.1, theme.outlineGrey) : darken(0.1, theme.royalBlue)};
}
:active {
background-color: ${({ selected, theme }) => (selected ? theme.buttonBackgroundPlain : theme.royalBlue)};
}
`
const StyledDropDown = styled(DropDown)`
height: 35%;
path {
stroke: ${({ selected, theme }) => (selected ? theme.textColor : theme.white)};
}
`
const InputGroup = styled(AutoColumn)`
position: relative;
padding: 40px 0;
`
const QRWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
border: 1px solid ${({ theme }) => theme.outlineGrey};
background: #fbfbfb;
padding: 4px;
border-radius: 8px;
`
const StyledInput = styled.input`
width: ${({ width }) => width};
border: none;
outline: none;
font-size: 20px;
::placeholder {
color: #edeef2;
}
`
const StyledNumerical = styled(NumericalInput)`
text-align: center;
font-size: 48px;
font-weight: 500px;
width: 100%;
::placeholder {
color: #edeef2;
}
`
const MaxButton = styled.button`
position: absolute;
right: 70px;
padding: 0.5rem 1rem;
background-color: ${({ theme }) => theme.zumthorBlue};
border: 1px solid ${({ theme }) => theme.zumthorBlue};
border-radius: 0.5rem;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
margin-right: 0.5rem;
color: ${({ theme }) => theme.royalBlue};
:hover {
border: 1px solid ${({ theme }) => theme.royalBlue};
}
:focus {
border: 1px solid ${({ theme }) => theme.royalBlue};
outline: none;
}
`
export default function Send() { export default function Send() {
const { account } = useWeb3React() return <ExchangePage sendingInput={true} />
// setting for send with swap or regular swap
const [withSwap, setWithSwap] = useState(true)
// modals
const [modalOpen, setModalOpen] = useState(false)
const [showConfirm, setShowConfirm] = useState(false)
// token selected
const [activeTokenAddress, setActiveTokenAddress] = useState()
const token = useToken(activeTokenAddress)
// user inputs
const [typedValue, setTypedValue] = useState('')
const [amount, setAmount] = useState(null)
const [recipient, setRecipient] = useState('0x74Aa01d162E6dC6A657caC857418C403D48E2D77')
//ENS
const recipientENS = useENSName(recipient)
// balances
const userBalance = useAddressBalance(account, token)
//errors
const [generalError, setGeneralError] = useState('')
const [amountError, setAmountError] = useState('')
const [recipientError, setRecipientError] = useState('')
function parseInputAmount(newtypedValue) {
setTypedValue(newtypedValue)
if (!!token && newtypedValue !== '' && newtypedValue !== '.') {
const typedValueParsed = parseUnits(newtypedValue, token.decimals).toString()
setAmount(new TokenAmount(token, typedValueParsed))
}
}
function onMax() {
if (userBalance) {
setTypedValue(userBalance.toExact())
setAmount(userBalance)
}
}
const atMax = amount && userBalance && JSBI.equal(amount.raw, userBalance.raw) ? true : false
//error detection
useEffect(() => {
setGeneralError('')
setRecipientError('')
setAmountError('')
if (!amount) {
setGeneralError('Enter an amount')
}
if (!isAddress(recipient)) {
setRecipientError('Enter a valid address')
}
if (!!!token) {
setGeneralError('Select a token')
}
if (amount && userBalance && JSBI.greaterThan(amount.raw, userBalance.raw)) {
setAmountError('Insufficient Balance')
}
}, [recipient, token, amount, userBalance])
const TopContent = () => {
return (
<AutoColumn gap="30px" style={{ marginTop: '40px' }}>
<RowBetween>
<Text fontSize={36} fontWeight={500}>
{amount?.toFixed(8)}
</Text>
<TokenLogo address={activeTokenAddress} size={'30px'} />
</RowBetween>
<ArrowDown size={24} color="#888D9B" />
<TYPE.blue fontSize={36}>
{recipient?.slice(0, 6)}...{recipient?.slice(36, 42)}
</TYPE.blue>
</AutoColumn>
)
}
const BottomContent = () => {
return (
<AutoColumn>
<ButtonPrimary>
<Text color="white" fontSize={20}>
Confirm send
</Text>
</ButtonPrimary>
</AutoColumn>
)
}
const [attemptedSend, setAttemptedSend] = useState(false) // clicke confirm
const [pendingConfirmation, setPendingConfirmation] = useState(true) // waiting for
return withSwap ? (
<ExchangePage sendingInput={true} />
) : (
<>
<SearchModal
isOpen={modalOpen}
onDismiss={() => {
setModalOpen(false)
}}
filterType="tokens"
onTokenSelect={tokenAddress => setActiveTokenAddress(tokenAddress)}
/>
<ConfirmationModal
isOpen={showConfirm}
onDismiss={() => setShowConfirm(false)}
hash=""
title="Confirm Send"
topContent={TopContent}
bottomContent={BottomContent}
attemptingTxn={attemptedSend}
pendingConfirmation={pendingConfirmation}
pendingText=""
/>
</>
)
} }
This diff is collapsed.
import React, { useState, useEffect } from 'react'
import { withRouter } from 'react-router'
import { createBrowserHistory } from 'history'
import { ethers } from 'ethers'
import styled from 'styled-components'
import { useTranslation } from 'react-i18next'
import ReactGA from 'react-ga'
import { useWeb3React, useFactoryContract } from '../../hooks'
import { Button } from '../../theme'
import AddressInputPanel from '../../components/AddressInputPanel'
import OversizedPanel from '../../components/OversizedPanel'
import { useTokenDetails } from '../../contexts/Tokens'
import { useTransactionAdder } from '../../contexts/Transactions'
const SummaryPanel = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
padding: 1rem 0;
`
const ExchangeRateWrapper = styled.div`
${({ theme }) => theme.flexRowNoWrap};
align-items: center;
color: ${({ theme }) => theme.doveGray};
font-size: 0.75rem;
padding: 0.25rem 1rem 0;
`
const ExchangeRate = styled.span`
flex: 1 1 auto;
width: 0;
color: ${({ theme }) => theme.doveGray};
`
const CreateExchangeWrapper = styled.div`
color: ${({ theme }) => theme.doveGray};
text-align: center;
margin-top: 1rem;
padding-top: 1rem;
`
const SummaryText = styled.div`
font-size: 0.75rem;
color: ${({ error, theme }) => error && theme.salmonRed};
`
const Flex = styled.div`
display: flex;
justify-content: center;
padding: 2rem;
button {
max-width: 20rem;
}
`
function CreateExchange({ location, params }) {
const { t } = useTranslation()
const { account } = useWeb3React()
const factory = useFactoryContract()
const [tokenAddress, setTokenAddress] = useState({
address: params.tokenAddress ? params.tokenAddress : '',
name: ''
})
const [tokenAddressError, setTokenAddressError] = useState()
const { name, symbol, decimals, exchangeAddress } = useTokenDetails(tokenAddress.address)
const addTransaction = useTransactionAdder()
// clear url of query
useEffect(() => {
const history = createBrowserHistory()
history.push(window.location.pathname + '')
}, [])
// validate everything
const [errorMessage, setErrorMessage] = useState(!account && t('noWallet'))
useEffect(() => {
if (tokenAddressError) {
setErrorMessage(t('invalidTokenAddress'))
} else if (symbol === undefined || decimals === undefined || exchangeAddress === undefined) {
setErrorMessage()
} else if (symbol === null) {
setErrorMessage(t('invalidSymbol'))
} else if (decimals === null) {
setErrorMessage(t('invalidDecimals'))
} else if (exchangeAddress !== ethers.constants.AddressZero) {
setErrorMessage(t('exchangeExists'))
} else if (!account) {
setErrorMessage(t('noWallet'))
} else {
setErrorMessage(null)
}
return () => {
setErrorMessage()
}
}, [tokenAddress.address, symbol, decimals, exchangeAddress, account, t, tokenAddressError])
async function createExchange() {
const estimatedGasLimit = await factory.estimate.createExchange(tokenAddress.address)
factory.createExchange(tokenAddress.address, { gasLimit: estimatedGasLimit }).then(response => {
ReactGA.event({
category: 'Transaction',
action: 'Create Exchange'
})
addTransaction(response)
})
}
const isValid = errorMessage === null
return (
<>
<AddressInputPanel
title={t('tokenAddress')}
initialInput={
params.tokenAddress
? { address: params.tokenAddress }
: { address: (location.state && location.state.tokenAddress) || '' }
}
onChange={setTokenAddress}
onError={setTokenAddressError}
/>
<OversizedPanel hideBottom>
<SummaryPanel>
<ExchangeRateWrapper>
<ExchangeRate>{t('name')}</ExchangeRate>
<span>{name ? name : ' - '}</span>
</ExchangeRateWrapper>
<ExchangeRateWrapper>
<ExchangeRate>{t('symbol')}</ExchangeRate>
<span>{symbol ? symbol : ' - '}</span>
</ExchangeRateWrapper>
<ExchangeRateWrapper>
<ExchangeRate>{t('decimals')}</ExchangeRate>
<span>{decimals || decimals === 0 ? decimals : ' - '}</span>
</ExchangeRateWrapper>
</SummaryPanel>
</OversizedPanel>
<CreateExchangeWrapper>
<SummaryText>{errorMessage ? errorMessage : t('enterTokenCont')}</SummaryText>
</CreateExchangeWrapper>
<Flex>
<Button disabled={!isValid} onClick={createExchange}>
{t('createExchange')}
</Button>
</Flex>
</>
)
}
export default withRouter(CreateExchange)
...@@ -2,7 +2,7 @@ import React, { useReducer, useState, useCallback, useEffect } from 'react' ...@@ -2,7 +2,7 @@ import React, { useReducer, useState, useCallback, useEffect } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { parseUnits } from '@ethersproject/units' import { parseUnits } from '@ethersproject/units'
import { TokenAmount, JSBI, Route, WETH, Percent } from '@uniswap/sdk' import { TokenAmount, JSBI, Route, WETH, Percent, Token, Exchange } from '@uniswap/sdk'
import Slider from '../../components/Slider' import Slider from '../../components/Slider'
import TokenLogo from '../../components/TokenLogo' import TokenLogo from '../../components/TokenLogo'
...@@ -21,8 +21,8 @@ import Row, { RowBetween, RowFixed } from '../../components/Row' ...@@ -21,8 +21,8 @@ import Row, { RowBetween, RowFixed } from '../../components/Row'
import { useToken } from '../../contexts/Tokens' import { useToken } from '../../contexts/Tokens'
import { useWeb3React } from '../../hooks' import { useWeb3React } from '../../hooks'
import { useAllBalances } from '../../contexts/Balances' import { useAllBalances } from '../../contexts/Balances'
import { useTransactionAdder } from '../../contexts/Transactions'
import { useExchangeContract } from '../../hooks' import { useExchangeContract } from '../../hooks'
import { useTransactionAdder } from '../../contexts/Transactions'
import { useExchange, useTotalSupply } from '../../contexts/Exchanges' import { useExchange, useTotalSupply } from '../../contexts/Exchanges'
import { BigNumber } from 'ethers/utils' import { BigNumber } from 'ethers/utils'
...@@ -145,34 +145,34 @@ const ConfirmedText = styled(Text)` ...@@ -145,34 +145,34 @@ const ConfirmedText = styled(Text)`
export default function RemoveLiquidity({ token0, token1 }) { export default function RemoveLiquidity({ token0, token1 }) {
const { account, chainId, library } = useWeb3React() const { account, chainId, library } = useWeb3React()
const routerAddress = ROUTER_ADDRESSES[chainId] const routerAddress: string = ROUTER_ADDRESSES[chainId]
const [showConfirm, setShowConfirm] = useState(false) const [showConfirm, setShowConfirm] = useState<boolean>(false)
const [showAdvanced, setShowAdvanced] = useState(false) const [showAdvanced, setShowAdvanced] = useState<boolean>(false)
const inputToken = useToken(token0) const inputToken: Token = useToken(token0)
const outputToken = useToken(token1) const outputToken: Token = useToken(token1)
// get basic SDK entities // get basic SDK entities
const tokens = { const tokens: { [field: number]: Token } = {
[Field.TOKEN0]: inputToken, [Field.TOKEN0]: inputToken,
[Field.TOKEN1]: outputToken [Field.TOKEN1]: outputToken
} }
const exchange = useExchange(inputToken, outputToken) const exchange: Exchange = useExchange(inputToken, outputToken)
const exchangeContract = useExchangeContract(exchange?.liquidityToken.address) const exchangeContract: ethers.Contract = useExchangeContract(exchange?.liquidityToken.address)
// pool token data // pool token data
const totalPoolTokens = useTotalSupply(exchange) const totalPoolTokens: TokenAmount = useTotalSupply(exchange)
const allBalances = useAllBalances() const allBalances: TokenAmount[] = useAllBalances()
const userLiquidity = allBalances?.[account]?.[exchange?.liquidityToken?.address] const userLiquidity: TokenAmount = allBalances?.[account]?.[exchange?.liquidityToken?.address]
// input state // input state
const [state, dispatch] = useReducer(reducer, initializeRemoveState(userLiquidity?.toExact(), token0, token1)) const [state, dispatch] = useReducer(reducer, initializeRemoveState(userLiquidity?.toExact(), token0, token1))
const { independentField, typedValue } = state const { independentField, typedValue } = state
const TokensDeposited = { const TokensDeposited: { [field: number]: TokenAmount } = {
[Field.TOKEN0]: [Field.TOKEN0]:
exchange && exchange &&
totalPoolTokens && totalPoolTokens &&
...@@ -185,7 +185,7 @@ export default function RemoveLiquidity({ token0, token1 }) { ...@@ -185,7 +185,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
exchange.getLiquidityValue(tokens[Field.TOKEN1], totalPoolTokens, userLiquidity, false) exchange.getLiquidityValue(tokens[Field.TOKEN1], totalPoolTokens, userLiquidity, false)
} }
const route = exchange const route: Route = exchange
? new Route([exchange], independentField !== Field.LIQUIDITY ? tokens[independentField] : tokens[Field.TOKEN1]) ? new Route([exchange], independentField !== Field.LIQUIDITY ? tokens[independentField] : tokens[Field.TOKEN1])
: undefined : undefined
...@@ -307,11 +307,11 @@ export default function RemoveLiquidity({ token0, token1 }) { ...@@ -307,11 +307,11 @@ export default function RemoveLiquidity({ token0, token1 }) {
: false : false
// errors // errors
const [generalError, setGeneralError] = useState('') const [generalError, setGeneralError] = useState<string>('')
const [inputError, setInputError] = useState('') const [inputError, setInputError] = useState<string>('')
const [outputError, setOutputError] = useState('') const [outputError, setOutputError] = useState<string>('')
const [poolTokenError, setPoolTokenError] = useState('') const [poolTokenError, setPoolTokenError] = useState<string>('')
const [isValid, setIsValid] = useState(false) const [isValid, setIsValid] = useState<boolean>(false)
// update errors live // update errors live
useEffect(() => { useEffect(() => {
...@@ -351,7 +351,7 @@ export default function RemoveLiquidity({ token0, token1 }) { ...@@ -351,7 +351,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
const addTransaction = useTransactionAdder() const addTransaction = useTransactionAdder()
const [txHash, setTxHash] = useState() const [txHash, setTxHash] = useState()
const [sigInputs, setSigInputs] = useState([]) const [sigInputs, setSigInputs] = useState([])
const [deadline, setDeadline] = useState() const [deadline, setDeadline] = useState(null)
const [signed, setSigned] = useState(false) // waiting for signature sign const [signed, setSigned] = useState(false) // waiting for signature sign
const [attemptedRemoval, setAttemptedRemoval] = useState(false) // clicke confirm const [attemptedRemoval, setAttemptedRemoval] = useState(false) // clicke confirm
const [pendingConfirmation, setPendingConfirmation] = useState(true) // waiting for const [pendingConfirmation, setPendingConfirmation] = useState(true) // waiting for
...@@ -359,7 +359,7 @@ export default function RemoveLiquidity({ token0, token1 }) { ...@@ -359,7 +359,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
async function onSign() { async function onSign() {
const nonce = await exchangeContract.nonces(account) const nonce = await exchangeContract.nonces(account)
const newDeadline = Math.ceil(Date.now() / 1000) + DEADLINE_FROM_NOW const newDeadline: number = Math.ceil(Date.now() / 1000) + DEADLINE_FROM_NOW
setDeadline(newDeadline) setDeadline(newDeadline)
const EIP712Domain = [ const EIP712Domain = [
...@@ -428,6 +428,7 @@ export default function RemoveLiquidity({ token0, token1 }) { ...@@ -428,6 +428,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
: parsedAmounts[Field.TOKEN0].raw.toString(), : parsedAmounts[Field.TOKEN0].raw.toString(),
account, account,
deadline, deadline,
false,
sigInputs[0], sigInputs[0],
sigInputs[1], sigInputs[1],
sigInputs[2] sigInputs[2]
...@@ -445,6 +446,7 @@ export default function RemoveLiquidity({ token0, token1 }) { ...@@ -445,6 +446,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
parsedAmounts[Field.TOKEN1].raw.toString(), parsedAmounts[Field.TOKEN1].raw.toString(),
account, account,
deadline, deadline,
false,
sigInputs[0], sigInputs[0],
sigInputs[1], sigInputs[1],
sigInputs[2] sigInputs[2]
...@@ -466,18 +468,13 @@ export default function RemoveLiquidity({ token0, token1 }) { ...@@ -466,18 +468,13 @@ export default function RemoveLiquidity({ token0, token1 }) {
setTxHash(response.hash) setTxHash(response.hash)
addTransaction(response) addTransaction(response)
}) })
.catch(() => { .catch(e => {
console.log(e)
resetModalState() resetModalState()
setShowConfirm(false) setShowConfirm(false)
}) })
} }
/**
* @todo
* if the input values stay the same,
* we should probably not reset the signature values,
* move to an effect
*/
function resetModalState() { function resetModalState() {
setSigned(false) setSigned(false)
setSigInputs(null) setSigInputs(null)
...@@ -562,7 +559,7 @@ export default function RemoveLiquidity({ token0, token1 }) { ...@@ -562,7 +559,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
</> </>
) )
} }
const pendingText = `Removed ${parsedAmounts[Field.TOKEN0]?.toSignificant(6)} ${ const pendingText: string = `Removed ${parsedAmounts[Field.TOKEN0]?.toSignificant(6)} ${
tokens[Field.TOKEN0]?.symbol tokens[Field.TOKEN0]?.symbol
} and ${parsedAmounts[Field.TOKEN1]?.toSignificant(6)} ${tokens[Field.TOKEN1]?.symbol}` } and ${parsedAmounts[Field.TOKEN1]?.toSignificant(6)} ${tokens[Field.TOKEN1]?.symbol}`
......
...@@ -72,7 +72,7 @@ const theme = darkMode => ({ ...@@ -72,7 +72,7 @@ const theme = darkMode => ({
tokenRowHover: darkMode ? '#404040' : '#F2F2F2', tokenRowHover: darkMode ? '#404040' : '#F2F2F2',
outlineGrey: darkMode ? '#292C2F' : '#EDEEF2', outlineGrey: darkMode ? '#292C2F' : '#EDEEF2',
darkGrey: darkMode ? '#888D9B' : '#888D9B', darkGray: darkMode ? '#888D9B' : '#888D9B',
//blacks //blacks
charcoalBlack: darkMode ? '#F2F2F2' : '#404040', charcoalBlack: darkMode ? '#F2F2F2' : '#404040',
...@@ -85,6 +85,7 @@ const theme = darkMode => ({ ...@@ -85,6 +85,7 @@ const theme = darkMode => ({
// purples // purples
wisteriaPurple: '#DC6BE5', wisteriaPurple: '#DC6BE5',
// reds // reds
salmonRed: '#FF6871', salmonRed: '#FF6871',
// orange // orange
...@@ -93,6 +94,7 @@ const theme = darkMode => ({ ...@@ -93,6 +94,7 @@ const theme = darkMode => ({
warningYellow: '#FFE270', warningYellow: '#FFE270',
// pink // pink
uniswapPink: '#DC6BE5', uniswapPink: '#DC6BE5',
darkPink: '#ff007a',
//green //green
connectedGreen: '#27AE60', connectedGreen: '#27AE60',
...@@ -124,6 +126,16 @@ export const TYPE = { ...@@ -124,6 +126,16 @@ export const TYPE = {
{children} {children}
</Text> </Text>
), ),
largeHeader: ({ children, ...rest }) => (
<Text fontWeight={600} fontSize={24} color={theme().black} {...rest}>
{children}
</Text>
),
body: ({ children, ...rest }) => (
<Text fontWeight={500} fontSize={16} color={'#565A69'} {...rest}>
{children}
</Text>
),
blue: ({ children, ...rest }) => ( blue: ({ children, ...rest }) => (
<Text fontWeight={500} color={theme().royalBlue} {...rest}> <Text fontWeight={500} color={theme().royalBlue} {...rest}>
{children} {children}
...@@ -134,6 +146,11 @@ export const TYPE = { ...@@ -134,6 +146,11 @@ export const TYPE = {
{children} {children}
</Text> </Text>
), ),
darkGray: ({ children, ...rest }) => (
<Text fontWeight={500} color={theme().darkGray} {...rest}>
{children}
</Text>
),
italic: ({ children, ...rest }) => ( italic: ({ children, ...rest }) => (
<Text fontWeight={500} fontSize={12} fontStyle={'italic'} color={theme().mineshaftGray} {...rest}> <Text fontWeight={500} fontSize={12} fontStyle={'italic'} color={theme().mineshaftGray} {...rest}>
{children} {children}
......
...@@ -5,7 +5,7 @@ import EXCHANGE_ABI from '../constants/abis/exchange' ...@@ -5,7 +5,7 @@ import EXCHANGE_ABI from '../constants/abis/exchange'
import ROUTER_ABI from '../constants/abis/router' import ROUTER_ABI from '../constants/abis/router'
import ERC20_ABI from '../constants/abis/erc20' import ERC20_ABI from '../constants/abis/erc20'
import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32' import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32'
import { FACTORY_ADDRESSES, SUPPORTED_THEMES } from '../constants' import { FACTORY_ADDRESSES, SUPPORTED_THEMES, ROUTER_ADDRESSES } from '../constants'
import { bigNumberify, keccak256, defaultAbiCoder, toUtf8Bytes, solidityPack } from 'ethers/utils' import { bigNumberify, keccak256, defaultAbiCoder, toUtf8Bytes, solidityPack } from 'ethers/utils'
import UncheckedJsonRpcSigner from './signer' import UncheckedJsonRpcSigner from './signer'
...@@ -131,8 +131,8 @@ export function getContract(address, ABI, library, account) { ...@@ -131,8 +131,8 @@ export function getContract(address, ABI, library, account) {
} }
// account is optional // account is optional
export function getRouterContract(networkId, library, account) { export function getRouterContract(chainId, library, account) {
const router = getContract('0xd9210Ff5A0780E083BB40e30d005d93a2DcFA4EF', ROUTER_ABI, library, account) const router = getContract(ROUTER_ADDRESSES[chainId], ROUTER_ABI, library, account)
return router return router
} }
......
...@@ -1319,7 +1319,7 @@ ...@@ -1319,7 +1319,7 @@
"@ethersproject/logger" ">=5.0.0-beta.129" "@ethersproject/logger" ">=5.0.0-beta.129"
"@ethersproject/strings" ">=5.0.0-beta.130" "@ethersproject/strings" ">=5.0.0-beta.130"
"@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.0-beta.130", "@ethersproject/keccak256@^5.0.0-beta.131": "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.0-beta.130":
version "5.0.0-beta.131" version "5.0.0-beta.131"
resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.0-beta.131.tgz#b5778723ee75208065b9b9ad30c71d480f41bb31" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.0-beta.131.tgz#b5778723ee75208065b9b9ad30c71d480f41bb31"
integrity sha512-KQnqMwGV0IMOjAr/UTFO8DuLrmN1uaMvcV3zh9hiXhh3rCuY+WXdeUh49w1VQ94kBKmaP0qfGb7z4SdhUWUHjw== integrity sha512-KQnqMwGV0IMOjAr/UTFO8DuLrmN1uaMvcV3zh9hiXhh3rCuY+WXdeUh49w1VQ94kBKmaP0qfGb7z4SdhUWUHjw==
...@@ -1382,6 +1382,15 @@ ...@@ -1382,6 +1382,15 @@
dependencies: dependencies:
"@ethersproject/bytes" ">=5.0.0-beta.129" "@ethersproject/bytes" ">=5.0.0-beta.129"
"@ethersproject/sha2@>=5.0.0-beta.129":
version "5.0.0-beta.135"
resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.0.0-beta.135.tgz#e597572ba991fe044d50f8d75704bb4a2b2c64b4"
integrity sha512-DK/cUT5ilCVLtf1xk7XDPB9xGHsXiU3TsULKsEg823cTBIhFl2l0IiHAGqu9uiMlSJRpb2BwrWQuMgmFe/vMwQ==
dependencies:
"@ethersproject/bytes" ">=5.0.0-beta.129"
"@ethersproject/logger" ">=5.0.0-beta.129"
hash.js "1.1.3"
"@ethersproject/signing-key@>=5.0.0-beta.129": "@ethersproject/signing-key@>=5.0.0-beta.129":
version "5.0.0-beta.135" version "5.0.0-beta.135"
resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.0-beta.135.tgz#f739e800aad9e01b77a8ec2c353b9b66ce5738fa" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.0-beta.135.tgz#f739e800aad9e01b77a8ec2c353b9b66ce5738fa"
...@@ -1392,6 +1401,17 @@ ...@@ -1392,6 +1401,17 @@
"@ethersproject/properties" ">=5.0.0-beta.131" "@ethersproject/properties" ">=5.0.0-beta.131"
elliptic "6.5.2" elliptic "6.5.2"
"@ethersproject/solidity@^5.0.0-beta.131":
version "5.0.0-beta.131"
resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.0.0-beta.131.tgz#7d826e98cc0a29e25f0ff52ae17c07483f5d93d4"
integrity sha512-i5vuj2CXGMkVPo08bmElC2cvhjRDNRZZ8nzvx2WCi75Zh42xD0XNV77E9ZLYgS0WoZSiAi/F71nXSBnM7FAqJg==
dependencies:
"@ethersproject/bignumber" ">=5.0.0-beta.130"
"@ethersproject/bytes" ">=5.0.0-beta.129"
"@ethersproject/keccak256" ">=5.0.0-beta.127"
"@ethersproject/sha2" ">=5.0.0-beta.129"
"@ethersproject/strings" ">=5.0.0-beta.130"
"@ethersproject/strings@>=5.0.0-beta.130": "@ethersproject/strings@>=5.0.0-beta.130":
version "5.0.0-beta.136" version "5.0.0-beta.136"
resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.0-beta.136.tgz#053cbf4f9f96a7537cbc50300597f2d707907f51" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.0-beta.136.tgz#053cbf4f9f96a7537cbc50300597f2d707907f51"
...@@ -2365,16 +2385,16 @@ ...@@ -2365,16 +2385,16 @@
semver "^6.3.0" semver "^6.3.0"
tsutils "^3.17.1" tsutils "^3.17.1"
"@uniswap/sdk@@uniswap/sdk@2.0.0-beta.17": "@uniswap/sdk@@uniswap/sdk@2.0.0-beta.19":
version "2.0.0-beta.17" version "2.0.0-beta.19"
resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-2.0.0-beta.17.tgz#8f24be0375d5f8137eae75afe75b2356c75bb793" resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-2.0.0-beta.19.tgz#1f0228a1d5451d62f209e09c48cd1d6bea5ffe01"
integrity sha512-Nd3S/VE51z4jsNs9G9hkslUkS862dpslnU86lXEJi7mbbbPIagh31iR0s/uBPnrBFGiktucgvzRn6WJJIvojWA== integrity sha512-mqDZkeX2TU7e3yOOKHSeUryv94//mJ6dsU6dCv6FExrfOY9yi6+O5ZWpe43rVi0XFVdQGRIRhJapdWKWaGsung==
dependencies: dependencies:
"@ethersproject/address" "^5.0.0-beta.134" "@ethersproject/address" "^5.0.0-beta.134"
"@ethersproject/contracts" "^5.0.0-beta.143" "@ethersproject/contracts" "^5.0.0-beta.143"
"@ethersproject/keccak256" "^5.0.0-beta.131"
"@ethersproject/networks" "^5.0.0-beta.135" "@ethersproject/networks" "^5.0.0-beta.135"
"@ethersproject/providers" "^5.0.0-beta.153" "@ethersproject/providers" "^5.0.0-beta.153"
"@ethersproject/solidity" "^5.0.0-beta.131"
big.js "^5.2.2" big.js "^5.2.2"
decimal.js-light "^2.5.0" decimal.js-light "^2.5.0"
jsbi "^3.1.1" jsbi "^3.1.1"
......
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