Commit 753e5f34 authored by ianlapham's avatar ianlapham

stable version with updated balances, add liquidity using SDK, pair menu search

parent 3091ebc1
......@@ -9,7 +9,7 @@
"swapAnyway": "Swap Anyway",
"send": "Send",
"sendAnyway": "Send Anyway",
"pool": "Pool",
"pool": "Supply",
"betaWarning": "This project is in beta. Use at your own risk.",
"input": "Input",
"output": "Output",
......
<svg width="94" height="94" viewBox="0 0 94 94" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M92 47C92 22.1472 71.8528 2 47 2C22.1472 2 2 22.1472 2 47C2 71.8528 22.1472 92 47 92" stroke="#2172E5" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="417px" viewBox="0 0 256 417" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<polygon fill="#343434" points="127.9611 0 125.1661 9.5 125.1661 285.168 127.9611 287.958 255.9231 212.32"/>
<polygon fill="#8C8C8C" points="127.962 0 0 212.32 127.962 287.959 127.962 154.158"/>
<polygon fill="#3C3C3B" points="127.9611 312.1866 126.3861 314.1066 126.3861 412.3056 127.9611 416.9066 255.9991 236.5866"/>
<polygon fill="#8C8C8C" points="127.962 416.9052 127.962 312.1852 0 236.5852"/>
<polygon fill="#141414" points="127.9611 287.9577 255.9211 212.3207 127.9611 154.1587"/>
<polygon fill="#393939" points="0.0009 212.3208 127.9609 287.9578 127.9609 154.1588"/>
</g>
</svg>
\ No newline at end of file
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="20" height="20" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0" transform="scale(0.0078125)"/>
</pattern>
<image id="image0" width="128" height="128" xlink:href="data:image/png;base64,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"/>
</defs>
</svg>
import React from 'react'
import { Button as RebassButton } from 'rebass/styled-components'
import styled from 'styled-components'
import { darken } from 'polished'
import { RowBetween } from '../Row'
import { ChevronDown } from 'react-feather'
const Base = styled(RebassButton)`
padding: ${({ padding }) => (padding ? padding : '16px')};
width: ${({ width }) => (width ? width : '100%')};
font-size: 1rem;
font-weight: 500;
text-align: center;
border-radius: 20px;
outline: none;
border: 1px solid transparent;
color: white;
cursor: pointer;
&:disabled {
cursor: auto;
}
`
export const ButtonPrimary = styled(Base)`
background-color: ${({ theme }) => theme.royalBlue};
color: white;
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.royalBlue)};
background-color: ${({ theme }) => darken(0.05, theme.royalBlue)};
}
&:hover {
background-color: ${({ theme }) => darken(0.05, theme.royalBlue)};
}
&:active {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.royalBlue)};
background-color: ${({ theme }) => darken(0.1, theme.royalBlue)};
}
&:disabled {
background-color: ${({ theme }) => theme.royalBlue};
opacity: 50%;
cursor: auto;
}
`
export const ButtonSecondary = styled(Base)`
background-color: #ebf4ff;
color: #2172e5;
border-radius: 8px;
padding: 10px;
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, '#ebf4ff')};
background-color: ${({ theme }) => darken(0.05, '#ebf4ff')};
}
&:hover {
background-color: ${({ theme }) => darken(0.05, '#ebf4ff')};
}
&:active {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, '#ebf4ff')};
background-color: ${({ theme }) => darken(0.1, '#ebf4ff')};
}
&:disabled {
background-color: ${({ theme }) => '#ebf4ff'};
opacity: 50%;
cursor: auto;
}
`
export const ButtonEmpty = styled(Base)`
border: 1px solid #edeef2;
background-color: transparent;
color: black;
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, '#edeef2')};
}
&:hover {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, '#edeef2')};
}
&:active {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, '#edeef2')};
}
&:disabled {
opacity: 50%;
cursor: auto;
}
`
export function ButtonDropwdown({ disabled, children, ...rest }) {
return (
<ButtonPrimary {...rest}>
<RowBetween>
<div style={{ display: 'flex', alignItems: 'center' }}>{children}</div>
<ChevronDown size={24} />
</RowBetween>
</ButtonPrimary>
)
}
import styled from 'styled-components'
import { Box } from 'rebass/styled-components'
const Card = styled(Box)`
width: 100%;
border-radius: 20px;
padding: 1rem;
padding: ${({ padding }) => padding};
border: ${({ border }) => border};
`
export default Card
export const LightCard = styled(Card)`
border: 1px solid ${({ theme }) => theme.outlineGrey};
`
import styled from 'styled-components'
const Column = styled.div`
display: flex;
flex-direction: column;
justify-content: flex-start;
`
export const ColumnCenter = styled(Column)`
width: 100%;
align-items: center;
`
export const AutoColumn = styled.div`
display: grid;
grid-auto-rows: auto;
grid-row-gap: ${({ gap }) => gap};
justify-items: ${({ justify }) => justify && justify};
`
export default Column
import React, { useState } from 'react'
import styled from 'styled-components'
import { ButtonPrimary } from '../../components/Button'
import { AutoColumn, ColumnCenter } from '../Column'
import Row, { RowBetween, RowFlat, RowFixed } from '../Row'
import { Text } from 'rebass'
import Modal from '../Modal'
import { CheckCircle } from 'react-feather'
import DoubleTokenLogo from '../DoubleLogo'
import TokenLogo from '../TokenLogo'
import { CloseIcon } from '../../theme/components'
import Loader from '../Loader'
const Wrapper = styled.div`
width: 100%;
`
const Section = styled(AutoColumn)`
padding: 2rem;
`
const BottomSection = styled(Section)`
background-color: ${({ theme }) => theme.activeGray};
`
const ConfirmedIcon = styled(ColumnCenter)`
padding: 60px 0;
`
export default function ConfirmationModal({
isOpen,
onDismiss,
liquidityMinted,
amount0,
amount1,
poolTokenPercentage,
price
}) {
const { address: address0, symbol: symbol0 } = amount0?.token || {}
const { address: address1, symbol: symbol1 } = amount1?.token || {}
const [confirmed, SetConfirmed] = useState(false)
const [waitingForConfirmation, setWaitingForConfirmation] = useState(false)
function WrappedOnDismissed() {
onDismiss()
SetConfirmed(false)
}
function fakeCall() {
setTimeout(() => {
setWaitingForConfirmation(false)
}, 2000)
}
return (
<Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={100}>
{!confirmed ? (
<Wrapper>
<Section gap="40px">
<RowBetween>
<Text fontWeight={500} fontSize={'20px'}>
You will receive
</Text>
<CloseIcon onClick={WrappedOnDismissed} />
</RowBetween>
<AutoColumn gap="16px">
<RowFlat>
<Text fontSize="48px" fontWeight={500} lineHeight="32px" marginRight={10}>
{liquidityMinted?.toFixed(6)}
</Text>
<DoubleTokenLogo a0={address0 || ''} a1={address1 || ''} size={20} />
</RowFlat>
<Row>
<Text fontSize="24px">{symbol0 + ':' + symbol1 + ' Pool Tokens'}</Text>
</Row>
</AutoColumn>
</Section>
<BottomSection gap="12px">
{/* <Text fontWeight={500} fontSize={16}>
Deposited Tokens
</Text> */}
{/* <LightCard>
<RowBetween>
<Text fontWeight={500} fontSize={20}>
{amountFormatter(amount0, decimals0, 4)}
</Text>
<RowFixed>
<TokenLogo address={token0 || ''} size={'24px'} />
<Text fontWeight={500} fontSize={20} marginLeft="12px">
{symbol0}
</Text>
</RowFixed>
</RowBetween>
</LightCard>
<ColumnCenter>
<Plus size="16" color="#888D9B" />
</ColumnCenter>
<LightCard>
<RowBetween>
<Text fontWeight={500} fontSize={20}>
{amountFormatter(amount1, decimals1, 4)}
</Text>
<RowFixed>
<TokenLogo address={token1 || ''} size={'24px'} />
<Text fontWeight={500} fontSize={16} marginLeft="12px">
{symbol1}
</Text>
</RowFixed>
</RowBetween>
</LightCard> */}
<AutoColumn gap="12px">
<RowBetween>
<Text color="#565A69" fontWeight={500} fontSize={16}>
{symbol0} Deposited
</Text>
<RowFixed>
<TokenLogo address={address0 || ''} style={{ marginRight: '8px' }} />
<Text fontWeight={500} fontSize={16}>
{amount0?.toSignificant(6)}
</Text>
</RowFixed>
</RowBetween>
<RowBetween>
<Text color="#565A69" fontWeight={500} fontSize={16}>
{symbol1} Deposited
</Text>
<RowFixed>
<TokenLogo address={address1 || ''} style={{ marginRight: '8px' }} />
<Text fontWeight={500} fontSize={16}>
{amount1?.toSignificant(6)}
</Text>
</RowFixed>
</RowBetween>
<RowBetween>
<Text color="#565A69" fontWeight={500} fontSize={16}>
Rate
</Text>
<Text fontWeight={500} fontSize={16}>
{price && `1 ${symbol0} = ${price?.adjusted.toFixed(8)} ${symbol1}`}
</Text>
</RowBetween>
<RowBetween>
<Text color="#565A69" fontWeight={500} fontSize={16}>
Minted Pool Share:
</Text>
<Text fontWeight={500} fontSize={16}>
{poolTokenPercentage?.toFixed(6) + '%'}
</Text>
</RowBetween>
<ButtonPrimary
style={{ margin: '20px 0' }}
onClick={() => {
setWaitingForConfirmation(true)
SetConfirmed(true)
fakeCall()
}}
>
<Text fontWeight={500} fontSize={20}>
Confirm Supply
</Text>
</ButtonPrimary>
<Text fontSize={12} color="#565A69" textAlign="center">
{`Output is estimated. You will receive at least ${liquidityMinted?.toFixed(
6
)} UNI ${symbol0}/${symbol1} or the transaction will revert.`}
</Text>
</AutoColumn>
</BottomSection>
</Wrapper>
) : (
<Wrapper>
<Section>
<RowBetween>
<div />
<CloseIcon onClick={WrappedOnDismissed} />
</RowBetween>
<ConfirmedIcon>
{waitingForConfirmation ? <Loader size="90px" /> : <CheckCircle size={90} color="#27AE60" />}
</ConfirmedIcon>
<AutoColumn gap="24px" justify={'center'}>
<Text fontWeight={500} fontSize={20}>
{!waitingForConfirmation ? 'Transaction Submitted' : 'Waiting For Confirmation'}
</Text>
<AutoColumn gap="12px" justify={'center'}>
<Text fontWeight={500} fontSize={16} color="#2172E5">
Supplied
</Text>
<Text fontWeight={600} fontSize={16} color="#2172E5">
{`${amount0?.toSignificant(6)} ${symbol0} and ${amount1?.toSignificant(6)} ${symbol1}`}
</Text>
</AutoColumn>
{!waitingForConfirmation && (
<>
<Text fontWeight={500} fontSize={14} color="#2172E5">
View on Etherscan
</Text>
<ButtonPrimary onClick={WrappedOnDismissed} style={{ margin: '20px 0' }}>
<Text fontWeight={500} fontSize={20}>
Close
</Text>
</ButtonPrimary>
</>
)}
{waitingForConfirmation && <div style={{ height: '138px' }} />}
<Text fontSize={12} color="#565A69">
{waitingForConfirmation
? 'Confirm this transaction in your wallet'
: `Estimated time until confirmation: 3 min`}
</Text>
</AutoColumn>
</Section>
</Wrapper>
)}
</Modal>
)
}
......@@ -81,7 +81,6 @@ class ContextualInfo extends Component {
if (!this.state.showDetails) {
return null
}
return <Details>{this.props.renderTransactionDetails()}</Details>
}
......
This diff is collapsed.
import React from 'react'
import styled from 'styled-components'
import TokenLogo from '../TokenLogo'
export default function DoubleTokenLogo({ a0, a1, size = 16, margin = false }) {
const TokenWrapper = styled.div`
position: relative;
display: flex;
flex-direction: row;
margin-right: ${({ sizeraw, margin }) => margin && (sizeraw / 2 + 10).toString() + 'px'};
`
const HigherLogo = styled(TokenLogo)`
z-index: 2;
`
const CoveredLogo = styled(TokenLogo)`
position: absolute;
left: ${({ sizeraw }) => (sizeraw / 2).toString() + 'px'};
`
return (
<TokenWrapper sizeraw={size} margin={margin}>
<HigherLogo address={a0} size={size.toString() + 'px'} />
<CoveredLogo address={a1} size={size.toString() + 'px'} sizeraw={size} />
</TokenWrapper>
)
}
......@@ -4,14 +4,13 @@ import { createBrowserHistory } from 'history'
import { ethers } from 'ethers'
import styled from 'styled-components'
import { useTranslation } from 'react-i18next'
import { BigNumber } from '@uniswap/sdk'
import { useWeb3React } from '../../hooks'
import { brokenTokens } from '../../constants'
import { amountFormatter, calculateGasMargin, isAddress } from '../../utils'
import { useExchangeContract } from '../../hooks'
import { useTokenDetails, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
import { useToken, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
import { useTransactionAdder } from '../../contexts/Transactions'
import { useAddressBalance, useExchangeReserves } from '../../contexts/Balances'
import { useAddressAllowance } from '../../contexts/Allowances'
......@@ -205,7 +204,6 @@ function getExchangeRate(inputValue, inputDecimals, outputValue, outputDecimals,
(outputDecimals || outputDecimals === 0)
) {
const factor = ethers.utils.bigNumberify(10).pow(ethers.utils.bigNumberify(18))
if (invert) {
return inputValue
.mul(factor)
......@@ -334,10 +332,10 @@ export default function ExchangePage({ initialCurrency, sending = false, params
const swapType = getSwapType(inputCurrency, outputCurrency)
// 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 } = useToken(
inputCurrency
)
const { symbol: outputSymbol, decimals: outputDecimals, exchangeAddress: outputExchangeAddress } = useTokenDetails(
const { symbol: outputSymbol, decimals: outputDecimals, exchangeAddress: outputExchangeAddress } = useToken(
outputCurrency
)
......@@ -353,8 +351,8 @@ export default function ExchangePage({ initialCurrency, sending = false, params
const { reserveETH: outputReserveETH, reserveToken: outputReserveToken } = useExchangeReserves(outputCurrency)
// get balances for each of the currency types
const inputBalance = useAddressBalance(account, inputCurrency)
const outputBalance = useAddressBalance(account, outputCurrency)
const inputBalance = 0
const outputBalance = 0
const inputBalanceFormatted = !!(inputBalance && Number.isInteger(inputDecimals))
? amountFormatter(inputBalance, inputDecimals, Math.min(4, inputDecimals))
: ''
......@@ -631,8 +629,8 @@ export default function ExchangePage({ initialCurrency, sending = false, params
} else if (outputCurrency === 'ETH') {
ethTransactionSize = inputValueFormatted * amountFormatter(exchangeRate, 18, 6, false)
} else {
const tokenBalance = inputReserveToken && new BigNumber(inputReserveToken.toString())
const ethBalance = inputReserveETH && new BigNumber(inputReserveETH.toString())
const tokenBalance = 1
const ethBalance = 1
let ethRate = ethBalance && tokenBalance && ethBalance.div(tokenBalance)
ethTransactionSize = inputValueFormatted * ethRate
}
......
import React from 'react'
import styled from 'styled-components'
import { Spinner } from '../../theme'
import Circle from '../../assets/images/blue-loader.svg'
const SpinnerWrapper = styled(Spinner)`
height: ${({ size }) => size};
width: ${({ size }) => size};
`
export default function Loader({ size }) {
return <SpinnerWrapper src={Circle} alt="loader" size={size} />
}
......@@ -54,7 +54,7 @@ const StyledDialogContent = styled(FilteredDialogContent)`
padding: 0px;
width: 50vw;
max-width: 650px;
max-width: 500px;
${({ maxHeight }) =>
maxHeight &&
css`
......
import React, { useCallback } from 'react'
import { withRouter, NavLink } from 'react-router-dom'
import { withRouter, NavLink, Link as HistoryLink } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { transparentize, darken } from 'polished'
import { useWeb3React, useBodyKeyDown } from '../../hooks'
import { useAddressBalance } from '../../contexts/Balances'
import { isAddress } from '../../utils'
import {
useBetaMessageManager,
useSaiHolderMessageManager,
useGeneralDaiMessageManager
} from '../../contexts/LocalStorage'
import { Link } from '../../theme/components'
import { RowBetween } from '../Row'
import QuestionHelper from '../Question'
import { ArrowLeft } from 'react-feather'
import { useBodyKeyDown } from '../../hooks'
import { useBetaMessageManager } from '../../contexts/LocalStorage'
const tabOrder = [
{
......@@ -26,9 +24,9 @@ const tabOrder = [
regex: /\/send/
},
{
path: '/add-liquidity',
path: '/supply',
textKey: 'pool',
regex: /\/add-liquidity|\/remove-liquidity|\/create-exchange.*/
regex: /\/supply/
}
]
......@@ -61,57 +59,12 @@ const BetaMessage = styled.div`
}
`
const DaiMessage = styled(BetaMessage)`
${({ theme }) => theme.flexColumnNoWrap}
position: relative;
word-wrap: wrap;
overflow: visible;
white-space: normal;
padding: 1rem 1rem;
padding-right: 2rem;
line-height: 1.2rem;
cursor: default;
color: ${({ theme }) => theme.textColor};
div {
width: 100%;
}
&:after {
content: '';
}
`
const CloseIcon = styled.div`
width: 10px !important;
top: 0.5rem;
right: 1rem;
position: absolute;
color: ${({ theme }) => theme.wisteriaPurple};
:hover {
cursor: pointer;
}
`
const WarningHeader = styled.div`
margin-bottom: 10px;
font-weight: 500;
color: ${({ theme }) => theme.uniswapPink};
`
const WarningFooter = styled.div`
margin-top: 10px;
font-size: 10px;
text-decoration: italic;
color: ${({ theme }) => theme.greyText};
`
const Tabs = styled.div`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
height: 2.5rem;
background-color: ${({ theme }) => theme.concreteGray};
height: 3rem;
border-radius: 3rem;
/* border: 1px solid ${({ theme }) => theme.mercuryGray}; */
margin-bottom: 1rem;
margin-bottom: 20px;
`
const activeClassName = 'ACTIVE'
......@@ -123,53 +76,42 @@ const StyledNavLink = styled(NavLink).attrs({
align-items: center;
justify-content: center;
height: 2.5rem;
border: 1px solid ${({ theme }) => transparentize(1, theme.mercuryGray)};
flex: 1 0 auto;
border-radius: 3rem;
outline: none;
cursor: pointer;
text-decoration: none;
color: ${({ theme }) => theme.doveGray};
font-size: 1rem;
font-size: 20px;
box-sizing: border-box;
&.${activeClassName} {
background-color: ${({ theme }) => theme.inputBackground};
border-radius: 3rem;
border: 1px solid ${({ theme }) => theme.mercuryGray};
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadowColor)};
box-sizing: border-box;
font-weight: 500;
color: ${({ theme }) => theme.royalBlue};
:hover {
/* border: 1px solid ${({ theme }) => darken(0.1, theme.mercuryGray)}; */
background-color: ${({ theme }) => darken(0.01, theme.inputBackground)};
}
color: ${({ theme }) => theme.black};
}
:hover,
:focus {
color: ${({ theme }) => darken(0.1, theme.royalBlue)};
color: ${({ theme }) => darken(0.1, theme.black)};
}
`
const ActiveText = styled.div`
font-weight: 500;
font-size: 20px;
`
const ArrowLink = styled(ArrowLeft)`
color: ${({ theme }) => theme.black};
`
function NavigationTabs({ location: { pathname }, history }) {
const { t } = useTranslation()
const [showBetaMessage, dismissBetaMessage] = useBetaMessageManager()
const [showGeneralDaiMessage, dismissGeneralDaiMessage] = useGeneralDaiMessageManager()
const [showSaiHolderMessage, dismissSaiHolderMessage] = useSaiHolderMessageManager()
const { account } = useWeb3React()
const daiBalance = useAddressBalance(account, isAddress('0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359'))
const daiPoolTokenBalance = useAddressBalance(account, isAddress('0x09cabEC1eAd1c0Ba254B09efb3EE13841712bE14'))
const onLiquidityPage = pathname === '/pool' || pathname === '/add-liquidity' || pathname === '/remove-liquidity'
const navigate = useCallback(
direction => {
const tabIndex = tabOrder.findIndex(({ regex }) => pathname.match(regex))
......@@ -187,54 +129,30 @@ function NavigationTabs({ location: { pathname }, history }) {
useBodyKeyDown('ArrowRight', navigateRight)
useBodyKeyDown('ArrowLeft', navigateLeft)
const providerMessage =
showSaiHolderMessage && daiPoolTokenBalance && !daiPoolTokenBalance.isZero() && onLiquidityPage
const generalMessage = showGeneralDaiMessage && daiBalance && !daiBalance.isZero()
const adding = pathname.match('/add')
return (
<>
<Tabs>
{tabOrder.map(({ path, textKey, regex }) => (
<StyledNavLink key={path} to={path} isActive={(_, { pathname }) => pathname.match(regex)}>
{t(textKey)}
</StyledNavLink>
))}
</Tabs>
{providerMessage && (
<DaiMessage>
<CloseIcon onClick={dismissSaiHolderMessage}></CloseIcon>
<WarningHeader>Missing your DAI?</WarningHeader>
<div>
Dont worry, check the{' '}
<Link href={'/remove-liquidity?poolTokenAddress=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359'}>
SAI liquidity pool.
</Link>{' '}
Your old DAI is now SAI. If you want to migrate,{' '}
<Link href="/remove-liquidity?poolTokenAddress=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359">
remove your SAI liquidity,
</Link>{' '}
migrate using the <Link href="https://migrate.makerdao.com/">migration tool</Link> then add your migrated
DAI to the{' '}
<Link href="add-liquidity?token=0x6B175474E89094C44Da98b954EedeAC495271d0F">new DAI liquidity pool.</Link>
</div>
<WarningFooter>
<Link href="https://blog.makerdao.com/looking-ahead-how-to-upgrade-to-multi-collateral-dai/">
Read more
</Link>{' '}
about this change on the official Maker blog.
</WarningFooter>
</DaiMessage>
)}
{generalMessage && !providerMessage && (
<DaiMessage>
<CloseIcon onClick={dismissGeneralDaiMessage}></CloseIcon>
<WarningHeader>DAI has upgraded!</WarningHeader>
<div>
Your old DAI is now SAI. To upgrade use the{' '}
<Link href="https://migrate.makerdao.com/">migration tool.</Link>
</div>
</DaiMessage>
{adding ? (
<Tabs>
<RowBetween style={{ padding: '1rem' }}>
<HistoryLink to="/supply">
<ArrowLink />
</HistoryLink>
<ActiveText>Add Liquidity</ActiveText>
<QuestionHelper text={'helper text'} />
</RowBetween>
</Tabs>
) : (
<Tabs>
{tabOrder.map(({ path, textKey, regex }) => (
<StyledNavLink key={path} to={path} isActive={(_, { pathname }) => pathname.match(regex)}>
{t(textKey)}
</StyledNavLink>
))}
</Tabs>
)}
{showBetaMessage && (
<BetaMessage onClick={dismissBetaMessage}>
<span role="img" aria-label="warning">
......
import React from 'react'
import styled from 'styled-components'
const StyledInput = styled.input`
color: ${({ error, theme }) => error && theme.salmonRed};
background-color: ${({ theme }) => theme.inputBackground};
color: ${({ theme }) => theme.textColor};
font-size: 20px;
outline: none;
border: none;
flex: 1 1 auto;
width: 0;
background-color: ${({ theme }) => theme.inputBackground};
[type='number'] {
-moz-appearance: textfield;
}
::-webkit-outer-spin-button,
::-webkit-inner-spin-button {
-webkit-appearance: none;
}
::placeholder {
color: ${({ theme }) => theme.chaliceGray};
}
`
const inputRegex = RegExp(`^\\d*(?:\\\\.)?\\d*$`) // match escaped "." characters via in a non-capturing group
function escapeRegExp(string: string): string {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string
}
export const Input = React.memo(({ field, value, onUserInput, ...rest }: any) => {
function enforcer(nextUserInput: string) {
if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
onUserInput(field, nextUserInput)
}
}
return (
<StyledInput
{...rest}
value={value}
onChange={event => {
enforcer(event.target.value)
}}
// universal input options
inputMode="decimal"
title="Token Amount"
autoComplete="off"
autoCorrect="off"
// text-specific options
type="text"
placeholder="0.0"
minLength={1}
maxLength={79}
spellCheck="false"
/>
)
})
export default Input
import React, { useState } from 'react'
import styled, { keyframes } from 'styled-components'
import question from '../../assets/images/question.svg'
const Wrapper = styled.div`
position: relative;
`
const QuestionWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
margin-left: 0.4rem;
padding: 0.2rem;
border: none;
background: none;
outline: none;
cursor: default;
border-radius: 36px;
:hover,
:focus {
opacity: 0.7;
}
`
const HelpCircleStyled = styled.img`
height: 24px;
width: 23px;
`
const fadeIn = keyframes`
from {
opacity : 0;
}
to {
opacity : 1;
}
`
const Popup = styled.div`
position: absolute;
width: 228px;
z-index: 9999;
left: 40px;
top: -10px;
display: flex;
flex-direction: column;
align-items: center;
padding: 0.6rem 1rem;
line-height: 150%;
background: ${({ theme }) => theme.inputBackground};
border: 1px solid ${({ theme }) => theme.mercuryGray};
border-radius: 8px;
animation: ${fadeIn} 0.15s linear;
color: ${({ theme }) => theme.textColor};
font-style: italic;
${({ theme }) => theme.mediaWidth.upToSmall`
left: -20px;
`}
`
export default function QuestionHelper({ text }) {
const [showPopup, setPopup] = useState(false)
return (
<Wrapper>
<QuestionWrapper
onClick={() => {
setPopup(!showPopup)
}}
onMouseEnter={() => {
setPopup(true)
}}
onMouseLeave={() => {
setPopup(false)
}}
>
<HelpCircleStyled src={question} alt="popup" />
</QuestionWrapper>
{showPopup && <Popup>{text}</Popup>}
</Wrapper>
)
}
import styled from 'styled-components'
const Row = styled.div`
width: 100%;
display: flex;
align-items: center;
`
export const RowBetween = styled(Row)`
justify-content: space-between;
`
export const RowFlat = styled.div`
display: flex;
align-items: flex-end;
`
export const AutoRow = styled(Row)`
flex-wrap: wrap;
margin: -${({ gap }) => gap};
& > * {
margin: ${({ gap }) => gap} !important;
}
`
export const RowFixed = styled(Row)`
width: fit-content;
`
export default Row
This diff is collapsed.
......@@ -35,7 +35,7 @@ export default function TokenLogo({ address, size = '1rem', ...rest }) {
let path = ''
if (address === 'ETH') {
return <StyledEthereumLogo size={size} />
return <StyledEthereumLogo size={size} {...rest} />
} else if (!error && !BAD_IMAGES[address]) {
path = TOKEN_ICON_API(address.toLowerCase())
} else {
......@@ -51,7 +51,7 @@ export default function TokenLogo({ address, size = '1rem', ...rest }) {
return (
<Image
{...rest}
alt={address}
// alt={address}
src={path}
size={size}
onError={() => {
......
......@@ -2,7 +2,7 @@ import React, { useState } from 'react'
import styled, { keyframes } from 'styled-components'
import { useWeb3React } from '../../hooks'
import { useTokenDetails } from '../../contexts/Tokens'
import { useToken } from '../../contexts/Tokens'
import { getEtherscanLink } from '../../utils'
import { Link } from '../../theme'
......@@ -128,7 +128,7 @@ const Text = styled.div`
function WarningCard({ onDismiss, urlAddedTokens, currency }) {
const [showPopup, setPopup] = useState()
const { chainId } = useWeb3React()
const { symbol: inputSymbol, name: inputName } = useTokenDetails(currency)
const { symbol: inputSymbol, name: inputName } = useToken(currency)
const fromURL = urlAddedTokens.hasOwnProperty(currency)
return (
......
......@@ -18,7 +18,7 @@ export const network = new NetworkConnector({
})
export const injected = new InjectedConnector({
supportedChainIds: [Number(process.env.REACT_APP_CHAIN_ID)]
supportedChainIds: [Number(process.env.REACT_APP_CHAIN_ID), 4]
})
// mainnet only
......
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import {} from '@web3-react/core'
import { Token, TokenAmount } from '@uniswap/sdk'
import { useWeb3React } from '../hooks'
import { safeAccess, isAddress, getTokenAllowance } from '../utils'
......@@ -7,7 +7,7 @@ import { useBlockNumber } from './Application'
const UPDATE = 'UPDATE'
const AllowancesContext = createContext()
const AllowancesContext = createContext([])
function useAllowancesContext() {
return useContext(AllowancesContext)
......@@ -54,18 +54,18 @@ export default function Provider({ children }) {
)
}
export function useAddressAllowance(address, tokenAddress, spenderAddress) {
export function useAddressAllowance(address: string, token: Token, spenderAddress: string): TokenAmount {
const { library, chainId } = useWeb3React()
const globalBlockNumber = useBlockNumber()
const [state, { update }] = useAllowancesContext()
const { value, blockNumber } = safeAccess(state, [chainId, address, tokenAddress, spenderAddress]) || {}
const { value, blockNumber } = safeAccess(state, [chainId, address, token.address, spenderAddress]) || {}
useEffect(() => {
if (
isAddress(address) &&
isAddress(tokenAddress) &&
isAddress(token.address) &&
isAddress(spenderAddress) &&
(value === undefined || blockNumber !== globalBlockNumber) &&
(chainId || chainId === 0) &&
......@@ -73,15 +73,15 @@ export function useAddressAllowance(address, tokenAddress, spenderAddress) {
) {
let stale = false
getTokenAllowance(address, tokenAddress, spenderAddress, library)
getTokenAllowance(address, token.address, spenderAddress, library)
.then(value => {
if (!stale) {
update(chainId, address, tokenAddress, spenderAddress, value, globalBlockNumber)
update(chainId, address, token.address, spenderAddress, value, globalBlockNumber)
}
})
.catch(() => {
if (!stale) {
update(chainId, address, tokenAddress, spenderAddress, null, globalBlockNumber)
update(chainId, address, token.address, spenderAddress, null, globalBlockNumber)
}
})
......@@ -89,7 +89,8 @@ export function useAddressAllowance(address, tokenAddress, spenderAddress) {
stale = true
}
}
}, [address, tokenAddress, spenderAddress, value, blockNumber, globalBlockNumber, chainId, library, update])
}, [address, token.address, spenderAddress, value, blockNumber, globalBlockNumber, chainId, library, update])
return value
const newTokenAmount: TokenAmount = value ? new TokenAmount(token, value) : null
return newTokenAmount
}
This diff is collapsed.
This diff is collapsed.
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { ChainId, WETH, Token, Exchange } from '@uniswap/sdk'
import { INITIAL_TOKENS_CONTEXT, useToken } from './Tokens'
import { useAddressBalance } from './Balances'
import { useWeb3React } from '../hooks'
const UPDATE = 'UPDATE'
const ALL_EXCHANGES: [Token, Token][] = [
[
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY][WETH[ChainId.RINKEBY].address],
INITIAL_TOKENS_CONTEXT[ChainId.RINKEBY]['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735']
]
]
const EXCHANGE_MAP: {
[chainId: number]: { [token0Address: string]: { [token1Address: string]: string } }
} = ALL_EXCHANGES.reduce((exchangeMap, [tokenA, tokenB]) => {
const tokens: [Token, Token] = tokenA?.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]
// ensure exchanges are unique
if (exchangeMap?.[tokens[0].chainId]?.[tokens[0].address]?.[tokens[1].address] !== undefined)
throw Error(`Duplicate exchange: ${tokenA} ${tokenB}`)
return {
...exchangeMap,
[tokens[0].chainId]: {
...exchangeMap?.[tokens[0].chainId],
[tokens[0].address]: {
...exchangeMap?.[tokens[0].chainId]?.[tokens[0].address],
[tokens[1].address]: Exchange.getAddress(...tokens)
}
}
}
}, {})
const ExchangeContext = createContext([])
function useExchangesContext() {
return useContext(ExchangeContext)
}
function reducer(state, { type, payload }) {
switch (type) {
case UPDATE: {
const { tokens } = payload
const tokensSorted: [Token, Token] = tokens[0].sortsBefore(tokens[1])
? [tokens[0], tokens[1]]
: [tokens[1], tokens[0]]
return {
...state,
[tokensSorted[0].chainId]: {
...state?.[tokensSorted[0].chainId],
[tokensSorted[0].address]: {
...state?.[tokensSorted[0].chainId]?.[tokensSorted[0].address],
[tokensSorted[1].address]: Exchange.getAddress(tokensSorted[0], tokensSorted[1])
}
}
}
}
default: {
throw Error(`Unexpected action type in ExchangesContext reducer: '${type}'.`)
}
}
}
export default function Provider({ children }) {
const [state, dispatch] = useReducer(reducer, EXCHANGE_MAP)
const update = useCallback((chainId, tokens) => {
dispatch({ type: UPDATE, payload: { chainId, tokens } })
}, [])
return (
<ExchangeContext.Provider value={useMemo(() => [state, { update }], [state, update])}>
{children}
</ExchangeContext.Provider>
)
}
export function useExchangeAddress(tokenA?: Token, tokenB?: Token): string | undefined {
const { chainId } = useWeb3React()
const [state, { update }] = useExchangesContext()
const tokens: [Token, Token] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]
const address = state?.[chainId]?.[tokens[0].address]?.[tokens[1].address]
useEffect(() => {
if (address === undefined && tokenA && tokenB) {
const exchangeAddress = Exchange.getAddress(...tokens)
exchangeAddress && update(chainId, tokens)
}
}, [chainId, address, tokenA, tokenB, tokens, update])
return address
}
export function useExchange(tokenA?: Token, tokenB?: Token): Exchange | undefined {
const address = useExchangeAddress(tokenA, tokenB)
const tokenAmountA = useAddressBalance(address, tokenA)
const tokenAmountB = useAddressBalance(address, tokenB)
const exchange = tokenAmountA && tokenAmountB && new Exchange(tokenAmountA, tokenAmountB)
return exchange
}
export function useAllExchanges() {
const { chainId } = useWeb3React()
const [state] = useExchangesContext()
return useMemo(() => {
return state?.[chainId] || {}
}, [state, chainId])
}
This diff is collapsed.
import React, { createContext, useContext, useReducer, useMemo, useCallback, useEffect } from 'react'
import { ChainId, WETH, Token } from '@uniswap/sdk'
import { useWeb3React } from '../hooks'
import { isAddress, getTokenName, getTokenSymbol, getTokenDecimals, safeAccess } from '../utils'
const UPDATE = 'UPDATE'
export const ALL_TOKENS = [
WETH[ChainId.RINKEBY],
new Token(ChainId.RINKEBY, '0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735', 18, 'DAI', 'Dai Stablecoin')
]
// only meant to be used in exchanges.ts!
export const INITIAL_TOKENS_CONTEXT = ALL_TOKENS.reduce((tokenMap, token) => {
// ensure tokens are unique
if (tokenMap?.[token.chainId]?.[token.address] !== undefined) throw Error(`Duplicate token: ${token}`)
return {
...tokenMap,
[token.chainId]: {
...tokenMap?.[token.chainId],
[token.address]: token
}
}
}, {})
const TokensContext = createContext([])
function useTokensContext() {
return useContext(TokensContext)
}
function reducer(state, { type, payload }) {
switch (type) {
case UPDATE: {
const { chainId, token } = payload
return {
...state,
[chainId]: {
...(state?.[chainId] || {}),
[token.address]: token
}
}
}
default: {
throw Error(`Unexpected action type in TokensContext reducer: '${type}'.`)
}
}
}
export default function Provider({ children }) {
const [state, dispatch] = useReducer(reducer, INITIAL_TOKENS_CONTEXT)
const update = useCallback((chainId, token) => {
dispatch({ type: UPDATE, payload: { chainId, token } })
}, [])
return (
<TokensContext.Provider value={useMemo(() => [state, { update }], [state, update])}>
{children}
</TokensContext.Provider>
)
}
export function useToken(tokenAddress: string): Token {
const { library, chainId } = useWeb3React()
const [state, { update }] = useTokensContext()
const allTokensInNetwork = state?.[chainId] || {}
const token = safeAccess(allTokensInNetwork, [tokenAddress]) || {}
useEffect(() => {
if (
isAddress(tokenAddress) &&
(token === undefined || token.name === undefined || token.symbol === undefined || token.decimals === undefined) &&
(chainId || chainId === 0) &&
library
) {
let stale = false
const namePromise = getTokenName(tokenAddress, library).catch(() => null)
const symbolPromise = getTokenSymbol(tokenAddress, library).catch(() => null)
const decimalsPromise = getTokenDecimals(tokenAddress, library).catch(() => null)
Promise.all([namePromise, symbolPromise, decimalsPromise]).then(
([resolvedName, resolvedSymbol, resolvedDecimals]) => {
if (!stale && resolvedDecimals) {
const newToken: Token = new Token(chainId, tokenAddress, resolvedDecimals, resolvedSymbol, resolvedName)
update(chainId, newToken)
}
}
)
return () => {
stale = true
}
}
}, [tokenAddress, token, chainId, library, update])
return token
}
export function useAllTokens() {
const { chainId } = useWeb3React()
const [state] = useTokensContext()
return useMemo(() => {
return state?.[chainId] || {}
}, [state, chainId])
}
......@@ -11,6 +11,7 @@ import ApplicationContextProvider, { Updater as ApplicationContextUpdater } from
import TransactionContextProvider, { Updater as TransactionContextUpdater } from './contexts/Transactions'
import BalancesContextProvider, { Updater as BalancesContextUpdater } from './contexts/Balances'
import TokensContextProvider from './contexts/Tokens'
import ExchangesContextProvider from './contexts/Exchanges'
import AllowancesContextProvider from './contexts/Allowances'
import App from './pages/App'
import ThemeProvider, { GlobalStyle } from './theme'
......@@ -40,11 +41,13 @@ function ContextProviders({ children }) {
<LocalStorageContextProvider>
<ApplicationContextProvider>
<TransactionContextProvider>
<TokensContextProvider>
<BalancesContextProvider>
<AllowancesContextProvider>{children}</AllowancesContextProvider>
</BalancesContextProvider>
</TokensContextProvider>
<ExchangesContextProvider>
<TokensContextProvider>
<BalancesContextProvider>
<AllowancesContextProvider>{children}</AllowancesContextProvider>
</BalancesContextProvider>
</TokensContextProvider>
</ExchangesContextProvider>
</TransactionContextProvider>
</ApplicationContextProvider>
</LocalStorageContextProvider>
......
......@@ -11,7 +11,9 @@ import { isAddress, getAllQueryParams } from '../utils'
const Swap = lazy(() => import('./Swap'))
const Send = lazy(() => import('./Send'))
const Pool = lazy(() => import('./Pool'))
const Pool = lazy(() => import('./Supply'))
const Add = lazy(() => import('./Supply/AddLiquidity'))
const Remove = lazy(() => import('./Supply/RemoveLiquidity'))
const AppWrapper = styled.div`
display: flex;
......@@ -42,9 +44,13 @@ const BodyWrapper = styled.div`
`
const Body = styled.div`
max-width: 35rem;
max-width: 28rem;
width: 90%;
/* margin: 0 1.25rem 1.25rem 1.25rem; */
background: ${({ theme }) => theme.panelBackground};
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
0px 24px 32px rgba(0, 0, 0, 0.04);
border-radius: 20px;
padding: 2rem 1rem;
`
export default function App() {
......@@ -96,16 +102,27 @@ export default function App() {
}
}}
/>
<Route exaxct path={'/supply'} component={() => <Pool params={params} />} />
<Route
path={[
'/add-liquidity',
'/remove-liquidity',
'/create-exchange',
'/create-exchange/:tokenAddress?'
]}
component={() => <Pool params={params} />}
exact
strict
path={'/add/:tokens'}
component={({ match }) => {
const tokens = match.params.tokens.split('-')
let t0
let t1
if (tokens) {
t0 = tokens[0] === 'ETH' ? 'ETH' : isAddress(tokens[0])
t1 = tokens[1] === 'ETH' ? 'ETH' : isAddress(tokens[1])
}
if (t0 && t1) {
return <Add params={params} token0={t0} token1={t1} />
} else {
return <Redirect to={{ pathname: '/supply' }} />
}
}}
/>
<Redirect to="/swap" />
<Route exaxct path={'/remove'} component={() => <Remove params={params} />} />
</Switch>
</Suspense>
</BrowserRouter>
......
This diff is collapsed.
import React, { useState, useCallback } from 'react'
import { withRouter, NavLink } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import OversizedPanel from '../../components/OversizedPanel'
import { ReactComponent as Dropdown } from '../../assets/images/dropdown-blue.svg'
import Modal from '../../components/Modal'
import { useBodyKeyDown } from '../../hooks'
import { lighten } from 'polished'
const poolTabOrder = [
{
path: '/add-liquidity',
textKey: 'addLiquidity',
regex: /\/add-liquidity/
},
{
path: '/remove-liquidity',
textKey: 'removeLiquidity',
regex: /\/remove-liquidity/
},
{
path: '/create-exchange',
textKey: 'createExchange',
regex: /\/create-exchange.*/
}
]
const LiquidityContainer = styled.div`
${({ theme }) => theme.flexRowNoWrap};
align-items: center;
padding: 1rem 1rem;
font-size: 1rem;
color: ${({ theme }) => theme.royalBlue};
font-weight: 500;
cursor: pointer;
:hover {
color: ${({ theme }) => lighten(0.1, theme.royalBlue)};
}
img {
height: 0.75rem;
width: 0.75rem;
}
`
const LiquidityLabel = styled.span`
flex: 1 0 auto;
`
const activeClassName = 'MODE'
const StyledNavLink = styled(NavLink).attrs({
activeClassName
})`
${({ theme }) => theme.flexRowNoWrap}
padding: 1rem;
margin-left: 1rem;
margin-right: 1rem;
font-size: 1rem;
cursor: pointer;
text-decoration: none;
color: ${({ theme }) => theme.doveGray};
font-size: 1rem;
&.${activeClassName} {
background-color: ${({ theme }) => theme.inputBackground};
border-radius: 3rem;
border: 1px solid ${({ theme }) => theme.mercuryGray};
font-weight: 500;
color: ${({ theme }) => theme.royalBlue};
}
`
const PoolModal = styled.div`
background-color: ${({ theme }) => theme.inputBackground};
width: 100%;
height: 100%;
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 }) {
const { t } = useTranslation()
const [modalIsOpen, setModalIsOpen] = useState(false)
const activeTabKey = poolTabOrder[poolTabOrder.findIndex(({ regex }) => pathname.match(regex))].textKey
const navigate = useCallback(
direction => {
const tabIndex = poolTabOrder.findIndex(({ regex }) => pathname.match(regex))
history.push(poolTabOrder[(tabIndex + poolTabOrder.length + direction) % poolTabOrder.length].path)
},
[pathname, history]
)
const navigateRight = useCallback(() => {
navigate(1)
}, [navigate])
const navigateLeft = useCallback(() => {
navigate(-1)
}, [navigate])
useBodyKeyDown('ArrowDown', navigateRight, modalIsOpen)
useBodyKeyDown('ArrowUp', navigateLeft, modalIsOpen)
return (
<OversizedPanel hideTop>
<LiquidityContainer
onClick={() => {
setModalIsOpen(true)
}}
>
<LiquidityLabel>{t(activeTabKey)}</LiquidityLabel>
<ColoredDropdown alt="arrow down" />
</LiquidityContainer>
<Modal
isOpen={modalIsOpen}
maxHeight={50}
onDismiss={() => {
setModalIsOpen(false)
}}
>
<PoolModal>
{poolTabOrder.map(({ path, textKey, regex }) => (
<StyledNavLink
key={path}
to={path}
isActive={(_, { pathname }) => pathname.match(regex)}
onClick={() => {
setModalIsOpen(false)
}}
>
{t(textKey)}
</StyledNavLink>
))}
</PoolModal>
</Modal>
</OversizedPanel>
)
}
export default withRouter(ModeSelector)
import React, { Suspense, lazy, useEffect } from 'react'
import ReactGA from 'react-ga'
import { Switch, Route, Redirect } from 'react-router-dom'
import ModeSelector from './ModeSelector'
const AddLiquidity = lazy(() => import('./AddLiquidity'))
const RemoveLiquidity = lazy(() => import('./RemoveLiquidity'))
const CreateExchange = lazy(() => import('./CreateExchange'))
export default function Pool({ params }) {
useEffect(() => {
ReactGA.pageview(window.location.pathname + window.location.search)
}, [])
const AddLiquidityParams = () => <AddLiquidity params={params} />
const RemoveLiquidityParams = () => <RemoveLiquidity params={params} />
const CreateExchangeParams = () => <CreateExchange params={params} />
return (
<>
<ModeSelector />
{/* this Suspense is for route code-splitting */}
<Suspense fallback={null}>
<Switch>
<Route exact strict path="/add-liquidity" component={AddLiquidityParams} />
<Route exact strict path="/remove-liquidity" component={RemoveLiquidityParams} />
<Route exact strict path="/create-exchange" component={CreateExchangeParams} />
<Route
path="/create-exchange/:tokenAddress"
render={({ match }) => {
return (
<Redirect to={{ pathname: '/create-exchange', state: { tokenAddress: match.params.tokenAddress } }} />
)
}}
/>
<Redirect to="/add-liquidity" />
</Switch>
</Suspense>
</>
)
}
This diff is collapsed.
......@@ -7,7 +7,7 @@ import styled from 'styled-components'
import { useWeb3React, useExchangeContract } from '../../hooks'
import { useTransactionAdder } from '../../contexts/Transactions'
import { useTokenDetails, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
import { useToken, INITIAL_TOKENS_CONTEXT } from '../../contexts/Tokens'
import { useAddressBalance } from '../../contexts/Balances'
import { calculateGasMargin, amountFormatter } from '../../utils'
......@@ -188,7 +188,7 @@ export default function RemoveLiquidity({ params }) {
}
}, [t, value])
const { symbol, decimals, exchangeAddress } = useTokenDetails(outputCurrency)
const { symbol, decimals, exchangeAddress } = useToken(outputCurrency)
const [totalPoolTokens, setTotalPoolTokens] = useState()
const poolTokenBalance = useAddressBalance(account, exchangeAddress)
......
This diff is collapsed.
/// <reference types="react-scripts" />
import styled, { keyframes } from 'styled-components'
import { darken } from 'polished'
import { X } from 'react-feather'
export const Button = styled.button.attrs(({ warning, theme }) => ({
backgroundColor: warning ? theme.salmonRed : theme.royalBlue
......@@ -31,6 +32,12 @@ export const Button = styled.button.attrs(({ warning, theme }) => ({
}
`
export const CloseIcon = styled(X)`
:hover {
cursor: pointer;
}
`
export const Link = styled.a.attrs({
target: '_blank',
rel: 'noopener noreferrer'
......
......@@ -45,15 +45,20 @@ const theme = darkMode => ({
black,
textColor: darkMode ? white : '#010101',
greyText: darkMode ? white : '#6C7284',
fadedText: darkMode ? white : '#C3C5CB',
// for setting css on <html>
backgroundColor: darkMode ? '#333639' : white,
panelBackground: darkMode ? '#292C2F' : '#FFFFFF',
// for setting css on <html>
backgroundColor: darkMode ? '#333639' : '#F7F8FA',
modalBackground: darkMode ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.5)',
inputBackground: darkMode ? '#202124' : white,
placeholderGray: darkMode ? '#5F5F5F' : '#E1E1E1',
shadowColor: darkMode ? '#000' : '#2F80ED',
buttonBackgroundPlain: darkMode ? '#333639' : white,
buttonOutlinePlain: darkMode ? '#292C2F' : white,
// grays
concreteGray: darkMode ? '#292C2F' : '#FAFAFA',
mercuryGray: darkMode ? '#333333' : '#E1E1E1',
......@@ -65,12 +70,15 @@ const theme = darkMode => ({
buttonOutlineGrey: darkMode ? '#FAFAFA' : '#F2F2F2',
tokenRowHover: darkMode ? '#404040' : '#F2F2F2',
outlineGrey: darkMode ? '#292C2F' : '#EDEEF2',
//blacks
charcoalBlack: darkMode ? '#F2F2F2' : '#404040',
// blues
zumthorBlue: darkMode ? '#212529' : '#EBF4FF',
malibuBlue: darkMode ? '#E67AEF' : '#5CA2FF',
royalBlue: darkMode ? '#DC6BE5' : '#2F80ED',
disabledBlue: darkMode ? '#2172E5' : '#2172E5',
loadingBlue: darkMode ? '#e4f0ff' : '#e4f0ff',
// purples
......
......@@ -5,7 +5,6 @@ import EXCHANGE_ABI from '../constants/abis/exchange'
import ERC20_ABI from '../constants/abis/erc20'
import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32'
import { FACTORY_ADDRESSES, SUPPORTED_THEMES } from '../constants'
import { formatFixed } from '@uniswap/sdk'
import UncheckedJsonRpcSigner from './signer'
......@@ -256,11 +255,7 @@ export function formatTokenBalance(balance, decimal) {
export function formatToUsd(price) {
const format = { decimalSeparator: '.', groupSeparator: ',', groupSize: 3 }
const usdPrice = formatFixed(price, {
decimalPlaces: 2,
dropTrailingZeros: false,
format
})
const usdPrice = 1
return usdPrice
}
......
......@@ -21,15 +21,5 @@ export function getUSDPrice(reserves) {
ethPrices,
forEachStablecoin(i => reserves[i].ethReserve.amount)
)
// 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 getMean([median, mean, weightedMean])[0]
}
This diff is collapsed.
module.exports = {
entry: [
path.join(process.cwd(), 'app/app.tsx'), // or whatever the path of your root file is
]
module: {
rules:[{ test: /\.tsx?$/, loader: 'awesome-typescript-loader' }], // other loader configuration goes in the array
resolve: {extensions: ['.js', '.jsx', '.react.js', '.ts', '.tsx']}
}
}
\ No newline at end of file
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