Use eslint/prettier for typescript files (#737)

* Use eslint/prettier for typescript files

* Fix more linting errors

* Turn linting errors off so CI passes
This commit is contained in:
Moody Salem 2020-05-08 16:15:45 -04:00 committed by GitHub
commit e023a02037
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 965 additions and 817 deletions

32
.eslintrc.json Normal file

@ -0,0 +1,32 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"ecmaFeatures": {
// Allows for the parsing of JSX
"jsx": true
}
},
"ignorePatterns": [
"node_modules/**/*"
],
"settings": {
"react": {
"version": "detect"
}
},
"extends": [
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"prettier/prettier": "error",
"react/prop-types": "off",
"@typescript-eslint/no-empty-function": "off",
"react/display-name": "off"
}
}

@ -31,4 +31,15 @@ jobs:
- run: yarn
- run: yarn test
# todo: add job for typescript linting
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '12'
- run: yarn
- run: yarn lint

@ -15,7 +15,7 @@
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
module.exports = () => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

@ -57,7 +57,13 @@
"@types/react-dom": "^16.9.7",
"@types/react-router-dom": "^5.0.0",
"@types/styled-components": "^4.2.0",
"@typescript-eslint/eslint-plugin": "^2.31.0",
"@typescript-eslint/parser": "^2.31.0",
"cypress": "^4.5.0",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-react": "^7.19.0",
"prettier": "^1.17.0",
"serve": "^11.3.0",
"start-server-and-test": "^1.11.0",
@ -68,14 +74,8 @@
"build": "cross-env REACT_APP_GIT_COMMIT_HASH=$(git show -s --format=%H) react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"lint:base": "yarn eslint './src/**/*.{js,jsx}'",
"format:base": "yarn prettier './src/**/*.{js,jsx,scss}'",
"fix:lint": "yarn lint:base --fix",
"fix:format": "yarn format:base --write",
"fix:all": "yarn fix:lint && yarn fix:format",
"check:lint": "yarn lint:base",
"check:format": "yarn format:base --check",
"check:all": "yarn check:lint && yarn check:format",
"lint": "eslint '**/*.{js,jsx,ts,tsx}'",
"lint:fix": "eslint '**/*.{js,jsx,ts,tsx}' --fix",
"cy:run": "cypress run",
"serve:build": "serve -s build -l 3000",
"integration-test": "yarn build && start-server-and-test 'yarn run serve:build' http://localhost:3000 cy:run"

@ -33,12 +33,12 @@ export default function CopyHelper(props: { toCopy: string; children?: React.Rea
{props.children}
{isCopied ? (
<TransactionStatusText>
<CheckCircle size={'16'}/>
<CheckCircle size={'16'} />
<TransactionStatusText>Copied</TransactionStatusText>
</TransactionStatusText>
) : (
<TransactionStatusText>
<Copy size={'16'}/>
<Copy size={'16'} />
</TransactionStatusText>
)}
</CopyIcon>

@ -43,7 +43,7 @@ const rotate = keyframes`
}
`
const TransactionState = styled.div<{pending?: boolean;}>`
const TransactionState = styled.div<{ pending?: boolean }>`
display: flex;
background-color: ${({ pending, theme }) =>
pending ? transparentize(0.95, theme.blue1) : transparentize(0.95, theme.green1)};
@ -64,13 +64,13 @@ const TransactionState = styled.div<{pending?: boolean;}>`
pending ? transparentize(0, theme.blue1) : transparentize(0, theme.green1)};
}
`
const ButtonWrapper = styled.div<{pending: boolean}>`
const ButtonWrapper = styled.div<{ pending: boolean }>`
a {
color: ${({ pending, theme }) => (pending ? theme.blue1 : theme.green1)};
}
`
export default function Transaction({ hash, pending }) {
export default function Transaction({ hash, pending }: { hash: string; pending: boolean }) {
const { chainId } = useWeb3React()
const allTransactions = useAllTransactions()

@ -129,7 +129,7 @@ const LowerSection = styled.div`
}
`
const AccountControl = styled.div<{ hasENS: boolean; isENS: boolean; }>`
const AccountControl = styled.div<{ hasENS: boolean; isENS: boolean }>`
${({ theme }) => theme.flexRowNoWrap};
align-items: center;
min-width: 0;
@ -156,7 +156,7 @@ const ConnectButtonRow = styled.div`
margin: 10px 0;
`
const StyledLink = styled(Link) <{ hasENS: boolean; isENS: boolean; }>`
const StyledLink = styled(Link)<{ hasENS: boolean; isENS: boolean }>`
color: ${({ hasENS, isENS, theme }) => (hasENS ? (isENS ? theme.blue1 : theme.text3) : theme.blue1)};
`
@ -181,7 +181,7 @@ const WalletName = styled.div`
width: initial;
`
const IconWrapper = styled.div<{size?: number}>`
const IconWrapper = styled.div<{ size?: number }>`
${({ theme }) => theme.flexColumnNoWrap};
align-items: center;
justify-content: center;
@ -217,19 +217,27 @@ function renderTransactions(transactions, pending) {
return (
<TransactionListWrapper>
{transactions.map((hash, i) => {
return <Transaction key={i} hash={hash} pending={pending}/>
return <Transaction key={i} hash={hash} pending={pending} />
})}
</TransactionListWrapper>
)
}
interface AccountDetailsProps {
toggleWalletModal: () => void
pendingTransactions: any[]
confirmedTransactions: any[]
ENSName?: string
openOptions: () => void
}
export default function AccountDetails({
toggleWalletModal,
pendingTransactions,
confirmedTransactions,
ENSName,
openOptions
}) {
toggleWalletModal,
pendingTransactions,
confirmedTransactions,
ENSName,
openOptions
}: AccountDetailsProps) {
const { chainId, account, connector } = useWeb3React()
const theme = useContext(ThemeContext)
@ -249,32 +257,32 @@ export default function AccountDetails({
if (connector === injected) {
return (
<IconWrapper size={16}>
<Identicon/> {formatConnectorName()}
<Identicon /> {formatConnectorName()}
</IconWrapper>
)
} else if (connector === walletconnect) {
return (
<IconWrapper size={16}>
<img src={WalletConnectIcon} alt={''}/> {formatConnectorName()}
<img src={WalletConnectIcon} alt={''} /> {formatConnectorName()}
</IconWrapper>
)
} else if (connector === walletlink) {
return (
<IconWrapper size={16}>
<img src={CoinbaseWalletIcon} alt={''}/> {formatConnectorName()}
<img src={CoinbaseWalletIcon} alt={''} /> {formatConnectorName()}
</IconWrapper>
)
} else if (connector === fortmatic) {
return (
<IconWrapper size={16}>
<img src={FortmaticIcon} alt={''}/> {formatConnectorName()}
<img src={FortmaticIcon} alt={''} /> {formatConnectorName()}
</IconWrapper>
)
} else if (connector === portis) {
return (
<>
<IconWrapper size={16}>
<img src={PortisIcon} alt={''}/> {formatConnectorName()}
<img src={PortisIcon} alt={''} /> {formatConnectorName()}
<MainWalletAction
onClick={() => {
portis.portis.showPortis()
@ -292,7 +300,7 @@ export default function AccountDetails({
<>
<UpperSection>
<CloseIcon onClick={toggleWalletModal}>
<CloseColor/>
<CloseColor />
</CloseIcon>
<HeaderRow>Account</HeaderRow>
<AccountSection>
@ -304,7 +312,7 @@ export default function AccountDetails({
{connector !== injected && connector !== walletlink && (
<WalletAction
onClick={() => {
(connector as any).close()
;(connector as any).close()
}}
>
Disconnect
@ -312,7 +320,7 @@ export default function AccountDetails({
)}
<CircleWrapper>
<GreenCircle>
<div/>
<div />
</GreenCircle>
</CircleWrapper>
</div>
@ -323,14 +331,14 @@ export default function AccountDetails({
<StyledLink hasENS={!!ENSName} isENS={true} href={getEtherscanLink(chainId, ENSName, 'address')}>
{ENSName} {' '}
</StyledLink>
<Copy toCopy={ENSName}/>
<Copy toCopy={ENSName} />
</AccountControl>
) : (
<AccountControl hasENS={!!ENSName} isENS={false}>
<StyledLink hasENS={!!ENSName} isENS={false} href={getEtherscanLink(chainId, account, 'address')}>
{account} {' '}
</StyledLink>
<Copy toCopy={account}/>
<Copy toCopy={account} />
</AccountControl>
)}
</AccountGroupingRow>

@ -63,7 +63,15 @@ const Input = styled.input<{ error: boolean }>`
// border-radius: 8px;
// `
export default function AddressInputPanel({ initialInput = '', onChange, onError }) {
export default function AddressInputPanel({
initialInput = '',
onChange,
onError
}: {
initialInput?: string
onChange: (val: { address: string; name?: string }) => void
onError: (error: boolean, input: string) => void
}) {
const { library } = useWeb3React()
const [input, setInput] = useState(initialInput ? initialInput : '')

@ -17,7 +17,7 @@ const InputWrapper = styled(RowBetween)`
padding: 4px 8px;
border: 1px solid transparent;
border: ${({ active, error, theme }) =>
error ? '1px solid ' + theme.red1 : active ? '1px solid ' + theme.blue1 : ''};
error ? '1px solid ' + theme.red1 : active ? '1px solid ' + theme.blue1 : ''};
`
const SLIPPAGE_INDEX = {
@ -34,7 +34,12 @@ interface AdvancedSettingsProps {
setAllowedSlippage: (number) => void
}
export default function AdvancedSettings({ setIsOpen, setDeadline, allowedSlippage, setAllowedSlippage }: AdvancedSettingsProps) {
export default function AdvancedSettings({
setIsOpen,
setDeadline,
allowedSlippage,
setAllowedSlippage
}: AdvancedSettingsProps) {
// text translation
const { t } = useTranslation()
@ -78,7 +83,7 @@ export default function AdvancedSettings({ setIsOpen, setDeadline, allowedSlippa
setActiveIndex(3)
} else {
setActiveIndex(4)
setSlippageInput('' + (allowedSlippage / 100))
setSlippageInput('' + allowedSlippage / 100)
parseCustomInput(allowedSlippage)
}
}, [allowedSlippage, parseCustomInput])
@ -94,7 +99,7 @@ export default function AdvancedSettings({ setIsOpen, setDeadline, allowedSlippa
</Link>
<RowBetween>
<TYPE.main>Front-running tolerance</TYPE.main>
<QuestionHelper text={t('toleranceExplanation')}/>
<QuestionHelper text={t('toleranceExplanation')} />
</RowBetween>
<Row>
<ButtonRadio

@ -10,7 +10,10 @@ export const ColumnCenter = styled(Column)`
align-items: center;
`
export const AutoColumn = styled.div<{gap?: 'sm' | 'md' | 'lg' | string; justify?: 'stretch' | 'center' | 'start' | 'end' | 'flex-start' | 'flex-end';}>`
export const AutoColumn = styled.div<{
gap?: 'sm' | 'md' | 'lg' | string
justify?: 'stretch' | 'center' | 'start' | 'end' | 'flex-start' | 'flex-end'
}>`
display: grid;
grid-auto-rows: auto;
grid-row-gap: ${({ gap }) => (gap === 'sm' && '8px') || (gap === 'md' && '12px') || (gap === 'lg' && '24px') || gap};

@ -45,17 +45,17 @@ interface ConfirmationModalProps extends RouteComponentProps<{}> {
}
function ConfirmationModal({
history,
isOpen,
onDismiss,
hash,
topContent,
bottomContent,
attemptingTxn,
pendingConfirmation,
pendingText,
title = ''
}: ConfirmationModalProps) {
history,
isOpen,
onDismiss,
hash,
topContent,
bottomContent,
attemptingTxn,
pendingConfirmation,
pendingText,
title = ''
}: ConfirmationModalProps) {
const { chainId } = useWeb3React()
const dismissAndReturn = useCallback(() => {
@ -74,7 +74,7 @@ function ConfirmationModal({
<Text fontWeight={500} fontSize={20}>
{title}
</Text>
<CloseIcon onClick={onDismiss}/>
<CloseIcon onClick={onDismiss} />
</RowBetween>
{topContent()}
</Section>
@ -84,14 +84,14 @@ function ConfirmationModal({
<Wrapper>
<Section>
<RowBetween>
<div/>
<CloseIcon onClick={onDismiss}/>
<div />
<CloseIcon onClick={onDismiss} />
</RowBetween>
<ConfirmedIcon>
{pendingConfirmation ? (
<Loader size="90px"/>
<Loader size="90px" />
) : (
<ArrowUpCircle strokeWidth={0.5} size={90} color="#ff007a"/>
<ArrowUpCircle strokeWidth={0.5} size={90} color="#ff007a" />
)}
</ConfirmedIcon>
<AutoColumn gap="12px" justify={'center'}>

@ -139,41 +139,41 @@ interface CurrencyInputPanelProps {
onUserInput: (field: number, val: string) => void
onMax: () => void
atMax: boolean
label?: string,
label?: string
urlAddedTokens?: Token[]
onTokenSelection?: (tokenAddress: string) => void
token?: Token | null,
disableTokenSelect?: boolean,
hideBalance?: boolean,
isExchange?: boolean,
pair?: Pair | null,
hideInput?: boolean,
showSendWithSwap?: boolean,
otherSelectedTokenAddress?: string | null,
advanced?: boolean,
token?: Token | null
disableTokenSelect?: boolean
hideBalance?: boolean
isExchange?: boolean
pair?: Pair | null
hideInput?: boolean
showSendWithSwap?: boolean
otherSelectedTokenAddress?: string | null
advanced?: boolean
inputId: string
}
export default function CurrencyInputPanel({
value,
field,
onUserInput,
onMax,
atMax,
label = 'Input',
urlAddedTokens = [], // used
onTokenSelection = null,
token = null,
disableTokenSelect = false,
hideBalance = false,
isExchange = false,
pair = null, // used for double token logo
hideInput = false,
showSendWithSwap = false,
otherSelectedTokenAddress = null,
advanced = false,
inputId,
}: CurrencyInputPanelProps) {
value,
field,
onUserInput,
onMax,
atMax,
label = 'Input',
urlAddedTokens = [], // used
onTokenSelection = null,
token = null,
disableTokenSelect = false,
hideBalance = false,
isExchange = false,
pair = null, // used for double token logo
hideInput = false,
showSendWithSwap = false,
otherSelectedTokenAddress = null,
advanced = false,
inputId
}: CurrencyInputPanelProps) {
const { t } = useTranslation()
const [modalOpen, setModalOpen] = useState(false)
@ -207,10 +207,7 @@ export default function CurrencyInputPanel({
</RowBetween>
</LabelRow>
)}
<InputRow
style={hideInput ? { padding: '0', borderRadius: '8px' } : {}}
selected={disableTokenSelect}
>
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={disableTokenSelect}>
{!hideInput && (
<>
<NumericalInput
@ -235,9 +232,9 @@ export default function CurrencyInputPanel({
>
<Aligner>
{isExchange ? (
<DoubleLogo a0={pair?.token0.address} a1={pair?.token1.address} size={24} margin={true}/>
<DoubleLogo a0={pair?.token0.address} a1={pair?.token1.address} size={24} margin={true} />
) : token?.address ? (
<TokenLogo address={token?.address} size={'24px'}/>
<TokenLogo address={token?.address} size={'24px'} />
) : null}
{isExchange ? (
<StyledTokenName>
@ -248,7 +245,7 @@ export default function CurrencyInputPanel({
{(token && token.symbol) || t('selectToken')}
</StyledTokenName>
)}
{!disableTokenSelect && <StyledDropDown selected={!!token?.address}/>}
{!disableTokenSelect && <StyledDropDown selected={!!token?.address} />}
</Aligner>
</CurrencySelect>
</InputRow>

@ -3,11 +3,11 @@ import styled from 'styled-components'
import TokenLogo from '../TokenLogo'
const TokenWrapper = styled.div<{ margin: boolean; sizeraw: number }>`
position: relative;
display: flex;
flex-direction: row;
margin-right: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'};
`
position: relative;
display: flex;
flex-direction: row;
margin-right: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'};
`
interface DoubleTokenLogoProps {
margin?: boolean
@ -17,18 +17,18 @@ interface DoubleTokenLogoProps {
}
const HigherLogo = styled(TokenLogo)`
z-index: 2;
`
z-index: 2;
`
const CoveredLogo = styled(TokenLogo)<{ sizeraw: number }>`
position: absolute;
left: ${({ sizeraw }) => (sizeraw / 2).toString() + 'px'};
`
position: absolute;
left: ${({ sizeraw }) => (sizeraw / 2).toString() + 'px'};
`
export default function DoubleTokenLogo({ a0, a1, size = 16, margin = false }: DoubleTokenLogoProps) {
return (
<TokenWrapper sizeraw={size} margin={margin}>
<HigherLogo address={a0} size={size.toString() + 'px'}/>
<CoveredLogo address={a1} size={size.toString() + 'px'} sizeraw={size}/>
<HigherLogo address={a0} size={size.toString() + 'px'} />
<CoveredLogo address={a1} size={size.toString() + 'px'} sizeraw={size} />
</TokenWrapper>
)
}

@ -178,14 +178,22 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
}
const pair = usePair(tokens[Field.INPUT], tokens[Field.OUTPUT])
let bestTradeExactIn = useTradeExactIn(tradeType === TradeType.EXACT_INPUT ? parsedAmounts[independentField] : null, tokens[Field.OUTPUT])
let bestTradeExactOut = useTradeExactOut(tokens[Field.INPUT], tradeType === TradeType.EXACT_OUTPUT ? parsedAmounts[independentField] : null)
const bestTradeExactIn = useTradeExactIn(
tradeType === TradeType.EXACT_INPUT ? parsedAmounts[independentField] : null,
tokens[Field.OUTPUT]
)
const bestTradeExactOut = useTradeExactOut(
tokens[Field.INPUT],
tradeType === TradeType.EXACT_OUTPUT ? parsedAmounts[independentField] : null
)
const trade = tradeType === TradeType.EXACT_INPUT ? bestTradeExactIn : bestTradeExactOut
const route = trade?.route
const userHasSpecifiedInputOutput = !!parsedAmounts[independentField] &&
const userHasSpecifiedInputOutput =
!!parsedAmounts[independentField] &&
parsedAmounts[independentField].greaterThan(JSBI.BigInt(0)) &&
!!tokens[Field.INPUT] && !!tokens[Field.OUTPUT]
!!tokens[Field.INPUT] &&
!!tokens[Field.OUTPUT]
const noRoute = !route
const slippageFromTrade: Percent = trade && trade.slippage
@ -213,12 +221,15 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
JSBI.multiply(slippageFromTrade.denominator, JSBI.BigInt('1000'))
)
const onTokenSelection = useCallback((field: Field, address: string) => {
dispatch({
type: SwapAction.SELECT_TOKEN,
payload: { field, address }
})
}, [dispatch])
const onTokenSelection = useCallback(
(field: Field, address: string) => {
dispatch({
type: SwapAction.SELECT_TOKEN,
payload: { field, address }
})
},
[dispatch]
)
const onSwapTokens = useCallback(() => {
dispatch({
@ -227,29 +238,38 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
})
}, [dispatch])
const onUserInput = useCallback((field: Field, typedValue: string) => {
dispatch({ type: SwapAction.TYPE, payload: { field, typedValue } })
}, [dispatch])
const onUserInput = useCallback(
(field: Field, typedValue: string) => {
dispatch({ type: SwapAction.TYPE, payload: { field, typedValue } })
},
[dispatch]
)
const onMaxInput = useCallback((typedValue: string) => {
dispatch({
type: SwapAction.TYPE,
payload: {
field: Field.INPUT,
typedValue
}
})
}, [dispatch])
const onMaxInput = useCallback(
(typedValue: string) => {
dispatch({
type: SwapAction.TYPE,
payload: {
field: Field.INPUT,
typedValue
}
})
},
[dispatch]
)
const onMaxOutput = useCallback((typedValue: string) => {
dispatch({
type: SwapAction.TYPE,
payload: {
field: Field.OUTPUT,
typedValue
}
})
}, [dispatch])
const onMaxOutput = useCallback(
(typedValue: string) => {
dispatch({
type: SwapAction.TYPE,
payload: {
field: Field.OUTPUT,
typedValue
}
})
},
[dispatch]
)
// reset field if sending with with swap is cancled
useEffect(() => {
@ -269,11 +289,10 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
WETH[chainId] &&
JSBI.greaterThan(userBalances[Field.INPUT].raw, isWETH(tokens[Field.INPUT]) ? MIN_ETHER.raw : JSBI.BigInt(0))
? isWETH(tokens[Field.INPUT])
? userBalances[Field.INPUT].subtract(MIN_ETHER)
: userBalances[Field.INPUT]
? userBalances[Field.INPUT].subtract(MIN_ETHER)
: userBalances[Field.INPUT]
: undefined
} catch {
}
} catch {}
const atMaxAmountInput: boolean =
!!maxAmountInput && !!parsedAmounts[Field.INPUT]
@ -323,17 +342,28 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
Field.INPUT === independentField
? parsedAmounts[Field.INPUT]
: calculateSlippageAmount(parsedAmounts[Field.INPUT])?.[0] &&
new TokenAmount(tokens[Field.INPUT], calculateSlippageAmount(parsedAmounts[Field.INPUT])?.[1]),
new TokenAmount(tokens[Field.INPUT], calculateSlippageAmount(parsedAmounts[Field.INPUT])?.[1]),
[Field.OUTPUT]:
Field.OUTPUT === independentField
? parsedAmounts[Field.OUTPUT]
: calculateSlippageAmount(parsedAmounts[Field.OUTPUT])?.[0] &&
new TokenAmount(tokens[Field.INPUT], calculateSlippageAmount(parsedAmounts[Field.OUTPUT])?.[0])
new TokenAmount(tokens[Field.INPUT], calculateSlippageAmount(parsedAmounts[Field.OUTPUT])?.[0])
}
const showInputApprove: boolean =
parsedAmounts[Field.INPUT] && inputApproval && JSBI.greaterThan(parsedAmounts[Field.INPUT].raw, inputApproval.raw)
// reset modal state when closed
function resetModal() {
// clear input if txn submitted
if (!pendingConfirmation) {
onUserInput(Field.INPUT, '')
}
setPendingConfirmation(true)
setAttemptingTxn(false)
setShowAdvanced(false)
}
// function for a pure send
async function onSend() {
setAttemptingTxn(true)
@ -349,11 +379,11 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
addTransaction(
response,
'Send ' +
parsedAmounts[Field.INPUT]?.toSignificant(3) +
' ' +
tokens[Field.INPUT]?.symbol +
' to ' +
recipient
parsedAmounts[Field.INPUT]?.toSignificant(3) +
' ' +
tokens[Field.INPUT]?.symbol +
' to ' +
recipient
)
setPendingConfirmation(false)
})
@ -376,11 +406,11 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
addTransaction(
response,
'Send ' +
parsedAmounts[Field.INPUT]?.toSignificant(3) +
' ' +
tokens[Field.INPUT]?.symbol +
' to ' +
recipient
parsedAmounts[Field.INPUT]?.toSignificant(3) +
' ' +
tokens[Field.INPUT]?.symbol +
' to ' +
recipient
)
setPendingConfirmation(false)
})
@ -487,13 +517,13 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
addTransaction(
response,
'Swap ' +
slippageAdjustedAmounts?.[Field.INPUT]?.toSignificant(3) +
' ' +
tokens[Field.INPUT]?.symbol +
' for ' +
slippageAdjustedAmounts?.[Field.OUTPUT]?.toSignificant(3) +
' ' +
tokens[Field.OUTPUT]?.symbol
slippageAdjustedAmounts?.[Field.INPUT]?.toSignificant(3) +
' ' +
tokens[Field.INPUT]?.symbol +
' for ' +
slippageAdjustedAmounts?.[Field.OUTPUT]?.toSignificant(3) +
' ' +
tokens[Field.OUTPUT]?.symbol
)
setPendingConfirmation(false)
})
@ -625,17 +655,6 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
const warningHigh: boolean =
slippageFromTrade && parseFloat(slippageFromTrade.toFixed(4)) > ALLOWED_SLIPPAGE_HIGH / 100
// reset modal state when closed
function resetModal() {
// clear input if txn submitted
if (!pendingConfirmation) {
onUserInput(Field.INPUT, '')
}
setPendingConfirmation(true)
setAttemptingTxn(false)
setShowAdvanced(false)
}
function modalHeader() {
if (sending && !sendingWithSwap) {
return (
@ -644,7 +663,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<Text fontSize={36} fontWeight={500}>
{parsedAmounts[Field.INPUT]?.toSignificant(6)} {tokens[Field.INPUT]?.symbol}
</Text>
<TokenLogo address={tokens[Field.INPUT]?.address} size={'30px'}/>
<TokenLogo address={tokens[Field.INPUT]?.address} size={'30px'} />
</RowBetween>
<TYPE.darkGray fontSize={20}>To</TYPE.darkGray>
{ENS ? (
@ -656,7 +675,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
{recipient?.slice(0, 8)}...{recipient?.slice(34, 42)}
</TYPE.blue>
</Link>
<Copy toCopy={recipient}/>
<Copy toCopy={recipient} />
</AutoRow>
</AutoColumn>
) : (
@ -666,7 +685,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
{recipient?.slice(0, 6)}...{recipient?.slice(36, 42)}
</TYPE.blue>
</Link>
<Copy toCopy={recipient}/>
<Copy toCopy={recipient} />
</AutoRow>
)}
</AutoColumn>
@ -678,7 +697,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<AutoColumn gap="lg" style={{ marginTop: '40px' }}>
<AutoColumn gap="sm">
<AutoRow gap="10px">
<TokenLogo address={tokens[Field.OUTPUT]?.address} size={'30px'}/>
<TokenLogo address={tokens[Field.OUTPUT]?.address} size={'30px'} />
<Text fontSize={36} fontWeight={500}>
{slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(4)} {tokens[Field.OUTPUT]?.symbol}
</Text>
@ -706,14 +725,14 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
{/* {!!slippageAdjustedAmounts[Field.INPUT] && slippageAdjustedAmounts[Field.INPUT].toSignificant(6)} */}
</TruncatedText>
<RowFixed gap="4px">
<TokenLogo address={tokens[Field.INPUT]?.address} size={'24px'}/>
<TokenLogo address={tokens[Field.INPUT]?.address} size={'24px'} />
<Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
{tokens[Field.INPUT]?.symbol || ''}
</Text>
</RowFixed>
</RowBetween>
<RowFixed>
<ArrowDown size="16" color={theme(isDark).text2}/>
<ArrowDown size="16" color={theme(isDark).text2} />
</RowFixed>
<RowBetween align="flex-end">
<TruncatedText fontSize={24} fontWeight={500} color={warningHigh ? theme(isDark).red1 : ''}>
@ -722,7 +741,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
{/* {!!slippageAdjustedAmounts[Field.OUTPUT] && slippageAdjustedAmounts[Field.OUTPUT].toSignificant(6)} */}
</TruncatedText>
<RowFixed gap="4px">
<TokenLogo address={tokens[Field.OUTPUT]?.address} size={'24px'}/>
<TokenLogo address={tokens[Field.OUTPUT]?.address} size={'24px'} />
<Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
{tokens[Field.OUTPUT]?.symbol || ''}
</Text>
@ -782,17 +801,17 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
>
{trade && showInverted
? (trade?.executionPrice?.invert()?.toSignificant(6) ?? '') +
' ' +
tokens[Field.INPUT]?.symbol +
' / ' +
tokens[Field.OUTPUT]?.symbol
' ' +
tokens[Field.INPUT]?.symbol +
' / ' +
tokens[Field.OUTPUT]?.symbol
: (trade?.executionPrice?.toSignificant(6) ?? '') +
' ' +
tokens[Field.OUTPUT]?.symbol +
' / ' +
tokens[Field.INPUT]?.symbol}
' ' +
tokens[Field.OUTPUT]?.symbol +
' / ' +
tokens[Field.INPUT]?.symbol}
<StyledBalanceMaxMini onClick={() => setShowInverted(!showInverted)}>
<Repeat size={14}/>
<Repeat size={14} />
</StyledBalanceMaxMini>
</Text>
</RowBetween>
@ -802,8 +821,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<TYPE.black fontSize={14} fontWeight={400}>
{independentField === Field.INPUT ? (sending ? 'Min sent' : 'Minimum received') : 'Maximum sold'}
</TYPE.black>
<QuestionHelper
text="A boundary is set so you are protected from large price movements after you submit your trade."/>
<QuestionHelper text="A boundary is set so you are protected from large price movements after you submit your trade." />
</RowFixed>
<RowFixed>
<TYPE.black fontSize={14}>
@ -814,10 +832,10 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
: slippageAdjustedAmounts[Field.OUTPUT]?.toFixed(5)
: '-'
: slippageAdjustedAmounts[Field.INPUT]
? slippageAdjustedAmounts[Field.INPUT]?.toFixed(5) === '0.00000'
? '<0.00001'
: slippageAdjustedAmounts[Field.INPUT]?.toFixed(5)
: '-'}
? slippageAdjustedAmounts[Field.INPUT]?.toFixed(5) === '0.00000'
? '<0.00001'
: slippageAdjustedAmounts[Field.INPUT]?.toFixed(5)
: '-'}
</TYPE.black>
{parsedAmounts[Field.OUTPUT] && parsedAmounts[Field.INPUT] && (
<TYPE.black fontSize={14} marginLeft={'4px'}>
@ -833,7 +851,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<TYPE.black color={theme(isDark).text1} fontSize={14} fontWeight={400}>
Price impact
</TYPE.black>
<QuestionHelper text="The difference between the market price and your price due to trade size."/>
<QuestionHelper text="The difference between the market price and your price due to trade size." />
</RowFixed>
<ErrorText
fontWeight={500}
@ -854,8 +872,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<TYPE.black fontSize={14} fontWeight={400}>
Liquidity Provider Fee
</TYPE.black>
<QuestionHelper
text="A portion of each trade (0.3%) goes to liquidity providers to incentivize liquidity on the protocol."/>
<QuestionHelper text="A portion of each trade (0.3%) goes to liquidity providers to incentivize liquidity on the protocol." />
</RowFixed>
<TYPE.black fontSize={14}>
{feeTimesInputFormatted
@ -877,7 +894,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
}
}
const PriceBar = function () {
const PriceBar = function() {
return (
<AutoRow justify="space-between">
<AutoColumn justify="center">
@ -924,7 +941,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
? `Sending ${parsedAmounts[Field.OUTPUT]?.toSignificant(6)} ${tokens[Field.OUTPUT]?.symbol} to ${recipient}`
: `Sending ${parsedAmounts[Field.INPUT]?.toSignificant(6)} ${tokens[Field.INPUT]?.symbol} to ${recipient}`
: ` Swapping ${parsedAmounts[Field.INPUT]?.toSignificant(6)} ${tokens[Field.INPUT]?.symbol} for ${parsedAmounts[
Field.OUTPUT
Field.OUTPUT
]?.toSignificant(6)} ${tokens[Field.OUTPUT]?.symbol}`
function _onTokenSelect(address: string) {
@ -979,7 +996,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
Max
</MaxButton>
)}
<StyledNumerical value={formattedAmounts[Field.INPUT]} onUserInput={val => onUserInput(Field.INPUT, val)}/>
<StyledNumerical value={formattedAmounts[Field.INPUT]} onUserInput={val => onUserInput(Field.INPUT, val)} />
<CurrencyInputPanel
field={Field.INPUT}
value={formattedAmounts[Field.INPUT]}
@ -1025,7 +1042,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<ColumnCenter>
<RowBetween padding="0 12px">
<ArrowWrapper onClick={onSwapTokens}>
<ArrowDown size="16" color={theme(isDark).text2} onClick={onSwapTokens}/>
<ArrowDown size="16" color={theme(isDark).text2} onClick={onSwapTokens} />
</ArrowWrapper>
<StyledBalanceMaxMini onClick={() => setSendingWithSwap(false)} style={{ marginRight: '0px' }}>
<TYPE.blue>Remove Swap</TYPE.blue>
@ -1063,7 +1080,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
/>
{sendingWithSwap && (
<RowBetween padding="0 12px">
<ArrowDown size="16"/>
<ArrowDown size="16" />
</RowBetween>
)}
</>
@ -1095,7 +1112,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
{!noRoute && tokens[Field.OUTPUT] && tokens[Field.INPUT] && (
<Card padding={advanced ? '.25rem 1.25rem 0 .75rem' : '.25rem .7rem .25rem 1.25rem'} borderRadius={'20px'}>
{advanced ? (
<PriceBar/>
<PriceBar />
) : (
<AutoColumn gap="4px">
{' '}
@ -1111,17 +1128,17 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
>
{trade && showInverted
? (trade?.executionPrice?.invert()?.toSignificant(6) ?? '') +
' ' +
tokens[Field.INPUT]?.symbol +
' per ' +
tokens[Field.OUTPUT]?.symbol
' ' +
tokens[Field.INPUT]?.symbol +
' per ' +
tokens[Field.OUTPUT]?.symbol
: (trade?.executionPrice?.toSignificant(6) ?? '') +
' ' +
tokens[Field.OUTPUT]?.symbol +
' per ' +
tokens[Field.INPUT]?.symbol}
' ' +
tokens[Field.OUTPUT]?.symbol +
' per ' +
tokens[Field.INPUT]?.symbol}
<StyledBalanceMaxMini onClick={() => setShowInverted(!showInverted)}>
<Repeat size={14}/>
<Repeat size={14} />
</StyledBalanceMaxMini>
</Text>
</RowBetween>
@ -1141,8 +1158,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
: priceSlippage.toFixed(4) + '%'
: '-'}{' '}
</ErrorText>
<QuestionHelper
text="The difference between the market price and your quoted price due to trade size."/>
<QuestionHelper text="The difference between the market price and your quoted price due to trade size." />
</RowFixed>
</RowBetween>
)}
@ -1189,9 +1205,12 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<Text fontSize={20} fontWeight={500}>
{!account
? 'Connect Wallet'
: (generalError || inputError || outputError || recipientError || tradeError) ||
(`${sending ? 'Send' : 'Swap'}${warningHigh ? ' Anyway' : ''}`)
}
: generalError ||
inputError ||
outputError ||
recipientError ||
tradeError ||
`${sending ? 'Send' : 'Swap'}${warningHigh ? ' Anyway' : ''}`}
</Text>
</ButtonError>
)}
@ -1204,7 +1223,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<Text fontSize={16} fontWeight={500} style={{ userSelect: 'none' }}>
Show Advanced
</Text>
<ChevronDown color={theme(isDark).text2}/>
<ChevronDown color={theme(isDark).text2} />
</RowBetween>
</Hover>
)}
@ -1215,10 +1234,10 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<Text fontSize={16} color={theme(isDark).text2} fontWeight={500} style={{ userSelect: 'none' }}>
Hide Advanced
</Text>
<ChevronUp color={theme(isDark).text2}/>
<ChevronUp color={theme(isDark).text2} />
</RowBetween>
</Hover>
<SectionBreak/>
<SectionBreak />
<AutoColumn style={{ padding: '0 20px' }}>
<RowBetween>
<RowFixed>
@ -1233,8 +1252,8 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
text={
independentField === Field.INPUT
? sending
? 'Price can change between when a transaction is submitted and when it is executed. This is the minimum amount you will send. A worse rate will cause your transaction to revert.'
: 'Price can change between when a transaction is submitted and when it is executed. This is the minimum amount you will receive. A worse rate will cause your transaction to revert.'
? 'Price can change between when a transaction is submitted and when it is executed. This is the minimum amount you will send. A worse rate will cause your transaction to revert.'
: 'Price can change between when a transaction is submitted and when it is executed. This is the minimum amount you will receive. A worse rate will cause your transaction to revert.'
: 'Price can change between when a transaction is submitted and when it is executed. This is the maximum amount you will pay. A worse rate will cause your transaction to revert.'
}
/>
@ -1244,18 +1263,18 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
{independentField === Field.INPUT
? slippageAdjustedAmounts[Field.OUTPUT]
? slippageAdjustedAmounts[Field.OUTPUT]?.lessThan(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(10000))
)
new Fraction(JSBI.BigInt(1), JSBI.BigInt(10000))
)
? '<0.00001'
: slippageAdjustedAmounts[Field.OUTPUT]?.toFixed(5)
: '-'
: slippageAdjustedAmounts[Field.INPUT]
? slippageAdjustedAmounts[Field.INPUT]?.lessThan(
? slippageAdjustedAmounts[Field.INPUT]?.lessThan(
new Fraction(JSBI.BigInt(1), JSBI.BigInt(10000))
)
? '<0.00001'
: slippageAdjustedAmounts[Field.INPUT]?.toFixed(5)
: '-'}
? '<0.00001'
: slippageAdjustedAmounts[Field.INPUT]?.toFixed(5)
: '-'}
</TYPE.black>
{parsedAmounts[Field.OUTPUT] && parsedAmounts[Field.INPUT] && (
<TYPE.black fontSize={14} marginLeft={'4px'} color={theme(isDark).text1}>
@ -1271,8 +1290,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<TYPE.black fontSize={14} fontWeight={400} color={theme(isDark).text1}>
Price Impact
</TYPE.black>
<QuestionHelper
text="The difference between the market price and your quoted price due to trade size."/>
<QuestionHelper text="The difference between the market price and your quoted price due to trade size." />
</RowFixed>
<ErrorText
fontWeight={500}
@ -1293,8 +1311,7 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
<TYPE.black fontSize={14} fontWeight={400} color={theme(isDark).text1}>
Liquidity Provider Fee
</TYPE.black>
<QuestionHelper
text="A portion of each trade (0.03%) goes to liquidity providers to incentivize liquidity on the protocol."/>
<QuestionHelper text="A portion of each trade (0.03%) goes to liquidity providers to incentivize liquidity on the protocol." />
</RowFixed>
<TYPE.black fontSize={14} color={theme(isDark).text1}>
{feeTimesInputFormatted
@ -1303,13 +1320,12 @@ function ExchangePage({ sendingInput = false, history, params }: ExchangePagePro
</TYPE.black>
</RowBetween>
</AutoColumn>
<SectionBreak/>
<SectionBreak />
<RowFixed padding={'0 20px'}>
<TYPE.black fontWeight={400} fontSize={14} color={theme(isDark).text1}>
Set front running resistance
</TYPE.black>
<QuestionHelper
text="Your transaction will revert if the price changes more than this amount after you submit your trade."/>
<QuestionHelper text="Your transaction will revert if the price changes more than this amount after you submit your trade." />
</RowFixed>
<SlippageTabs
rawSlippage={allowedSlippage}

@ -57,7 +57,7 @@ export const BottomGrouping = styled.div`
export const ErrorText = styled(Text)`
color: ${({ theme, warningLow, warningMedium, warningHigh }) =>
warningHigh ? theme.red1 : warningMedium ? theme.yellow2 : warningLow ? theme.green1 : theme.text1};
warningHigh ? theme.red1 : warningMedium ? theme.yellow2 : warningLow ? theme.green1 : theme.text1};
`
export const InputGroup = styled(AutoColumn)`

@ -19,7 +19,12 @@ export interface SwapState {
}
}
export function initializeSwapState({ inputTokenAddress, outputTokenAddress, typedValue, independentField }): SwapState {
export function initializeSwapState({
inputTokenAddress,
outputTokenAddress,
typedValue,
independentField
}): SwapState {
return {
independentField: independentField,
typedValue: typedValue,
@ -110,21 +115,21 @@ export function useSwapStateReducer(params: QueryParams) {
typedValue:
params.inputTokenAddress && !params.outputTokenAddress
? params.inputTokenAmount
? params.inputTokenAmount
: ''
? params.inputTokenAmount
: ''
: !params.inputTokenAddress && params.outputTokenAddress
? params.outputTokenAmount
? params.outputTokenAmount
: ''
: params.inputTokenAddress && params.outputTokenAddress
? params.inputTokenAmount
? params.inputTokenAmount
? params.inputTokenAmount
: ''
: ''
? ''
: ''
? ''
: ''
: ''
? ''
: ''
? ''
: ''
},
initializeSwapState
)

@ -1,6 +1,5 @@
import React from 'react'
import styled from 'styled-components'
import { withRouter } from 'react-router-dom'
import Row from '../Row'
import Menu from '../Menu'
@ -64,7 +63,7 @@ const TitleText = styled(Row)`
`};
`
const AccountElement = styled.div<{active: boolean;}>`
const AccountElement = styled.div<{ active: boolean }>`
display: flex;
display: flex;
flex-direction: row;
@ -107,7 +106,7 @@ const Alpha = styled(GreyCard)`
font-weight: 600;
`
const UniIcon = styled.div<{href: string}>`
const UniIcon = styled.div<{ href: string }>`
transition: transform 0.3s ease;
:hover {
transform: rotate(-5deg);
@ -133,7 +132,7 @@ const MigrateBanner = styled(AutoColumn)`
`};
`
function Header({ history }) {
export default function Header() {
const { account, chainId } = useWeb3React()
const userEthBalance = useAddressBalance(account, WETH[chainId])
@ -196,5 +195,3 @@ function Header({ history }) {
</HeaderFrame>
)
}
export default withRouter(Header)

@ -24,5 +24,5 @@ export default function Identicon() {
}
}, [account])
return <StyledIdenticon ref={ref}/>
return <StyledIdenticon ref={ref} />
}

@ -11,5 +11,5 @@ const SpinnerWrapper = styled(Spinner)<{ size: string }>`
`
export default function Loader({ size }: { size: string }) {
return <SpinnerWrapper src={Circle} alt="loader" size={size}/>
return <SpinnerWrapper src={Circle} alt="loader" size={size} />
}

@ -78,9 +78,9 @@ const MenuItem = styled(Link)`
}
`
const CODE_LINK = !!process.env.REACT_APP_GIT_COMMIT_HASH ?
`https://github.com/Uniswap/uniswap-frontend/tree/${process.env.REACT_APP_GIT_COMMIT_HASH}`:
'https://github.com/Uniswap/uniswap-frontend'
const CODE_LINK = !!process.env.REACT_APP_GIT_COMMIT_HASH
? `https://github.com/Uniswap/uniswap-frontend/tree/${process.env.REACT_APP_GIT_COMMIT_HASH}`
: 'https://github.com/Uniswap/uniswap-frontend'
export default function Menu() {
const node = useRef<HTMLDivElement>()

@ -22,8 +22,8 @@ const StyledDialogOverlay = styled(WrappedDialogOverlay).attrs({
background-color: ${({ theme }) => 'transparent'};
${({ mobile }) =>
mobile &&
css`
mobile &&
css`
align-items: flex-end;
`}
@ -56,13 +56,13 @@ const StyledDialogContent = styled(FilteredDialogContent)`
max-width: 420px;
${({ maxHeight }) =>
maxHeight &&
css`
maxHeight &&
css`
max-height: ${maxHeight}vh;
`}
${({ minHeight }) =>
minHeight &&
css`
minHeight &&
css`
min-height: ${minHeight}vh;
`}
display: flex;
@ -77,7 +77,7 @@ const StyledDialogContent = styled(FilteredDialogContent)`
width: 85vw;
max-height: 66vh;
${mobile &&
css`
css`
width: 100vw;
border-radius: 20px;
border-bottom-left-radius: 0;
@ -105,13 +105,13 @@ interface ModalProps {
}
export default function Modal({
isOpen,
onDismiss,
minHeight = false,
maxHeight = 50,
initialFocusRef = null,
children
}: ModalProps) {
isOpen,
onDismiss,
minHeight = false,
maxHeight = 50,
initialFocusRef = null,
children
}: ModalProps) {
const transitions = useTransition(isOpen, null, {
config: { duration: 200 },
from: { opacity: 0 },
@ -163,7 +163,9 @@ export default function Modal({
{props => (
<animated.div
{...bind()}
style={{ transform: (xy as any).interpolate((x, y) => `translate3d(${0}px,${y > 0 ? y : 0}px,0)`) }}
style={{
transform: (xy as any).interpolate((x, y) => `translate3d(${0}px,${y > 0 ? y : 0}px,0)`)
}}
>
<StyledDialogContent
style={props}
@ -172,7 +174,7 @@ export default function Modal({
maxHeight={maxHeight}
mobile={isMobile}
>
<HiddenCloseButton onClick={onDismiss}/>
<HiddenCloseButton onClick={onDismiss} />
{children}
</StyledDialogContent>
</animated.div>
@ -203,7 +205,7 @@ export default function Modal({
isOpen={isOpen}
mobile={isMobile}
>
<HiddenCloseButton onClick={onDismiss}/>
<HiddenCloseButton onClick={onDismiss} />
{children}
</StyledDialogContent>
</StyledDialogOverlay>

@ -1,15 +1,15 @@
import React, { useCallback } from 'react';
import styled from 'styled-components';
import { darken } from 'polished';
import { useTranslation } from 'react-i18next';
import { withRouter, NavLink, Link as HistoryLink, RouteComponentProps } from 'react-router-dom';
import React, { useCallback } from 'react'
import styled from 'styled-components'
import { darken } from 'polished'
import { useTranslation } from 'react-i18next'
import { withRouter, NavLink, Link as HistoryLink, RouteComponentProps } from 'react-router-dom'
import { Hover } from '../../theme';
import { ArrowLeft } from 'react-feather';
import { RowBetween } from '../Row';
import QuestionHelper from '../Question';
import { Hover } from '../../theme'
import { ArrowLeft } from 'react-feather'
import { RowBetween } from '../Row'
import QuestionHelper from '../Question'
import { useBodyKeyDown } from '../../hooks';
import { useBodyKeyDown } from '../../hooks'
const tabOrder = [
{
@ -27,16 +27,16 @@ const tabOrder = [
textKey: 'pool',
regex: /\/pool/
}
];
]
const Tabs = styled.div`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
border-radius: 3rem;
margin-bottom: 20px;
`;
`
const activeClassName = 'ACTIVE';
const activeClassName = 'ACTIVE'
const StyledNavLink = styled(NavLink).attrs({
activeClassName
@ -65,41 +65,41 @@ const StyledNavLink = styled(NavLink).attrs({
:focus {
color: ${({ theme }) => darken(0.1, theme.text1)};
}
`;
`
const ActiveText = styled.div`
font-weight: 500;
font-size: 20px;
`;
`
const ArrowLink = styled(ArrowLeft)`
color: ${({ theme }) => theme.text1};
`;
`
function NavigationTabs({ location: { pathname }, history }: RouteComponentProps<{}>) {
const { t } = useTranslation();
const { t } = useTranslation()
const navigate = useCallback(
direction => {
const tabIndex = tabOrder.findIndex(({ regex }) => pathname.match(regex));
history.push(tabOrder[ (tabIndex + tabOrder.length + direction) % tabOrder.length ].path);
const tabIndex = tabOrder.findIndex(({ regex }) => pathname.match(regex))
history.push(tabOrder[(tabIndex + tabOrder.length + direction) % tabOrder.length].path)
},
[pathname, history]
);
)
const navigateRight = useCallback(() => {
navigate(1);
}, [navigate]);
navigate(1)
}, [navigate])
const navigateLeft = useCallback(() => {
navigate(-1);
}, [navigate]);
navigate(-1)
}, [navigate])
useBodyKeyDown('ArrowRight', navigateRight);
useBodyKeyDown('ArrowLeft', navigateLeft);
useBodyKeyDown('ArrowRight', navigateRight)
useBodyKeyDown('ArrowLeft', navigateLeft)
const adding = pathname.match('/add');
const removing = pathname.match('/remove');
const finding = pathname.match('/find');
const creating = pathname.match('/create');
const adding = pathname.match('/add')
const removing = pathname.match('/remove')
const finding = pathname.match('/find')
const creating = pathname.match('/create')
return (
<>
@ -107,7 +107,7 @@ function NavigationTabs({ location: { pathname }, history }: RouteComponentProps
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
<Hover onClick={() => history.goBack()}>
<ArrowLink/>
<ArrowLink />
</Hover>
<ActiveText>{adding ? 'Add' : 'Remove'} Liquidity</ActiveText>
<QuestionHelper
@ -123,20 +123,20 @@ function NavigationTabs({ location: { pathname }, history }: RouteComponentProps
<Tabs>
<RowBetween style={{ padding: '1rem' }}>
<HistoryLink to="/pool">
<ArrowLink/>
<ArrowLink />
</HistoryLink>
<ActiveText>Import Pool</ActiveText>
<QuestionHelper text={'Use this tool to find pairs that don\'t automatically appear in the interface.'}/>
<QuestionHelper text={"Use this tool to find pairs that don't automatically appear in the interface."} />
</RowBetween>
</Tabs>
) : creating ? (
<Tabs>
<RowBetween style={{ padding: '1rem' }}>
<HistoryLink to="/pool">
<ArrowLink/>
<ArrowLink />
</HistoryLink>
<ActiveText>Create Pool</ActiveText>
<QuestionHelper text={'Use this interface to create a new pool.'}/>
<QuestionHelper text={'Use this interface to create a new pool.'} />
</RowBetween>
</Tabs>
) : (
@ -149,7 +149,7 @@ function NavigationTabs({ location: { pathname }, history }: RouteComponentProps
</Tabs>
)}
</>
);
)
}
export default withRouter(NavigationTabs);
export default withRouter(NavigationTabs)

@ -1,7 +1,7 @@
import React from 'react'
import styled from 'styled-components'
const StyledInput = styled.input<{error?: boolean; fontSize?: string; align?: string}>`
const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: string }>`
color: ${({ error, theme }) => error && theme.red1};
color: ${({ theme }) => theme.text1};
width: 0;
@ -45,8 +45,20 @@ function escapeRegExp(string: string): string {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string
}
export const Input = React.memo(({ value, onUserInput, placeHolder = null, ...rest }: any) => {
function enforcer(nextUserInput: string) {
export const Input = React.memo(function InnerInput({
value,
onUserInput,
placeHolder,
...rest
}: {
value: string | number
onUserInput: (string) => void
placeHolder?: string
align?: 'right' | 'left'
id?: string
onClick?: () => void
}) {
const enforcer = (nextUserInput: string) => {
if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
onUserInput(nextUserInput)
}

@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'
import { withRouter } from 'react-router-dom'
import { RouteComponentProps, withRouter } from 'react-router-dom'
import { TokenAmount, JSBI, Token, Pair } from '@uniswap/sdk'
import Row from '../Row'
@ -21,7 +21,7 @@ import { useWeb3React } from '@web3-react/core'
import { useAddressBalance } from '../../contexts/Balances'
import { usePair, useAllPairs } from '../../contexts/Pairs'
function PoolFinder({ history }) {
function PoolFinder({ history }: RouteComponentProps) {
const Fields = {
TOKEN0: 0,
TOKEN1: 1
@ -65,20 +65,20 @@ function PoolFinder({ history }) {
function endSearch() {
history.goBack() // return to previous page
newLiquidity &&
addPopup(
<AutoColumn gap={'10px'}>
<Text fontSize={20} fontWeight={500}>
Pool Imported
</Text>
<Row>
<DoubleTokenLogo a0={token0Address || ''} a1={token1Address || ''} margin={true}/>
<Text fontSize={16} fotnWeight={500}>
UNI {token0?.symbol} / {token1?.symbol}
addPopup(
<AutoColumn gap={'10px'}>
<Text fontSize={20} fontWeight={500}>
Pool Imported
</Text>
</Row>
<Link>View on Uniswap Info.</Link>
</AutoColumn>
)
<Row>
<DoubleTokenLogo a0={token0Address || ''} a1={token1Address || ''} margin={true} />
<Text fontSize={16} fotnWeight={500}>
UNI {token0?.symbol} / {token1?.symbol}
</Text>
</Row>
<Link>View on Uniswap Info.</Link>
</AutoColumn>
)
}
return (
@ -101,7 +101,7 @@ function PoolFinder({ history }) {
}}
>
<Row>
<TokenLogo address={token0Address}/>
<TokenLogo address={token0Address} />
<Text fontWeight={500} fontSize={20} marginLeft={'12px'}>
{token0?.symbol}
</Text>
@ -109,7 +109,7 @@ function PoolFinder({ history }) {
</ButtonDropwdownLight>
)}
<ColumnCenter>
<Plus size="16" color="#888D9B"/>
<Plus size="16" color="#888D9B" />
</ColumnCenter>
{!token1Address ? (
<ButtonDropwdown
@ -128,7 +128,7 @@ function PoolFinder({ history }) {
}}
>
<Row>
<TokenLogo address={token1Address}/>
<TokenLogo address={token1Address} />
<Text fontWeight={500} fontSize={20} marginLeft={'12px'}>
{token1?.symbol}
</Text>
@ -137,7 +137,8 @@ function PoolFinder({ history }) {
)}
{allowImport && (
<ColumnCenter
style={{ justifyItems: 'center', backgroundColor: '', padding: '12px 0px', borderRadius: '12px' }}>
style={{ justifyItems: 'center', backgroundColor: '', padding: '12px 0px', borderRadius: '12px' }}
>
<Text textAlign="center" fontWeight={500} color="">
{newLiquidity ? 'Pool Found!' : 'Pool already imported.'}
</Text>

@ -79,7 +79,7 @@ export default function App() {
{activePopups.map(item => {
return (
<Popup key={item.key}>
<StyledClose color="#888D9B" onClick={() => removePopup(item.key)}/>
<StyledClose color="#888D9B" onClick={() => removePopup(item.key)} />
{React.cloneElement(item.content, { popKey: item.key })}
</Popup>
)
@ -98,7 +98,7 @@ export default function App() {
.map(item => {
return (
<Popup key={item.key}>
<StyledClose color="#888D9B" onClick={() => removePopup(item.key)}/>
<StyledClose color="#888D9B" onClick={() => removePopup(item.key)} />
{React.cloneElement(item.content, { popKey: item.key })}
</Popup>
)

@ -31,7 +31,7 @@ const HoverCard = styled(Card)`
`
interface PositionCardProps extends RouteComponentProps<{}> {
pairAddress: string;
pairAddress: string
token0: Token
token1: Token
minimal?: boolean
@ -86,7 +86,7 @@ function PositionCard({ pairAddress, token0, token1, history, border, minimal =
</FixedHeightRow>
<FixedHeightRow onClick={() => setShowMore(!showMore)}>
<RowFixed>
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20}/>
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} />
<Text fontWeight={500} fontSize={20}>
{token0?.symbol}:{token1?.symbol}
</Text>
@ -104,7 +104,7 @@ function PositionCard({ pairAddress, token0, token1, history, border, minimal =
</Text>
{token0Deposited ? (
<RowFixed>
{!minimal && <TokenLogo address={token0?.address || ''}/>}
{!minimal && <TokenLogo address={token0?.address || ''} />}
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token0Deposited?.toFixed(8)}
</Text>
@ -119,7 +119,7 @@ function PositionCard({ pairAddress, token0, token1, history, border, minimal =
</Text>
{token1Deposited ? (
<RowFixed>
{!minimal && <TokenLogo address={token1?.address || ''}/>}
{!minimal && <TokenLogo address={token1?.address || ''} />}
<Text color="#888D9B" fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token1Deposited?.toFixed(8)}
</Text>
@ -140,16 +140,16 @@ function PositionCard({ pairAddress, token0, token1, history, border, minimal =
<AutoColumn gap="12px">
<FixedHeightRow onClick={() => setShowMore(!showMore)} style={{ cursor: 'pointer' }}>
<RowFixed>
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20}/>
<DoubleLogo a0={token0?.address || ''} a1={token1?.address || ''} margin={true} size={20} />
<Text fontWeight={500} fontSize={20}>
{token0?.symbol}:{token1?.symbol}
</Text>
</RowFixed>
<RowFixed>
{showMore ? (
<ChevronUp size="20" style={{ marginLeft: '10px' }}/>
<ChevronUp size="20" style={{ marginLeft: '10px' }} />
) : (
<ChevronDown size="20" style={{ marginLeft: '10px' }}/>
<ChevronDown size="20" style={{ marginLeft: '10px' }} />
)}
</RowFixed>
</FixedHeightRow>
@ -167,7 +167,7 @@ function PositionCard({ pairAddress, token0, token1, history, border, minimal =
{token0Deposited?.toFixed(8)}
</Text>
{!minimal && (
<TokenLogo size="20px" style={{ marginLeft: '8px' }} address={token0?.address || ''}/>
<TokenLogo size="20px" style={{ marginLeft: '8px' }} address={token0?.address || ''} />
)}
</RowFixed>
) : (
@ -187,7 +187,7 @@ function PositionCard({ pairAddress, token0, token1, history, border, minimal =
{token1Deposited?.toFixed(8)}
</Text>
{!minimal && (
<TokenLogo size="20px" style={{ marginLeft: '8px' }} address={token1?.address || ''}/>
<TokenLogo size="20px" style={{ marginLeft: '8px' }} address={token1?.address || ''} />
)}
</RowFixed>
) : (

@ -62,7 +62,7 @@ const Popup = styled.div`
`}
`
export default function QuestionHelper({ text }) {
export default function QuestionHelper({ text }: { text: string }) {
const [showPopup, setPopup] = useState(false)
return (

@ -154,18 +154,18 @@ interface SearchModalProps extends RouteComponentProps<{}> {
}
function SearchModal({
history,
isOpen,
onDismiss,
onTokenSelect,
urlAddedTokens,
filterType,
hiddenToken,
showSendWithSwap,
otherSelectedTokenAddress,
otherSelectedText,
showCommonBases = false
}: SearchModalProps) {
history,
isOpen,
onDismiss,
onTokenSelect,
urlAddedTokens,
filterType,
hiddenToken,
showSendWithSwap,
otherSelectedTokenAddress,
otherSelectedText,
showCommonBases = false
}: SearchModalProps) {
const { t } = useTranslation()
const { account, chainId } = useWeb3React()
const theme = useContext(ThemeContext)
@ -377,7 +377,7 @@ function SearchModal({
}}
>
<RowFixed>
<DoubleTokenLogo a0={token0?.address || ''} a1={token1?.address || ''} size={24} margin={true}/>
<DoubleTokenLogo a0={token0?.address || ''} a1={token1?.address || ''} size={24} margin={true} />
<Text fontWeight={500} fontSize={16}>{`${token0?.symbol}/${token1?.symbol}`}</Text>
</RowFixed>
{/* <Text fontWeight={500} fontSize={16}>
@ -418,7 +418,7 @@ function SearchModal({
}}
>
<RowFixed>
<TokenLogo address={temporaryToken.address} size={'24px'} style={{ marginRight: '14px' }}/>
<TokenLogo address={temporaryToken.address} size={'24px'} style={{ marginRight: '14px' }} />
<Column>
<Text fontWeight={500}>{temporaryToken.symbol}</Text>
<FadedSpan>(Found by search)</FadedSpan>
@ -431,16 +431,16 @@ function SearchModal({
return <TokenModalInfo>{t('noToken')}</TokenModalInfo>
}
}
// TODO is this the right place to link to create exchange?
// else if (isAddress(searchQuery) && tokenAddress === ethers.constants.AddressZero) {
// return (
// <>
// <TokenModalInfo>{t('noToken')}</TokenModalInfo>
// <TokenModalInfo>
// <Link to={`/create-exchange/${searchQuery}`}>{t('createExchange')}</Link>
// </TokenModalInfo>
// </>
// )
// TODO is this the right place to link to create exchange?
// else if (isAddress(searchQuery) && tokenAddress === ethers.constants.AddressZero) {
// return (
// <>
// <TokenModalInfo>{t('noToken')}</TokenModalInfo>
// <TokenModalInfo>
// <Link to={`/create-exchange/${searchQuery}`}>{t('createExchange')}</Link>
// </TokenModalInfo>
// </>
// )
// }
else {
return filteredTokenList
@ -453,8 +453,8 @@ function SearchModal({
? -1
: 1
: sortDirection
? 1
: -1
? 1
: -1
})
.map(({ address, symbol, balance }) => {
const urlAdded = urlAddedTokens && urlAddedTokens.hasOwnProperty(address)
@ -467,13 +467,12 @@ function SearchModal({
return (
<MenuItem
key={address}
onClick={() => (hiddenToken && hiddenToken === address ? () => {
} : _onTokenSelect(address))}
onClick={() => (hiddenToken && hiddenToken === address ? () => {} : _onTokenSelect(address))}
disabled={hiddenToken && hiddenToken === address}
selected={otherSelectedTokenAddress === address}
>
<RowFixed>
<TokenLogo address={address} size={'24px'} style={{ marginRight: '14px' }}/>
<TokenLogo address={address} size={'24px'} style={{ marginRight: '14px' }} />
<Column>
<Text fontWeight={500}>
{symbol}
@ -500,9 +499,7 @@ function SearchModal({
{balance ? (
<Text>
{zeroBalance && showSendWithSwap ? (
<ColumnCenter
style={{ backgroundColor: theme.bg2, padding: '8px', borderRadius: '12px' }}
>
<ColumnCenter style={{ backgroundColor: theme.bg2, padding: '8px', borderRadius: '12px' }}>
<Text textAlign="center" fontWeight={500} color={theme.blue1}>
Send With Swap
</Text>
@ -514,7 +511,7 @@ function SearchModal({
)}
</Text>
) : account ? (
<SpinnerWrapper src={Circle} alt="loader"/>
<SpinnerWrapper src={Circle} alt="loader" />
) : (
'-'
)}
@ -569,7 +566,7 @@ function SearchModal({
Import A Token
</Text>
</RowFixed>
<CloseIcon onClick={onDismiss}/>
<CloseIcon onClick={onDismiss} />
</RowBetween>
<TYPE.body style={{ marginTop: '10px' }}>
To import a custom token, paste token address in the search bar.
@ -589,7 +586,7 @@ function SearchModal({
<Text fontWeight={500} fontSize={16}>
{filterType === 'tokens' ? 'Select A Token' : 'Select A Pool'}
</Text>
<CloseIcon onClick={onDismiss}/>
<CloseIcon onClick={onDismiss} />
</RowBetween>
<Input
type={'text'}
@ -604,7 +601,7 @@ function SearchModal({
<Text fontWeight={500} fontSize={16}>
Common Bases
</Text>
<QuestionHelper text="These tokens are commonly used in pairs."/>
<QuestionHelper text="These tokens are commonly used in pairs." />
</AutoRow>
<AutoRow gap="10px">
{COMMON_BASES[chainId]?.map(token => {
@ -615,7 +612,7 @@ function SearchModal({
disable={hiddenToken === token.address}
key={token.address}
>
<TokenLogo address={token.address}/>
<TokenLogo address={token.address} />
<Text fontWeight={500} fontSize={16}>
{token.symbol}
</Text>
@ -637,16 +634,16 @@ function SearchModal({
</RowBetween>
</PaddedColumn>
)}
{!showTokenImport && <div style={{ width: '100%', height: '1px', backgroundColor: theme.bg2 }}/>}
{!showTokenImport && <div style={{ width: '100%', height: '1px', backgroundColor: theme.bg2 }} />}
{!showTokenImport && <TokenList>{filterType === 'tokens' ? renderTokenList() : renderPairsList()}</TokenList>}
{!showTokenImport && <div style={{ width: '100%', height: '1px', backgroundColor: theme.bg2 }}/>}
{!showTokenImport && <div style={{ width: '100%', height: '1px', backgroundColor: theme.bg2 }} />}
{!showTokenImport && (
<Card>
<AutoRow justify={'center'}>
<div>
{filterType !== 'tokens' && (
<Text fontWeight={500}>
{!isMobile && 'Don\'t see a pool? '}
{!isMobile && "Don't see a pool? "}
<StyledLink
onClick={() => {
history.push('/find')
@ -658,7 +655,7 @@ function SearchModal({
)}
{filterType === 'tokens' && (
<Text fontWeight={500} color={theme.text2} fontSize={14}>
{!isMobile && 'Don\'t see a token? '}
{!isMobile && "Don't see a token? "}
<StyledLink
onClick={() => {

@ -58,12 +58,15 @@ export default function InputSlider({ value, onChange, override }: InputSliderPr
const [internalVal, setInternalVal] = useState<number>(value)
const debouncedInternalValue = useDebounce(internalVal, 100)
const handleChange = useCallback((e, val) => {
setInternalVal(val)
if (val !== debouncedInternalValue) {
onChange(val)
}
}, [setInternalVal, onChange, debouncedInternalValue])
const handleChange = useCallback(
(e, val) => {
setInternalVal(val)
if (val !== debouncedInternalValue) {
onChange(val)
}
},
[setInternalVal, onChange, debouncedInternalValue]
)
useEffect(() => {
if (override) {

@ -61,21 +61,21 @@ const Input = styled.input<{ active?: boolean }>`
color: ${({ theme }) => theme.text1};
text-align: left;
${({ active }) =>
active &&
css`
active &&
css`
color: initial;
cursor: initial;
text-align: right;
`}
${({ placeholder }) =>
placeholder !== 'Custom' &&
css`
placeholder !== 'Custom' &&
css`
text-align: right;
color: ${({ theme }) => theme.text1};
`}
${({ color }) =>
color === 'red' &&
css`
color === 'red' &&
css`
color: ${({ theme }) => theme.red1};
`}
`
@ -84,8 +84,8 @@ const BottomError = styled(Text)`
font-size: 14px;
font-weight: 400;
${({ show }) =>
show &&
css`
show &&
css`
padding-top: 12px;
`}
`
@ -94,9 +94,10 @@ const OptionCustom = styled(FancyButton)<{ active?: boolean; warning?: boolean }
height: 2rem;
position: relative;
padding: 0 0.75rem;
border: ${({ theme, active, warning }) => active && `1px solid ${(warning ? theme.red1 : theme.blue1)}`};
border: ${({ theme, active, warning }) => active && `1px solid ${warning ? theme.red1 : theme.blue1}`};
:hover {
border: ${({ theme, active, warning }) => active && `1px solid ${(warning ? darken(0.1, theme.red1) : darken(0.1, theme.blue1))}`};
border: ${({ theme, active, warning }) =>
active && `1px solid ${warning ? darken(0.1, theme.red1) : darken(0.1, theme.blue1)}`};
}
input {
@ -116,12 +117,12 @@ const Percent = styled.div`
font-size: 0, 8rem;
flex-grow: 0;
${({ color, theme }) =>
(color === 'faded' &&
css`
(color === 'faded' &&
css`
color: ${theme.bg1};
`) ||
(color === 'red' &&
css`
(color === 'red' &&
css`
color: ${theme.red1};
`)};
`
@ -133,7 +134,12 @@ interface TransactionDetailsProps {
setDeadline: (deadline: number) => void
}
export default function TransactionDetails({ setRawSlippage, rawSlippage, deadline, setDeadline }: TransactionDetailsProps) {
export default function TransactionDetails({
setRawSlippage,
rawSlippage,
deadline,
setDeadline
}: TransactionDetailsProps) {
const [activeIndex, setActiveIndex] = useState(2)
const [warningType, setWarningType] = useState(WARNING_TYPE.none)
@ -147,26 +153,10 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
const [deadlineInput, setDeadlineInput] = useState(deadline / 60)
function parseCustomDeadline(e) {
let val = e.target.value
const acceptableValues = [/^$/, /^\d+$/]
if (acceptableValues.some(re => re.test(val))) {
setDeadlineInput(val)
setDeadline(val * 60)
}
}
const setFromCustom = () => {
setActiveIndex(4)
inputRef.current.focus()
// if there's a value, evaluate the bounds
checkBounds(debouncedInput)
}
const updateSlippage = useCallback(
newSlippage => {
// round to 2 decimals to prevent ethers error
let numParsed = newSlippage * 100
const numParsed = newSlippage * 100
// set both slippage values in parents
setRawSlippage(numParsed)
@ -174,39 +164,6 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
[setRawSlippage]
)
// used for slippage presets
const setFromFixed = useCallback(
(index, slippage) => {
// update slippage in parent, reset errors and input state
updateSlippage(slippage)
setWarningType(WARNING_TYPE.none)
setActiveIndex(index)
},
[updateSlippage]
)
useEffect(() => {
switch (initialSlippage) {
case 10:
setFromFixed(1, 0.1)
break
case 50:
setFromFixed(2, 0.5)
break
case 100:
setFromFixed(3, 1)
break
default:
// restrict to 2 decimal places
let acceptableValues = [/^$/, /^\d{1,2}$/, /^\d{0,2}\.\d{0,2}$/]
// if its within accepted decimal limit, update the input state
if (acceptableValues.some(val => val.test('' + (initialSlippage / 100)))) {
setUserInput('' + (initialSlippage / 100))
setActiveIndex(4)
}
}
}, [initialSlippage, setFromFixed])
const checkBounds = useCallback(
slippageValue => {
setWarningType(WARNING_TYPE.none)
@ -231,12 +188,60 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
[updateSlippage]
)
function parseCustomDeadline(e) {
const val = e.target.value
const acceptableValues = [/^$/, /^\d+$/]
if (acceptableValues.some(re => re.test(val))) {
setDeadlineInput(val)
setDeadline(val * 60)
}
}
const setFromCustom = () => {
setActiveIndex(4)
inputRef.current.focus()
// if there's a value, evaluate the bounds
checkBounds(debouncedInput)
}
// used for slippage presets
const setFromFixed = useCallback(
(index, slippage) => {
// update slippage in parent, reset errors and input state
updateSlippage(slippage)
setWarningType(WARNING_TYPE.none)
setActiveIndex(index)
},
[updateSlippage]
)
useEffect(() => {
switch (initialSlippage) {
case 10:
setFromFixed(1, 0.1)
break
case 50:
setFromFixed(2, 0.5)
break
case 100:
setFromFixed(3, 1)
break
default:
// restrict to 2 decimal places
const acceptableValues = [/^$/, /^\d{1,2}$/, /^\d{0,2}\.\d{0,2}$/]
// if its within accepted decimal limit, update the input state
if (acceptableValues.some(val => val.test('' + initialSlippage / 100))) {
setUserInput('' + initialSlippage / 100)
setActiveIndex(4)
}
}
}, [initialSlippage, setFromFixed])
// check that the theyve entered number and correct decimal
const parseInput = e => {
let input = e.target.value
const input = e.target.value
// restrict to 2 decimal places
let acceptableValues = [/^$/, /^\d{1,2}$/, /^\d{0,2}\.\d{0,2}$/]
const acceptableValues = [/^$/, /^\d{1,2}$/, /^\d{0,2}\.\d{0,2}$/]
// if its within accepted decimal limit, update the input state
if (acceptableValues.some(a => a.test(input))) {
setUserInput(input)
@ -313,8 +318,8 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
placeholder={
activeIndex === 4
? !!userInput
? ''
: '0'
? ''
: '0'
: activeIndex !== 4 && userInput !== ''
? userInput
: 'Custom'
@ -352,8 +357,8 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
: warningType !== WARNING_TYPE.none && warningType !== WARNING_TYPE.riskyEntryLow
? 'red'
: warningType === WARNING_TYPE.riskyEntryLow
? '#F3841E'
: ''
? '#F3841E'
: ''
}
>
{warningType === WARNING_TYPE.emptyInput && 'Enter a slippage percentage'}
@ -366,12 +371,16 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
<AutoColumn gap="sm">
<RowFixed padding={'0 20px'}>
<TYPE.body fontSize={14}>Deadline</TYPE.body>
<QuestionHelper text="Deadline in minutes. If your transaction takes longer than this it will revert."/>
<QuestionHelper text="Deadline in minutes. If your transaction takes longer than this it will revert." />
</RowFixed>
<RowFixed padding={'0 20px'}>
<OptionCustom style={{ width: '80px' }}>
<Input tabIndex={-1} placeholder={'' + deadlineInput} value={deadlineInput}
onChange={parseCustomDeadline}/>
<Input
tabIndex={-1}
placeholder={'' + deadlineInput}
value={deadlineInput}
onChange={parseCustomDeadline}
/>
</OptionCustom>
<TYPE.body style={{ paddingLeft: '8px' }} fontSize={14}>
minutes

@ -20,7 +20,7 @@ const Image = styled.img<{ size: string }>`
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
`
const Emoji = styled.span<{size?:string}>`
const Emoji = styled.span<{ size?: string }>`
display: flex;
align-items: center;
justify-content: center;
@ -30,7 +30,7 @@ const Emoji = styled.span<{size?:string}>`
margin-bottom: -4px;
`
const StyledEthereumLogo = styled(EthereumLogo)<{size: string}>`
const StyledEthereumLogo = styled(EthereumLogo)<{ size: string }>`
width: ${({ size }) => size};
height: ${({ size }) => size};
`

@ -28,7 +28,7 @@ const delay = 100
export default function TxnPopup({ hash, success, summary, popKey }) {
const { chainId } = useWeb3React()
let [count, setCount] = useState(1)
const [count, setCount] = useState(1)
const [isRunning, setIsRunning] = useState(true)
const [, , removePopup] = usePopups()
@ -44,9 +44,9 @@ export default function TxnPopup({ hash, success, summary, popKey }) {
return (
<AutoRow onMouseEnter={() => setIsRunning(false)} onMouseLeave={() => setIsRunning(true)}>
{success ? (
<CheckCircle color={'#27AE60'} size={24} style={{ paddingRight: '24px' }}/>
<CheckCircle color={'#27AE60'} size={24} style={{ paddingRight: '24px' }} />
) : (
<AlertCircle color={'#FF6871'} size={24} style={{ paddingRight: '24px' }}/>
<AlertCircle color={'#FF6871'} size={24} style={{ paddingRight: '24px' }} />
)}
<AutoColumn gap="8px">
<TYPE.body fontWeight={500}>
@ -54,7 +54,7 @@ export default function TxnPopup({ hash, success, summary, popKey }) {
</TYPE.body>
<Link href={getEtherscanLink(chainId, hash, 'transaction')}>View on Etherscan</Link>
</AutoColumn>
<Fader count={count}/>
<Fader count={count} />
</AutoRow>
)
}

@ -88,16 +88,16 @@ const IconWrapper = styled.div<{ size?: number }>`
`
export default function Option({
link = null,
clickable = true,
size = null,
onClick = null,
color,
header,
subheader = null,
icon,
active = false
}) {
link = null,
clickable = true,
size = null,
onClick = null,
color,
header,
subheader = null,
icon,
active = false
}) {
const content = (
<OptionCardClickable onClick={onClick} clickable={clickable && !active} active={active}>
<OptionCardLeft>
@ -106,7 +106,7 @@ export default function Option({
{active ? (
<CircleWrapper>
<GreenCircle>
<div/>
<div />
</GreenCircle>
</CircleWrapper>
) : (
@ -117,7 +117,7 @@ export default function Option({
{subheader && <SubHeader>{subheader}</SubHeader>}
</OptionCardLeft>
<IconWrapper size={size}>
<img src={icon} alt={'Icon'}/>
<img src={icon} alt={'Icon'} />
</IconWrapper>
</OptionCardClickable>
)

@ -75,10 +75,10 @@ export default function PendingView({ uri = '', size, connector, error = false,
return (
<PendingSection>
{!error && connector === walletconnect && <WalletConnectData size={size} uri={uri}/>}
{!error && connector === walletconnect && <WalletConnectData size={size} uri={uri} />}
<LoadingMessage error={error}>
<LoadingWrapper>
{!error && <SpinnerWrapper src={Circle}/>}
{!error && <SpinnerWrapper src={Circle} />}
{error ? (
<ErrorGroup>
<div>Error connecting.</div>

@ -21,7 +21,7 @@ export default function WalletConnectData({ uri = '', size }: WalletConnectDataP
return (
<QRCodeWrapper>
{uri && (
<QRCode size={size} value={uri} bgColor={isDark ? '#333639' : 'white'} fgColor={isDark ? 'white' : 'black'}/>
<QRCode size={size} value={uri} bgColor={isDark ? '#333639' : 'white'} fgColor={isDark ? 'white' : 'black'} />
)}
</QRCodeWrapper>
)

@ -286,7 +286,7 @@ export default function WalletModal({ pendingTransactions, confirmedTransactions
return (
<UpperSection>
<CloseIcon onClick={toggleWalletModal}>
<CloseColor/>
<CloseColor />
</CloseIcon>
<HeaderRow>{error instanceof UnsupportedChainIdError ? 'Wrong Network' : 'Error connecting'}</HeaderRow>
<ContentWrapper>
@ -313,7 +313,7 @@ export default function WalletModal({ pendingTransactions, confirmedTransactions
return (
<UpperSection>
<CloseIcon onClick={toggleWalletModal}>
<CloseColor/>
<CloseColor />
</CloseIcon>
{walletView !== WALLET_VIEWS.ACCOUNT ? (
<HeaderRow color="blue">
@ -358,12 +358,7 @@ export default function WalletModal({ pendingTransactions, confirmedTransactions
}
return (
<Modal
isOpen={walletModalOpen}
onDismiss={toggleWalletModal}
minHeight={null}
maxHeight={90}
>
<Modal isOpen={walletModalOpen} onDismiss={toggleWalletModal} minHeight={null} maxHeight={90}>
<Wrapper>{getModalContent()}</Wrapper>
</Modal>
)

@ -76,8 +76,8 @@ const Web3StatusConnect = styled(Web3StatusGeneric)`
}
${({ faded }) =>
faded &&
css`
faded &&
css`
background-color: ${({ theme }) => theme.blue5};
border: 1px solid ${({ theme }) => theme.blue5};
color: ${({ theme }) => theme.buttonSecondaryText};
@ -140,29 +140,29 @@ export default function Web3Status() {
// handle the logo we want to show with the account
function getStatusIcon() {
if (connector === injected) {
return <Identicon/>
return <Identicon />
} else if (connector === walletconnect) {
return (
<IconWrapper size={16}>
<img src={WalletConnectIcon} alt={''}/>
<img src={WalletConnectIcon} alt={''} />
</IconWrapper>
)
} else if (connector === walletlink) {
return (
<IconWrapper size={16}>
<img src={CoinbaseWalletIcon} alt={''}/>
<img src={CoinbaseWalletIcon} alt={''} />
</IconWrapper>
)
} else if (connector === fortmatic) {
return (
<IconWrapper size={16}>
<img src={FortmaticIcon} alt={''}/>
<img src={FortmaticIcon} alt={''} />
</IconWrapper>
)
} else if (connector === portis) {
return (
<IconWrapper size={16}>
<img src={PortisIcon} alt={''}/>
<img src={PortisIcon} alt={''} />
</IconWrapper>
)
}
@ -174,7 +174,7 @@ export default function Web3Status() {
<Web3StatusConnected onClick={toggleWalletModal} pending={hasPendingTransactions}>
{hasPendingTransactions ? (
<RowBetween>
<Text>{pending?.length} Pending</Text> <SpinnerWrapper src={LightCircle} alt="loader"/>
<Text>{pending?.length} Pending</Text> <SpinnerWrapper src={LightCircle} alt="loader" />
</RowBetween>
) : (
<Text>{ENSName || shortenAddress(account)}</Text>
@ -185,7 +185,7 @@ export default function Web3Status() {
} else if (error) {
return (
<Web3StatusError onClick={toggleWalletModal}>
<NetworkIcon/>
<NetworkIcon />
<Text>{error instanceof UnsupportedChainIdError ? 'Wrong Network' : 'Error'}</Text>
</Web3StatusError>
)
@ -205,7 +205,7 @@ export default function Web3Status() {
return (
<>
{getWeb3Status()}
<WalletModal ENSName={ENSName} pendingTransactions={pending} confirmedTransactions={confirmed}/>
<WalletModal ENSName={ENSName} pendingTransactions={pending} confirmedTransactions={confirmed} />
</>
)
}

@ -14,10 +14,7 @@ export class FortmaticConnector extends FortmaticConnectorCore {
if (!this.fortmatic) {
const { default: Fortmatic } = await import('fortmatic')
const { apiKey, chainId } = this as any
this.fortmatic = new Fortmatic(
apiKey,
chainId === 1 || chainId === 4 ? undefined : chainIdToNetwork[chainId]
)
this.fortmatic = new Fortmatic(apiKey, chainId === 1 || chainId === 4 ? undefined : chainIdToNetwork[chainId])
}
const provider = this.fortmatic.getProvider()

@ -3,13 +3,13 @@ import { NetworkConnector as NetworkConnectorCore } from '@web3-react/network-co
export class NetworkConnector extends NetworkConnectorCore {
pause() {
if ((this as any).active) {
(this as any).providers[(this as any).currentChainId].stop()
;(this as any).providers[(this as any).currentChainId].stop()
}
}
resume() {
if ((this as any).active) {
(this as any).providers[(this as any).currentChainId].start()
;(this as any).providers[(this as any).currentChainId].start()
}
}
}

@ -1,13 +1,27 @@
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { Token, TokenAmount, WETH } from '@uniswap/sdk'
import { BigintIsh, Token, TokenAmount, WETH } from '@uniswap/sdk'
import { BigNumber } from 'ethers/utils'
import React, { createContext, useCallback, useContext, useEffect, useMemo, useReducer } from 'react'
import { useWeb3React } from '../hooks'
import { safeAccess, isAddress, getTokenAllowance } from '../utils'
import { getTokenAllowance, isAddress } from '../utils'
import { useBlockNumber } from './Application'
const UPDATE = 'UPDATE'
const AllowancesContext = createContext([])
interface AllowancesState {
[chainId: number]: {
[address: string]: {
[tokenAddress: string]: {
[spenderAddress: string]: {
value: BigintIsh
blockNumber: BigNumber
}
}
}
}
}
const AllowancesContext = createContext<[AllowancesState, any]>([{}, {}])
function useAllowancesContext() {
return useContext(AllowancesContext)
@ -20,11 +34,11 @@ function reducer(state, { type, payload }) {
return {
...state,
[networkId]: {
...(safeAccess(state, [networkId]) || {}),
...state?.[networkId],
[address]: {
...(safeAccess(state, [networkId, address]) || {}),
...state?.[networkId]?.[address],
[tokenAddress]: {
...(safeAccess(state, [networkId, address, tokenAddress]) || {}),
...state?.[networkId]?.[address]?.[tokenAddress],
[spenderAddress]: {
value,
blockNumber
@ -34,9 +48,8 @@ function reducer(state, { type, payload }) {
}
}
}
default: {
default:
throw Error(`Unexpected action type in AllowancesContext reducer: '${type}'.`)
}
}
}
@ -60,7 +73,7 @@ export function useAddressAllowance(address: string, token: Token, spenderAddres
const globalBlockNumber = useBlockNumber()
const [state, { update }] = useAllowancesContext()
const { value, blockNumber } = safeAccess(state, [chainId, address, token?.address, spenderAddress]) || {}
const { value, blockNumber } = state?.[chainId]?.[address]?.[token?.address]?.[spenderAddress] ?? {}
useEffect(() => {
if (
@ -92,6 +105,5 @@ export function useAddressAllowance(address: string, token: Token, spenderAddres
}
}, [address, token, spenderAddress, value, blockNumber, globalBlockNumber, chainId, library, update])
const newTokenAmount: TokenAmount = value ? new TokenAmount(token, value) : null
return newTokenAmount
return value ? new TokenAmount(token, value) : null
}

@ -1,7 +1,6 @@
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { useWeb3React } from '../hooks'
import { safeAccess } from '../utils'
const BLOCK_NUMBER = 'BLOCK_NUMBER'
const USD_PRICE = 'USD_PRICE'
@ -17,22 +16,25 @@ const USER_ADVANCED = 'USER_ADVANCED'
const TOGGLE_USER_ADVANCED = 'TOGGLE_USER_ADVANCED'
interface ApplicationState {
BLOCK_NUMBER: {},
USD_PRICE: {},
POPUP_LIST: Array<{ key: number; show: boolean; content: React.ReactElement }>,
POPUP_KEY: number,
WALLET_MODAL_OPEN: boolean,
BLOCK_NUMBER: {}
USD_PRICE: {}
POPUP_LIST: Array<{ key: number; show: boolean; content: React.ReactElement }>
POPUP_KEY: number
WALLET_MODAL_OPEN: boolean
USER_ADVANCED: boolean
}
const ApplicationContext = createContext<[ApplicationState, { [updater: string]: (...args: any[]) => void }]>([{
[BLOCK_NUMBER]: {},
[USD_PRICE]: {},
[POPUP_LIST]: [],
[POPUP_KEY]: 0,
[WALLET_MODAL_OPEN]: false,
[USER_ADVANCED]: false
}, {}])
const ApplicationContext = createContext<[ApplicationState, { [updater: string]: (...args: any[]) => void }]>([
{
[BLOCK_NUMBER]: {},
[USD_PRICE]: {},
[POPUP_LIST]: [],
[POPUP_KEY]: 0,
[WALLET_MODAL_OPEN]: false,
[USER_ADVANCED]: false
},
{}
])
function useApplicationContext() {
return useContext(ApplicationContext)
@ -45,7 +47,7 @@ function reducer(state: ApplicationState, { type, payload }): ApplicationState {
return {
...state,
[BLOCK_NUMBER]: {
...(safeAccess(state, [BLOCK_NUMBER]) || {}),
...state?.[BLOCK_NUMBER],
[networkId]: blockNumber
}
}
@ -80,9 +82,12 @@ export default function Provider({ children }) {
[USER_ADVANCED]: false
})
const updateBlockNumber = useCallback((networkId, blockNumber) => {
dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } })
}, [dispatch])
const updateBlockNumber = useCallback(
(networkId, blockNumber) => {
dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } })
},
[dispatch]
)
const toggleWalletModal = useCallback(() => {
dispatch({ type: TOGGLE_WALLET_MODAL, payload: null })
@ -92,9 +97,12 @@ export default function Provider({ children }) {
dispatch({ type: TOGGLE_USER_ADVANCED, payload: null })
}, [dispatch])
const setPopups = useCallback(newList => {
dispatch({ type: ADD_POPUP, payload: { newList } })
}, [dispatch])
const setPopups = useCallback(
newList => {
dispatch({ type: ADD_POPUP, payload: { newList } })
},
[dispatch]
)
return (
<ApplicationContext.Provider
@ -154,7 +162,7 @@ export function useBlockNumber() {
const [state] = useApplicationContext()
return safeAccess(state, [BLOCK_NUMBER, chainId])
return state?.[BLOCK_NUMBER]?.[chainId]
}
export function useWalletModalOpen() {
@ -180,7 +188,11 @@ export function useToggleUserAdvanced() {
return toggleUserAdvanced
}
export function usePopups(): [ApplicationState['POPUP_LIST'], (content: React.ReactElement) => void, (key: number) => void] {
export function usePopups(): [
ApplicationState['POPUP_LIST'],
(content: React.ReactElement) => void,
(key: number) => void
] {
const [state, { setPopups }] = useApplicationContext()
const index = state[POPUP_KEY]

@ -411,7 +411,7 @@ export function useAllBalances(): Array<TokenAmount> {
if (!state || !state[chainId]) {
return {}
} else {
let newBalances = {}
const newBalances = {}
Object.keys(state[chainId]).map(address => {
return Object.keys(state[chainId][address]).map(tokenAddress => {
if (state[chainId][address][tokenAddress].value) {

@ -3,21 +3,23 @@ import React, { createContext, useContext, useReducer, useMemo, useCallback, use
import TxnPopup from '../components/TxnPopup'
import { useWeb3React } from '../hooks'
import { safeAccess } from '../utils'
import { useBlockNumber, usePopups } from './Application'
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'
const FINALIZE = 'FINALIZE'
interface TransactionState {
[chainId: number]: {
[txHash: string]: {
blockNumberChecked: any
response: {
customData?: any
summary: any
}
receipt: any
}
}
}
const TransactionsContext = createContext<[TransactionState, { [updater: string]: (...args: any[]) => void }]>([{}, {}])
@ -26,21 +28,21 @@ export function useTransactionsContext() {
return useContext(TransactionsContext)
}
function reducer(state, { type, payload }) {
function reducer(state: TransactionState, { type, payload }): TransactionState {
switch (type) {
case ADD: {
const { networkId, hash, response } = payload
if (safeAccess(state, [networkId, hash]) !== null) {
if (state[networkId]?.[hash]) {
throw Error('Attempted to add existing transaction.')
}
return {
...state,
[networkId]: {
...(safeAccess(state, [networkId]) || {}),
...state[networkId],
[hash]: {
[RESPONSE]: response
response
}
}
}
@ -48,17 +50,17 @@ function reducer(state, { type, payload }) {
case CHECK: {
const { networkId, hash, blockNumber } = payload
if (safeAccess(state, [networkId, hash]) === null) {
if (!state[networkId]?.[hash]) {
throw Error('Attempted to check non-existent transaction.')
}
return {
...state,
[networkId]: {
...(safeAccess(state, [networkId]) || {}),
...state[networkId],
[hash]: {
...(safeAccess(state, [networkId, hash]) || {}),
[BLOCK_NUMBER_CHECKED]: blockNumber
...state[networkId]?.[hash],
blockNumberChecked: blockNumber
}
}
}
@ -66,17 +68,17 @@ function reducer(state, { type, payload }) {
case FINALIZE: {
const { networkId, hash, receipt } = payload
if (safeAccess(state, [networkId, hash]) === null) {
if (!state[networkId]?.[hash]) {
throw Error('Attempted to finalize non-existent transaction.')
}
return {
...state,
[networkId]: {
...(safeAccess(state, [networkId]) || {}),
...state[networkId],
[hash]: {
...(safeAccess(state, [networkId, hash]) || {}),
[RECEIPT]: receipt
...state[networkId]?.[hash],
receipt
}
}
}
@ -115,7 +117,7 @@ export function Updater() {
const globalBlockNumber = useBlockNumber()
const [state, { check, finalize }] = useTransactionsContext()
const allTransactions = safeAccess(state, [chainId]) || {}
const allTransactions = state[chainId] ?? {}
// show popup on confirm
const [, addPopup] = usePopups()
@ -125,7 +127,7 @@ export function Updater() {
let stale = false
Object.keys(allTransactions)
.filter(
hash => !allTransactions[hash][RECEIPT] && allTransactions[hash][BLOCK_NUMBER_CHECKED] !== globalBlockNumber
hash => !allTransactions[hash].receipt && allTransactions[hash].blockNumberChecked !== globalBlockNumber
)
.forEach(hash => {
library
@ -138,12 +140,22 @@ export function Updater() {
finalize(chainId, hash, receipt)
// add success or failure popup
if (receipt.status === 1) {
addPopup(<TxnPopup popKey={1} hash={hash} success={true}
summary={allTransactions[hash]?.response?.summary}/>)
addPopup(
<TxnPopup
popKey={1}
hash={hash}
success={true}
summary={allTransactions[hash]?.response?.summary}
/>
)
} else {
addPopup(
<TxnPopup popKey={2} hash={hash} success={false}
summary={allTransactions[hash]?.response?.summary}/>
<TxnPopup
popKey={2}
hash={hash}
success={false}
summary={allTransactions[hash]?.response?.summary}
/>
)
}
}
@ -173,11 +185,11 @@ export function useTransactionAdder() {
if (!(chainId || chainId === 0)) {
throw Error(`Invalid networkId '${chainId}`)
}
const hash = safeAccess(response, ['hash'])
const hash = response?.hash
if (!hash) {
throw Error('No transaction hash found.')
}
add(chainId, hash, { ...response, [CUSTOM_DATA]: customData, [SUMMARY]: summary })
add(chainId, hash, { ...response, customData: customData, summary })
},
[chainId, add]
)
@ -188,18 +200,18 @@ export function useAllTransactions() {
const [state] = useTransactionsContext()
return safeAccess(state, [chainId]) || {}
return state[chainId] || {}
}
export function usePendingApproval(tokenAddress) {
const allTransactions = useAllTransactions()
return (
Object.keys(allTransactions).filter(hash => {
if (allTransactions[hash][RECEIPT]) {
if (allTransactions[hash]?.receipt) {
return false
} else if (!allTransactions[hash][RESPONSE]) {
} else if (!allTransactions[hash]?.response) {
return false
} else if (allTransactions[hash][RESPONSE][CUSTOM_DATA].approval !== tokenAddress) {
} else if (allTransactions[hash]?.response?.customData?.approval !== tokenAddress) {
return false
} else {
return true

@ -6,8 +6,6 @@ export default function useInterval(callback: () => void, delay: null | number)
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback
return () => {
}
}, [callback])
// Set up the interval.
@ -17,10 +15,8 @@ export default function useInterval(callback: () => void, delay: null | number)
}
if (delay !== null) {
let id = setInterval(tick, delay)
const id = setInterval(tick, delay)
return () => clearInterval(id)
}
return () => {
}
}, [delay])
}

@ -99,21 +99,21 @@ export default function App() {
<BrowserRouter>
<AppWrapper>
<HeaderWrapper>
<Header/>
<Header />
</HeaderWrapper>
<BodyWrapper>
<Popups/>
<Popups />
<Body>
<Web3ReactManager>
<NavigationTabs/>
<NavigationTabs />
{/* this Suspense is for route code-splitting */}
<Switch>
<Route exact strict path="/" render={() => <Redirect to="/swap"/>}/>
<Route exact strict path="/swap" component={() => <Swap params={params}/>}/>
<Route exact strict path="/send" component={() => <Send params={params}/>}/>
<Route exact strict path="/find" component={() => <Find/>}/>
<Route exact strict path="/create" component={() => <Create/>}/>
<Route exact strict path="/pool" component={() => <Pool/>}/>
<Route exact strict path="/" render={() => <Redirect to="/swap" />} />
<Route exact strict path="/swap" component={() => <Swap params={params} />} />
<Route exact strict path="/send" component={() => <Send params={params} />} />
<Route exact strict path="/find" component={() => <Find />} />
<Route exact strict path="/create" component={() => <Create />} />
<Route exact strict path="/pool" component={() => <Pool />} />
<Route
exact
strict
@ -127,7 +127,7 @@ export default function App() {
if (t0 && t1) {
return <Add token0={t0} token1={t1} />
} else {
return <Redirect to="/pool"/>
return <Redirect to="/pool" />
}
}}
/>
@ -142,19 +142,19 @@ export default function App() {
const t1 =
tokens?.[1] === 'ETH' ? 'ETH' : isAddress(tokens?.[1]) ? isAddress(tokens[1]) : undefined
if (t0 && t1) {
return <Remove token0={t0} token1={t1}/>
return <Remove token0={t0} token1={t1} />
} else {
return <Redirect to="/pool"/>
return <Redirect to="/pool" />
}
}}
/>
<Redirect to="/"/>
<Redirect to="/" />
</Switch>
</Web3ReactManager>
</Body>
<Footer/>
<Footer />
</BodyWrapper>
<StyledRed/>
<StyledRed />
</AppWrapper>
</BrowserRouter>
</Suspense>

@ -675,7 +675,7 @@ function AddLiquidity({ token0, token1 }: AddLiquidityProps) {
)
}
const pendingText: string = `Supplying ${parsedAmounts[Field.INPUT]?.toSignificant(6)} ${
const pendingText = `Supplying ${parsedAmounts[Field.INPUT]?.toSignificant(6)} ${
tokens[Field.INPUT]?.symbol
} ${'and'} ${parsedAmounts[Field.OUTPUT]?.toSignificant(6)} ${tokens[Field.OUTPUT]?.symbol}`

@ -179,15 +179,15 @@ export default function RemoveLiquidity({ token0, token1 }) {
const TokensDeposited: { [field: number]: TokenAmount } = {
[Field.TOKEN0]:
pair &&
totalPoolTokens &&
userLiquidity &&
pair.getLiquidityValue(tokens[Field.TOKEN0], totalPoolTokens, userLiquidity, false),
pair &&
totalPoolTokens &&
userLiquidity &&
pair.getLiquidityValue(tokens[Field.TOKEN0], totalPoolTokens, userLiquidity, false),
[Field.TOKEN1]:
pair &&
totalPoolTokens &&
userLiquidity &&
pair.getLiquidityValue(tokens[Field.TOKEN1], totalPoolTokens, userLiquidity, false)
pair &&
totalPoolTokens &&
userLiquidity &&
pair.getLiquidityValue(tokens[Field.TOKEN1], totalPoolTokens, userLiquidity, false)
}
const route: Route = pair
@ -282,7 +282,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
)
}
const handleSliderChange = (newPercent) => {
const handleSliderChange = newPercent => {
onUserInput(
Field.LIQUIDITY,
new TokenAmount(
@ -425,6 +425,13 @@ export default function RemoveLiquidity({ token0, token1 }) {
})
}
function resetModalState() {
setSigned(false)
setSigInputs(null)
setAttemptedRemoval(false)
setPendingConfirmation(true)
}
async function onRemove() {
setAttemptedRemoval(true)
const router = getRouterContract(chainId, library, account)
@ -482,13 +489,13 @@ export default function RemoveLiquidity({ token0, token1 }) {
addTransaction(
response,
'Remove ' +
parsedAmounts[Field.TOKEN0]?.toSignificant(3) +
' ' +
tokens[Field.TOKEN0]?.symbol +
' and ' +
parsedAmounts[Field.TOKEN1]?.toSignificant(3) +
' ' +
tokens[Field.TOKEN1]?.symbol
parsedAmounts[Field.TOKEN0]?.toSignificant(3) +
' ' +
tokens[Field.TOKEN0]?.symbol +
' and ' +
parsedAmounts[Field.TOKEN1]?.toSignificant(3) +
' ' +
tokens[Field.TOKEN1]?.symbol
)
})
)
@ -499,13 +506,6 @@ export default function RemoveLiquidity({ token0, token1 }) {
})
}
function resetModalState() {
setSigned(false)
setSigInputs(null)
setAttemptedRemoval(false)
setPendingConfirmation(true)
}
function modalHeader() {
return (
<AutoColumn gap={'sm'} style={{ marginTop: '20px' }}>
@ -514,21 +514,21 @@ export default function RemoveLiquidity({ token0, token1 }) {
{!!parsedAmounts[Field.TOKEN0] && parsedAmounts[Field.TOKEN0].toSignificant(6)}
</Text>
<RowFixed gap="4px">
<TokenLogo address={tokens[Field.TOKEN0]?.address} size={'24px'}/>
<TokenLogo address={tokens[Field.TOKEN0]?.address} size={'24px'} />
<Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
{tokens[Field.TOKEN0]?.symbol || ''}
</Text>
</RowFixed>
</RowBetween>
<RowFixed>
<Plus size="16" color={'#888D9B'}/>
<Plus size="16" color={'#888D9B'} />
</RowFixed>
<RowBetween align="flex-end">
<Text fontSize={24} fontWeight={600}>
{!!parsedAmounts[Field.TOKEN1] && parsedAmounts[Field.TOKEN1].toSignificant(6)}
</Text>
<RowFixed gap="4px">
<TokenLogo address={tokens[Field.TOKEN1]?.address} size={'24px'}/>
<TokenLogo address={tokens[Field.TOKEN1]?.address} size={'24px'} />
<Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
{tokens[Field.TOKEN1]?.symbol || ''}
</Text>
@ -596,7 +596,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
)
}
const pendingText: string = `Removing ${parsedAmounts[Field.TOKEN0]?.toSignificant(6)} ${
const pendingText = `Removing ${parsedAmounts[Field.TOKEN0]?.toSignificant(6)} ${
tokens[Field.TOKEN0]?.symbol
} and ${parsedAmounts[Field.TOKEN1]?.toSignificant(6)} ${tokens[Field.TOKEN1]?.symbol}`
@ -637,7 +637,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
</Text>
</Row>
{!showAdvanced && (
<Slider value={parseFloat(derivedPerecent)} onChange={handleSliderChange} override={override}/>
<Slider value={parseFloat(derivedPerecent)} onChange={handleSliderChange} override={override} />
)}
{!showAdvanced && (
<RowBetween>
@ -660,7 +660,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
{!showAdvanced && (
<>
<ColumnCenter>
<ArrowDown size="16" color="#888D9B"/>
<ArrowDown size="16" color="#888D9B" />
</ColumnCenter>{' '}
<LightCard>
<AutoColumn gap="10px">
@ -669,7 +669,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
{formattedAmounts[Field.TOKEN0] ? formattedAmounts[Field.TOKEN0] : '-'}
</Text>
<RowFixed>
<TokenLogo address={tokens[Field.TOKEN0]?.address || ''} style={{ marginRight: '12px' }}/>
<TokenLogo address={tokens[Field.TOKEN0]?.address || ''} style={{ marginRight: '12px' }} />
<Text fontSize={24} fontWeight={500}>
{tokens[Field.TOKEN0]?.symbol}
</Text>
@ -680,7 +680,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
{formattedAmounts[Field.TOKEN1] ? formattedAmounts[Field.TOKEN1] : '-'}
</Text>
<RowFixed>
<TokenLogo address={tokens[Field.TOKEN1]?.address || ''} style={{ marginRight: '12px' }}/>
<TokenLogo address={tokens[Field.TOKEN1]?.address || ''} style={{ marginRight: '12px' }} />
<Text fontSize={24} fontWeight={500}>
{tokens[Field.TOKEN1]?.symbol}
</Text>
@ -706,7 +706,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
inputId="liquidityAmount"
/>
<ColumnCenter>
<ArrowDown size="16" color="#888D9B"/>
<ArrowDown size="16" color="#888D9B" />
</ColumnCenter>
<CurrencyInputPanel
field={Field.TOKEN0}
@ -720,7 +720,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
inputId="removeLiquidityToken0"
/>
<ColumnCenter>
<Plus size="16" color="#888D9B"/>
<Plus size="16" color="#888D9B" />
</ColumnCenter>
<CurrencyInputPanel
field={Field.TOKEN1}

@ -1,7 +1,7 @@
import React, { useState, useContext } from 'react'
import styled, { ThemeContext } from 'styled-components'
import { JSBI } from '@uniswap/sdk'
import { withRouter } from 'react-router-dom'
import { RouteComponentProps, withRouter } from 'react-router-dom'
import Question from '../../components/Question'
import SearchModal from '../../components/SearchModal'
@ -29,7 +29,7 @@ const FixedBottom = styled.div`
width: 100%;
`
function Supply({ history }) {
function Supply({ history }: RouteComponentProps) {
const { account } = useWeb3React()
const [showPoolSearch, setShowPoolSearch] = useState(false)
@ -42,7 +42,7 @@ function Supply({ history }) {
useAccountLPBalances(account)
const filteredExchangeList = Object.keys(allPairs)
.filter((pairAddress, i) => {
.filter(pairAddress => {
return (
allBalances &&
allBalances[account] &&

@ -1,6 +1,7 @@
import React from 'react'
import ExchangePage from '../../components/ExchangePage'
import { QueryParams } from '../../utils'
export default function Swap({ params }) {
export default function Swap({ params }: { params: QueryParams }) {
return <ExchangePage sendingInput={false} params={params} />
}

@ -2,10 +2,9 @@ import styled, { keyframes } from 'styled-components'
import { darken } from 'polished'
import { X } from 'react-feather'
export const Button = styled.button.attrs<{ warning: boolean }, { backgroundColor: string }>(
({ warning, theme }) => ({
backgroundColor: warning ? theme.red1 : theme.blue1
}))`
export const Button = styled.button.attrs<{ warning: boolean }, { backgroundColor: string }>(({ warning, theme }) => ({
backgroundColor: warning ? theme.red1 : theme.blue1
}))`
padding: 1rem 2rem 1rem 2rem;
border-radius: 3rem;
cursor: pointer;
@ -33,7 +32,7 @@ export const Button = styled.button.attrs<{ warning: boolean }, { backgroundColo
}
`
export const CloseIcon = styled(X)<{ onClick: () => void}>`
export const CloseIcon = styled(X)<{ onClick: () => void }>`
cursor: pointer;
`

@ -3,12 +3,12 @@ import styled, {
ThemeProvider as StyledComponentsThemeProvider,
createGlobalStyle,
css,
DefaultTheme
} from 'styled-components'
import { getQueryParam, checkSupportedTheme } from '../utils'
import { SUPPORTED_THEMES } from '../constants'
import { useDarkModeManager } from '../contexts/LocalStorage'
import { Text } from 'rebass'
import { UniswapTheme } from './styled'
export * from './components'
@ -19,20 +19,95 @@ const MEDIA_WIDTHS = {
upToLarge: 1280
}
const mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } =
Object.keys(MEDIA_WIDTHS)
.reduce((accumulator, size) => {
accumulator[size] = (a, b, c) => css`
@media (max-width: ${MEDIA_WIDTHS[size]}px) {
${css(a, b, c)}
}
`
return accumulator
}, {}) as any
const mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys(MEDIA_WIDTHS).reduce(
(accumulator, size) => {
accumulator[size] = (a, b, c) => css`
@media (max-width: ${MEDIA_WIDTHS[size]}px) {
${css(a, b, c)}
}
`
return accumulator
},
{}
) as any
const white = '#FFFFFF'
const black = '#000000'
export function theme(darkMode: boolean): DefaultTheme {
return {
// base
white,
black,
// text
text1: darkMode ? '#FFFFFF' : '#000000',
text2: darkMode ? '#CED0D9' : '#565A69',
text3: darkMode ? '#6C7284' : '#888D9B',
text4: darkMode ? '#565A69' : '#C3C5CB',
text5: '#EDEEF2',
// backgrounds / greys
bg1: darkMode ? '#212429' : '#FFFFFF',
bg2: darkMode ? '#2C2F36' : '#F7F8FA',
bg3: darkMode ? '#40444F' : '#EDEEF2',
bg4: darkMode ? '#565A69' : '#CED0D9',
bg5: darkMode ? '#565A69' : '#888D9B',
modalBG: darkMode ? 'rgba(0,0,0,0.85)' : 'rgba(0,0,0,0.6)',
advancedBG: darkMode ? 'rgba(0,0,0,0.15)' : 'rgba(255,255,255,0.6)',
//blues
blue1: darkMode ? '#2172E5' : '#ff007a',
blue2: darkMode ? '#3680E7' : '#1966D2',
blue3: darkMode ? '#4D8FEA' : '#165BBB',
// blue4: darkMode ? '#153d6f70' : '#C4D9F8',
// blue5: darkMode ? '#153d6f70' : '#EBF4FF',
blue4: darkMode ? '#153d6f70' : '#F6DDE8',
blue5: darkMode ? '#153d6f70' : '#FDEAF1',
buttonSecondaryText: darkMode ? '#6da8ff' : '#ff007a',
// blue1: '#ff007a',
// blue4: '#F6DDE8',
// blue5: '#FDEAF1',
// pinks
pink1: '#DC6BE5',
pink2: darkMode ? '#2172E5' : '#ff007a',
pink3: darkMode ? '#17000b26' : '#F6DDE8',
pink4: darkMode ? '#17000b26' : '#FDEAF1',
// other
red1: '#FF6871',
green1: '#27AE60',
yellow1: '#FFE270',
yellow2: '#F3841E',
grids: {
sm: 8,
md: 12,
lg: 24
},
//shadows
shadow1: darkMode ? '#000' : '#2F80ED',
// media queries
mediaWidth: mediaWidthTemplates,
// css snippets
flexColumnNoWrap: css`
display: flex;
flex-flow: column nowrap;
`,
flexRowNoWrap: css`
display: flex;
flex-flow: row nowrap;
`
}
}
export default function ThemeProvider({ children }) {
const [darkMode, toggleDarkMode] = useDarkModeManager()
const themeURL = checkSupportedTheme(getQueryParam(window.location, 'theme'))
@ -40,8 +115,8 @@ export default function ThemeProvider({ children }) {
? themeURL.toUpperCase() === SUPPORTED_THEMES.DARK
? true
: themeURL.toUpperCase() === SUPPORTED_THEMES.LIGHT
? false
: darkMode
? false
: darkMode
: darkMode
useEffect(() => {
toggleDarkMode(themeToRender)
@ -49,78 +124,6 @@ export default function ThemeProvider({ children }) {
return <StyledComponentsThemeProvider theme={theme(themeToRender)}>{children}</StyledComponentsThemeProvider>
}
export const theme: (darkMode: boolean) => UniswapTheme = darkMode => ({
// base
white,
black,
// text
text1: darkMode ? '#FFFFFF' : '#000000',
text2: darkMode ? '#CED0D9' : '#565A69',
text3: darkMode ? '#6C7284' : '#888D9B',
text4: darkMode ? '#565A69' : '#C3C5CB',
text5: '#EDEEF2',
// backgrounds / greys
bg1: darkMode ? '#212429' : '#FFFFFF',
bg2: darkMode ? '#2C2F36' : '#F7F8FA',
bg3: darkMode ? '#40444F' : '#EDEEF2',
bg4: darkMode ? '#565A69' : '#CED0D9',
bg5: darkMode ? '#565A69' : '#888D9B',
modalBG: darkMode ? 'rgba(0,0,0,0.85)' : 'rgba(0,0,0,0.6)',
advancedBG: darkMode ? 'rgba(0,0,0,0.15)' : 'rgba(255,255,255,0.6)',
//blues
blue1: darkMode ? '#2172E5' : '#ff007a',
blue2: darkMode ? '#3680E7' : '#1966D2',
blue3: darkMode ? '#4D8FEA' : '#165BBB',
// blue4: darkMode ? '#153d6f70' : '#C4D9F8',
// blue5: darkMode ? '#153d6f70' : '#EBF4FF',
blue4: darkMode ? '#153d6f70' : '#F6DDE8',
blue5: darkMode ? '#153d6f70' : '#FDEAF1',
buttonSecondaryText: darkMode ? '#6da8ff' : '#ff007a',
// blue1: '#ff007a',
// blue4: '#F6DDE8',
// blue5: '#FDEAF1',
// pinks
pink1: '#DC6BE5',
pink2: darkMode ? '#2172E5' : '#ff007a',
pink3: darkMode ? '#17000b26' : '#F6DDE8',
pink4: darkMode ? '#17000b26' : '#FDEAF1',
// other
red1: '#FF6871',
green1: '#27AE60',
yellow1: '#FFE270',
yellow2: '#F3841E',
grids: {
sm: 8,
md: 12,
lg: 24
},
//shadows
shadow1: darkMode ? '#000' : '#2F80ED',
// media queries
mediaWidth: mediaWidthTemplates,
// css snippets
flexColumnNoWrap: css`
display: flex;
flex-flow: column nowrap;
`,
flexRowNoWrap: css`
display: flex;
flex-flow: row nowrap;
`
})
const TextWrapper = styled(Text)`
color = ${({ color, theme }) => theme[color]}
`

118
src/theme/styled.d.ts vendored

@ -1,66 +1,62 @@
import { css, FlattenSimpleInterpolation } from 'styled-components'
export interface UniswapTheme {
// base
white: string
black: string
// text
text1: string
text2: string
text3: string
text4: string
text5: string
// backgrounds / greys
bg1: string
bg2: string
bg3: string
bg4: string
bg5: string
modalBG: string
advancedBG: string
//blues
blue1: string
blue2: string
blue3: string
blue4: string
blue5: string
buttonSecondaryText: string
// pinks
pink1: string
pink2: string
pink3: string
pink4: string
// other
red1: string
green1: string
yellow1: string
yellow2: string
grids: {
sm: number,
md: number,
lg: number
},
// shadows
shadow1: string
// media queries
mediaWidth: { [width in keyof typeof MEDIA_WIDTHS]: typeof css },
// css snippets
flexColumnNoWrap: FlattenSimpleInterpolation
flexRowNoWrap: FlattenSimpleInterpolation
}
declare module 'styled-components' {
export interface DefaultTheme extends UniswapTheme {
export interface DefaultTheme {
// base
white: string
black: string
// text
text1: string
text2: string
text3: string
text4: string
text5: string
// backgrounds / greys
bg1: string
bg2: string
bg3: string
bg4: string
bg5: string
modalBG: string
advancedBG: string
//blues
blue1: string
blue2: string
blue3: string
blue4: string
blue5: string
buttonSecondaryText: string
// pinks
pink1: string
pink2: string
pink3: string
pink4: string
// other
red1: string
green1: string
yellow1: string
yellow2: string
grids: {
sm: number
md: number
lg: number
}
// shadows
shadow1: string
// media queries
mediaWidth: { [width in keyof typeof MEDIA_WIDTHS]: typeof css }
// css snippets
flexColumnNoWrap: FlattenSimpleInterpolation
flexRowNoWrap: FlattenSimpleInterpolation
}
}

@ -12,20 +12,19 @@ import { ROUTER_ADDRESS, SUPPORTED_THEMES } from '../constants'
import ERC20_ABI from '../constants/abis/erc20.json'
import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32.json'
export function isAddress(value: any): string | false {
try {
return getAddress(value.toLowerCase())
} catch {
return false
}
}
export enum ERROR_CODES {
TOKEN_SYMBOL = 1,
TOKEN_DECIMALS = 2
}
export function safeAccess(object, path) {
return object
? path.reduce(
(accumulator, currentValue) => (accumulator && accumulator[currentValue] ? accumulator[currentValue] : null),
object
)
: null
}
const ETHERSCAN_PREFIXES = {
1: '',
3: 'ropsten.',
@ -49,7 +48,7 @@ export function getEtherscanLink(networkId: 1 | 3 | 4 | 5 | 42 | any, data: stri
}
export function getQueryParam(windowLocation, name) {
var q = windowLocation.search.match(new RegExp('[?&]' + name + '=([^&#?]*)'))
const q = windowLocation.search.match(new RegExp('[?&]' + name + '=([^&#?]*)'))
return q && q[1]
}
@ -122,18 +121,6 @@ export function shortenAddress(address, digits = 4) {
return `${address.substring(0, digits + 2)}...${address.substring(42 - digits)}`
}
export function shortenTransactionHash(hash, digits = 4) {
return `${hash.substring(0, digits + 2)}...${hash.substring(66 - digits)}`
}
export function isAddress(value: any): string | false {
try {
return getAddress(value.toLowerCase())
} catch {
return false
}
}
export function calculateGasMargin(value: BigNumber) {
return value.mul(BigNumber.from(10000).add(BigNumber.from(1000))).div(BigNumber.from(10000)) // add 10%
}

@ -20,7 +20,8 @@
"downlevelIteration": true,
"allowSyntheticDefaultImports": true,
"types": [
"react-spring"
"react-spring",
"jest"
]
},
"exclude": [

@ -2630,7 +2630,7 @@
dependencies:
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^2.10.0":
"@typescript-eslint/eslint-plugin@^2.10.0", "@typescript-eslint/eslint-plugin@^2.31.0":
version "2.31.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.31.0.tgz#942c921fec5e200b79593c71fafb1e3f57aa2e36"
integrity sha512-iIC0Pb8qDaoit+m80Ln/aaeu9zKQdOLF4SHcGLarSeY1gurW6aU4JsOPMjKQwXlw70MvWKZQc6S2NamA8SJ/gg==
@ -2650,7 +2650,7 @@
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/parser@^2.10.0":
"@typescript-eslint/parser@^2.10.0", "@typescript-eslint/parser@^2.31.0":
version "2.31.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.31.0.tgz#beddd4e8efe64995108b229b2862cd5752d40d6f"
integrity sha512-uph+w6xUOlyV2DLSC6o+fBDzZ5i7+3/TxAsH4h3eC64tlga57oMb96vVlXoMwjR/nN+xyWlsnxtbDkB46M2EPQ==
@ -6772,6 +6772,13 @@ escodegen@^1.11.0, escodegen@^1.9.1:
optionalDependencies:
source-map "~0.6.1"
eslint-config-prettier@^6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1"
integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==
dependencies:
get-stdin "^6.0.0"
eslint-config-react-app@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz#698bf7aeee27f0cea0139eaef261c7bf7dd623df"
@ -6846,12 +6853,19 @@ eslint-plugin-jsx-a11y@6.2.3:
has "^1.0.3"
jsx-ast-utils "^2.2.1"
eslint-plugin-prettier@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca"
integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ==
dependencies:
prettier-linter-helpers "^1.0.0"
eslint-plugin-react-hooks@^1.6.1:
version "1.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04"
integrity sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==
eslint-plugin-react@7.19.0:
eslint-plugin-react@7.19.0, eslint-plugin-react@^7.19.0:
version "7.19.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz#6d08f9673628aa69c5559d33489e855d83551666"
integrity sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ==
@ -6904,7 +6918,7 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
eslint@^6.6.0:
eslint@^6.6.0, eslint@^6.8.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb"
integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==
@ -7800,6 +7814,11 @@ fast-deep-equal@^3.1.1:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
fast-diff@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
fast-glob@^2.0.2:
version "2.2.7"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
@ -8376,6 +8395,11 @@ get-own-enumerable-property-symbols@^3.0.0:
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==
get-stdin@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
get-stream@^2.2.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
@ -13448,6 +13472,13 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
prettier-linter-helpers@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
dependencies:
fast-diff "^1.1.2"
prettier@^1.17.0:
version "1.19.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"