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
- run: yarn test - 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} * @type {Cypress.PluginConfig}
*/ */
module.exports = (on, config) => { module.exports = () => {
// `on` is used to hook into various events Cypress emits // `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config // `config` is the resolved Cypress config
} }

@ -57,7 +57,13 @@
"@types/react-dom": "^16.9.7", "@types/react-dom": "^16.9.7",
"@types/react-router-dom": "^5.0.0", "@types/react-router-dom": "^5.0.0",
"@types/styled-components": "^4.2.0", "@types/styled-components": "^4.2.0",
"@typescript-eslint/eslint-plugin": "^2.31.0",
"@typescript-eslint/parser": "^2.31.0",
"cypress": "^4.5.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", "prettier": "^1.17.0",
"serve": "^11.3.0", "serve": "^11.3.0",
"start-server-and-test": "^1.11.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", "build": "cross-env REACT_APP_GIT_COMMIT_HASH=$(git show -s --format=%H) react-scripts build",
"test": "react-scripts test --env=jsdom", "test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"lint:base": "yarn eslint './src/**/*.{js,jsx}'", "lint": "eslint '**/*.{js,jsx,ts,tsx}'",
"format:base": "yarn prettier './src/**/*.{js,jsx,scss}'", "lint:fix": "eslint '**/*.{js,jsx,ts,tsx}' --fix",
"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",
"cy:run": "cypress run", "cy:run": "cypress run",
"serve:build": "serve -s build -l 3000", "serve:build": "serve -s build -l 3000",
"integration-test": "yarn build && start-server-and-test 'yarn run serve:build' http://localhost:3000 cy:run" "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} {props.children}
{isCopied ? ( {isCopied ? (
<TransactionStatusText> <TransactionStatusText>
<CheckCircle size={'16'}/> <CheckCircle size={'16'} />
<TransactionStatusText>Copied</TransactionStatusText> <TransactionStatusText>Copied</TransactionStatusText>
</TransactionStatusText> </TransactionStatusText>
) : ( ) : (
<TransactionStatusText> <TransactionStatusText>
<Copy size={'16'}/> <Copy size={'16'} />
</TransactionStatusText> </TransactionStatusText>
)} )}
</CopyIcon> </CopyIcon>

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

@ -63,7 +63,15 @@ const Input = styled.input<{ error: boolean }>`
// border-radius: 8px; // 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 { library } = useWeb3React()
const [input, setInput] = useState(initialInput ? initialInput : '') const [input, setInput] = useState(initialInput ? initialInput : '')

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

@ -10,7 +10,10 @@ export const ColumnCenter = styled(Column)`
align-items: center; 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; display: grid;
grid-auto-rows: auto; grid-auto-rows: auto;
grid-row-gap: ${({ gap }) => (gap === 'sm' && '8px') || (gap === 'md' && '12px') || (gap === 'lg' && '24px') || gap}; grid-row-gap: ${({ gap }) => (gap === 'sm' && '8px') || (gap === 'md' && '12px') || (gap === 'lg' && '24px') || gap};

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

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

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

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

@ -57,7 +57,7 @@ export const BottomGrouping = styled.div`
export const ErrorText = styled(Text)` export const ErrorText = styled(Text)`
color: ${({ theme, warningLow, warningMedium, warningHigh }) => 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)` 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 { return {
independentField: independentField, independentField: independentField,
typedValue: typedValue, typedValue: typedValue,
@ -110,21 +115,21 @@ export function useSwapStateReducer(params: QueryParams) {
typedValue: typedValue:
params.inputTokenAddress && !params.outputTokenAddress params.inputTokenAddress && !params.outputTokenAddress
? params.inputTokenAmount ? params.inputTokenAmount
? params.inputTokenAmount ? params.inputTokenAmount
: '' : ''
: !params.inputTokenAddress && params.outputTokenAddress : !params.inputTokenAddress && params.outputTokenAddress
? params.outputTokenAmount ? params.outputTokenAmount
? params.outputTokenAmount ? params.outputTokenAmount
: '' : ''
: params.inputTokenAddress && params.outputTokenAddress : params.inputTokenAddress && params.outputTokenAddress
? params.inputTokenAmount
? params.inputTokenAmount ? params.inputTokenAmount
? params.inputTokenAmount
: ''
: '' : ''
? '' : ''
: '' ? ''
? '' : ''
: '' ? ''
: ''
}, },
initializeSwapState initializeSwapState
) )

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

@ -24,5 +24,5 @@ export default function Identicon() {
} }
}, [account]) }, [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 }) { 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 ? 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/tree/${process.env.REACT_APP_GIT_COMMIT_HASH}`
'https://github.com/Uniswap/uniswap-frontend' : 'https://github.com/Uniswap/uniswap-frontend'
export default function Menu() { export default function Menu() {
const node = useRef<HTMLDivElement>() const node = useRef<HTMLDivElement>()

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

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

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

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

@ -79,7 +79,7 @@ export default function App() {
{activePopups.map(item => { {activePopups.map(item => {
return ( return (
<Popup key={item.key}> <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 })} {React.cloneElement(item.content, { popKey: item.key })}
</Popup> </Popup>
) )
@ -98,7 +98,7 @@ export default function App() {
.map(item => { .map(item => {
return ( return (
<Popup key={item.key}> <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 })} {React.cloneElement(item.content, { popKey: item.key })}
</Popup> </Popup>
) )

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

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

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

@ -61,21 +61,21 @@ const Input = styled.input<{ active?: boolean }>`
color: ${({ theme }) => theme.text1}; color: ${({ theme }) => theme.text1};
text-align: left; text-align: left;
${({ active }) => ${({ active }) =>
active && active &&
css` css`
color: initial; color: initial;
cursor: initial; cursor: initial;
text-align: right; text-align: right;
`} `}
${({ placeholder }) => ${({ placeholder }) =>
placeholder !== 'Custom' && placeholder !== 'Custom' &&
css` css`
text-align: right; text-align: right;
color: ${({ theme }) => theme.text1}; color: ${({ theme }) => theme.text1};
`} `}
${({ color }) => ${({ color }) =>
color === 'red' && color === 'red' &&
css` css`
color: ${({ theme }) => theme.red1}; color: ${({ theme }) => theme.red1};
`} `}
` `
@ -84,8 +84,8 @@ const BottomError = styled(Text)`
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
${({ show }) => ${({ show }) =>
show && show &&
css` css`
padding-top: 12px; padding-top: 12px;
`} `}
` `
@ -94,9 +94,10 @@ const OptionCustom = styled(FancyButton)<{ active?: boolean; warning?: boolean }
height: 2rem; height: 2rem;
position: relative; position: relative;
padding: 0 0.75rem; 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 { :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 { input {
@ -116,12 +117,12 @@ const Percent = styled.div`
font-size: 0, 8rem; font-size: 0, 8rem;
flex-grow: 0; flex-grow: 0;
${({ color, theme }) => ${({ color, theme }) =>
(color === 'faded' && (color === 'faded' &&
css` css`
color: ${theme.bg1}; color: ${theme.bg1};
`) || `) ||
(color === 'red' && (color === 'red' &&
css` css`
color: ${theme.red1}; color: ${theme.red1};
`)}; `)};
` `
@ -133,7 +134,12 @@ interface TransactionDetailsProps {
setDeadline: (deadline: number) => void 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 [activeIndex, setActiveIndex] = useState(2)
const [warningType, setWarningType] = useState(WARNING_TYPE.none) const [warningType, setWarningType] = useState(WARNING_TYPE.none)
@ -147,26 +153,10 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
const [deadlineInput, setDeadlineInput] = useState(deadline / 60) 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( const updateSlippage = useCallback(
newSlippage => { newSlippage => {
// round to 2 decimals to prevent ethers error // round to 2 decimals to prevent ethers error
let numParsed = newSlippage * 100 const numParsed = newSlippage * 100
// set both slippage values in parents // set both slippage values in parents
setRawSlippage(numParsed) setRawSlippage(numParsed)
@ -174,39 +164,6 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
[setRawSlippage] [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( const checkBounds = useCallback(
slippageValue => { slippageValue => {
setWarningType(WARNING_TYPE.none) setWarningType(WARNING_TYPE.none)
@ -231,12 +188,60 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
[updateSlippage] [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 // check that the theyve entered number and correct decimal
const parseInput = e => { const parseInput = e => {
let input = e.target.value const input = e.target.value
// restrict to 2 decimal places // 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 its within accepted decimal limit, update the input state
if (acceptableValues.some(a => a.test(input))) { if (acceptableValues.some(a => a.test(input))) {
setUserInput(input) setUserInput(input)
@ -313,8 +318,8 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
placeholder={ placeholder={
activeIndex === 4 activeIndex === 4
? !!userInput ? !!userInput
? '' ? ''
: '0' : '0'
: activeIndex !== 4 && userInput !== '' : activeIndex !== 4 && userInput !== ''
? userInput ? userInput
: 'Custom' : 'Custom'
@ -352,8 +357,8 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
: warningType !== WARNING_TYPE.none && warningType !== WARNING_TYPE.riskyEntryLow : warningType !== WARNING_TYPE.none && warningType !== WARNING_TYPE.riskyEntryLow
? 'red' ? 'red'
: warningType === WARNING_TYPE.riskyEntryLow : warningType === WARNING_TYPE.riskyEntryLow
? '#F3841E' ? '#F3841E'
: '' : ''
} }
> >
{warningType === WARNING_TYPE.emptyInput && 'Enter a slippage percentage'} {warningType === WARNING_TYPE.emptyInput && 'Enter a slippage percentage'}
@ -366,12 +371,16 @@ export default function TransactionDetails({ setRawSlippage, rawSlippage, deadli
<AutoColumn gap="sm"> <AutoColumn gap="sm">
<RowFixed padding={'0 20px'}> <RowFixed padding={'0 20px'}>
<TYPE.body fontSize={14}>Deadline</TYPE.body> <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>
<RowFixed padding={'0 20px'}> <RowFixed padding={'0 20px'}>
<OptionCustom style={{ width: '80px' }}> <OptionCustom style={{ width: '80px' }}>
<Input tabIndex={-1} placeholder={'' + deadlineInput} value={deadlineInput} <Input
onChange={parseCustomDeadline}/> tabIndex={-1}
placeholder={'' + deadlineInput}
value={deadlineInput}
onChange={parseCustomDeadline}
/>
</OptionCustom> </OptionCustom>
<TYPE.body style={{ paddingLeft: '8px' }} fontSize={14}> <TYPE.body style={{ paddingLeft: '8px' }} fontSize={14}>
minutes minutes

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

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

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

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

@ -21,7 +21,7 @@ export default function WalletConnectData({ uri = '', size }: WalletConnectDataP
return ( return (
<QRCodeWrapper> <QRCodeWrapper>
{uri && ( {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> </QRCodeWrapper>
) )

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

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

@ -3,13 +3,13 @@ import { NetworkConnector as NetworkConnectorCore } from '@web3-react/network-co
export class NetworkConnector extends NetworkConnectorCore { export class NetworkConnector extends NetworkConnectorCore {
pause() { pause() {
if ((this as any).active) { if ((this as any).active) {
(this as any).providers[(this as any).currentChainId].stop() ;(this as any).providers[(this as any).currentChainId].stop()
} }
} }
resume() { resume() {
if ((this as any).active) { 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 { BigintIsh, Token, TokenAmount, WETH } from '@uniswap/sdk'
import { 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 { useWeb3React } from '../hooks'
import { safeAccess, isAddress, getTokenAllowance } from '../utils' import { getTokenAllowance, isAddress } from '../utils'
import { useBlockNumber } from './Application' import { useBlockNumber } from './Application'
const UPDATE = 'UPDATE' 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() { function useAllowancesContext() {
return useContext(AllowancesContext) return useContext(AllowancesContext)
@ -20,11 +34,11 @@ function reducer(state, { type, payload }) {
return { return {
...state, ...state,
[networkId]: { [networkId]: {
...(safeAccess(state, [networkId]) || {}), ...state?.[networkId],
[address]: { [address]: {
...(safeAccess(state, [networkId, address]) || {}), ...state?.[networkId]?.[address],
[tokenAddress]: { [tokenAddress]: {
...(safeAccess(state, [networkId, address, tokenAddress]) || {}), ...state?.[networkId]?.[address]?.[tokenAddress],
[spenderAddress]: { [spenderAddress]: {
value, value,
blockNumber blockNumber
@ -34,9 +48,8 @@ function reducer(state, { type, payload }) {
} }
} }
} }
default: { default:
throw Error(`Unexpected action type in AllowancesContext reducer: '${type}'.`) 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 globalBlockNumber = useBlockNumber()
const [state, { update }] = useAllowancesContext() const [state, { update }] = useAllowancesContext()
const { value, blockNumber } = safeAccess(state, [chainId, address, token?.address, spenderAddress]) || {} const { value, blockNumber } = state?.[chainId]?.[address]?.[token?.address]?.[spenderAddress] ?? {}
useEffect(() => { useEffect(() => {
if ( if (
@ -92,6 +105,5 @@ export function useAddressAllowance(address: string, token: Token, spenderAddres
} }
}, [address, token, spenderAddress, value, blockNumber, globalBlockNumber, chainId, library, update]) }, [address, token, spenderAddress, value, blockNumber, globalBlockNumber, chainId, library, update])
const newTokenAmount: TokenAmount = value ? new TokenAmount(token, value) : null return value ? new TokenAmount(token, value) : null
return newTokenAmount
} }

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

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

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

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

@ -99,21 +99,21 @@ export default function App() {
<BrowserRouter> <BrowserRouter>
<AppWrapper> <AppWrapper>
<HeaderWrapper> <HeaderWrapper>
<Header/> <Header />
</HeaderWrapper> </HeaderWrapper>
<BodyWrapper> <BodyWrapper>
<Popups/> <Popups />
<Body> <Body>
<Web3ReactManager> <Web3ReactManager>
<NavigationTabs/> <NavigationTabs />
{/* this Suspense is for route code-splitting */} {/* this Suspense is for route code-splitting */}
<Switch> <Switch>
<Route exact strict path="/" render={() => <Redirect to="/swap"/>}/> <Route exact strict path="/" render={() => <Redirect to="/swap" />} />
<Route exact strict path="/swap" component={() => <Swap params={params}/>}/> <Route exact strict path="/swap" component={() => <Swap params={params} />} />
<Route exact strict path="/send" component={() => <Send params={params}/>}/> <Route exact strict path="/send" component={() => <Send params={params} />} />
<Route exact strict path="/find" component={() => <Find/>}/> <Route exact strict path="/find" component={() => <Find />} />
<Route exact strict path="/create" component={() => <Create/>}/> <Route exact strict path="/create" component={() => <Create />} />
<Route exact strict path="/pool" component={() => <Pool/>}/> <Route exact strict path="/pool" component={() => <Pool />} />
<Route <Route
exact exact
strict strict
@ -127,7 +127,7 @@ export default function App() {
if (t0 && t1) { if (t0 && t1) {
return <Add token0={t0} token1={t1} /> return <Add token0={t0} token1={t1} />
} else { } else {
return <Redirect to="/pool"/> return <Redirect to="/pool" />
} }
}} }}
/> />
@ -142,19 +142,19 @@ export default function App() {
const t1 = const t1 =
tokens?.[1] === 'ETH' ? 'ETH' : isAddress(tokens?.[1]) ? isAddress(tokens[1]) : undefined tokens?.[1] === 'ETH' ? 'ETH' : isAddress(tokens?.[1]) ? isAddress(tokens[1]) : undefined
if (t0 && t1) { if (t0 && t1) {
return <Remove token0={t0} token1={t1}/> return <Remove token0={t0} token1={t1} />
} else { } else {
return <Redirect to="/pool"/> return <Redirect to="/pool" />
} }
}} }}
/> />
<Redirect to="/"/> <Redirect to="/" />
</Switch> </Switch>
</Web3ReactManager> </Web3ReactManager>
</Body> </Body>
<Footer/> <Footer />
</BodyWrapper> </BodyWrapper>
<StyledRed/> <StyledRed />
</AppWrapper> </AppWrapper>
</BrowserRouter> </BrowserRouter>
</Suspense> </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 tokens[Field.INPUT]?.symbol
} ${'and'} ${parsedAmounts[Field.OUTPUT]?.toSignificant(6)} ${tokens[Field.OUTPUT]?.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 } = { const TokensDeposited: { [field: number]: TokenAmount } = {
[Field.TOKEN0]: [Field.TOKEN0]:
pair && pair &&
totalPoolTokens && totalPoolTokens &&
userLiquidity && userLiquidity &&
pair.getLiquidityValue(tokens[Field.TOKEN0], totalPoolTokens, userLiquidity, false), pair.getLiquidityValue(tokens[Field.TOKEN0], totalPoolTokens, userLiquidity, false),
[Field.TOKEN1]: [Field.TOKEN1]:
pair && pair &&
totalPoolTokens && totalPoolTokens &&
userLiquidity && userLiquidity &&
pair.getLiquidityValue(tokens[Field.TOKEN1], totalPoolTokens, userLiquidity, false) pair.getLiquidityValue(tokens[Field.TOKEN1], totalPoolTokens, userLiquidity, false)
} }
const route: Route = pair const route: Route = pair
@ -282,7 +282,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
) )
} }
const handleSliderChange = (newPercent) => { const handleSliderChange = newPercent => {
onUserInput( onUserInput(
Field.LIQUIDITY, Field.LIQUIDITY,
new TokenAmount( 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() { async function onRemove() {
setAttemptedRemoval(true) setAttemptedRemoval(true)
const router = getRouterContract(chainId, library, account) const router = getRouterContract(chainId, library, account)
@ -482,13 +489,13 @@ export default function RemoveLiquidity({ token0, token1 }) {
addTransaction( addTransaction(
response, response,
'Remove ' + 'Remove ' +
parsedAmounts[Field.TOKEN0]?.toSignificant(3) + parsedAmounts[Field.TOKEN0]?.toSignificant(3) +
' ' + ' ' +
tokens[Field.TOKEN0]?.symbol + tokens[Field.TOKEN0]?.symbol +
' and ' + ' and ' +
parsedAmounts[Field.TOKEN1]?.toSignificant(3) + parsedAmounts[Field.TOKEN1]?.toSignificant(3) +
' ' + ' ' +
tokens[Field.TOKEN1]?.symbol 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() { function modalHeader() {
return ( return (
<AutoColumn gap={'sm'} style={{ marginTop: '20px' }}> <AutoColumn gap={'sm'} style={{ marginTop: '20px' }}>
@ -514,21 +514,21 @@ export default function RemoveLiquidity({ token0, token1 }) {
{!!parsedAmounts[Field.TOKEN0] && parsedAmounts[Field.TOKEN0].toSignificant(6)} {!!parsedAmounts[Field.TOKEN0] && parsedAmounts[Field.TOKEN0].toSignificant(6)}
</Text> </Text>
<RowFixed gap="4px"> <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' }}> <Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
{tokens[Field.TOKEN0]?.symbol || ''} {tokens[Field.TOKEN0]?.symbol || ''}
</Text> </Text>
</RowFixed> </RowFixed>
</RowBetween> </RowBetween>
<RowFixed> <RowFixed>
<Plus size="16" color={'#888D9B'}/> <Plus size="16" color={'#888D9B'} />
</RowFixed> </RowFixed>
<RowBetween align="flex-end"> <RowBetween align="flex-end">
<Text fontSize={24} fontWeight={600}> <Text fontSize={24} fontWeight={600}>
{!!parsedAmounts[Field.TOKEN1] && parsedAmounts[Field.TOKEN1].toSignificant(6)} {!!parsedAmounts[Field.TOKEN1] && parsedAmounts[Field.TOKEN1].toSignificant(6)}
</Text> </Text>
<RowFixed gap="4px"> <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' }}> <Text fontSize={24} fontWeight={500} style={{ marginLeft: '10px' }}>
{tokens[Field.TOKEN1]?.symbol || ''} {tokens[Field.TOKEN1]?.symbol || ''}
</Text> </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 tokens[Field.TOKEN0]?.symbol
} and ${parsedAmounts[Field.TOKEN1]?.toSignificant(6)} ${tokens[Field.TOKEN1]?.symbol}` } and ${parsedAmounts[Field.TOKEN1]?.toSignificant(6)} ${tokens[Field.TOKEN1]?.symbol}`
@ -637,7 +637,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
</Text> </Text>
</Row> </Row>
{!showAdvanced && ( {!showAdvanced && (
<Slider value={parseFloat(derivedPerecent)} onChange={handleSliderChange} override={override}/> <Slider value={parseFloat(derivedPerecent)} onChange={handleSliderChange} override={override} />
)} )}
{!showAdvanced && ( {!showAdvanced && (
<RowBetween> <RowBetween>
@ -660,7 +660,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
{!showAdvanced && ( {!showAdvanced && (
<> <>
<ColumnCenter> <ColumnCenter>
<ArrowDown size="16" color="#888D9B"/> <ArrowDown size="16" color="#888D9B" />
</ColumnCenter>{' '} </ColumnCenter>{' '}
<LightCard> <LightCard>
<AutoColumn gap="10px"> <AutoColumn gap="10px">
@ -669,7 +669,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
{formattedAmounts[Field.TOKEN0] ? formattedAmounts[Field.TOKEN0] : '-'} {formattedAmounts[Field.TOKEN0] ? formattedAmounts[Field.TOKEN0] : '-'}
</Text> </Text>
<RowFixed> <RowFixed>
<TokenLogo address={tokens[Field.TOKEN0]?.address || ''} style={{ marginRight: '12px' }}/> <TokenLogo address={tokens[Field.TOKEN0]?.address || ''} style={{ marginRight: '12px' }} />
<Text fontSize={24} fontWeight={500}> <Text fontSize={24} fontWeight={500}>
{tokens[Field.TOKEN0]?.symbol} {tokens[Field.TOKEN0]?.symbol}
</Text> </Text>
@ -680,7 +680,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
{formattedAmounts[Field.TOKEN1] ? formattedAmounts[Field.TOKEN1] : '-'} {formattedAmounts[Field.TOKEN1] ? formattedAmounts[Field.TOKEN1] : '-'}
</Text> </Text>
<RowFixed> <RowFixed>
<TokenLogo address={tokens[Field.TOKEN1]?.address || ''} style={{ marginRight: '12px' }}/> <TokenLogo address={tokens[Field.TOKEN1]?.address || ''} style={{ marginRight: '12px' }} />
<Text fontSize={24} fontWeight={500}> <Text fontSize={24} fontWeight={500}>
{tokens[Field.TOKEN1]?.symbol} {tokens[Field.TOKEN1]?.symbol}
</Text> </Text>
@ -706,7 +706,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
inputId="liquidityAmount" inputId="liquidityAmount"
/> />
<ColumnCenter> <ColumnCenter>
<ArrowDown size="16" color="#888D9B"/> <ArrowDown size="16" color="#888D9B" />
</ColumnCenter> </ColumnCenter>
<CurrencyInputPanel <CurrencyInputPanel
field={Field.TOKEN0} field={Field.TOKEN0}
@ -720,7 +720,7 @@ export default function RemoveLiquidity({ token0, token1 }) {
inputId="removeLiquidityToken0" inputId="removeLiquidityToken0"
/> />
<ColumnCenter> <ColumnCenter>
<Plus size="16" color="#888D9B"/> <Plus size="16" color="#888D9B" />
</ColumnCenter> </ColumnCenter>
<CurrencyInputPanel <CurrencyInputPanel
field={Field.TOKEN1} field={Field.TOKEN1}

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

@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import ExchangePage from '../../components/ExchangePage' 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} /> return <ExchangePage sendingInput={false} params={params} />
} }

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

@ -3,12 +3,12 @@ import styled, {
ThemeProvider as StyledComponentsThemeProvider, ThemeProvider as StyledComponentsThemeProvider,
createGlobalStyle, createGlobalStyle,
css, css,
DefaultTheme
} from 'styled-components' } from 'styled-components'
import { getQueryParam, checkSupportedTheme } from '../utils' import { getQueryParam, checkSupportedTheme } from '../utils'
import { SUPPORTED_THEMES } from '../constants' import { SUPPORTED_THEMES } from '../constants'
import { useDarkModeManager } from '../contexts/LocalStorage' import { useDarkModeManager } from '../contexts/LocalStorage'
import { Text } from 'rebass' import { Text } from 'rebass'
import { UniswapTheme } from './styled'
export * from './components' export * from './components'
@ -19,20 +19,95 @@ const MEDIA_WIDTHS = {
upToLarge: 1280 upToLarge: 1280
} }
const mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = const mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys(MEDIA_WIDTHS).reduce(
Object.keys(MEDIA_WIDTHS) (accumulator, size) => {
.reduce((accumulator, size) => { accumulator[size] = (a, b, c) => css`
accumulator[size] = (a, b, c) => css` @media (max-width: ${MEDIA_WIDTHS[size]}px) {
@media (max-width: ${MEDIA_WIDTHS[size]}px) { ${css(a, b, c)}
${css(a, b, c)} }
} `
` return accumulator
return accumulator },
}, {}) as any {}
) as any
const white = '#FFFFFF' const white = '#FFFFFF'
const black = '#000000' 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 }) { export default function ThemeProvider({ children }) {
const [darkMode, toggleDarkMode] = useDarkModeManager() const [darkMode, toggleDarkMode] = useDarkModeManager()
const themeURL = checkSupportedTheme(getQueryParam(window.location, 'theme')) const themeURL = checkSupportedTheme(getQueryParam(window.location, 'theme'))
@ -40,8 +115,8 @@ export default function ThemeProvider({ children }) {
? themeURL.toUpperCase() === SUPPORTED_THEMES.DARK ? themeURL.toUpperCase() === SUPPORTED_THEMES.DARK
? true ? true
: themeURL.toUpperCase() === SUPPORTED_THEMES.LIGHT : themeURL.toUpperCase() === SUPPORTED_THEMES.LIGHT
? false ? false
: darkMode : darkMode
: darkMode : darkMode
useEffect(() => { useEffect(() => {
toggleDarkMode(themeToRender) toggleDarkMode(themeToRender)
@ -49,78 +124,6 @@ export default function ThemeProvider({ children }) {
return <StyledComponentsThemeProvider theme={theme(themeToRender)}>{children}</StyledComponentsThemeProvider> 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)` const TextWrapper = styled(Text)`
color = ${({ color, theme }) => theme[color]} color = ${({ color, theme }) => theme[color]}
` `

118
src/theme/styled.d.ts vendored

@ -1,66 +1,62 @@
import { css, FlattenSimpleInterpolation } from 'styled-components' 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' { 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_ABI from '../constants/abis/erc20.json'
import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32.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 { export enum ERROR_CODES {
TOKEN_SYMBOL = 1, TOKEN_SYMBOL = 1,
TOKEN_DECIMALS = 2 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 = { const ETHERSCAN_PREFIXES = {
1: '', 1: '',
3: 'ropsten.', 3: 'ropsten.',
@ -49,7 +48,7 @@ export function getEtherscanLink(networkId: 1 | 3 | 4 | 5 | 42 | any, data: stri
} }
export function getQueryParam(windowLocation, name) { 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] return q && q[1]
} }
@ -122,18 +121,6 @@ export function shortenAddress(address, digits = 4) {
return `${address.substring(0, digits + 2)}...${address.substring(42 - digits)}` 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) { export function calculateGasMargin(value: BigNumber) {
return value.mul(BigNumber.from(10000).add(BigNumber.from(1000))).div(BigNumber.from(10000)) // add 10% return value.mul(BigNumber.from(10000).add(BigNumber.from(1000))).div(BigNumber.from(10000)) // add 10%
} }

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

@ -2630,7 +2630,7 @@
dependencies: dependencies:
"@types/yargs-parser" "*" "@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" version "2.31.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.31.0.tgz#942c921fec5e200b79593c71fafb1e3f57aa2e36" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.31.0.tgz#942c921fec5e200b79593c71fafb1e3f57aa2e36"
integrity sha512-iIC0Pb8qDaoit+m80Ln/aaeu9zKQdOLF4SHcGLarSeY1gurW6aU4JsOPMjKQwXlw70MvWKZQc6S2NamA8SJ/gg== integrity sha512-iIC0Pb8qDaoit+m80Ln/aaeu9zKQdOLF4SHcGLarSeY1gurW6aU4JsOPMjKQwXlw70MvWKZQc6S2NamA8SJ/gg==
@ -2650,7 +2650,7 @@
eslint-scope "^5.0.0" eslint-scope "^5.0.0"
eslint-utils "^2.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" version "2.31.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.31.0.tgz#beddd4e8efe64995108b229b2862cd5752d40d6f" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.31.0.tgz#beddd4e8efe64995108b229b2862cd5752d40d6f"
integrity sha512-uph+w6xUOlyV2DLSC6o+fBDzZ5i7+3/TxAsH4h3eC64tlga57oMb96vVlXoMwjR/nN+xyWlsnxtbDkB46M2EPQ== integrity sha512-uph+w6xUOlyV2DLSC6o+fBDzZ5i7+3/TxAsH4h3eC64tlga57oMb96vVlXoMwjR/nN+xyWlsnxtbDkB46M2EPQ==
@ -6772,6 +6772,13 @@ escodegen@^1.11.0, escodegen@^1.9.1:
optionalDependencies: optionalDependencies:
source-map "~0.6.1" 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: eslint-config-react-app@^5.2.1:
version "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" 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" has "^1.0.3"
jsx-ast-utils "^2.2.1" 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: eslint-plugin-react-hooks@^1.6.1:
version "1.7.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04"
integrity sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA== 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" version "7.19.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz#6d08f9673628aa69c5559d33489e855d83551666" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz#6d08f9673628aa69c5559d33489e855d83551666"
integrity sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ== 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" 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== 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" version "6.8.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb"
integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== 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" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== 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: fast-glob@^2.0.2:
version "2.2.7" version "2.2.7"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" 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" 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== 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: get-stream@^2.2.0:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" 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" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= 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: prettier@^1.17.0:
version "1.19.1" version "1.19.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"