Commit cb7dbe79 authored by ian-jh's avatar ian-jh

abort rebase fix conflicts

parents ab3faaa2 4e834c4c
**PLEASE DO NOT SUBMIT TOKEN ADDITIONS AS PULL REQUESTS**
All token requests should be made via an issue.
...@@ -25,3 +25,5 @@ yarn-error.log* ...@@ -25,3 +25,5 @@ yarn-error.log*
notes.txt notes.txt
.idea/ .idea/
.vscode/
\ No newline at end of file
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
"unlock": "Unlock", "unlock": "Unlock",
"pending": "Pending", "pending": "Pending",
"selectToken": "Select a token", "selectToken": "Select a token",
"searchOrPaste": "Search Token or Paste Address", "searchOrPaste": "Search Token Name, Symbol, or Address",
"searchOrPasteMobile": "Name, Symbol, or Address",
"noExchange": "No Exchange Found", "noExchange": "No Exchange Found",
"exchangeRate": "Exchange Rate", "exchangeRate": "Exchange Rate",
"unknownError": "Oops! An unknown error occurred. Please refresh the page, or visit from another browser or device.", "unknownError": "Oops! An unknown error occurred. Please refresh the page, or visit from another browser or device.",
...@@ -36,6 +37,7 @@ ...@@ -36,6 +37,7 @@
"youWillReceive": "You will receive at least", "youWillReceive": "You will receive at least",
"youAreBuying": "You are buying", "youAreBuying": "You are buying",
"itWillCost": "It will cost at most", "itWillCost": "It will cost at most",
"forAtMost": "for at most",
"insufficientBalance": "Insufficient Balance", "insufficientBalance": "Insufficient Balance",
"inputNotValid": "Not a valid input value", "inputNotValid": "Not a valid input value",
"differentToken": "Must be different token.", "differentToken": "Must be different token.",
......
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 9.27455 20.9097 6.80375 19.1414 5" stroke="#AEAEAE" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
\ No newline at end of file
import React from 'react'
const SVGArrowDown = props => (
<svg width="1em" height="1em" viewBox="0 0 9 10" fill="currentColor" {...props}>
<path
d="M5.298 0H4.24v7.911h-.075L1.256 4.932l-.717.735L4.769 10 9 5.667l-.718-.735-2.908 2.979h-.076V0z"
fill="currentColor"
/>
</svg>
)
export default SVGArrowDown
...@@ -2,17 +2,17 @@ import React, { useState, useEffect } from 'react' ...@@ -2,17 +2,17 @@ import React, { useState, useEffect } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useWeb3Context } from 'web3-react' import { useWeb3Context } from 'web3-react'
import { lighten } from 'polished' import { transparentize } from 'polished'
import { isAddress } from '../../utils' import { isAddress } from '../../utils'
import { useDebounce } from '../../hooks' import { useDebounce } from '../../hooks'
const InputPanel = styled.div` const InputPanel = styled.div`
${({ theme }) => theme.flexColumnNoWrap} ${({ theme }) => theme.flexColumnNoWrap}
box-shadow: 0 4px 8px 0 ${({ theme }) => lighten(0.9, theme.royalBlue)}; box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.royalBlue)};
position: relative; position: relative;
border-radius: 1.25rem; border-radius: 1.25rem;
background-color: ${({ theme }) => theme.white}; background-color: ${({ theme }) => theme.inputBackground};
z-index: 1; z-index: 1;
` `
...@@ -21,9 +21,10 @@ const ContainerRow = styled.div` ...@@ -21,9 +21,10 @@ const ContainerRow = styled.div`
justify-content: center; justify-content: center;
align-items: center; align-items: center;
border-radius: 1.25rem; border-radius: 1.25rem;
box-shadow: 0 0 0 1px ${({ error, theme }) => (error ? theme.salmonRed : theme.mercuryGray)}; border: 1px solid ${({ error, theme }) => (error ? theme.salmonRed : theme.mercuryGray)};
background-color: ${({ theme }) => theme.white};
transition: box-shadow 200ms ease-in-out; background-color: ${({ theme }) => theme.inputBackground};
transition: box-shadow 125ms ease-in-out;
` `
const InputContainer = styled.div` const InputContainer = styled.div`
...@@ -59,13 +60,15 @@ const Input = styled.input` ...@@ -59,13 +60,15 @@ const Input = styled.input`
border: none; border: none;
flex: 1 1 auto; flex: 1 1 auto;
width: 0; width: 0;
background-color: ${({ theme }) => theme.inputBackground};
color: ${({ error, theme }) => (error ? theme.salmonRed : theme.royalBlue)}; color: ${({ error, theme }) => (error ? theme.salmonRed : theme.royalBlue)};
transition: color 200ms ease-in-out; transition: color 125ms ease-in-out;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
::placeholder { ::placeholder {
color: ${({ theme }) => theme.chaliceGray}; color: ${({ theme }) => theme.placeholderGray};
} }
` `
...@@ -93,6 +96,7 @@ export default function AddressInputPanel({ title, initialInput = '', onChange = ...@@ -93,6 +96,7 @@ export default function AddressInputPanel({ title, initialInput = '', onChange =
let stale = false let stale = false
if (isAddress(debouncedInput)) { if (isAddress(debouncedInput)) {
try {
library library
.lookupAddress(debouncedInput) .lookupAddress(debouncedInput)
.then(name => { .then(name => {
...@@ -107,11 +111,18 @@ export default function AddressInputPanel({ title, initialInput = '', onChange = ...@@ -107,11 +111,18 @@ export default function AddressInputPanel({ title, initialInput = '', onChange =
} }
}) })
.catch(() => { .catch(() => {
if (!stale) {
setData({ address: debouncedInput, name: '' }) setData({ address: debouncedInput, name: '' })
setError(null) setError(null)
}
}) })
} catch {
setData({ address: debouncedInput, name: '' })
setError(null)
}
} else { } else {
if (debouncedInput !== '') { if (debouncedInput !== '') {
try {
library library
.resolveName(debouncedInput) .resolveName(debouncedInput)
.then(address => { .then(address => {
...@@ -126,8 +137,13 @@ export default function AddressInputPanel({ title, initialInput = '', onChange = ...@@ -126,8 +137,13 @@ export default function AddressInputPanel({ title, initialInput = '', onChange =
} }
}) })
.catch(() => { .catch(() => {
if (!stale) {
setError(true) setError(true)
}
}) })
} catch {
setError(true)
}
} }
} }
......
...@@ -2,8 +2,8 @@ import React, { Component } from 'react' ...@@ -2,8 +2,8 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import styled from 'styled-components' import styled from 'styled-components'
import DropdownBlue from '../../assets/images/dropdown-blue.svg' import { ReactComponent as Dropup } from '../../assets/images/dropup-blue.svg'
import DropupBlue from '../../assets/images/dropup-blue.svg' import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg'
const SummaryWrapper = styled.div` const SummaryWrapper = styled.div`
color: ${({ error, theme }) => (error ? theme.salmonRed : theme.doveGray)}; color: ${({ error, theme }) => (error ? theme.salmonRed : theme.doveGray)};
...@@ -42,6 +42,20 @@ const SummaryWrapperContainer = styled.div` ...@@ -42,6 +42,20 @@ const SummaryWrapperContainer = styled.div`
} }
` `
const WrappedDropup = ({ isError, highSlippageWarning, ...rest }) => <Dropup {...rest} />
const ColoredDropup = styled(WrappedDropup)`
path {
stroke: ${({ theme }) => theme.royalBlue};
}
`
const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => <Dropdown {...rest} />
const ColoredDropdown = styled(WrappedDropdown)`
path {
stroke: ${({ theme }) => theme.royalBlue};
}
`
class ContextualInfo extends Component { class ContextualInfo extends Component {
static propTypes = { static propTypes = {
openDetailsText: PropTypes.string, openDetailsText: PropTypes.string,
...@@ -89,12 +103,12 @@ class ContextualInfo extends Component { ...@@ -89,12 +103,12 @@ class ContextualInfo extends Component {
{!this.state.showDetails ? ( {!this.state.showDetails ? (
<> <>
<span>{openDetailsText}</span> <span>{openDetailsText}</span>
<img src={DropdownBlue} alt="dropdown" /> <ColoredDropup />
</> </>
) : ( ) : (
<> <>
<span>{closeDetailsText}</span> <span>{closeDetailsText}</span>
<img src={DropupBlue} alt="dropup" /> <ColoredDropdown />
</> </>
)} )}
</SummaryWrapperContainer> </SummaryWrapperContainer>
......
...@@ -32,8 +32,7 @@ const SummaryWrapperContainer = styled.div` ...@@ -32,8 +32,7 @@ const SummaryWrapperContainer = styled.div`
const Details = styled.div` const Details = styled.div`
background-color: ${({ theme }) => theme.concreteGray}; background-color: ${({ theme }) => theme.concreteGray};
padding: 1.5rem; /* padding: 1.25rem 1.25rem 1rem 1.25rem; */
padding-bottom: 1rem;
border-radius: 1rem; border-radius: 1rem;
font-size: 0.75rem; font-size: 0.75rem;
margin: 1rem 0.5rem 0 0.5rem; margin: 1rem 0.5rem 0 0.5rem;
...@@ -62,7 +61,7 @@ const ErrorSpan = styled.span` ...@@ -62,7 +61,7 @@ const ErrorSpan = styled.span`
const WrappedDropup = ({ isError, highSlippageWarning, ...rest }) => <Dropup {...rest} /> const WrappedDropup = ({ isError, highSlippageWarning, ...rest }) => <Dropup {...rest} />
const ColoredDropup = styled(WrappedDropup)` const ColoredDropup = styled(WrappedDropup)`
path { path {
stroke: ${({ isError, theme }) => isError && theme.salmonRed}; stroke: ${({ isError, theme }) => (isError ? theme.salmonRed : theme.royalBlue)};
${({ highSlippageWarning, theme }) => ${({ highSlippageWarning, theme }) =>
highSlippageWarning && highSlippageWarning &&
...@@ -75,7 +74,7 @@ const ColoredDropup = styled(WrappedDropup)` ...@@ -75,7 +74,7 @@ const ColoredDropup = styled(WrappedDropup)`
const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => <Dropdown {...rest} /> const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => <Dropdown {...rest} />
const ColoredDropdown = styled(WrappedDropdown)` const ColoredDropdown = styled(WrappedDropdown)`
path { path {
stroke: ${({ isError, theme }) => isError && theme.salmonRed}; stroke: ${({ isError, theme }) => (isError ? theme.salmonRed : theme.royalBlue)};
${({ highSlippageWarning, theme }) => ${({ highSlippageWarning, theme }) =>
highSlippageWarning && highSlippageWarning &&
......
This diff is collapsed.
import React, { useState, useReducer, useEffect } from 'react' import React, { useState, useReducer, useEffect } from 'react'
import ReactGA from 'react-ga' import ReactGA from 'react-ga'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useWeb3Context } from 'web3-react' import { useWeb3Context } from 'web3-react'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import styled from 'styled-components' import styled from 'styled-components'
...@@ -10,13 +12,13 @@ import CurrencyInputPanel from '../CurrencyInputPanel' ...@@ -10,13 +12,13 @@ import CurrencyInputPanel from '../CurrencyInputPanel'
import AddressInputPanel from '../AddressInputPanel' import AddressInputPanel from '../AddressInputPanel'
import OversizedPanel from '../OversizedPanel' import OversizedPanel from '../OversizedPanel'
import TransactionDetails from '../TransactionDetails' import TransactionDetails from '../TransactionDetails'
import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg' import ArrowDown from '../../assets/svg/SVGArrowDown'
import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg'
import { amountFormatter, calculateGasMargin } from '../../utils' import { amountFormatter, calculateGasMargin } from '../../utils'
import { useExchangeContract } from '../../hooks' import { useExchangeContract } from '../../hooks'
import { useTokenDetails } from '../../contexts/Tokens' import { useTokenDetails } from '../../contexts/Tokens'
import { useTransactionAdder } from '../../contexts/Transactions' import { useTransactionAdder } from '../../contexts/Transactions'
import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances' import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances'
import { useFetchAllBalances } from '../../contexts/AllBalances'
import { useAddressAllowance } from '../../contexts/Allowances' import { useAddressAllowance } from '../../contexts/Allowances'
const INPUT = 0 const INPUT = 0
...@@ -27,23 +29,24 @@ const TOKEN_TO_ETH = 1 ...@@ -27,23 +29,24 @@ const TOKEN_TO_ETH = 1
const TOKEN_TO_TOKEN = 2 const TOKEN_TO_TOKEN = 2
// denominated in bips // denominated in bips
const ALLOWED_SLIPPAGE_DEFAULT = 150 const ALLOWED_SLIPPAGE_DEFAULT = 100
const TOKEN_ALLOWED_SLIPPAGE_DEFAULT = 200 const TOKEN_ALLOWED_SLIPPAGE_DEFAULT = 100
// denominated in seconds // 15 minutes, denominated in seconds
const DEADLINE_FROM_NOW = 60 * 15 const DEADLINE_FROM_NOW = 60 * 15
// denominated in bips // % above the calculated gas cost that we actually send, denominated in bips
const GAS_MARGIN = ethers.utils.bigNumberify(1000) const GAS_MARGIN = ethers.utils.bigNumberify(1000)
const DownArrowBackground = styled.div` const DownArrowBackground = styled.div`
${({ theme }) => theme.flexRowNoWrap} ${({ theme }) => theme.flexRowNoWrap}
justify-content: center; justify-content: center;
align-items: center; align-items: center;
` `
const DownArrow = styled.img` const WrappedArrowDown = ({ clickable, active, ...rest }) => <ArrowDown {...rest} />
const DownArrow = styled(WrappedArrowDown)`
color: ${({ theme, active }) => (active ? theme.royalBlue : theme.chaliceGray)};
width: 0.625rem; width: 0.625rem;
height: 0.625rem; height: 0.625rem;
position: relative; position: relative;
...@@ -62,7 +65,7 @@ const ExchangeRateWrapper = styled.div` ...@@ -62,7 +65,7 @@ const ExchangeRateWrapper = styled.div`
const ExchangeRate = styled.span` const ExchangeRate = styled.span`
flex: 1 1 auto; flex: 1 1 auto;
width: 0; width: 0;
color: ${({ theme }) => theme.chaliceGray}; color: ${({ theme }) => theme.doveGray};
` `
const Flex = styled.div` const Flex = styled.div`
...@@ -241,16 +244,17 @@ export default function ExchangePage({ initialCurrency, sending }) { ...@@ -241,16 +244,17 @@ export default function ExchangePage({ initialCurrency, sending }) {
const [rawSlippage, setRawSlippage] = useState(ALLOWED_SLIPPAGE_DEFAULT) const [rawSlippage, setRawSlippage] = useState(ALLOWED_SLIPPAGE_DEFAULT)
const [rawTokenSlippage, setRawTokenSlippage] = useState(TOKEN_ALLOWED_SLIPPAGE_DEFAULT) const [rawTokenSlippage, setRawTokenSlippage] = useState(TOKEN_ALLOWED_SLIPPAGE_DEFAULT)
let allowedSlippageBig = ethers.utils.bigNumberify(rawSlippage) const allowedSlippageBig = ethers.utils.bigNumberify(rawSlippage)
let tokenAllowedSlippageBig = ethers.utils.bigNumberify(rawTokenSlippage) const tokenAllowedSlippageBig = ethers.utils.bigNumberify(rawTokenSlippage)
// analytics // analytics
useEffect(() => { useEffect(() => {
ReactGA.pageview(window.location.pathname + window.location.search) ReactGA.pageview(window.location.pathname + window.location.search)
}, []) }, [])
// core swap state- // core swap state
const [swapState, dispatchSwapState] = useReducer(swapStateReducer, initialCurrency, getInitialSwapState) const [swapState, dispatchSwapState] = useReducer(swapStateReducer, initialCurrency, getInitialSwapState)
const { independentValue, dependentValue, independentField, inputCurrency, outputCurrency } = swapState const { independentValue, dependentValue, independentField, inputCurrency, outputCurrency } = swapState
const [recipient, setRecipient] = useState({ address: '', name: '' }) const [recipient, setRecipient] = useState({ address: '', name: '' })
...@@ -259,7 +263,7 @@ export default function ExchangePage({ initialCurrency, sending }) { ...@@ -259,7 +263,7 @@ export default function ExchangePage({ initialCurrency, sending }) {
// get swap type from the currency types // get swap type from the currency types
const swapType = getSwapType(inputCurrency, outputCurrency) const swapType = getSwapType(inputCurrency, outputCurrency)
// get decimals and exchange addressfor each of the currency types // get decimals and exchange address for each of the currency types
const { symbol: inputSymbol, decimals: inputDecimals, exchangeAddress: inputExchangeAddress } = useTokenDetails( const { symbol: inputSymbol, decimals: inputDecimals, exchangeAddress: inputExchangeAddress } = useTokenDetails(
inputCurrency inputCurrency
) )
...@@ -583,10 +587,13 @@ export default function ExchangePage({ initialCurrency, sending }) { ...@@ -583,10 +587,13 @@ export default function ExchangePage({ initialCurrency, sending }) {
const [customSlippageError, setcustomSlippageError] = useState('') const [customSlippageError, setcustomSlippageError] = useState('')
const allBalances = useFetchAllBalances()
return ( return (
<> <>
<CurrencyInputPanel <CurrencyInputPanel
title={t('input')} title={t('input')}
allBalances={allBalances}
description={inputValueFormatted && independentField === OUTPUT ? estimatedText : ''} description={inputValueFormatted && independentField === OUTPUT ? estimatedText : ''}
extraText={inputBalanceFormatted && formatBalance(inputBalanceFormatted)} extraText={inputBalanceFormatted && formatBalance(inputBalanceFormatted)}
extraTextClickHander={() => { extraTextClickHander={() => {
...@@ -620,12 +627,13 @@ export default function ExchangePage({ initialCurrency, sending }) { ...@@ -620,12 +627,13 @@ export default function ExchangePage({ initialCurrency, sending }) {
}} }}
clickable clickable
alt="swap" alt="swap"
src={isValid ? ArrowDownBlue : ArrowDownGrey} active={isValid}
/> />
</DownArrowBackground> </DownArrowBackground>
</OversizedPanel> </OversizedPanel>
<CurrencyInputPanel <CurrencyInputPanel
title={t('output')} title={t('output')}
allBalances={allBalances}
description={outputValueFormatted && independentField === INPUT ? estimatedText : ''} description={outputValueFormatted && independentField === INPUT ? estimatedText : ''}
extraText={outputBalanceFormatted && formatBalance(outputBalanceFormatted)} extraText={outputBalanceFormatted && formatBalance(outputBalanceFormatted)}
onCurrencySelected={outputCurrency => { onCurrencySelected={outputCurrency => {
...@@ -644,7 +652,7 @@ export default function ExchangePage({ initialCurrency, sending }) { ...@@ -644,7 +652,7 @@ export default function ExchangePage({ initialCurrency, sending }) {
<> <>
<OversizedPanel> <OversizedPanel>
<DownArrowBackground> <DownArrowBackground>
<DownArrow src={isValid ? ArrowDownBlue : ArrowDownGrey} alt="arrow" /> <DownArrow active={isValid} alt="arrow" />
</DownArrowBackground> </DownArrowBackground>
</OversizedPanel> </OversizedPanel>
<AddressInputPanel onChange={setRecipient} onError={setRecipientError} /> <AddressInputPanel onChange={setRecipient} onError={setRecipientError} />
...@@ -662,13 +670,13 @@ export default function ExchangePage({ initialCurrency, sending }) { ...@@ -662,13 +670,13 @@ export default function ExchangePage({ initialCurrency, sending }) {
{inverted ? ( {inverted ? (
<span> <span>
{exchangeRate {exchangeRate
? `1 ${outputSymbol} = ${amountFormatter(exchangeRateInverted, 18, 4, false)} ${inputSymbol}` ? `1 ${inputSymbol} = ${amountFormatter(exchangeRate, 18, 4, false)} ${outputSymbol}`
: ' - '} : ' - '}
</span> </span>
) : ( ) : (
<span> <span>
{exchangeRate {exchangeRate
? `1 ${inputSymbol} = ${amountFormatter(exchangeRate, 18, 4, false)} ${outputSymbol}` ? `1 ${outputSymbol} = ${amountFormatter(exchangeRateInverted, 18, 4, false)} ${inputSymbol}`
: ' - '} : ' - '}
</span> </span>
)} )}
......
import React from 'react'
import styled from 'styled-components'
import { darken, transparentize } from 'polished'
import Toggle from 'react-switch'
import { Link } from '../../theme'
import { useDarkModeManager } from '../../contexts/LocalStorage'
const FooterFrame = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
`
const FooterElement = styled.div`
margin: 1.25rem;
display: flex;
min-width: 0;
display: flex;
align-items: center;
`
const Title = styled.div`
display: flex;
align-items: center;
color: ${({ theme }) => theme.uniswapPink};
:hover {
cursor: pointer;
}
#link {
text-decoration-color: ${({ theme }) => theme.uniswapPink};
}
#title {
display: inline;
font-size: 0.825rem;
margin-right: 12px;
font-weight: 400;
color: ${({ theme }) => theme.uniswapPink};
:hover {
color: ${({ theme }) => darken(0.2, theme.uniswapPink)};
}
}
`
const StyledToggle = styled(Toggle)`
margin-right: 24px;
.react-switch-bg[style] {
background-color: ${({ theme }) => theme.inputBackground} !important;
border: 1px solid ${({ theme }) => theme.concreteGray} !important;
}
.react-switch-handle[style] {
background-color: ${({ theme }) => theme.inputBackground};
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.93, theme.royalBlue)};
border: 1px solid ${({ theme }) => theme.mercuryGray};
border-color: ${({ theme }) => theme.mercuryGray} !important;
}
`
const EmojiToggle = styled.span`
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-family: Arial sans-serif;
`
export default function Footer() {
const [isDark, toggleDarkMode] = useDarkModeManager()
return (
<FooterFrame>
<FooterElement>
<Title>
<Link id="link" href="https://uniswap.io/">
<h1 id="title">About</h1>
</Link>
<Link id="link" href="https://docs.uniswap.io/">
<h1 id="title">Docs</h1>
</Link>
<Link id="link" href="https://github.com/Uniswap">
<h1 id="title">Code</h1>
</Link>
</Title>
</FooterElement>
<StyledToggle
checked={!isDark}
uncheckedIcon={
<EmojiToggle role="img" aria-label="moon">
{/* eslint-disable-line jsx-a11y/accessible-emoji */}
🌙️
</EmojiToggle>
}
checkedIcon={
<EmojiToggle role="img" aria-label="sun">
{/* eslint-disable-line jsx-a11y/accessible-emoji */}
{'☀️'}
</EmojiToggle>
}
onChange={() => toggleDarkMode()}
/>
</FooterFrame>
)
}
...@@ -5,23 +5,40 @@ import { Link } from '../../theme' ...@@ -5,23 +5,40 @@ import { Link } from '../../theme'
import Web3Status from '../Web3Status' import Web3Status from '../Web3Status'
import { darken } from 'polished' import { darken } from 'polished'
const HeaderFrame = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
`
const HeaderElement = styled.div` const HeaderElement = styled.div`
margin: 1.25rem; margin: 1.25rem;
display: flex; display: flex;
min-width: 0; min-width: 0;
display: flex;
align-items: center;
`
const Nod = styled.span`
transform: rotate(0deg);
transition: transform 150ms ease-out;
:hover {
transform: rotate(-10deg);
}
` `
const Title = styled.div` const Title = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
#image { :hover {
font-size: 1.5rem; cursor: pointer;
margin-right: 1rem;
} }
#link { #link {
text-decoration-color: ${({ theme }) => theme.wisteriaPurple}; text-decoration-color: ${({ theme }) => theme.UniswapPink};
} }
#title { #title {
...@@ -37,22 +54,24 @@ const Title = styled.div` ...@@ -37,22 +54,24 @@ const Title = styled.div`
export default function Header() { export default function Header() {
return ( return (
<> <HeaderFrame>
<HeaderElement> <HeaderElement>
<Title> <Title>
<span id="image" role="img" aria-label="Unicorn Emoji"> <Nod>
🦄 <Link id="link" href="https://uniswap.io">
<span role="img" aria-label="unicorn">
🦄{' '}
</span> </span>
</Link>
</Nod>
<Link id="link" href="https://uniswap.io"> <Link id="link" href="https://uniswap.io">
<h1 id="title">Uniswap</h1> <h1 id="title">Uniswap</h1>
</Link> </Link>
</Title> </Title>
</HeaderElement> </HeaderElement>
<HeaderElement> <HeaderElement>
<Web3Status /> <Web3Status />
</HeaderElement> </HeaderElement>
</> </HeaderFrame>
) )
} }
...@@ -5,8 +5,9 @@ import { DialogOverlay, DialogContent } from '@reach/dialog' ...@@ -5,8 +5,9 @@ import { DialogOverlay, DialogContent } from '@reach/dialog'
import '@reach/dialog/styles.css' import '@reach/dialog/styles.css'
const AnimatedDialogOverlay = animated(DialogOverlay) const AnimatedDialogOverlay = animated(DialogOverlay)
const StyledDialogOverlay = styled(AnimatedDialogOverlay).attrs({ const WrappedDialogOverlay = ({ suppressClassNameWarning, ...rest }) => <AnimatedDialogOverlay {...rest} />
suppressclassnamewarning: 'true' const StyledDialogOverlay = styled(WrappedDialogOverlay).attrs({
suppressClassNameWarning: true
})` })`
&[data-reach-dialog-overlay] { &[data-reach-dialog-overlay] {
z-index: 2; z-index: 2;
...@@ -20,12 +21,14 @@ const FilteredDialogContent = ({ minHeight, ...rest }) => <DialogContent {...res ...@@ -20,12 +21,14 @@ const FilteredDialogContent = ({ minHeight, ...rest }) => <DialogContent {...res
const StyledDialogContent = styled(FilteredDialogContent)` const StyledDialogContent = styled(FilteredDialogContent)`
&[data-reach-dialog-content] { &[data-reach-dialog-content] {
margin: 0 0 2rem 0; margin: 0 0 2rem 0;
${({ theme }) => theme.mediaWidth.upToMedium`margin: 0;`} border: 1px solid ${({ theme }) => theme.concreteGray};
padding: 0; background-color: ${({ theme }) => theme.inputBackground};
${({ theme }) => theme.mediaWidth.upToMedium`margin: 0;`};
padding: 0px;
width: 50vw; width: 50vw;
max-width: 650px; max-width: 650px;
${({ theme }) => theme.mediaWidth.upToMedium`width: 65vw;`} ${({ theme }) => theme.mediaWidth.upToMedium`width: 65vw;`}
${({ theme }) => theme.mediaWidth.upToSmall`width: 80vw;`} ${({ theme }) => theme.mediaWidth.upToSmall`width: 85vw;`}
max-height: 50vh; max-height: 50vh;
${({ minHeight }) => ${({ minHeight }) =>
minHeight && minHeight &&
...@@ -36,7 +39,7 @@ const StyledDialogContent = styled(FilteredDialogContent)` ...@@ -36,7 +39,7 @@ const StyledDialogContent = styled(FilteredDialogContent)`
${({ theme }) => theme.mediaWidth.upToSmall`max-height: 80vh;`} ${({ theme }) => theme.mediaWidth.upToSmall`max-height: 80vh;`}
display: flex; display: flex;
overflow: hidden; overflow: hidden;
border-radius: 1.5rem; border-radius: 10px;
} }
` `
...@@ -50,7 +53,7 @@ const HiddenCloseButton = styled.button` ...@@ -50,7 +53,7 @@ const HiddenCloseButton = styled.button`
export default function Modal({ isOpen, onDismiss, minHeight = false, initialFocusRef, children }) { export default function Modal({ isOpen, onDismiss, minHeight = false, initialFocusRef, children }) {
const transitions = useTransition(isOpen, null, { const transitions = useTransition(isOpen, null, {
config: { duration: 125 }, config: { duration: 150 },
from: { opacity: 0 }, from: { opacity: 0 },
enter: { opacity: 1 }, enter: { opacity: 1 },
leave: { opacity: 0 } leave: { opacity: 0 }
......
...@@ -60,7 +60,7 @@ const Tabs = styled.div` ...@@ -60,7 +60,7 @@ const Tabs = styled.div`
height: 2.5rem; height: 2.5rem;
background-color: ${({ theme }) => theme.concreteGray}; background-color: ${({ theme }) => theme.concreteGray};
border-radius: 3rem; border-radius: 3rem;
box-shadow: 0 0 0 1px ${({ theme }) => darken(0.05, theme.concreteGray)}; /* border: 1px solid ${({ theme }) => theme.mercuryGray}; */
margin-bottom: 1rem; margin-bottom: 1rem;
` `
...@@ -73,6 +73,7 @@ const StyledNavLink = styled(NavLink).attrs({ ...@@ -73,6 +73,7 @@ const StyledNavLink = styled(NavLink).attrs({
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 2.5rem; height: 2.5rem;
border: 1px solid ${({ theme }) => transparentize(1, theme.mercuryGray)};
flex: 1 0 auto; flex: 1 0 auto;
border-radius: 3rem; border-radius: 3rem;
outline: none; outline: none;
...@@ -80,21 +81,24 @@ const StyledNavLink = styled(NavLink).attrs({ ...@@ -80,21 +81,24 @@ const StyledNavLink = styled(NavLink).attrs({
text-decoration: none; text-decoration: none;
color: ${({ theme }) => theme.doveGray}; color: ${({ theme }) => theme.doveGray};
font-size: 1rem; font-size: 1rem;
box-sizing: border-box;
&.${activeClassName} { &.${activeClassName} {
background-color: ${({ theme }) => theme.white}; background-color: ${({ theme }) => theme.inputBackground};
border-radius: 3rem; border-radius: 3rem;
box-shadow: 0 0 1px 1px ${({ theme }) => theme.mercuryGray}; border: 1px solid ${({ theme }) => theme.mercuryGray};
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.royalBlue)};
box-sizing: border-box;
font-weight: 500; font-weight: 500;
color: ${({ theme }) => theme.royalBlue}; color: ${({ theme }) => theme.royalBlue};
:hover { :hover {
box-shadow: 0 0 1px 1px ${({ theme }) => darken(0.1, theme.mercuryGray)}; border: 1px solid ${({ theme }) => darken(0.1, theme.mercuryGray)};
background-color: ${({ theme }) => darken(0.01, theme.inputBackground)};
} }
} }
:hover, :hover,
:focus { :focus {
font-weight: 500;
color: ${({ theme }) => darken(0.1, theme.royalBlue)}; color: ${({ theme }) => darken(0.1, theme.royalBlue)};
} }
` `
......
...@@ -3,18 +3,20 @@ import styled from 'styled-components' ...@@ -3,18 +3,20 @@ import styled from 'styled-components'
import { ReactComponent as EthereumLogo } from '../../assets/images/ethereum-logo.svg' import { ReactComponent as EthereumLogo } from '../../assets/images/ethereum-logo.svg'
const TOKEN_ICON_API = 'https://raw.githubusercontent.com/TrustWallet/tokens/master/tokens' const TOKEN_ICON_API = address =>
`https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${address}/logo.png`
const BAD_IMAGES = {} const BAD_IMAGES = {}
const Image = styled.img` const Image = styled.img`
width: ${({ size }) => size}; width: ${({ size }) => size};
height: ${({ size }) => size}; height: ${({ size }) => size};
background-color: white;
border-radius: 1rem; border-radius: 1rem;
` `
const Emoji = styled.span` const Emoji = styled.span`
width: ${({ size }) => size}; width: ${({ size }) => size};
font-size: ${({ size }) => size}; height: ${({ size }) => size};
` `
const StyledEthereumLogo = styled(EthereumLogo)` const StyledEthereumLogo = styled(EthereumLogo)`
...@@ -29,10 +31,10 @@ export default function TokenLogo({ address, size = '1rem', ...rest }) { ...@@ -29,10 +31,10 @@ export default function TokenLogo({ address, size = '1rem', ...rest }) {
if (address === 'ETH') { if (address === 'ETH') {
return <StyledEthereumLogo size={size} /> return <StyledEthereumLogo size={size} />
} else if (!error && !BAD_IMAGES[address]) { } else if (!error && !BAD_IMAGES[address]) {
path = `${TOKEN_ICON_API}/${address.toLowerCase()}.png` path = TOKEN_ICON_API(address.toLowerCase())
} else { } else {
return ( return (
<Emoji {...rest}> <Emoji {...rest} size={size}>
<span role="img" aria-label="Thinking"> <span role="img" aria-label="Thinking">
🤔 🤔
</span> </span>
......
This diff is collapsed.
...@@ -32,8 +32,8 @@ const Web3StatusGeneric = styled.button` ...@@ -32,8 +32,8 @@ const Web3StatusGeneric = styled.button`
` `
const Web3StatusError = styled(Web3StatusGeneric)` const Web3StatusError = styled(Web3StatusGeneric)`
background-color: ${({ theme }) => theme.salmonRed}; background-color: ${({ theme }) => theme.salmonRed};
color: ${({ theme }) => theme.white};
border: 1px solid ${({ theme }) => theme.salmonRed}; border: 1px solid ${({ theme }) => theme.salmonRed};
color: ${({ theme }) => theme.white};
font-weight: 500; font-weight: 500;
:hover, :hover,
:focus { :focus {
...@@ -43,9 +43,10 @@ const Web3StatusError = styled(Web3StatusGeneric)` ...@@ -43,9 +43,10 @@ const Web3StatusError = styled(Web3StatusGeneric)`
const Web3StatusConnect = styled(Web3StatusGeneric)` const Web3StatusConnect = styled(Web3StatusGeneric)`
background-color: ${({ theme }) => theme.royalBlue}; background-color: ${({ theme }) => theme.royalBlue};
color: ${({ theme }) => theme.white};
border: 1px solid ${({ theme }) => theme.royalBlue}; border: 1px solid ${({ theme }) => theme.royalBlue};
color: ${({ theme }) => theme.white};
font-weight: 500; font-weight: 500;
:hover, :hover,
:focus { :focus {
background-color: ${({ theme }) => darken(0.1, theme.royalBlue)}; background-color: ${({ theme }) => darken(0.1, theme.royalBlue)};
...@@ -53,14 +54,18 @@ const Web3StatusConnect = styled(Web3StatusGeneric)` ...@@ -53,14 +54,18 @@ const Web3StatusConnect = styled(Web3StatusGeneric)`
` `
const Web3StatusConnected = styled(Web3StatusGeneric)` const Web3StatusConnected = styled(Web3StatusGeneric)`
background-color: ${({ pending, theme }) => (pending ? theme.zumthorBlue : theme.white)}; background-color: ${({ pending, theme }) => (pending ? theme.zumthorBlue : theme.inputBackground)};
color: ${({ pending, theme }) => (pending ? theme.royalBlue : theme.doveGray)};
border: 1px solid ${({ pending, theme }) => (pending ? theme.royalBlue : theme.mercuryGray)}; border: 1px solid ${({ pending, theme }) => (pending ? theme.royalBlue : theme.mercuryGray)};
color: ${({ pending, theme }) => (pending ? theme.royalBlue : theme.doveGray)};
font-weight: 400; font-weight: 400;
:hover { :hover {
> P {
color: ${({ theme }) => theme.uniswapPink};
}
background-color: ${({ pending, theme }) => background-color: ${({ pending, theme }) =>
pending ? transparentize(0.9, theme.royalBlue) : transparentize(0.9, theme.mercuryGray)}; pending ? transparentize(0.9, theme.royalBlue) : transparentize(0.9, theme.mercuryGray)};
}
:focus { :focus {
border: 1px solid border: 1px solid
${({ pending, theme }) => (pending ? darken(0.1, theme.royalBlue) : darken(0.1, theme.mercuryGray))}; ${({ pending, theme }) => (pending ? darken(0.1, theme.royalBlue) : darken(0.1, theme.mercuryGray))};
...@@ -72,7 +77,6 @@ const Text = styled.p` ...@@ -72,7 +77,6 @@ const Text = styled.p`
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
margin: 0 0.5rem 0 0.25rem; margin: 0 0.5rem 0 0.25rem;
font-size: 0.83rem; font-size: 0.83rem;
` `
......
import React, { createContext, useContext, useReducer, useMemo, useCallback } from 'react'
import { ethers } from 'ethers'
import { getTokenReserves, getMarketDetails, BigNumber } from '@uniswap/sdk'
import { useWeb3Context } from 'web3-react'
import { safeAccess, isAddress, getEtherBalance, getTokenBalance } from '../utils'
import { useAllTokenDetails } from './Tokens'
const ZERO = ethers.utils.bigNumberify(0)
const ONE = new BigNumber(1)
const UPDATE = 'UPDATE'
const AllBalancesContext = createContext()
function useAllBalancesContext() {
return useContext(AllBalancesContext)
}
function reducer(state, { type, payload }) {
switch (type) {
case UPDATE: {
const { allBalanceData, networkId, address } = payload
return {
...state,
[networkId]: {
...(safeAccess(state, [networkId]) || {}),
[address]: {
...(safeAccess(state, [networkId, address]) || {}),
allBalanceData
}
}
}
}
default: {
throw Error(`Unexpected action type in AllBalancesContext reducer: '${type}'.`)
}
}
}
export default function Provider({ children }) {
const [state, dispatch] = useReducer(reducer, {})
const update = useCallback((allBalanceData, networkId, address) => {
dispatch({ type: UPDATE, payload: { allBalanceData, networkId, address } })
}, [])
return (
<AllBalancesContext.Provider value={useMemo(() => [state, { update }], [state, update])}>
{children}
</AllBalancesContext.Provider>
)
}
export function useFetchAllBalances() {
const { account, networkId, library } = useWeb3Context()
const allTokens = useAllTokenDetails()
const [state, { update }] = useAllBalancesContext()
const { allBalanceData } = safeAccess(state, [networkId, account]) || {}
const getData = async () => {
if (!!library && !!account) {
const newBalances = {}
await Promise.all(
Object.keys(allTokens).map(async k => {
let balance = null
let ethRate = null
if (isAddress(k) || k === 'ETH') {
if (k === 'ETH') {
balance = await getEtherBalance(account, library).catch(() => null)
ethRate = ONE
} else {
balance = await getTokenBalance(k, account, library).catch(() => null)
// only get values for tokens with positive balances
if (!!balance && balance.gt(ZERO)) {
const tokenReserves = await getTokenReserves(k, library).catch(() => null)
if (!!tokenReserves) {
const marketDetails = getMarketDetails(tokenReserves)
if (marketDetails.marketRate && marketDetails.marketRate.rate) {
ethRate = marketDetails.marketRate.rate
}
}
}
}
return (newBalances[k] = { balance, ethRate })
}
})
)
update(newBalances, networkId, account)
}
}
useMemo(getData, [account, state])
return allBalanceData
}
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react' import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { useWeb3Context } from 'web3-react' import { useWeb3Context } from 'web3-react'
import { safeAccess } from '../utils' import { safeAccess } from '../utils'
import { getUSDPrice } from '../utils/price'
const BLOCK_NUMBERS = 'BLOCK_NUMBERS' const BLOCK_NUMBER = 'BLOCK_NUMBER'
const USD_PRICE = 'USD_PRICE'
const UPDATE_BLOCK_NUMBER = 'UPDATE_BLOCK_NUMBER' const UPDATE_BLOCK_NUMBER = 'UPDATE_BLOCK_NUMBER'
const UPDATE_USD_PRICE = 'UPDATE_USD_PRICE'
const ApplicationContext = createContext() const ApplicationContext = createContext()
...@@ -18,12 +22,22 @@ function reducer(state, { type, payload }) { ...@@ -18,12 +22,22 @@ function reducer(state, { type, payload }) {
const { networkId, blockNumber } = payload const { networkId, blockNumber } = payload
return { return {
...state, ...state,
[BLOCK_NUMBERS]: { [BLOCK_NUMBER]: {
...(safeAccess(state, [BLOCK_NUMBERS]) || {}), ...(safeAccess(state, [BLOCK_NUMBER]) || {}),
[networkId]: blockNumber [networkId]: blockNumber
} }
} }
} }
case UPDATE_USD_PRICE: {
const { networkId, USDPrice } = payload
return {
...state,
[USD_PRICE]: {
...(safeAccess(state, [USD_PRICE]) || {}),
[networkId]: USDPrice
}
}
}
default: { default: {
throw Error(`Unexpected action type in ApplicationContext reducer: '${type}'.`) throw Error(`Unexpected action type in ApplicationContext reducer: '${type}'.`)
} }
...@@ -32,15 +46,22 @@ function reducer(state, { type, payload }) { ...@@ -32,15 +46,22 @@ function reducer(state, { type, payload }) {
export default function Provider({ children }) { export default function Provider({ children }) {
const [state, dispatch] = useReducer(reducer, { const [state, dispatch] = useReducer(reducer, {
[BLOCK_NUMBERS]: {} [BLOCK_NUMBER]: {},
[USD_PRICE]: {}
}) })
const updateBlockNumber = useCallback((networkId, blockNumber) => { const updateBlockNumber = useCallback((networkId, blockNumber) => {
dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } }) dispatch({ type: UPDATE_BLOCK_NUMBER, payload: { networkId, blockNumber } })
}, []) }, [])
const updateUSDPrice = useCallback((networkId, USDPrice) => {
dispatch({ type: UPDATE_USD_PRICE, payload: { networkId, USDPrice } })
}, [])
return ( return (
<ApplicationContext.Provider value={useMemo(() => [state, { updateBlockNumber }], [state, updateBlockNumber])}> <ApplicationContext.Provider
value={useMemo(() => [state, { updateBlockNumber, updateUSDPrice }], [state, updateBlockNumber, updateUSDPrice])}
>
{children} {children}
</ApplicationContext.Provider> </ApplicationContext.Provider>
) )
...@@ -49,7 +70,24 @@ export default function Provider({ children }) { ...@@ -49,7 +70,24 @@ export default function Provider({ children }) {
export function Updater() { export function Updater() {
const { networkId, library } = useWeb3Context() const { networkId, library } = useWeb3Context()
const [, { updateBlockNumber }] = useApplicationContext() const globalBlockNumber = useBlockNumber()
const [, { updateBlockNumber, updateUSDPrice }] = useApplicationContext()
useEffect(() => {
let stale = false
getUSDPrice(library)
.then(([price]) => {
if (!stale) {
updateUSDPrice(networkId, price)
}
})
.catch(() => {
if (!stale) {
updateUSDPrice(networkId, null)
}
})
}, [globalBlockNumber, library, networkId, updateUSDPrice])
useEffect(() => { useEffect(() => {
if ((networkId || networkId === 0) && library) { if ((networkId || networkId === 0) && library) {
...@@ -88,5 +126,13 @@ export function useBlockNumber() { ...@@ -88,5 +126,13 @@ export function useBlockNumber() {
const [state] = useApplicationContext() const [state] = useApplicationContext()
return safeAccess(state, [BLOCK_NUMBERS, networkId]) return safeAccess(state, [BLOCK_NUMBER, networkId])
}
export function useUSDPrice() {
const { networkId } = useWeb3Context()
const [state] = useApplicationContext()
return safeAccess(state, [USD_PRICE, networkId])
} }
...@@ -79,7 +79,6 @@ export function useAddressBalance(address, tokenAddress) { ...@@ -79,7 +79,6 @@ export function useAddressBalance(address, tokenAddress) {
update(networkId, address, tokenAddress, null, globalBlockNumber) update(networkId, address, tokenAddress, null, globalBlockNumber)
} }
}) })
return () => { return () => {
stale = true stale = true
} }
......
...@@ -7,7 +7,9 @@ const CURRENT_VERSION = 0 ...@@ -7,7 +7,9 @@ const CURRENT_VERSION = 0
const LAST_SAVED = 'LAST_SAVED' const LAST_SAVED = 'LAST_SAVED'
const BETA_MESSAGE_DISMISSED = 'BETA_MESSAGE_DISMISSED' const BETA_MESSAGE_DISMISSED = 'BETA_MESSAGE_DISMISSED'
const UPDATABLE_KEYS = [BETA_MESSAGE_DISMISSED] const DARK_MODE = 'DARK_MODE'
const UPDATABLE_KEYS = [BETA_MESSAGE_DISMISSED, DARK_MODE]
const UPDATE_KEY = 'UPDATE_KEY' const UPDATE_KEY = 'UPDATE_KEY'
...@@ -39,7 +41,8 @@ function reducer(state, { type, payload }) { ...@@ -39,7 +41,8 @@ function reducer(state, { type, payload }) {
function init() { function init() {
const defaultLocalStorage = { const defaultLocalStorage = {
[VERSION]: CURRENT_VERSION, [VERSION]: CURRENT_VERSION,
[BETA_MESSAGE_DISMISSED]: false [BETA_MESSAGE_DISMISSED]: false,
[DARK_MODE]: false
} }
try { try {
...@@ -48,7 +51,7 @@ function init() { ...@@ -48,7 +51,7 @@ function init() {
// this is where we could run migration logic // this is where we could run migration logic
return defaultLocalStorage return defaultLocalStorage
} else { } else {
return parsed return { ...defaultLocalStorage, ...parsed }
} }
} catch { } catch {
return defaultLocalStorage return defaultLocalStorage
...@@ -88,3 +91,15 @@ export function useBetaMessageManager() { ...@@ -88,3 +91,15 @@ export function useBetaMessageManager() {
return [!state[BETA_MESSAGE_DISMISSED], dismissBetaMessage] return [!state[BETA_MESSAGE_DISMISSED], dismissBetaMessage]
} }
export function useDarkModeManager() {
const [state, { updateKey }] = useLocalStorageContext()
const isDarkMode = state[DARK_MODE]
const toggleDarkMode = useCallback(() => {
updateKey(DARK_MODE, !isDarkMode)
}, [updateKey, isDarkMode])
return [state[DARK_MODE], toggleDarkMode]
}
...@@ -89,14 +89,12 @@ const INITIAL_TOKENS_CONTEXT = { ...@@ -89,14 +89,12 @@ const INITIAL_TOKENS_CONTEXT = {
[DECIMALS]: 9, [DECIMALS]: 9,
[EXCHANGE_ADDRESS]: '0xb92dE8B30584392Af27726D5ce04Ef3c4e5c9924' [EXCHANGE_ADDRESS]: '0xb92dE8B30584392Af27726D5ce04Ef3c4e5c9924'
}, },
'0xc719d010B63E5bbF2C0551872CD5316ED26AcD83': { '0xc719d010B63E5bbF2C0551872CD5316ED26AcD83': {
[NAME]: 'Decentralized Insurance Protocol', [NAME]: 'Decentralized Insurance Protocol',
[SYMBOL]: 'DIP', [SYMBOL]: 'DIP',
[DECIMALS]: 18, [DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0x61792F290e5100FBBcBb2309F03A1Bab869fb850' [EXCHANGE_ADDRESS]: '0x61792F290e5100FBBcBb2309F03A1Bab869fb850'
}, },
'0x4946Fcea7C692606e8908002e55A582af44AC121': { '0x4946Fcea7C692606e8908002e55A582af44AC121': {
[NAME]: 'FOAM Token', [NAME]: 'FOAM Token',
[SYMBOL]: 'FOAM', [SYMBOL]: 'FOAM',
...@@ -247,6 +245,12 @@ const INITIAL_TOKENS_CONTEXT = { ...@@ -247,6 +245,12 @@ const INITIAL_TOKENS_CONTEXT = {
[DECIMALS]: 18, [DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0x755899F0540c3548b99E68C59AdB0f15d2695188' [EXCHANGE_ADDRESS]: '0x755899F0540c3548b99E68C59AdB0f15d2695188'
}, },
'0xF970b8E36e23F7fC3FD752EeA86f8Be8D83375A6': {
[NAME]: 'Ripio Credit Network Token',
[SYMBOL]: 'RCN',
[DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0xD91FF16Ef92568fC27F466C3c5613e43313Ab1dc'
},
'0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6': { '0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6': {
[NAME]: 'Raiden Token', [NAME]: 'Raiden Token',
[SYMBOL]: 'RDN', [SYMBOL]: 'RDN',
...@@ -301,11 +305,11 @@ const INITIAL_TOKENS_CONTEXT = { ...@@ -301,11 +305,11 @@ const INITIAL_TOKENS_CONTEXT = {
[DECIMALS]: 18, [DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0x1aEC8F11A7E78dC22477e91Ed924Fab46e3A88Fd' [EXCHANGE_ADDRESS]: '0x1aEC8F11A7E78dC22477e91Ed924Fab46e3A88Fd'
}, },
'0x2Dea20405c52Fb477ecCa8Fe622661d316Ac5400': { '0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F': {
[NAME]: 'Synthetix Network Token', [NAME]: 'Synthetix Network Token',
[SYMBOL]: 'SNX', [SYMBOL]: 'SNX',
[DECIMALS]: 18, [DECIMALS]: 18,
[EXCHANGE_ADDRESS]: '0x9fAA0Cb10912DE7Ad1D86705C65de291a9088A61' [EXCHANGE_ADDRESS]: '0x3958B4eC427F8fa24eB60F42821760e88d485f7F'
}, },
'0x42d6622deCe394b54999Fbd73D108123806f6a18': { '0x42d6622deCe394b54999Fbd73D108123806f6a18': {
[NAME]: 'SPANK', [NAME]: 'SPANK',
...@@ -462,7 +466,6 @@ export function useTokenDetails(tokenAddress) { ...@@ -462,7 +466,6 @@ export function useTokenDetails(tokenAddress) {
} }
} }
) )
return () => { return () => {
stale = true stale = true
} }
......
...@@ -58,9 +58,8 @@ export function useENSName(address) { ...@@ -58,9 +58,8 @@ export function useENSName(address) {
useEffect(() => { useEffect(() => {
if (isAddress(address)) { if (isAddress(address)) {
let stale = false let stale = false
library try {
.lookupAddress(address) library.lookupAddress(address).then(name => {
.then(name => {
if (!stale) { if (!stale) {
if (name) { if (name) {
setENSNname(name) setENSNname(name)
...@@ -69,11 +68,9 @@ export function useENSName(address) { ...@@ -69,11 +68,9 @@ export function useENSName(address) {
} }
} }
}) })
.catch(() => { } catch {
if (!stale) {
setENSNname(null) setENSNname(null)
} }
})
return () => { return () => {
stale = true stale = true
......
...@@ -10,6 +10,7 @@ import TransactionContextProvider, { Updater as TransactionContextUpdater } from ...@@ -10,6 +10,7 @@ import TransactionContextProvider, { Updater as TransactionContextUpdater } from
import TokensContextProvider from './contexts/Tokens' import TokensContextProvider from './contexts/Tokens'
import BalancesContextProvider from './contexts/Balances' import BalancesContextProvider from './contexts/Balances'
import AllowancesContextProvider from './contexts/Allowances' import AllowancesContextProvider from './contexts/Allowances'
import AllBalancesContextProvider from './contexts/AllBalances'
import App from './pages/App' import App from './pages/App'
import InjectedConnector from './InjectedConnector' import InjectedConnector from './InjectedConnector'
...@@ -35,7 +36,9 @@ function ContextProviders({ children }) { ...@@ -35,7 +36,9 @@ function ContextProviders({ children }) {
<TransactionContextProvider> <TransactionContextProvider>
<TokensContextProvider> <TokensContextProvider>
<BalancesContextProvider> <BalancesContextProvider>
<AllBalancesContextProvider>
<AllowancesContextProvider>{children}</AllowancesContextProvider> <AllowancesContextProvider>{children}</AllowancesContextProvider>
</AllBalancesContextProvider>
</BalancesContextProvider> </BalancesContextProvider>
</TokensContextProvider> </TokensContextProvider>
</TransactionContextProvider> </TransactionContextProvider>
...@@ -55,16 +58,16 @@ function Updaters() { ...@@ -55,16 +58,16 @@ function Updaters() {
} }
ReactDOM.render( ReactDOM.render(
<ThemeProvider>
<>
<GlobalStyle />
<Web3Provider connectors={connectors} libraryName="ethers.js"> <Web3Provider connectors={connectors} libraryName="ethers.js">
<ContextProviders> <ContextProviders>
<Updaters /> <Updaters />
<ThemeProvider>
<>
<GlobalStyle />
<App /> <App />
</ContextProviders>
</Web3Provider>
</> </>
</ThemeProvider>, </ThemeProvider>
</ContextProviders>
</Web3Provider>,
document.getElementById('root') document.getElementById('root')
) )
...@@ -4,6 +4,8 @@ import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom' ...@@ -4,6 +4,8 @@ import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'
import Web3ReactManager from '../components/Web3ReactManager' import Web3ReactManager from '../components/Web3ReactManager'
import Header from '../components/Header' import Header from '../components/Header'
import Footer from '../components/Footer'
import NavigationTabs from '../components/NavigationTabs' import NavigationTabs from '../components/NavigationTabs'
import { isAddress } from '../utils' import { isAddress } from '../utils'
...@@ -11,24 +13,38 @@ const Swap = lazy(() => import('./Swap')) ...@@ -11,24 +13,38 @@ const Swap = lazy(() => import('./Swap'))
const Send = lazy(() => import('./Send')) const Send = lazy(() => import('./Send'))
const Pool = lazy(() => import('./Pool')) const Pool = lazy(() => import('./Pool'))
const AppWrapper = styled.div`
display: flex;
flex-flow: column;
align-items: flex-start;
height: 100vh;
`
const HeaderWrapper = styled.div` const HeaderWrapper = styled.div`
${({ theme }) => theme.flexRowNoWrap} ${({ theme }) => theme.flexRowNoWrap}
width: 100%; width: 100%;
justify-content: space-between; justify-content: space-between;
` `
const FooterWrapper = styled.div`
width: 100%;
min-height: 30px;
align-self: flex-end;
`
const BodyWrapper = styled.div` const BodyWrapper = styled.div`
${({ theme }) => theme.flexRowNoWrap} display: flex;
flex-direction: column;
width: 100%; width: 100%;
justify-content: center; justify-content: flex-start;
flex-grow: 1; align-items: center;
flex-basis: 0; flex: 1;
overflow: auto; overflow: auto;
` `
const Body = styled.div` const Body = styled.div`
width: 35rem; max-width: 35rem;
margin: 1.25rem; width: 90%;
/* margin: 0 1.25rem 1.25rem 1.25rem; */
` `
export default function App() { export default function App() {
......
...@@ -9,12 +9,13 @@ import { Button } from '../../theme' ...@@ -9,12 +9,13 @@ import { Button } from '../../theme'
import CurrencyInputPanel from '../../components/CurrencyInputPanel' import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import OversizedPanel from '../../components/OversizedPanel' import OversizedPanel from '../../components/OversizedPanel'
import ContextualInfo from '../../components/ContextualInfo' import ContextualInfo from '../../components/ContextualInfo'
import PlusBlue from '../../assets/images/plus-blue.svg' import { ReactComponent as Plus } from '../../assets/images/plus-blue.svg'
import PlusGrey from '../../assets/images/plus-grey.svg'
import { useExchangeContract } from '../../hooks' import { useExchangeContract } from '../../hooks'
import { amountFormatter, calculateGasMargin } from '../../utils' import { amountFormatter, calculateGasMargin } from '../../utils'
import { useTransactionAdder } from '../../contexts/Transactions' import { useTransactionAdder } from '../../contexts/Transactions'
import { useTokenDetails } from '../../contexts/Tokens' import { useTokenDetails } from '../../contexts/Tokens'
import { useFetchAllBalances } from '../../contexts/AllBalances'
import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances' import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances'
import { useAddressAllowance } from '../../contexts/Allowances' import { useAddressAllowance } from '../../contexts/Allowances'
...@@ -63,14 +64,6 @@ const DownArrowBackground = styled.div` ...@@ -63,14 +64,6 @@ const DownArrowBackground = styled.div`
justify-content: center; justify-content: center;
align-items: center; align-items: center;
` `
const DownArrow = styled.img`
width: 0.625rem;
height: 0.625rem;
position: relative;
padding: 0.875rem;
`
const SummaryPanel = styled.div` const SummaryPanel = styled.div`
${({ theme }) => theme.flexColumnNoWrap} ${({ theme }) => theme.flexColumnNoWrap}
padding: 1rem 0; padding: 1rem 0;
...@@ -87,7 +80,7 @@ const ExchangeRateWrapper = styled.div` ...@@ -87,7 +80,7 @@ const ExchangeRateWrapper = styled.div`
const ExchangeRate = styled.span` const ExchangeRate = styled.span`
flex: 1 1 auto; flex: 1 1 auto;
width: 0; width: 0;
color: ${({ theme }) => theme.chaliceGray}; color: ${({ theme }) => theme.doveGray};
` `
const Flex = styled.div` const Flex = styled.div`
...@@ -100,6 +93,17 @@ const Flex = styled.div` ...@@ -100,6 +93,17 @@ const Flex = styled.div`
} }
` `
const WrappedPlus = ({ isError, highSlippageWarning, ...rest }) => <Plus {...rest} />
const ColoredWrappedPlus = styled(WrappedPlus)`
width: 0.625rem;
height: 0.625rem;
position: relative;
padding: 0.875rem;
path {
stroke: ${({ active, theme }) => (active ? theme.royalBlue : theme.chaliceGray)};
}
`
function calculateSlippageBounds(value) { function calculateSlippageBounds(value) {
if (value) { if (value) {
const offset = value.mul(ALLOWED_SLIPPAGE).div(ethers.utils.bigNumberify(10000)) const offset = value.mul(ALLOWED_SLIPPAGE).div(ethers.utils.bigNumberify(10000))
...@@ -531,6 +535,8 @@ export default function AddLiquidity() { ...@@ -531,6 +535,8 @@ export default function AddLiquidity() {
const isActive = active && account const isActive = active && account
const isValid = (inputError === null || outputError === null) && !showUnlock const isValid = (inputError === null || outputError === null) && !showUnlock
const allBalances = useFetchAllBalances()
return ( return (
<> <>
{isNewExchange ? ( {isNewExchange ? (
...@@ -547,6 +553,7 @@ export default function AddLiquidity() { ...@@ -547,6 +553,7 @@ export default function AddLiquidity() {
<CurrencyInputPanel <CurrencyInputPanel
title={t('deposit')} title={t('deposit')}
allBalances={allBalances}
extraText={inputBalance && formatBalance(amountFormatter(inputBalance, 18, 4))} extraText={inputBalance && formatBalance(amountFormatter(inputBalance, 18, 4))}
onValueChange={inputValue => { onValueChange={inputValue => {
dispatchAddLiquidityState({ type: 'UPDATE_VALUE', payload: { value: inputValue, field: INPUT } }) dispatchAddLiquidityState({ type: 'UPDATE_VALUE', payload: { value: inputValue, field: INPUT } })
...@@ -558,11 +565,12 @@ export default function AddLiquidity() { ...@@ -558,11 +565,12 @@ export default function AddLiquidity() {
/> />
<OversizedPanel> <OversizedPanel>
<DownArrowBackground> <DownArrowBackground>
<DownArrow src={isActive ? PlusBlue : PlusGrey} alt="plus" /> <ColoredWrappedPlus active={isActive} alt="plus" />
</DownArrowBackground> </DownArrowBackground>
</OversizedPanel> </OversizedPanel>
<CurrencyInputPanel <CurrencyInputPanel
title={t('deposit')} title={t('deposit')}
allBalances={allBalances}
description={isNewExchange ? '' : outputValue ? `(${t('estimated')})` : ''} description={isNewExchange ? '' : outputValue ? `(${t('estimated')})` : ''}
extraText={outputBalance && formatBalance(amountFormatter(outputBalance, decimals, Math.min(decimals, 4)))} extraText={outputBalance && formatBalance(amountFormatter(outputBalance, decimals, Math.min(decimals, 4)))}
selectedTokenAddress={outputCurrency} selectedTokenAddress={outputCurrency}
......
...@@ -29,7 +29,7 @@ const ExchangeRateWrapper = styled.div` ...@@ -29,7 +29,7 @@ const ExchangeRateWrapper = styled.div`
const ExchangeRate = styled.span` const ExchangeRate = styled.span`
flex: 1 1 auto; flex: 1 1 auto;
width: 0; width: 0;
color: ${({ theme }) => theme.chaliceGray}; color: ${({ theme }) => theme.doveGray};
` `
const CreateExchangeWrapper = styled.div` const CreateExchangeWrapper = styled.div`
......
...@@ -4,10 +4,13 @@ import { useTranslation } from 'react-i18next' ...@@ -4,10 +4,13 @@ import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import OversizedPanel from '../../components/OversizedPanel' import OversizedPanel from '../../components/OversizedPanel'
import Dropdown from '../../assets/images/dropdown-blue.svg' import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg'
import Modal from '../../components/Modal' import Modal from '../../components/Modal'
import { useBodyKeyDown } from '../../hooks' import { useBodyKeyDown } from '../../hooks'
import { lighten } from 'polished'
const poolTabOrder = [ const poolTabOrder = [
{ {
path: '/add-liquidity', path: '/add-liquidity',
...@@ -29,13 +32,16 @@ const poolTabOrder = [ ...@@ -29,13 +32,16 @@ const poolTabOrder = [
const LiquidityContainer = styled.div` const LiquidityContainer = styled.div`
${({ theme }) => theme.flexRowNoWrap}; ${({ theme }) => theme.flexRowNoWrap};
align-items: center; align-items: center;
font-size: 0.75rem; padding: 1rem 1rem;
padding: 0.625rem 1rem; font-size: 1rem;
font-size: 0.75rem;
color: ${({ theme }) => theme.royalBlue}; color: ${({ theme }) => theme.royalBlue};
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
:hover {
color: ${({ theme }) => lighten(0.1, theme.royalBlue)};
}
img { img {
height: 0.75rem; height: 0.75rem;
width: 0.75rem; width: 0.75rem;
...@@ -62,21 +68,28 @@ const StyledNavLink = styled(NavLink).attrs({ ...@@ -62,21 +68,28 @@ const StyledNavLink = styled(NavLink).attrs({
font-size: 1rem; font-size: 1rem;
&.${activeClassName} { &.${activeClassName} {
background-color: ${({ theme }) => theme.white}; background-color: ${({ theme }) => theme.inputBackground};
border-radius: 3rem; border-radius: 3rem;
box-shadow: 0 0 1px 1px ${({ theme }) => theme.mercuryGray}; border: 1px solid ${({ theme }) => theme.mercuryGray};
font-weight: 500; font-weight: 500;
color: ${({ theme }) => theme.royalBlue}; color: ${({ theme }) => theme.royalBlue};
} }
` `
const PoolModal = styled.div` const PoolModal = styled.div`
background-color: ${({ theme }) => theme.white}; background-color: ${({ theme }) => theme.inputBackground};
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 2rem 0 2rem 0; padding: 2rem 0 2rem 0;
` `
const WrappedDropdown = ({ isError, highSlippageWarning, ...rest }) => <Dropdown {...rest} />
const ColoredDropdown = styled(WrappedDropdown)`
path {
stroke: ${({ theme }) => theme.royalBlue};
}
`
function ModeSelector({ location: { pathname }, history }) { function ModeSelector({ location: { pathname }, history }) {
const { t } = useTranslation() const { t } = useTranslation()
...@@ -109,7 +122,7 @@ function ModeSelector({ location: { pathname }, history }) { ...@@ -109,7 +122,7 @@ function ModeSelector({ location: { pathname }, history }) {
}} }}
> >
<LiquidityLabel>{t(activeTabKey)}</LiquidityLabel> <LiquidityLabel>{t(activeTabKey)}</LiquidityLabel>
<img src={Dropdown} alt="dropdown" /> <ColoredDropdown alt="arrow down" />
</LiquidityContainer> </LiquidityContainer>
<Modal <Modal
isOpen={modalIsOpen} isOpen={modalIsOpen}
......
...@@ -9,12 +9,13 @@ import { Button } from '../../theme' ...@@ -9,12 +9,13 @@ import { Button } from '../../theme'
import CurrencyInputPanel from '../../components/CurrencyInputPanel' import CurrencyInputPanel from '../../components/CurrencyInputPanel'
import ContextualInfo from '../../components/ContextualInfo' import ContextualInfo from '../../components/ContextualInfo'
import OversizedPanel from '../../components/OversizedPanel' import OversizedPanel from '../../components/OversizedPanel'
import ArrowDownBlue from '../../assets/images/arrow-down-blue.svg' import ArrowDown from '../../assets/svg/SVGArrowDown'
import ArrowDownGrey from '../../assets/images/arrow-down-grey.svg'
import { useExchangeContract } from '../../hooks' import { useExchangeContract } from '../../hooks'
import { useTransactionAdder } from '../../contexts/Transactions' import { useTransactionAdder } from '../../contexts/Transactions'
import { useTokenDetails } from '../../contexts/Tokens' import { useTokenDetails } from '../../contexts/Tokens'
import { useAddressBalance } from '../../contexts/Balances' import { useAddressBalance } from '../../contexts/Balances'
import { useFetchAllBalances } from '../../contexts/AllBalances'
import { calculateGasMargin, amountFormatter } from '../../utils' import { calculateGasMargin, amountFormatter } from '../../utils'
// denominated in bips // denominated in bips
...@@ -36,7 +37,9 @@ const DownArrowBackground = styled.div` ...@@ -36,7 +37,9 @@ const DownArrowBackground = styled.div`
align-items: center; align-items: center;
` `
const DownArrow = styled.img` const DownArrow = styled(ArrowDown)`
${({ theme }) => theme.flexRowNoWrap}
color: ${({ theme, active }) => (active ? theme.royalBlue : theme.doveGray)};
width: 0.625rem; width: 0.625rem;
height: 0.625rem; height: 0.625rem;
position: relative; position: relative;
...@@ -80,7 +83,7 @@ const ExchangeRateWrapper = styled.div` ...@@ -80,7 +83,7 @@ const ExchangeRateWrapper = styled.div`
const ExchangeRate = styled.span` const ExchangeRate = styled.span`
flex: 1 1 auto; flex: 1 1 auto;
width: 0; width: 0;
color: ${({ theme }) => theme.chaliceGray}; color: ${({ theme }) => theme.doveGray};
` `
const Flex = styled.div` const Flex = styled.div`
...@@ -326,10 +329,13 @@ export default function RemoveLiquidity() { ...@@ -326,10 +329,13 @@ export default function RemoveLiquidity() {
const marketRate = getMarketRate(exchangeETHBalance, exchangeTokenBalance, decimals) const marketRate = getMarketRate(exchangeETHBalance, exchangeTokenBalance, decimals)
const allBalances = useFetchAllBalances()
return ( return (
<> <>
<CurrencyInputPanel <CurrencyInputPanel
title={t('poolTokens')} title={t('poolTokens')}
allBalances={allBalances}
extraText={poolTokenBalance && formatBalance(amountFormatter(poolTokenBalance, 18, 4))} extraText={poolTokenBalance && formatBalance(amountFormatter(poolTokenBalance, 18, 4))}
extraTextClickHander={() => { extraTextClickHander={() => {
if (poolTokenBalance) { if (poolTokenBalance) {
...@@ -347,11 +353,12 @@ export default function RemoveLiquidity() { ...@@ -347,11 +353,12 @@ export default function RemoveLiquidity() {
/> />
<OversizedPanel> <OversizedPanel>
<DownArrowBackground> <DownArrowBackground>
<DownArrow src={isActive ? ArrowDownBlue : ArrowDownGrey} alt="arrow" /> <DownArrow active={isActive} alt="arrow" />
</DownArrowBackground> </DownArrowBackground>
</OversizedPanel> </OversizedPanel>
<CurrencyInputPanel <CurrencyInputPanel
title={t('output')} title={t('output')}
allBalances={allBalances}
description={!!(ethWithdrawn && tokenWithdrawn) ? `(${t('estimated')})` : ''} description={!!(ethWithdrawn && tokenWithdrawn) ? `(${t('estimated')})` : ''}
key="remove-liquidity-input" key="remove-liquidity-input"
renderInput={() => renderInput={() =>
......
...@@ -12,8 +12,8 @@ export const Button = styled.button.attrs(({ warning, theme }) => ({ ...@@ -12,8 +12,8 @@ export const Button = styled.button.attrs(({ warning, theme }) => ({
border: none; border: none;
outline: none; outline: none;
background-color: ${({ backgroundColor }) => backgroundColor}; background-color: ${({ backgroundColor }) => backgroundColor};
transition: background-color 150ms ease-out;
color: ${({ theme }) => theme.white}; color: ${({ theme }) => theme.white};
transition: background-color 125ms ease-in-out;
width: 100%; width: 100%;
:hover, :hover,
...@@ -26,7 +26,8 @@ export const Button = styled.button.attrs(({ warning, theme }) => ({ ...@@ -26,7 +26,8 @@ export const Button = styled.button.attrs(({ warning, theme }) => ({
} }
:disabled { :disabled {
background-color: ${({ theme }) => theme.mercuryGray}; background-color: ${({ theme }) => theme.concreteGray};
color: ${({ theme }) => theme.silverGray};
cursor: auto; cursor: auto;
} }
` `
...@@ -50,12 +51,13 @@ export const Link = styled.a.attrs({ ...@@ -50,12 +51,13 @@ export const Link = styled.a.attrs({
` `
export const BorderlessInput = styled.input` export const BorderlessInput = styled.input`
color: ${({ theme }) => theme.mineshaftGray}; color: ${({ theme }) => theme.textColor};
font-size: 1rem; font-size: 1rem;
outline: none; outline: none;
border: none; border: none;
flex: 1 1 auto; flex: 1 1 auto;
width: 0; width: 0;
background-color: ${({ theme }) => theme.inputBackground};
[type='number'] { [type='number'] {
-moz-appearance: textfield; -moz-appearance: textfield;
...@@ -67,7 +69,7 @@ export const BorderlessInput = styled.input` ...@@ -67,7 +69,7 @@ export const BorderlessInput = styled.input`
} }
::placeholder { ::placeholder {
color: ${({ theme }) => theme.mercuryGray}; color: ${({ theme }) => theme.chaliceGray};
} }
` `
......
import React from 'react' import React from 'react'
import { ThemeProvider as StyledComponentsThemeProvider, createGlobalStyle, css } from 'styled-components' import { ThemeProvider as StyledComponentsThemeProvider, createGlobalStyle, css } from 'styled-components'
import { useDarkModeManager } from '../contexts/LocalStorage'
export * from './components' export * from './components'
...@@ -28,23 +29,36 @@ const flexRowNoWrap = css` ...@@ -28,23 +29,36 @@ const flexRowNoWrap = css`
flex-flow: row nowrap; flex-flow: row nowrap;
` `
const theme = { const white = '#FFFFFF'
white: '#FFFFFF', const black = '#000000'
black: '#000000',
const theme = darkMode => ({
white,
black,
textColor: darkMode ? white : '#010101',
// for setting css on <html>
backgroundColor: darkMode ? '#333639' : white,
inputBackground: darkMode ? '#202124' : white,
placeholderGray: darkMode ? '#5F5F5F' : '#E1E1E1',
// grays // grays
concreteGray: '#FAFAFA', concreteGray: darkMode ? '#292C2F' : '#FAFAFA',
mercuryGray: '#E1E1E1', mercuryGray: darkMode ? '#333333' : '#E1E1E1',
silverGray: '#C4C4C4', silverGray: darkMode ? '#737373' : '#C4C4C4',
chaliceGray: '#AEAEAE', chaliceGray: darkMode ? '#7B7B7B' : '#AEAEAE',
doveGray: '#737373', doveGray: darkMode ? '#C4C4C4' : '#737373',
mineshaftGray: '#2B2B2B', mineshaftGray: darkMode ? '#E1E1E1' : '#2B2B2B',
buttonOutlineGrey: '#f2f2f2', buttonOutlineGrey: darkMode ? '#FAFAFA' : '#F2F2F2',
tokenRowHover: darkMode ? '#404040' : '#F2F2F2',
//blacks //blacks
charcoalBlack: '#404040', charcoalBlack: darkMode ? '#F2F2F2' : '#404040',
// blues // blues
zumthorBlue: '#EBF4FF', zumthorBlue: darkMode ? '#212529' : '#EBF4FF',
malibuBlue: '#5CA2FF', malibuBlue: darkMode ? '#E67AEF' : '#5CA2FF',
royalBlue: '#2F80ED', royalBlue: darkMode ? '#DC6BE5' : '#2F80ED',
loadingBlue: darkMode ? '#e4f0ff' : '#e4f0ff',
// purples // purples
wisteriaPurple: '#DC6BE5', wisteriaPurple: '#DC6BE5',
// reds // reds
...@@ -57,15 +71,20 @@ const theme = { ...@@ -57,15 +71,20 @@ const theme = {
uniswapPink: '#DC6BE5', uniswapPink: '#DC6BE5',
connectedGreen: '#27AE60', connectedGreen: '#27AE60',
//specific
textHover: darkMode ? theme.uniswapPink : theme.doveGray,
// media queries // media queries
mediaWidth: mediaWidthTemplates, mediaWidth: mediaWidthTemplates,
// css snippets // css snippets
flexColumnNoWrap, flexColumnNoWrap,
flexRowNoWrap flexRowNoWrap
} })
export default function ThemeProvider({ children }) { export default function ThemeProvider({ children }) {
return <StyledComponentsThemeProvider theme={theme}>{children}</StyledComponentsThemeProvider> const [darkMode] = useDarkModeManager()
return <StyledComponentsThemeProvider theme={theme(darkMode)}>{children}</StyledComponentsThemeProvider>
} }
export const GlobalStyle = createGlobalStyle` export const GlobalStyle = createGlobalStyle`
...@@ -79,20 +98,25 @@ export const GlobalStyle = createGlobalStyle` ...@@ -79,20 +98,25 @@ export const GlobalStyle = createGlobalStyle`
body { body {
margin: 0; margin: 0;
padding: 0; padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
body > div {
height: 100%;
overflow: scroll;
-webkit-overflow-scrolling: touch;
}
html {
font-size: 16px; font-size: 16px;
font-variant: none; font-variant: none;
color: ${({ theme }) => theme.textColor};
background-color: ${({ theme }) => theme.backgroundColor};
transition: color 150ms ease-out, background-color 150ms ease-out;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
} }
#root {
${({ theme }) => theme.flexColumnNoWrap}
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
overflow-x: hidden;
overflow-y: auto;
}
` `
...@@ -5,6 +5,7 @@ import EXCHANGE_ABI from '../constants/abis/exchange' ...@@ -5,6 +5,7 @@ import EXCHANGE_ABI from '../constants/abis/exchange'
import ERC20_ABI from '../constants/abis/erc20' import ERC20_ABI from '../constants/abis/erc20'
import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32' import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32'
import { FACTORY_ADDRESSES } from '../constants' import { FACTORY_ADDRESSES } from '../constants'
import { formatFixed } from '@uniswap/sdk'
import UncheckedJsonRpcSigner from './signer' import UncheckedJsonRpcSigner from './signer'
...@@ -178,10 +179,27 @@ export async function getEtherBalance(address, library) { ...@@ -178,10 +179,27 @@ export async function getEtherBalance(address, library) {
if (!isAddress(address)) { if (!isAddress(address)) {
throw Error(`Invalid 'address' parameter '${address}'`) throw Error(`Invalid 'address' parameter '${address}'`)
} }
return library.getBalance(address) return library.getBalance(address)
} }
export function formatEthBalance(balance) {
return amountFormatter(balance, 18, 6)
}
export function formatTokenBalance(balance, decimal) {
return !!(balance && Number.isInteger(decimal)) ? amountFormatter(balance, decimal, Math.min(4, decimal)) : 0
}
export function formatToUsd(price) {
const format = { decimalSeparator: '.', groupSeparator: ',', groupSize: 3 }
const usdPrice = formatFixed(price, {
decimalPlaces: 2,
dropTrailingZeros: false,
format
})
return usdPrice
}
// get the token balance of an address // get the token balance of an address
export async function getTokenBalance(tokenAddress, address, library) { export async function getTokenBalance(tokenAddress, address, library) {
if (!isAddress(tokenAddress) || !isAddress(address)) { if (!isAddress(tokenAddress) || !isAddress(address)) {
......
import { BigNumber } from '@uniswap/sdk'
// returns a deep copied + sorted list of values, as well as a sortmap
export function sortBigNumbers(values) {
const valueMap = values.map((value, i) => ({ value, i }))
valueMap.sort((a, b) => {
if (a.value.isGreaterThan(b.value)) {
return 1
} else if (a.value.isLessThan(b.value)) {
return -1
} else {
return 0
}
})
return [
valueMap.map(element => values[element.i]),
values.map((_, i) => valueMap.findIndex(element => element.i === i))
]
}
export function getMedian(values) {
const [sortedValues, sortMap] = sortBigNumbers(values)
if (values.length % 2 === 0) {
const middle = values.length / 2
const indices = [middle - 1, middle]
return [
sortedValues[middle - 1].plus(sortedValues[middle]).dividedBy(2),
sortMap.map(element => (indices.includes(element) ? new BigNumber(0.5) : new BigNumber(0)))
]
} else {
const middle = Math.floor(values.length / 2)
return [sortedValues[middle], sortMap.map(element => (element === middle ? new BigNumber(1) : new BigNumber(0)))]
}
}
export function getMean(values, _weights) {
const weights = _weights ? _weights : values.map(() => new BigNumber(1))
const weightedValues = values.map((value, i) => value.multipliedBy(weights[i]))
const numerator = weightedValues.reduce(
(accumulator, currentValue) => accumulator.plus(currentValue),
new BigNumber(0)
)
const denominator = weights.reduce((accumulator, currentValue) => accumulator.plus(currentValue), new BigNumber(0))
return [numerator.dividedBy(denominator), weights.map(weight => weight.dividedBy(denominator))]
}
import { getTokenReserves, getMarketDetails } from '@uniswap/sdk'
import { getMedian, getMean } from './math'
const DAI = 'DAI'
const USDC = 'USDC'
const TUSD = 'TUSD'
const USD_STABLECOINS = [DAI, USDC, TUSD]
const USD_STABLECOIN_ADDRESSES = [
'0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
'0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E'
]
function forEachStablecoin(runner) {
return USD_STABLECOINS.map((stablecoin, index) => runner(index, stablecoin))
}
export async function getUSDPrice(library) {
return Promise.all(forEachStablecoin(i => getTokenReserves(USD_STABLECOIN_ADDRESSES[i], library))).then(reserves => {
const ethReserves = forEachStablecoin(i => reserves[i].ethReserve.amount)
const marketDetails = forEachStablecoin(i => getMarketDetails(reserves[i], undefined))
const ethPrices = forEachStablecoin(i => marketDetails[i].marketRate.rateInverted)
const [median, medianWeights] = getMedian(ethPrices)
const [mean, meanWeights] = getMean(ethPrices)
const [weightedMean, weightedMeanWeights] = getMean(ethPrices, ethReserves)
const ethPrice = getMean([median, mean, weightedMean])[0]
const _stablecoinWeights = [
getMean([medianWeights[0], meanWeights[0], weightedMeanWeights[0]])[0],
getMean([medianWeights[1], meanWeights[1], weightedMeanWeights[1]])[0],
getMean([medianWeights[2], meanWeights[2], weightedMeanWeights[2]])[0]
]
const stablecoinWeights = forEachStablecoin((i, stablecoin) => ({
[stablecoin]: _stablecoinWeights[i]
})).reduce((accumulator, currentValue) => ({ ...accumulator, ...currentValue }), {})
return [ethPrice, stablecoinWeights]
})
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment