feat(polygon): polygon mainnet and testnet support (#3015)
* feat(polygon): polygon mainnet and testnet support WIP! DO NOT USE * fix unit test * fix explorer links * compute usdc prices * - fix the header currency label - fix unit tests * polygon gradient colors * chore: adding weth to common bases (#3025) * adding weth to common bases * adding usdc and dai * adding usdt and wbtc * fix a bunch of polish issues - 3085 detection - some wrapping stuff - the network selector dropdown * fix wrap/unwrap notification text on polygon * background color per the figma * subgraph url * fix the re-render blinking on the network selector * failed network switch * clean up duplicate code in the network switching functions * fix text color in the privacy notice on light mode * add some routing constants for polygon * do not show the separator in the trade route if auto router is not supported * - network switching without a wallet connected - remove v2 stuff from pool page when n/a - remove WMATIC from common bases on polygon * background colors of network alert * oops fix background on network alert * clean up optimism labels * fix alignment of text on downtime warning * finish the network alert styles Co-authored-by: Sara Reynolds <30504811+snreynolds@users.noreply.github.com>
This commit is contained in:
parent
7ac1ed3f52
commit
f047f0d196
4
src/assets/svg/matic-token-icon.svg
Normal file
4
src/assets/svg/matic-token-icon.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="512" cy="512" r="512" fill="#8247E5"/>
|
||||
<path d="M681.469 402.456C669.189 395.312 653.224 395.312 639.716 402.456L543.928 457.228L478.842 492.949L383.055 547.721C370.774 554.865 354.81 554.865 341.301 547.721L265.162 504.856C252.882 497.712 244.286 484.614 244.286 470.325V385.786C244.286 371.498 251.654 358.4 265.162 351.256L340.073 309.581C352.353 302.437 368.318 302.437 381.827 309.581L456.737 351.256C469.018 358.4 477.614 371.498 477.614 385.786V440.558L542.7 403.646V348.874C542.7 334.586 535.332 321.488 521.824 314.344L383.055 235.758C370.774 228.614 354.81 228.614 341.301 235.758L200.076 314.344C186.567 321.488 179.199 334.586 179.199 348.874V507.237C179.199 521.525 186.567 534.623 200.076 541.767L341.301 620.353C353.582 627.498 369.546 627.498 383.055 620.353L478.842 566.772L543.928 529.86L639.716 476.279C651.996 469.135 667.961 469.135 681.469 476.279L756.38 517.953C768.66 525.098 777.257 538.195 777.257 552.484V637.023C777.257 651.312 769.888 664.409 756.38 671.553L681.469 714.419C669.189 721.563 653.224 721.563 639.716 714.419L564.805 672.744C552.525 665.6 543.928 652.502 543.928 638.214V583.442L478.842 620.353V675.125C478.842 689.414 486.21 702.512 499.719 709.656L640.944 788.242C653.224 795.386 669.189 795.386 682.697 788.242L823.922 709.656C836.203 702.512 844.799 689.414 844.799 675.125V516.763C844.799 502.474 837.431 489.377 823.922 482.232L681.469 402.456Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
16
src/assets/svg/polygon-matic-logo.svg
Normal file
16
src/assets/svg/polygon-matic-logo.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 38.4 33.5" style="enable-background:new 0 0 38.4 33.5;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#8247E5;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M29,10.2c-0.7-0.4-1.6-0.4-2.4,0L21,13.5l-3.8,2.1l-5.5,3.3c-0.7,0.4-1.6,0.4-2.4,0L5,16.3
|
||||
c-0.7-0.4-1.2-1.2-1.2-2.1v-5c0-0.8,0.4-1.6,1.2-2.1l4.3-2.5c0.7-0.4,1.6-0.4,2.4,0L16,7.2c0.7,0.4,1.2,1.2,1.2,2.1v3.3l3.8-2.2V7
|
||||
c0-0.8-0.4-1.6-1.2-2.1l-8-4.7c-0.7-0.4-1.6-0.4-2.4,0L1.2,5C0.4,5.4,0,6.2,0,7v9.4c0,0.8,0.4,1.6,1.2,2.1l8.1,4.7
|
||||
c0.7,0.4,1.6,0.4,2.4,0l5.5-3.2l3.8-2.2l5.5-3.2c0.7-0.4,1.6-0.4,2.4,0l4.3,2.5c0.7,0.4,1.2,1.2,1.2,2.1v5c0,0.8-0.4,1.6-1.2,2.1
|
||||
L29,28.8c-0.7,0.4-1.6,0.4-2.4,0l-4.3-2.5c-0.7-0.4-1.2-1.2-1.2-2.1V21l-3.8,2.2v3.3c0,0.8,0.4,1.6,1.2,2.1l8.1,4.7
|
||||
c0.7,0.4,1.6,0.4,2.4,0l8.1-4.7c0.7-0.4,1.2-1.2,1.2-2.1V17c0-0.8-0.4-1.6-1.2-2.1L29,10.2z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro'
|
||||
import { Fraction, TradeType } from '@uniswap/sdk-core'
|
||||
import JSBI from 'jsbi'
|
||||
|
||||
import { nativeOnChain } from '../../constants/tokens'
|
||||
import { useCurrency, useToken } from '../../hooks/Tokens'
|
||||
import useENSName from '../../hooks/useENSName'
|
||||
import { VoteOption } from '../../state/governance/types'
|
||||
@ -130,18 +131,33 @@ function DelegateSummary({ info: { delegatee } }: { info: DelegateTransactionInf
|
||||
return <Trans>Delegate voting power to {ENSName ?? delegatee}</Trans>
|
||||
}
|
||||
|
||||
function WrapSummary({ info: { currencyAmountRaw, unwrapped } }: { info: WrapTransactionInfo }) {
|
||||
function WrapSummary({ info: { chainId, currencyAmountRaw, unwrapped } }: { info: WrapTransactionInfo }) {
|
||||
const native = chainId ? nativeOnChain(chainId) : undefined
|
||||
|
||||
if (unwrapped) {
|
||||
return (
|
||||
<Trans>
|
||||
Unwrap <FormattedCurrencyAmount rawAmount={currencyAmountRaw} symbol={'WETH'} decimals={18} sigFigs={6} /> to
|
||||
ETH
|
||||
Unwrap{' '}
|
||||
<FormattedCurrencyAmount
|
||||
rawAmount={currencyAmountRaw}
|
||||
symbol={native?.wrapped?.symbol ?? 'WETH'}
|
||||
decimals={18}
|
||||
sigFigs={6}
|
||||
/>{' '}
|
||||
to {native?.symbol ?? 'ETH'}
|
||||
</Trans>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Trans>
|
||||
Wrap <FormattedCurrencyAmount rawAmount={currencyAmountRaw} symbol={'ETH'} decimals={18} sigFigs={6} /> to WETH
|
||||
Wrap{' '}
|
||||
<FormattedCurrencyAmount
|
||||
rawAmount={currencyAmountRaw}
|
||||
symbol={native?.symbol ?? 'ETH'}
|
||||
decimals={18}
|
||||
sigFigs={6}
|
||||
/>{' '}
|
||||
to {native?.wrapped?.symbol ?? 'WETH'}
|
||||
</Trans>
|
||||
)
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import EthereumLogo from 'assets/images/ethereum-logo.png'
|
||||
import MaticLogo from 'assets/svg/matic-token-icon.svg'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useHttpLocations from 'hooks/useHttpLocations'
|
||||
import React, { useMemo } from 'react'
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import EthereumLogo from '../../assets/images/ethereum-logo.png'
|
||||
import useHttpLocations from '../../hooks/useHttpLocations'
|
||||
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
|
||||
import Logo from '../Logo'
|
||||
|
||||
type Network = 'ethereum' | 'arbitrum' | 'optimism'
|
||||
@ -34,7 +35,7 @@ export const getTokenLogoURL = (
|
||||
}
|
||||
}
|
||||
|
||||
const StyledEthereumLogo = styled.img<{ size: string }>`
|
||||
const StyledNativeLogo = styled.img<{ size: string }>`
|
||||
width: ${({ size }) => size};
|
||||
height: ${({ size }) => size};
|
||||
background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%);
|
||||
@ -87,7 +88,17 @@ export default function CurrencyLogo({
|
||||
}, [currency, uriLocations])
|
||||
|
||||
if (currency?.isNative) {
|
||||
return <StyledEthereumLogo src={EthereumLogo} alt="ethereum logo" size={size} style={style} {...rest} />
|
||||
let nativeLogoUrl: string
|
||||
switch (currency.chainId) {
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
case SupportedChainId.POLYGON:
|
||||
nativeLogoUrl = MaticLogo
|
||||
break
|
||||
default:
|
||||
nativeLogoUrl = EthereumLogo
|
||||
break
|
||||
}
|
||||
return <StyledNativeLogo src={nativeLogoUrl} alt="ethereum logo" size={size} style={style} {...rest} />
|
||||
}
|
||||
|
||||
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} {...rest} />
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { L2_CHAIN_IDS, SupportedChainId } from 'constants/chains'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink } from 'theme'
|
||||
|
||||
import { isL2ChainId } from '../../utils/chains'
|
||||
|
||||
const Root = styled.div`
|
||||
background-color: ${({ theme }) => (theme.darkMode ? '#888D9B' : '#CED0D9')};
|
||||
border-radius: 18px;
|
||||
@ -18,7 +20,6 @@ const Root = styled.div`
|
||||
max-width: 880px;
|
||||
`
|
||||
const WarningIcon = styled(AlertOctagon)`
|
||||
display: block;
|
||||
margin: auto 16px auto 0;
|
||||
min-height: 22px;
|
||||
min-width: 22px;
|
||||
@ -28,50 +29,54 @@ const ReadMoreLink = styled(ExternalLink)`
|
||||
text-decoration: underline;
|
||||
`
|
||||
|
||||
export default function DowntimeWarning() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
if (!chainId || !L2_CHAIN_IDS.includes(chainId)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const Content = () => {
|
||||
switch (chainId) {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
return (
|
||||
<div>
|
||||
<Trans>
|
||||
Optimistic Ethereum is in Beta and may experience downtime. Optimism expects planned downtime to upgrade
|
||||
the network in the near future. During downtime, your position will not earn fees and you will be unable
|
||||
to remove liquidity.{' '}
|
||||
<ReadMoreLink href="https://help.uniswap.org/en/articles/5406082-what-happens-if-the-optimistic-ethereum-network-experiences-downtime">
|
||||
Read more.
|
||||
</ReadMoreLink>
|
||||
</Trans>
|
||||
</div>
|
||||
)
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
return (
|
||||
<div>
|
||||
<Trans>
|
||||
Arbitrum is in Beta and may experience downtime. During downtime, your position will not earn fees and you
|
||||
will be unable to remove liquidity.{' '}
|
||||
<ReadMoreLink href="https://help.uniswap.org/en/articles/5576122-arbitrum-network-downtime">
|
||||
Read more.
|
||||
</ReadMoreLink>
|
||||
</Trans>
|
||||
</div>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function Wrapper({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Root>
|
||||
<WarningIcon />
|
||||
<Content />
|
||||
<div>{children}</div>
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a downtime warning for the network if it's relevant
|
||||
*/
|
||||
export default function DowntimeWarning() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
if (!isL2ChainId(chainId)) {
|
||||
return null
|
||||
}
|
||||
|
||||
switch (chainId) {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
return (
|
||||
<Wrapper>
|
||||
<Trans>
|
||||
Optimism is in Beta and may experience downtime. Optimism expects planned downtime to upgrade the network in
|
||||
the near future. During downtime, your position will not earn fees and you will be unable to remove
|
||||
liquidity.{' '}
|
||||
<ReadMoreLink href="https://help.uniswap.org/en/articles/5406082-what-happens-if-the-optimistic-ethereum-network-experiences-downtime">
|
||||
Read more.
|
||||
</ReadMoreLink>
|
||||
</Trans>
|
||||
</Wrapper>
|
||||
)
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
return (
|
||||
<Wrapper>
|
||||
<Trans>
|
||||
Arbitrum is in Beta and may experience downtime. During downtime, your position will not earn fees and you
|
||||
will be unable to remove liquidity.{' '}
|
||||
<ReadMoreLink href="https://help.uniswap.org/en/articles/5576122-arbitrum-network-downtime">
|
||||
Read more.
|
||||
</ReadMoreLink>
|
||||
</Trans>
|
||||
</Wrapper>
|
||||
)
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,16 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import {
|
||||
ARBITRUM_HELP_CENTER_LINK,
|
||||
CHAIN_INFO,
|
||||
L2_CHAIN_IDS,
|
||||
OPTIMISM_HELP_CENTER_LINK,
|
||||
SupportedChainId,
|
||||
SupportedL2ChainId,
|
||||
} from 'constants/chains'
|
||||
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { useCallback, useRef } from 'react'
|
||||
import { ArrowDownCircle, ChevronDown } from 'react-feather'
|
||||
import { useModalOpen, useToggleModal } from 'state/application/hooks'
|
||||
import { ApplicationModal } from 'state/application/reducer'
|
||||
import { useAppSelector } from 'state/hooks'
|
||||
import { addPopup, ApplicationModal } from 'state/application/reducer'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
|
||||
import { switchToNetwork } from 'utils/switchToNetwork'
|
||||
|
||||
import { useAppDispatch } from '../../state/hooks'
|
||||
import { switchToNetwork } from '../../utils/switchToNetwork'
|
||||
|
||||
const ActiveRowLinkList = styled.div`
|
||||
display: flex;
|
||||
@ -136,7 +130,7 @@ const SelectorWrapper = styled.div`
|
||||
const StyledChevronDown = styled(ChevronDown)`
|
||||
width: 12px;
|
||||
`
|
||||
const BridgeText = ({ chainId }: { chainId: SupportedL2ChainId }) => {
|
||||
const BridgeLabel = ({ chainId }: { chainId: SupportedChainId }) => {
|
||||
switch (chainId) {
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
@ -144,11 +138,14 @@ const BridgeText = ({ chainId }: { chainId: SupportedL2ChainId }) => {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
return <Trans>Optimism Gateway</Trans>
|
||||
case SupportedChainId.POLYGON:
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
return <Trans>Polygon Bridge</Trans>
|
||||
default:
|
||||
return <Trans>Bridge</Trans>
|
||||
}
|
||||
}
|
||||
const ExplorerText = ({ chainId }: { chainId: SupportedL2ChainId }) => {
|
||||
const ExplorerLabel = ({ chainId }: { chainId: SupportedChainId }) => {
|
||||
switch (chainId) {
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
@ -156,91 +153,108 @@ const ExplorerText = ({ chainId }: { chainId: SupportedL2ChainId }) => {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
return <Trans>Optimistic Etherscan</Trans>
|
||||
case SupportedChainId.POLYGON:
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
return <Trans>Polygonscan</Trans>
|
||||
default:
|
||||
return <Trans>Explorer</Trans>
|
||||
return <Trans>Etherscan</Trans>
|
||||
}
|
||||
}
|
||||
|
||||
function Row({
|
||||
targetChain,
|
||||
onSelectChain,
|
||||
}: {
|
||||
targetChain: SupportedChainId
|
||||
onSelectChain: (targetChain: number) => void
|
||||
}) {
|
||||
const { library, chainId } = useActiveWeb3React()
|
||||
if (!library || !chainId) {
|
||||
return null
|
||||
}
|
||||
const active = chainId === targetChain
|
||||
const { helpCenterUrl, explorer, bridge, label, logoUrl } = CHAIN_INFO[targetChain]
|
||||
|
||||
const rowContent = (
|
||||
<FlyoutRow onClick={() => onSelectChain(targetChain)} active={active}>
|
||||
<Logo src={logoUrl} />
|
||||
<NetworkLabel>{label}</NetworkLabel>
|
||||
{chainId === targetChain && <FlyoutRowActiveIndicator />}
|
||||
</FlyoutRow>
|
||||
)
|
||||
|
||||
if (active) {
|
||||
return (
|
||||
<ActiveRowWrapper>
|
||||
{rowContent}
|
||||
<ActiveRowLinkList>
|
||||
{bridge ? (
|
||||
<ExternalLink href={bridge}>
|
||||
<BridgeLabel chainId={chainId} /> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
{explorer ? (
|
||||
<ExternalLink href={explorer}>
|
||||
<ExplorerLabel chainId={chainId} /> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
{helpCenterUrl ? (
|
||||
<ExternalLink href={helpCenterUrl}>
|
||||
<Trans>Help Center</Trans> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
</ActiveRowLinkList>
|
||||
</ActiveRowWrapper>
|
||||
)
|
||||
}
|
||||
return rowContent
|
||||
}
|
||||
|
||||
export default function NetworkSelector() {
|
||||
const { chainId, library } = useActiveWeb3React()
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.NETWORK_SELECTOR)
|
||||
const toggle = useToggleModal(ApplicationModal.NETWORK_SELECTOR)
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
const implements3085 = useAppSelector((state) => state.application.implements3085)
|
||||
|
||||
const info = chainId ? CHAIN_INFO[chainId] : undefined
|
||||
|
||||
const isOnL2 = chainId ? L2_CHAIN_IDS.includes(chainId) : false
|
||||
const showSelector = Boolean(implements3085 || isOnL2)
|
||||
const mainnetInfo = CHAIN_INFO[SupportedChainId.MAINNET]
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const conditionalToggle = useCallback(() => {
|
||||
if (showSelector) {
|
||||
toggle()
|
||||
}
|
||||
}, [showSelector, toggle])
|
||||
const handleRowClick = useCallback(
|
||||
(targetChain: number) => {
|
||||
if (!library) return
|
||||
switchToNetwork({ library, chainId: targetChain })
|
||||
.then(() => toggle())
|
||||
.catch((error) => {
|
||||
console.error('Failed to switch networks', error)
|
||||
toggle()
|
||||
dispatch(addPopup({ content: { failedSwitchNetwork: targetChain }, key: `failed-network-switch` }))
|
||||
})
|
||||
},
|
||||
[dispatch, library, toggle]
|
||||
)
|
||||
|
||||
if (!chainId || !info || !library) {
|
||||
return null
|
||||
}
|
||||
|
||||
function Row({ targetChain }: { targetChain: number }) {
|
||||
if (!library || !chainId || (!implements3085 && targetChain !== chainId)) {
|
||||
return null
|
||||
}
|
||||
const handleRowClick = () => {
|
||||
switchToNetwork({ library, chainId: targetChain })
|
||||
toggle()
|
||||
}
|
||||
const active = chainId === targetChain
|
||||
const hasExtendedInfo = L2_CHAIN_IDS.includes(targetChain)
|
||||
const isOptimism = targetChain === SupportedChainId.OPTIMISM
|
||||
const rowText = `${CHAIN_INFO[targetChain].label}${isOptimism ? ' (Optimism)' : ''}`
|
||||
const RowContent = () => (
|
||||
<FlyoutRow onClick={handleRowClick} active={active}>
|
||||
<Logo src={CHAIN_INFO[targetChain].logoUrl} />
|
||||
<NetworkLabel>{rowText}</NetworkLabel>
|
||||
{chainId === targetChain && <FlyoutRowActiveIndicator />}
|
||||
</FlyoutRow>
|
||||
)
|
||||
const helpCenterLink = isOptimism ? OPTIMISM_HELP_CENTER_LINK : ARBITRUM_HELP_CENTER_LINK
|
||||
if (active && hasExtendedInfo) {
|
||||
return (
|
||||
<ActiveRowWrapper>
|
||||
<RowContent />
|
||||
<ActiveRowLinkList>
|
||||
<ExternalLink href={CHAIN_INFO[targetChain as SupportedL2ChainId].bridge}>
|
||||
<BridgeText chainId={chainId} /> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
<ExternalLink href={CHAIN_INFO[targetChain].explorer}>
|
||||
<ExplorerText chainId={chainId} /> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
<ExternalLink href={helpCenterLink}>
|
||||
<Trans>Help Center</Trans> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
</ActiveRowLinkList>
|
||||
</ActiveRowWrapper>
|
||||
)
|
||||
}
|
||||
return <RowContent />
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectorWrapper ref={node as any}>
|
||||
<SelectorControls onClick={conditionalToggle} interactive={showSelector}>
|
||||
<SelectorLogo interactive={showSelector} src={info.logoUrl || mainnetInfo.logoUrl} />
|
||||
<SelectorControls onClick={toggle} interactive>
|
||||
<SelectorLogo interactive src={info.logoUrl} />
|
||||
<SelectorLabel>{info.label}</SelectorLabel>
|
||||
{showSelector && <StyledChevronDown />}
|
||||
<StyledChevronDown />
|
||||
</SelectorControls>
|
||||
{open && (
|
||||
<FlyoutMenu>
|
||||
<FlyoutHeader>
|
||||
<Trans>Select a network</Trans>
|
||||
</FlyoutHeader>
|
||||
<Row targetChain={SupportedChainId.MAINNET} />
|
||||
<Row targetChain={SupportedChainId.OPTIMISM} />
|
||||
<Row targetChain={SupportedChainId.ARBITRUM_ONE} />
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.MAINNET} />
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.POLYGON} />
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.OPTIMISM} />
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.ARBITRUM_ONE} />
|
||||
</FlyoutMenu>
|
||||
)}
|
||||
</SelectorWrapper>
|
||||
|
@ -147,8 +147,8 @@ export default function Polling() {
|
||||
<MouseoverTooltip
|
||||
text={
|
||||
<Trans>
|
||||
{`The current fast gas amount for sending a transaction on L1.
|
||||
Gas fees are paid in Ethereum's native currency Ether (ETH) and denominated in gwei. `}
|
||||
The current fast gas amount for sending a transaction on L1. Gas fees are paid in
|
||||
Ethereum's native currency Ether (ETH) and denominated in GWEI.
|
||||
</Trans>
|
||||
}
|
||||
>
|
||||
@ -166,7 +166,7 @@ export default function Polling() {
|
||||
}
|
||||
>
|
||||
<MouseoverTooltip
|
||||
text={<Trans>{`The most recent block number on this network. Prices update on every block.`}</Trans>}
|
||||
text={<Trans>The most recent block number on this network. Prices update on every block.</Trans>}
|
||||
>
|
||||
{blockNumber} 
|
||||
</MouseoverTooltip>
|
||||
|
@ -1,144 +0,0 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
import { useMemo } from 'react'
|
||||
import { X } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import tokenLogo from '../../assets/images/token-logo.png'
|
||||
import { UNI } from '../../constants/tokens'
|
||||
import { useMerkleDistributorContract } from '../../hooks/useContract'
|
||||
import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
import useUSDCPrice from '../../hooks/useUSDCPrice'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useTotalUniEarned } from '../../state/stake/hooks'
|
||||
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { ExternalLink, StyledInternalLink, ThemedText, UniTokenAnimated } from '../../theme'
|
||||
import { computeUniCirculation } from '../../utils/computeUniCirculation'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
|
||||
import { RowBetween } from '../Row'
|
||||
|
||||
const ContentWrapper = styled(AutoColumn)`
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const ModalUpper = styled(DataCard)`
|
||||
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
||||
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #ff007a 0%, #021d43 100%);
|
||||
padding: 0.5rem;
|
||||
`
|
||||
|
||||
const StyledClose = styled(X)`
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
|
||||
/**
|
||||
* Content for balance stats modal
|
||||
*/
|
||||
export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowUniBalanceModal: any }) {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
const uni = chainId ? UNI[chainId] : undefined
|
||||
|
||||
const total = useAggregateUniBalance()
|
||||
const uniBalance: CurrencyAmount<Token> | undefined = useTokenBalance(account ?? undefined, uni)
|
||||
const uniToClaim: CurrencyAmount<Token> | undefined = useTotalUniEarned()
|
||||
|
||||
const totalSupply: CurrencyAmount<Token> | undefined = useTotalSupply(uni)
|
||||
const uniPrice = useUSDCPrice(uni)
|
||||
const blockTimestamp = useCurrentBlockTimestamp()
|
||||
const unclaimedUni = useTokenBalance(useMerkleDistributorContract()?.address, uni)
|
||||
const circulation: CurrencyAmount<Token> | undefined = useMemo(
|
||||
() =>
|
||||
blockTimestamp && uni && chainId === 1 ? computeUniCirculation(uni, blockTimestamp, unclaimedUni) : totalSupply,
|
||||
[blockTimestamp, chainId, totalSupply, unclaimedUni, uni]
|
||||
)
|
||||
|
||||
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
|
||||
return (
|
||||
<ContentWrapper gap="lg">
|
||||
<ModalUpper>
|
||||
<CardBGImage />
|
||||
<CardNoise />
|
||||
<CardSection gap="md">
|
||||
<RowBetween>
|
||||
<ThemedText.White color="white">
|
||||
<Trans>Your UNI Breakdown</Trans>
|
||||
</ThemedText.White>
|
||||
<StyledClose stroke="white" onClick={() => setShowUniBalanceModal(false)} />
|
||||
</RowBetween>
|
||||
</CardSection>
|
||||
<Break />
|
||||
{account && (
|
||||
<>
|
||||
<CardSection gap="sm">
|
||||
<AutoColumn gap="md" justify="center">
|
||||
<UniTokenAnimated width="48px" src={tokenLogo} />{' '}
|
||||
<ThemedText.White fontSize={48} fontWeight={600} color="white">
|
||||
{total?.toFixed(2, { groupSeparator: ',' })}
|
||||
</ThemedText.White>
|
||||
</AutoColumn>
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<ThemedText.White color="white">
|
||||
<Trans>Balance:</Trans>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White color="white">{uniBalance?.toFixed(2, { groupSeparator: ',' })}</ThemedText.White>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<ThemedText.White color="white">
|
||||
<Trans>Unclaimed:</Trans>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White color="white">
|
||||
{uniToClaim?.toFixed(4, { groupSeparator: ',' })}{' '}
|
||||
{uniToClaim && uniToClaim.greaterThan('0') && (
|
||||
<StyledInternalLink onClick={() => setShowUniBalanceModal(false)} to="/uni">
|
||||
<Trans>(claim)</Trans>
|
||||
</StyledInternalLink>
|
||||
)}
|
||||
</ThemedText.White>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</CardSection>
|
||||
<Break />
|
||||
</>
|
||||
)}
|
||||
<CardSection gap="sm">
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<ThemedText.White color="white">
|
||||
<Trans>UNI price:</Trans>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White color="white">${uniPrice?.toFixed(2) ?? '-'}</ThemedText.White>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<ThemedText.White color="white">
|
||||
<Trans>UNI in circulation:</Trans>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White color="white">{circulation?.toFixed(0, { groupSeparator: ',' })}</ThemedText.White>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<ThemedText.White color="white">
|
||||
<Trans>Total Supply</Trans>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</ThemedText.White>
|
||||
</RowBetween>
|
||||
{uni && uni.chainId === 1 ? (
|
||||
<ExternalLink href={`${infoLink}/token/${uni.address}`}>
|
||||
<Trans>View UNI Analytics</Trans>
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
</CardSection>
|
||||
</ModalUpper>
|
||||
</ContentWrapper>
|
||||
)
|
||||
}
|
@ -3,14 +3,13 @@ import useScrollPosition from '@react-hook/window-scroll'
|
||||
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { darken } from 'polished'
|
||||
import { useState } from 'react'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
import { useShowClaimPopup, useToggleSelfClaimModal } from 'state/application/hooks'
|
||||
import { useUserHasAvailableClaim } from 'state/claim/hooks'
|
||||
import { useUserHasSubmittedClaim } from 'state/transactions/hooks'
|
||||
import { useDarkModeManager } from 'state/user/hooks'
|
||||
import { useETHBalances } from 'state/wallet/hooks'
|
||||
import { useNativeCurrencyBalances } from 'state/wallet/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { ReactComponent as Logo } from '../../assets/svg/logo.svg'
|
||||
@ -19,12 +18,10 @@ import { ExternalLink, ThemedText } from '../../theme'
|
||||
import ClaimModal from '../claim/ClaimModal'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
import Menu from '../Menu'
|
||||
import Modal from '../Modal'
|
||||
import Row from '../Row'
|
||||
import { Dots } from '../swap/styleds'
|
||||
import Web3Status from '../Web3Status'
|
||||
import NetworkSelector from './NetworkSelector'
|
||||
import UniBalanceContent from './UniBalanceContent'
|
||||
|
||||
const HeaderFrame = styled.div<{ showBackground: boolean }>`
|
||||
display: grid;
|
||||
@ -246,7 +243,7 @@ const StyledExternalLink = styled(ExternalLink).attrs({
|
||||
export default function Header() {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
|
||||
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
|
||||
const userEthBalance = useNativeCurrencyBalances(account ? [account] : [])?.[account ?? '']
|
||||
const [darkMode] = useDarkModeManager()
|
||||
const { white, black } = useTheme()
|
||||
|
||||
@ -256,18 +253,20 @@ export default function Header() {
|
||||
|
||||
const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined)
|
||||
|
||||
const [showUniBalanceModal, setShowUniBalanceModal] = useState(false)
|
||||
const showClaimPopup = useShowClaimPopup()
|
||||
|
||||
const scrollY = useScrollPosition()
|
||||
|
||||
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
const {
|
||||
infoLink,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { symbol: nativeCurrencySymbol },
|
||||
},
|
||||
} = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
|
||||
return (
|
||||
<HeaderFrame showBackground={scrollY > 45}>
|
||||
<ClaimModal />
|
||||
<Modal isOpen={showUniBalanceModal} onDismiss={() => setShowUniBalanceModal(false)}>
|
||||
<UniBalanceContent setShowUniBalanceModal={setShowUniBalanceModal} />
|
||||
</Modal>
|
||||
<Title href=".">
|
||||
<UniIcon>
|
||||
<Logo fill={darkMode ? white : black} width="24px" height="100%" title="logo" />
|
||||
@ -325,7 +324,9 @@ export default function Header() {
|
||||
<AccountElement active={!!account}>
|
||||
{account && userEthBalance ? (
|
||||
<BalanceText style={{ flexShrink: 0, userSelect: 'none' }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
|
||||
<Trans>{userEthBalance?.toSignificant(3)} ETH</Trans>
|
||||
<Trans>
|
||||
{userEthBalance?.toSignificant(3)} {nativeCurrencySymbol}
|
||||
</Trans>
|
||||
</BalanceText>
|
||||
) : null}
|
||||
<Web3Status />
|
||||
|
@ -1,32 +1,22 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import {
|
||||
ARBITRUM_HELP_CENTER_LINK,
|
||||
L2_CHAIN_IDS,
|
||||
OPTIMISM_HELP_CENTER_LINK,
|
||||
SupportedChainId,
|
||||
SupportedL2ChainId,
|
||||
} from 'constants/chains'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { ArrowDownCircle, X } from 'react-feather'
|
||||
import { useArbitrumAlphaAlert, useDarkModeManager, useOptimismAlphaAlert } from 'state/user/hooks'
|
||||
import { useETHBalances } from 'state/wallet/hooks'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
import { useDarkModeManager, useNetworkAlertStatus } from 'state/user/hooks'
|
||||
import { useNativeCurrencyBalances } from 'state/wallet/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
|
||||
|
||||
import { CHAIN_INFO } from '../../constants/chains'
|
||||
|
||||
export const DesktopTextBreak = styled.div`
|
||||
display: none;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
|
||||
display: block;
|
||||
}
|
||||
`
|
||||
import { ThemedText } from '../../theme'
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
const L2Icon = styled.img`
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
justify-self: center;
|
||||
margin-right: 14px;
|
||||
`
|
||||
const BetaTag = styled.span<{ color: string }>`
|
||||
align-items: center;
|
||||
@ -44,24 +34,11 @@ const BetaTag = styled.span<{ color: string }>`
|
||||
width: 60px;
|
||||
z-index: 1;
|
||||
`
|
||||
const Body = styled.p`
|
||||
font-size: 12px;
|
||||
grid-column: 1 / 3;
|
||||
line-height: 143%;
|
||||
margin: 0;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
grid-column: 2 / 3;
|
||||
}
|
||||
`
|
||||
export const Controls = styled.div<{ thin?: boolean }>`
|
||||
export const Controls = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
${({ thin }) =>
|
||||
thin &&
|
||||
css`
|
||||
margin: auto 32px auto 0;
|
||||
`}
|
||||
padding: 0 20px 20px 20px;
|
||||
`
|
||||
const CloseIcon = styled(X)`
|
||||
cursor: pointer;
|
||||
@ -71,17 +48,13 @@ const CloseIcon = styled(X)`
|
||||
`
|
||||
const BodyText = styled.div`
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-gap: 4px;
|
||||
grid-template-columns: 40px 4fr;
|
||||
grid-template-rows: auto auto;
|
||||
margin: 20px 16px;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
grid-template-columns: 42px 4fr;
|
||||
grid-gap: 8px;
|
||||
}
|
||||
`
|
||||
const LearnMoreLink = styled(ExternalLink)<{ thin?: boolean }>`
|
||||
const LearnMoreLink = styled(ExternalLink)`
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
@ -91,7 +64,6 @@ const LearnMoreLink = styled(ExternalLink)<{ thin?: boolean }>`
|
||||
font-size: 16px;
|
||||
height: 44px;
|
||||
justify-content: space-between;
|
||||
margin: 0 0 20px 0;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
width: auto;
|
||||
@ -101,57 +73,51 @@ const LearnMoreLink = styled(ExternalLink)<{ thin?: boolean }>`
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
transition: background-color 150ms ease-in-out;
|
||||
${({ thin }) =>
|
||||
thin &&
|
||||
css`
|
||||
font-size: 14px;
|
||||
margin: auto;
|
||||
width: 112px;
|
||||
`}
|
||||
`
|
||||
const RootWrapper = styled.div`
|
||||
position: relative;
|
||||
`
|
||||
export const ArbitrumWrapperBackgroundDarkMode = css`
|
||||
background: radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),
|
||||
radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.3) 0%, rgba(33, 114, 229, 0.3) 100%), hsla(0, 0%, 100%, 0.1);
|
||||
`
|
||||
export const ArbitrumWrapperBackgroundLightMode = css`
|
||||
background: radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),
|
||||
radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1);
|
||||
`
|
||||
export const OptimismWrapperBackgroundDarkMode = css`
|
||||
background: radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.2) 0%, rgba(255, 255, 255, 0.1) 100%),
|
||||
radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.5) 0%, rgba(235, 0, 255, 0.345) 96%);
|
||||
`
|
||||
export const OptimismWrapperBackgroundLightMode = css`
|
||||
background: radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),
|
||||
radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.5);
|
||||
`
|
||||
const ContentWrapper = styled.div<{ chainId: SupportedChainId; darkMode: boolean; logoUrl: string; thin?: boolean }>`
|
||||
${({ chainId, darkMode }) =>
|
||||
[SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)
|
||||
? darkMode
|
||||
? OptimismWrapperBackgroundDarkMode
|
||||
: OptimismWrapperBackgroundLightMode
|
||||
: darkMode
|
||||
? ArbitrumWrapperBackgroundDarkMode
|
||||
: ArbitrumWrapperBackgroundLightMode};
|
||||
const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
|
||||
[darkMode in 'dark' | 'light']: { [chainId in SupportedChainId]?: string }
|
||||
} = {
|
||||
dark: {
|
||||
[SupportedChainId.POLYGON]:
|
||||
'radial-gradient(100% 93.36% at 0% 6.64%, rgba(160, 108, 247, 0.3) 0%, rgba(82, 32, 166, 0.3) 100%)',
|
||||
[SupportedChainId.POLYGON_MUMBAI]:
|
||||
'radial-gradient(100% 93.36% at 0% 6.64%, rgba(160, 108, 247, 0.3) 0%, rgba(82, 32, 166, 0.3) 100%)',
|
||||
[SupportedChainId.OPTIMISM]:
|
||||
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.2) 0%, rgba(255, 255, 255, 0.1) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.5) 0%, rgba(235, 0, 255, 0.345) 96%)',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]:
|
||||
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.2) 0%, rgba(255, 255, 255, 0.1) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.5) 0%, rgba(235, 0, 255, 0.345) 96%)',
|
||||
[SupportedChainId.ARBITRUM_ONE]:
|
||||
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.3) 0%, rgba(33, 114, 229, 0.3) 100%), hsla(0, 0%, 100%, 0.1)',
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]:
|
||||
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.3) 0%, rgba(33, 114, 229, 0.3) 100%), hsla(0, 0%, 100%, 0.1)',
|
||||
},
|
||||
light: {
|
||||
[SupportedChainId.POLYGON]:
|
||||
'radial-gradient(182.71% 205.59% at 2.81% 7.69%, rgba(130, 71, 229, 0.3) 0%, rgba(167, 202, 255, 0.3) 100%)',
|
||||
[SupportedChainId.POLYGON_MUMBAI]:
|
||||
'radial-gradient(182.71% 205.59% at 2.81% 7.69%, rgba(130, 71, 229, 0.3) 0%, rgba(167, 202, 255, 0.3) 100%)',
|
||||
[SupportedChainId.OPTIMISM]:
|
||||
'radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.5)',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]:
|
||||
'radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.5)',
|
||||
[SupportedChainId.ARBITRUM_ONE]:
|
||||
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1)',
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]:
|
||||
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1)',
|
||||
},
|
||||
}
|
||||
|
||||
const ContentWrapper = styled.div<{ chainId: SupportedChainId; darkMode: boolean; logoUrl: string }>`
|
||||
background: ${({ chainId, darkMode }) => BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID[darkMode ? 'dark' : 'light'][chainId]};
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 480px;
|
||||
min-height: 174px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
${({ thin }) =>
|
||||
thin &&
|
||||
css`
|
||||
flex-direction: row;
|
||||
max-width: max-content;
|
||||
min-height: min-content;
|
||||
`}
|
||||
:before {
|
||||
background-image: url(${({ logoUrl }) => logoUrl});
|
||||
background-repeat: no-repeat;
|
||||
@ -165,12 +131,11 @@ const ContentWrapper = styled.div<{ chainId: SupportedChainId; darkMode: boolean
|
||||
z-index: -1;
|
||||
}
|
||||
`
|
||||
const Header = styled.h2<{ thin?: boolean }>`
|
||||
const Header = styled.h2`
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
padding-right: 30px;
|
||||
display: ${({ thin }) => (thin ? 'none' : 'block')};
|
||||
`
|
||||
const LinkOutCircle = styled(ArrowDownCircle)`
|
||||
margin-left: 12px;
|
||||
@ -178,7 +143,7 @@ const LinkOutCircle = styled(ArrowDownCircle)`
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
`
|
||||
const LinkOutToBridge = styled(ExternalLink)<{ thin?: boolean }>`
|
||||
const LinkOutToBridge = styled(ExternalLink)`
|
||||
align-items: center;
|
||||
background-color: black;
|
||||
border-radius: 8px;
|
||||
@ -187,8 +152,8 @@ const LinkOutToBridge = styled(ExternalLink)<{ thin?: boolean }>`
|
||||
font-size: 16px;
|
||||
height: 44px;
|
||||
justify-content: space-between;
|
||||
margin: 0 12px 20px 18px;
|
||||
padding: 12px 16px;
|
||||
margin-right: 20px;
|
||||
text-decoration: none;
|
||||
width: auto;
|
||||
:hover,
|
||||
@ -196,85 +161,154 @@ const LinkOutToBridge = styled(ExternalLink)<{ thin?: boolean }>`
|
||||
:active {
|
||||
background-color: black;
|
||||
}
|
||||
${({ thin }) =>
|
||||
thin &&
|
||||
css`
|
||||
font-size: 14px;
|
||||
margin: auto 10px;
|
||||
width: 168px;
|
||||
`}
|
||||
`
|
||||
|
||||
interface NetworkAlertProps {
|
||||
thin?: boolean
|
||||
const DisclaimerText = styled(ThemedText.Body)`
|
||||
padding: 0 0.5em;
|
||||
font-size: 14px !important;
|
||||
`
|
||||
|
||||
const BETA_TAG_COLORS: { [chainId in SupportedChainId]?: string } = {
|
||||
[SupportedChainId.OPTIMISM]: '#ff0420',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: '#ff0420',
|
||||
[SupportedChainId.ARBITRUM_ONE]: '#0490ed',
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: '#0490ed',
|
||||
}
|
||||
|
||||
export function NetworkAlert(props: NetworkAlertProps) {
|
||||
const SHOULD_SHOW_ALERT: { [chainId in SupportedChainId]?: true } = {
|
||||
[SupportedChainId.OPTIMISM]: true,
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: true,
|
||||
[SupportedChainId.ARBITRUM_ONE]: true,
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: true,
|
||||
[SupportedChainId.POLYGON]: true,
|
||||
[SupportedChainId.POLYGON_MUMBAI]: true,
|
||||
}
|
||||
|
||||
function shouldShowAlert(chainId: number | undefined): chainId is SupportedChainId {
|
||||
return Boolean(chainId && SHOULD_SHOW_ALERT[chainId as SupportedChainId])
|
||||
}
|
||||
|
||||
export function NetworkAlert() {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
const [darkMode] = useDarkModeManager()
|
||||
const [arbitrumAlphaAcknowledged, setArbitrumAlphaAcknowledged] = useArbitrumAlphaAlert()
|
||||
const [optimismAlphaAcknowledged, setOptimismAlphaAcknowledged] = useOptimismAlphaAlert()
|
||||
const [alertAcknowledged, acknowledgeAlert] = useNetworkAlertStatus(chainId)
|
||||
const [locallyDismissed, setLocallyDimissed] = useState(false)
|
||||
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
|
||||
const accounts = useMemo(() => (account ? [account] : []), [account])
|
||||
const userNativeCurrencyBalance = useNativeCurrencyBalances(accounts)?.[account ?? '']
|
||||
|
||||
const dismiss = useCallback(() => {
|
||||
if (userEthBalance?.greaterThan(0)) {
|
||||
switch (chainId) {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
setOptimismAlphaAcknowledged(true)
|
||||
break
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
setArbitrumAlphaAcknowledged(true)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
setLocallyDimissed(true)
|
||||
}
|
||||
}, [chainId, setArbitrumAlphaAcknowledged, setOptimismAlphaAcknowledged, userEthBalance])
|
||||
setLocallyDimissed(true)
|
||||
if (!alertAcknowledged) acknowledgeAlert()
|
||||
}, [acknowledgeAlert, alertAcknowledged])
|
||||
|
||||
const onOptimismAndOptimismAcknowledged = SupportedChainId.OPTIMISM === chainId && optimismAlphaAcknowledged
|
||||
const onArbitrumAndArbitrumAcknowledged = SupportedChainId.ARBITRUM_ONE === chainId && arbitrumAlphaAcknowledged
|
||||
if (
|
||||
!chainId ||
|
||||
!L2_CHAIN_IDS.includes(chainId) ||
|
||||
onArbitrumAndArbitrumAcknowledged ||
|
||||
onOptimismAndOptimismAcknowledged ||
|
||||
locallyDismissed
|
||||
) {
|
||||
if (!shouldShowAlert(chainId) || alertAcknowledged || locallyDismissed) {
|
||||
return null
|
||||
}
|
||||
const info = CHAIN_INFO[chainId as SupportedL2ChainId]
|
||||
const isOptimism = [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)
|
||||
const depositUrl = isOptimism ? `${info.bridge}?chainId=1` : info.bridge
|
||||
const helpCenterLink = isOptimism ? OPTIMISM_HELP_CENTER_LINK : ARBITRUM_HELP_CENTER_LINK
|
||||
const showCloseIcon = Boolean(userEthBalance?.greaterThan(0) && !props.thin)
|
||||
|
||||
const { label, logoUrl, bridge, helpCenterUrl } = CHAIN_INFO[chainId]
|
||||
const showCloseIcon = Boolean(userNativeCurrencyBalance?.greaterThan(0))
|
||||
const betaColor = BETA_TAG_COLORS[chainId]
|
||||
return (
|
||||
<RootWrapper>
|
||||
<BetaTag color={isOptimism ? '#ff0420' : '#0490ed'}>Beta</BetaTag>
|
||||
<ContentWrapper chainId={chainId} darkMode={darkMode} logoUrl={info.logoUrl} thin={props.thin}>
|
||||
{betaColor ? <BetaTag color={betaColor}>Beta</BetaTag> : null}
|
||||
<ContentWrapper chainId={chainId} darkMode={darkMode} logoUrl={logoUrl}>
|
||||
{showCloseIcon && <CloseIcon onClick={dismiss} />}
|
||||
<BodyText>
|
||||
<L2Icon src={info.logoUrl} />
|
||||
<Header thin={props.thin}>
|
||||
<Trans>Uniswap on {info.label}</Trans>
|
||||
</Header>
|
||||
<Body>
|
||||
<Trans>
|
||||
To start trading on {info.label}, first bridge your assets from L1 to L2. Please treat this as a beta
|
||||
release and learn about the risks before using {info.label}.
|
||||
</Trans>
|
||||
</Body>
|
||||
<AutoRow style={{ marginBottom: '1em' }}>
|
||||
<L2Icon src={logoUrl} />
|
||||
<Header>
|
||||
<Trans>Uniswap on {label}</Trans>
|
||||
</Header>
|
||||
</AutoRow>
|
||||
<DisclaimerText>
|
||||
{betaColor ? (
|
||||
<Trans>
|
||||
Please treat this as a beta release and learn about the risks before using {label}. To start trading on{' '}
|
||||
{label}, first bridge your assets from L1 to L2.
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>To start trading on {label}, first bridge your assets from L1 to L2.</Trans>
|
||||
)}
|
||||
</DisclaimerText>
|
||||
</BodyText>
|
||||
<Controls thin={props.thin}>
|
||||
<LinkOutToBridge href={depositUrl} thin={props.thin}>
|
||||
<Trans>Deposit Assets</Trans>
|
||||
<LinkOutCircle />
|
||||
</LinkOutToBridge>
|
||||
<LearnMoreLink href={helpCenterLink} thin={props.thin}>
|
||||
<Trans>Learn More</Trans>
|
||||
</LearnMoreLink>
|
||||
<Controls>
|
||||
{bridge ? (
|
||||
<LinkOutToBridge href={bridge}>
|
||||
<Trans>Deposit Assets</Trans>
|
||||
<LinkOutCircle />
|
||||
</LinkOutToBridge>
|
||||
) : null}
|
||||
{helpCenterUrl ? (
|
||||
<LearnMoreLink href={helpCenterUrl}>
|
||||
<Trans>Learn More</Trans>
|
||||
</LearnMoreLink>
|
||||
) : null}
|
||||
</Controls>
|
||||
</ContentWrapper>
|
||||
</RootWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
const AlertRow = styled.div`
|
||||
display: flex;
|
||||
padding: 1em;
|
||||
align-items: center;
|
||||
`
|
||||
const ButtonContainer = styled.div`
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
`
|
||||
const FlexGrow = styled.div`
|
||||
flex-grow: 1;
|
||||
`
|
||||
export function SingleRowNetworkAlert() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const [darkMode] = useDarkModeManager()
|
||||
|
||||
if (!shouldShowAlert(chainId)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { label, logoUrl, bridge, helpCenterUrl } = CHAIN_INFO[chainId]
|
||||
const betaColor = BETA_TAG_COLORS[chainId]
|
||||
|
||||
return (
|
||||
<RootWrapper>
|
||||
{betaColor ? <BetaTag color={betaColor}>Beta</BetaTag> : null}
|
||||
<ContentWrapper chainId={chainId} darkMode={darkMode} logoUrl={logoUrl}>
|
||||
<AlertRow>
|
||||
<L2Icon src={logoUrl} />
|
||||
|
||||
<FlexGrow>
|
||||
<DisclaimerText>
|
||||
{betaColor ? (
|
||||
<Trans>
|
||||
Please treat this as a beta release and learn about the risks before using {label}. To start trading
|
||||
on {label}, first bridge your assets from L1 to L2.
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>To start trading on {label}, first bridge your assets from L1 to L2.</Trans>
|
||||
)}
|
||||
</DisclaimerText>
|
||||
</FlexGrow>
|
||||
|
||||
<ButtonContainer>
|
||||
{bridge ? (
|
||||
<LinkOutToBridge href={bridge}>
|
||||
<Trans>Deposit Assets</Trans>
|
||||
<LinkOutCircle />
|
||||
</LinkOutToBridge>
|
||||
) : null}
|
||||
{helpCenterUrl ? (
|
||||
<LearnMoreLink href={helpCenterUrl}>
|
||||
<Trans>Learn More</Trans>
|
||||
</LearnMoreLink>
|
||||
) : null}
|
||||
</ButtonContainer>
|
||||
</AlertRow>
|
||||
</ContentWrapper>
|
||||
</RootWrapper>
|
||||
)
|
||||
}
|
||||
|
34
src/components/Popups/FailedNetworkSwitchPopup.tsx
Normal file
34
src/components/Popups/FailedNetworkSwitchPopup.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useContext } from 'react'
|
||||
import { AlertCircle } from 'react-feather'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { CHAIN_INFO, SupportedChainId } from '../../constants/chains'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
const RowNoFlex = styled(AutoRow)`
|
||||
flex-wrap: nowrap;
|
||||
`
|
||||
|
||||
export default function FailedNetworkSwitchPopup({ chainId }: { chainId: SupportedChainId }) {
|
||||
const chainInfo = CHAIN_INFO[chainId]
|
||||
const theme = useContext(ThemeContext)
|
||||
|
||||
return (
|
||||
<RowNoFlex>
|
||||
<div style={{ paddingRight: 16 }}>
|
||||
<AlertCircle color={theme.red1} size={24} />
|
||||
</div>
|
||||
<AutoColumn gap="8px">
|
||||
<ThemedText.Body fontWeight={500}>
|
||||
<Trans>
|
||||
Your wallet does not support switching networks from the Uniswap Interface. In order to use Uniswap on{' '}
|
||||
{chainInfo.label}, you must change the network in your wallet.
|
||||
</Trans>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
</RowNoFlex>
|
||||
)
|
||||
}
|
@ -6,6 +6,7 @@ import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { useRemovePopup } from '../../state/application/hooks'
|
||||
import { PopupContent } from '../../state/application/reducer'
|
||||
import FailedNetworkSwitchPopup from './FailedNetworkSwitchPopup'
|
||||
import TransactionPopup from './TransactionPopup'
|
||||
|
||||
const StyledClose = styled(X)`
|
||||
@ -77,6 +78,8 @@ export default function PopupItem({
|
||||
txn: { hash },
|
||||
} = content
|
||||
popupContent = <TransactionPopup hash={hash} />
|
||||
} else if ('failedSwitchNetwork' in content) {
|
||||
popupContent = <FailedNetworkSwitchPopup chainId={content.failedSwitchNetwork} />
|
||||
}
|
||||
|
||||
const faderStyle = useSpring({
|
||||
|
@ -19,7 +19,7 @@ import { PositionDetails } from 'types/position'
|
||||
import { formatTickPrice } from 'utils/formatTickPrice'
|
||||
import { unwrappedToken } from 'utils/unwrappedToken'
|
||||
|
||||
import { DAI, USDC, USDT, WBTC, WETH9_EXTENDED } from '../../constants/tokens'
|
||||
import { DAI, USDC, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
|
||||
|
||||
const LinkRow = styled(Link)`
|
||||
align-items: center;
|
||||
@ -156,7 +156,7 @@ export function getPriceOrderingFromPositionForUI(position?: Position): {
|
||||
}
|
||||
|
||||
// if token1 is an ETH-/BTC-stable asset, set it as the base token
|
||||
const bases = [...Object.values(WETH9_EXTENDED), WBTC]
|
||||
const bases = [...Object.values(WRAPPED_NATIVE_CURRENCY), WBTC]
|
||||
if (bases.some((base) => base.equals(token1))) {
|
||||
return {
|
||||
priceLower: position.token0PriceUpper.invert(),
|
||||
|
@ -13,8 +13,13 @@ import { FixedSizeList } from 'react-window'
|
||||
import { Text } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { ExtendedEther } from '../../constants/tokens'
|
||||
import { useAllTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
|
||||
import {
|
||||
useAllTokens,
|
||||
useIsUserAddedToken,
|
||||
useNativeCurrency,
|
||||
useSearchInactiveTokenLists,
|
||||
useToken,
|
||||
} from '../../hooks/Tokens'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ButtonText, CloseIcon, IconWrapper, ThemedText } from '../../theme'
|
||||
import { isAddress } from '../../utils'
|
||||
@ -112,15 +117,17 @@ export function CurrencySearch({
|
||||
|
||||
const filteredSortedTokens = useSortedTokensByQuery(sortedTokens, debouncedQuery)
|
||||
|
||||
const ether = useMemo(() => chainId && ExtendedEther.onChain(chainId), [chainId])
|
||||
const native = useNativeCurrency()
|
||||
|
||||
const filteredSortedTokensWithETH: Currency[] = useMemo(() => {
|
||||
if (!native) return filteredSortedTokens
|
||||
|
||||
const s = debouncedQuery.toLowerCase().trim()
|
||||
if (s === '' || s === 'e' || s === 'et' || s === 'eth') {
|
||||
return ether ? [ether, ...filteredSortedTokens] : filteredSortedTokens
|
||||
if (native.symbol?.toLowerCase()?.indexOf(s) !== -1) {
|
||||
return native ? [native, ...filteredSortedTokens] : filteredSortedTokens
|
||||
}
|
||||
return filteredSortedTokens
|
||||
}, [debouncedQuery, ether, filteredSortedTokens])
|
||||
}, [debouncedQuery, native, filteredSortedTokens])
|
||||
|
||||
const handleCurrencySelect = useCallback(
|
||||
(currency: Currency) => {
|
||||
@ -148,8 +155,8 @@ export function CurrencySearch({
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
const s = debouncedQuery.toLowerCase().trim()
|
||||
if (s === 'eth' && ether) {
|
||||
handleCurrencySelect(ether)
|
||||
if (s === native?.symbol?.toLowerCase()) {
|
||||
handleCurrencySelect(native)
|
||||
} else if (filteredSortedTokensWithETH.length > 0) {
|
||||
if (
|
||||
filteredSortedTokensWithETH[0].symbol?.toLowerCase() === debouncedQuery.trim().toLowerCase() ||
|
||||
@ -160,7 +167,7 @@ export function CurrencySearch({
|
||||
}
|
||||
}
|
||||
},
|
||||
[debouncedQuery, ether, filteredSortedTokensWithETH, handleCurrencySelect]
|
||||
[debouncedQuery, native, filteredSortedTokensWithETH, handleCurrencySelect]
|
||||
)
|
||||
|
||||
// menu ui
|
||||
|
@ -111,7 +111,7 @@ const HoverText = styled.div`
|
||||
|
||||
const LinkCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
color: ${({ theme }) => theme.white};
|
||||
color: ${({ theme }) => theme.text3};
|
||||
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
@ -416,9 +416,9 @@ export default function WalletModal({
|
||||
<RowBetween>
|
||||
<AutoRow gap="4px">
|
||||
<Info size={20} />
|
||||
<ThemedText.White fontSize={14}>
|
||||
<ThemedText.Label fontSize={14}>
|
||||
<Trans>How this app uses APIs</Trans>
|
||||
</ThemedText.White>
|
||||
</ThemedText.Label>
|
||||
</AutoRow>
|
||||
<ArrowRight size={16} />
|
||||
</RowBetween>
|
||||
|
@ -83,23 +83,27 @@ export default memo(function SwapRoute({ trade, syncing, fixedOpen = false, ...r
|
||||
routes={routes}
|
||||
/>
|
||||
)}
|
||||
<Separator />
|
||||
{autoRouterSupported &&
|
||||
(syncing ? (
|
||||
<LoadingRows>
|
||||
<div style={{ width: '250px', height: '15px' }} />
|
||||
</LoadingRows>
|
||||
) : (
|
||||
<ThemedText.Main fontSize={12} width={400} margin={0}>
|
||||
{trade?.gasUseEstimateUSD && chainId && SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId) ? (
|
||||
<Trans>Best price route costs ~{formattedGasPriceString} in gas. </Trans>
|
||||
) : null}{' '}
|
||||
<Trans>
|
||||
This route optimizes your total output by considering split routes, multiple hops, and the gas cost of
|
||||
each step.
|
||||
</Trans>
|
||||
</ThemedText.Main>
|
||||
))}
|
||||
|
||||
{autoRouterSupported && (
|
||||
<>
|
||||
<Separator />
|
||||
{syncing ? (
|
||||
<LoadingRows>
|
||||
<div style={{ width: '250px', height: '15px' }} />
|
||||
</LoadingRows>
|
||||
) : (
|
||||
<ThemedText.Main fontSize={12} width={400} margin={0}>
|
||||
{trade?.gasUseEstimateUSD && chainId && SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId) ? (
|
||||
<Trans>Best price route costs ~{formattedGasPriceString} in gas. </Trans>
|
||||
) : null}{' '}
|
||||
<Trans>
|
||||
This route optimizes your total output by considering split routes, multiple hops, and the gas cost
|
||||
of each step.
|
||||
</Trans>
|
||||
</ThemedText.Main>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</AutoRow>
|
||||
</AnimatedDropdown>
|
||||
</Wrapper>
|
||||
|
@ -36,11 +36,14 @@ class MiniRpcProvider implements AsyncSendable {
|
||||
public readonly path: string
|
||||
public readonly batchWaitTimeMs: number
|
||||
|
||||
private readonly connector: NetworkConnector
|
||||
|
||||
private nextId = 1
|
||||
private batchTimeoutId: ReturnType<typeof setTimeout> | null = null
|
||||
private batch: BatchItem[] = []
|
||||
|
||||
constructor(chainId: number, url: string, batchWaitTimeMs?: number) {
|
||||
constructor(connector: NetworkConnector, chainId: number, url: string, batchWaitTimeMs?: number) {
|
||||
this.connector = connector
|
||||
this.chainId = chainId
|
||||
this.url = url
|
||||
const parsed = new URL(url)
|
||||
@ -52,7 +55,21 @@ class MiniRpcProvider implements AsyncSendable {
|
||||
|
||||
public readonly clearBatch = async () => {
|
||||
console.debug('Clearing batch', this.batch)
|
||||
const batch = this.batch
|
||||
let batch = this.batch
|
||||
|
||||
batch = batch.filter((b) => {
|
||||
if (b.request.method === 'wallet_switchEthereumChain') {
|
||||
try {
|
||||
this.connector.changeChainId(parseInt((b.request.params as [{ chainId: string }])[0].chainId))
|
||||
b.resolve({ id: b.request.id })
|
||||
} catch (error) {
|
||||
b.reject(error)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
this.batch = []
|
||||
this.batchTimeoutId = null
|
||||
let response: Response
|
||||
@ -148,9 +165,9 @@ export class NetworkConnector extends AbstractConnector {
|
||||
invariant(defaultChainId || Object.keys(urls).length === 1, 'defaultChainId is a required argument with >1 url')
|
||||
super({ supportedChainIds: Object.keys(urls).map((k): number => Number(k)) })
|
||||
|
||||
this.currentChainId = defaultChainId || Number(Object.keys(urls)[0])
|
||||
this.currentChainId = defaultChainId ?? Number(Object.keys(urls)[0])
|
||||
this.providers = Object.keys(urls).reduce<{ [chainId: number]: MiniRpcProvider }>((accumulator, chainId) => {
|
||||
accumulator[Number(chainId)] = new MiniRpcProvider(Number(chainId), urls[Number(chainId)])
|
||||
accumulator[Number(chainId)] = new MiniRpcProvider(this, Number(chainId), urls[Number(chainId)])
|
||||
return accumulator
|
||||
}, {})
|
||||
}
|
||||
@ -178,4 +195,21 @@ export class NetworkConnector extends AbstractConnector {
|
||||
public deactivate() {
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* Meant to be called only by MiniRpcProvider
|
||||
* @param chainId the new chain id
|
||||
*/
|
||||
public changeChainId(chainId: number) {
|
||||
if (chainId in this.providers) {
|
||||
this.currentChainId = chainId
|
||||
this.emitUpdate({
|
||||
chainId,
|
||||
account: null,
|
||||
provider: this.providers[chainId],
|
||||
})
|
||||
} else {
|
||||
throw new Error(`Unsupported chain ID: ${chainId}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,33 +6,16 @@ import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
|
||||
import { WalletLinkConnector } from '@web3-react/walletlink-connector'
|
||||
|
||||
import UNISWAP_LOGO_URL from '../assets/svg/logo.svg'
|
||||
import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from '../constants/chains'
|
||||
import { ALL_SUPPORTED_CHAIN_IDS, INFURA_NETWORK_URLS, SupportedChainId } from '../constants/chains'
|
||||
import getLibrary from '../utils/getLibrary'
|
||||
import { FortmaticConnector } from './Fortmatic'
|
||||
import { NetworkConnector } from './NetworkConnector'
|
||||
|
||||
const INFURA_KEY = process.env.REACT_APP_INFURA_KEY
|
||||
const FORMATIC_KEY = process.env.REACT_APP_FORTMATIC_KEY
|
||||
const PORTIS_ID = process.env.REACT_APP_PORTIS_ID
|
||||
|
||||
if (typeof INFURA_KEY === 'undefined') {
|
||||
throw new Error(`REACT_APP_INFURA_KEY must be a defined environment variable`)
|
||||
}
|
||||
|
||||
export const NETWORK_URLS: { [key in SupportedChainId]: string } = {
|
||||
[SupportedChainId.MAINNET]: `https://mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.RINKEBY]: `https://rinkeby.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ROPSTEN]: `https://ropsten.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.GOERLI]: `https://goerli.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.KOVAN]: `https://kovan.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.OPTIMISM]: `https://optimism-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: `https://optimism-kovan.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ARBITRUM_ONE]: `https://arbitrum-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: `https://arbitrum-rinkeby.infura.io/v3/${INFURA_KEY}`,
|
||||
}
|
||||
|
||||
export const network = new NetworkConnector({
|
||||
urls: NETWORK_URLS,
|
||||
urls: INFURA_NETWORK_URLS,
|
||||
defaultChainId: 1,
|
||||
})
|
||||
|
||||
@ -49,7 +32,7 @@ export const gnosisSafe = new SafeAppConnector()
|
||||
|
||||
export const walletconnect = new WalletConnectConnector({
|
||||
supportedChainIds: ALL_SUPPORTED_CHAIN_IDS,
|
||||
rpc: NETWORK_URLS,
|
||||
rpc: INFURA_NETWORK_URLS,
|
||||
qrcode: true,
|
||||
})
|
||||
|
||||
@ -67,7 +50,7 @@ export const portis = new PortisConnector({
|
||||
|
||||
// mainnet only
|
||||
export const walletlink = new WalletLinkConnector({
|
||||
url: NETWORK_URLS[SupportedChainId.MAINNET],
|
||||
url: INFURA_NETWORK_URLS[SupportedChainId.MAINNET],
|
||||
appName: 'Uniswap',
|
||||
appLogoUrl: UNISWAP_LOGO_URL,
|
||||
supportedChainIds: [SupportedChainId.MAINNET],
|
||||
|
@ -11,6 +11,8 @@ export const MULTICALL_ADDRESS: AddressMap = {
|
||||
...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984', [
|
||||
SupportedChainId.OPTIMISTIC_KOVAN,
|
||||
SupportedChainId.OPTIMISM,
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
SupportedChainId.POLYGON,
|
||||
]),
|
||||
[SupportedChainId.ARBITRUM_ONE]: '0xadF885960B47eA2CD9B55E6DAc6B42b7Cb2806dB',
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: '0xa501c031958F579dB7676fF1CE78AD305794d579',
|
||||
@ -63,12 +65,16 @@ export const V3_CORE_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V3_
|
||||
SupportedChainId.OPTIMISTIC_KOVAN,
|
||||
SupportedChainId.ARBITRUM_ONE,
|
||||
SupportedChainId.ARBITRUM_RINKEBY,
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
SupportedChainId.POLYGON,
|
||||
])
|
||||
export const QUOTER_ADDRESSES: AddressMap = constructSameAddressMap('0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6', [
|
||||
SupportedChainId.OPTIMISM,
|
||||
SupportedChainId.OPTIMISTIC_KOVAN,
|
||||
SupportedChainId.ARBITRUM_ONE,
|
||||
SupportedChainId.ARBITRUM_RINKEBY,
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
SupportedChainId.POLYGON,
|
||||
])
|
||||
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: AddressMap = constructSameAddressMap(
|
||||
'0xC36442b4a4522E871399CD717aBDD847Ab11FE88',
|
||||
@ -77,6 +83,8 @@ export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: AddressMap = constructSameA
|
||||
SupportedChainId.OPTIMISTIC_KOVAN,
|
||||
SupportedChainId.ARBITRUM_ONE,
|
||||
SupportedChainId.ARBITRUM_RINKEBY,
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
SupportedChainId.POLYGON,
|
||||
]
|
||||
)
|
||||
export const ENS_REGISTRAR_ADDRESSES: AddressMap = {
|
||||
@ -92,4 +100,6 @@ export const SOCKS_CONTROLLER_ADDRESSES: AddressMap = {
|
||||
export const V3_MIGRATOR_ADDRESSES: AddressMap = constructSameAddressMap('0xA5644E29708357803b5A882D272c41cC0dF92B34', [
|
||||
SupportedChainId.ARBITRUM_ONE,
|
||||
SupportedChainId.ARBITRUM_RINKEBY,
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
SupportedChainId.POLYGON,
|
||||
])
|
||||
|
@ -1,10 +1,14 @@
|
||||
import ethereumLogoUrl from 'assets/images/ethereum-logo.png'
|
||||
import arbitrumLogoUrl from 'assets/svg/arbitrum_logo.svg'
|
||||
import optimismLogoUrl from 'assets/svg/optimistic_ethereum.svg'
|
||||
import polygonMaticLogo from 'assets/svg/polygon-matic-logo.svg'
|
||||
import ms from 'ms.macro'
|
||||
|
||||
import { ARBITRUM_LIST, OPTIMISM_LIST } from './lists'
|
||||
|
||||
/**
|
||||
* List of all the networks supported by the Uniswap Interface
|
||||
*/
|
||||
export enum SupportedChainId {
|
||||
MAINNET = 1,
|
||||
ROPSTEN = 3,
|
||||
@ -14,33 +18,45 @@ export enum SupportedChainId {
|
||||
|
||||
ARBITRUM_ONE = 42161,
|
||||
ARBITRUM_RINKEBY = 421611,
|
||||
|
||||
OPTIMISM = 10,
|
||||
OPTIMISTIC_KOVAN = 69,
|
||||
|
||||
POLYGON = 137,
|
||||
POLYGON_MUMBAI = 80001,
|
||||
}
|
||||
|
||||
export const ALL_SUPPORTED_CHAIN_IDS: SupportedChainId[] = [
|
||||
SupportedChainId.MAINNET,
|
||||
SupportedChainId.ROPSTEN,
|
||||
SupportedChainId.RINKEBY,
|
||||
SupportedChainId.GOERLI,
|
||||
SupportedChainId.KOVAN,
|
||||
const INFURA_KEY = process.env.REACT_APP_INFURA_KEY
|
||||
if (typeof INFURA_KEY === 'undefined') {
|
||||
throw new Error(`REACT_APP_INFURA_KEY must be a defined environment variable`)
|
||||
}
|
||||
|
||||
SupportedChainId.ARBITRUM_ONE,
|
||||
SupportedChainId.ARBITRUM_RINKEBY,
|
||||
SupportedChainId.OPTIMISM,
|
||||
SupportedChainId.OPTIMISTIC_KOVAN,
|
||||
]
|
||||
/**
|
||||
* Array of all the supported chain IDs
|
||||
*/
|
||||
export const ALL_SUPPORTED_CHAIN_IDS: SupportedChainId[] = Object.values(SupportedChainId).filter(
|
||||
(id) => typeof id === 'number'
|
||||
) as SupportedChainId[]
|
||||
|
||||
/**
|
||||
* All the chain IDs that are running the Ethereum protocol.
|
||||
*/
|
||||
export const L1_CHAIN_IDS = [
|
||||
SupportedChainId.MAINNET,
|
||||
SupportedChainId.ROPSTEN,
|
||||
SupportedChainId.RINKEBY,
|
||||
SupportedChainId.GOERLI,
|
||||
SupportedChainId.KOVAN,
|
||||
SupportedChainId.POLYGON,
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
] as const
|
||||
|
||||
export type SupportedL1ChainId = typeof L1_CHAIN_IDS[number]
|
||||
|
||||
/**
|
||||
* Controls some L2 specific behavior, e.g. slippage tolerance, special UI behavior.
|
||||
* The expectation is that all of these networks have immediate transaction confirmation.
|
||||
*/
|
||||
export const L2_CHAIN_IDS = [
|
||||
SupportedChainId.ARBITRUM_ONE,
|
||||
SupportedChainId.ARBITRUM_RINKEBY,
|
||||
@ -50,106 +66,149 @@ export const L2_CHAIN_IDS = [
|
||||
|
||||
export type SupportedL2ChainId = typeof L2_CHAIN_IDS[number]
|
||||
|
||||
export interface L1ChainInfo {
|
||||
readonly blockWaitMsBeforeWarning?: number
|
||||
readonly docs: string
|
||||
readonly explorer: string
|
||||
readonly infoLink: string
|
||||
readonly label: string
|
||||
readonly logoUrl?: string
|
||||
readonly rpcUrls?: string[]
|
||||
/**
|
||||
* These are the network URLs used by the interface when there is not another available source of chain data
|
||||
*/
|
||||
export const INFURA_NETWORK_URLS: { [key in SupportedChainId]: string } = {
|
||||
[SupportedChainId.MAINNET]: `https://mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.RINKEBY]: `https://rinkeby.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ROPSTEN]: `https://ropsten.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.GOERLI]: `https://goerli.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.KOVAN]: `https://kovan.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.OPTIMISM]: `https://optimism-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: `https://optimism-kovan.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ARBITRUM_ONE]: `https://arbitrum-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: `https://arbitrum-rinkeby.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.POLYGON]: `https://polygon-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.POLYGON_MUMBAI]: `https://polygon-mumbai.infura.io/v3/${INFURA_KEY}`,
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to call the add network RPC
|
||||
*/
|
||||
interface AddNetworkInfo {
|
||||
readonly rpcUrl: string
|
||||
readonly nativeCurrency: {
|
||||
name: string // 'Goerli ETH',
|
||||
symbol: string // 'gorETH',
|
||||
decimals: number //18,
|
||||
name: string // e.g. 'Goerli ETH',
|
||||
symbol: string // e.g. 'gorETH',
|
||||
decimals: number // e.g. 18,
|
||||
}
|
||||
}
|
||||
export interface L2ChainInfo extends L1ChainInfo {
|
||||
readonly bridge: string
|
||||
|
||||
export enum NetworkType {
|
||||
L1,
|
||||
L2,
|
||||
}
|
||||
|
||||
interface BaseChainInfo {
|
||||
readonly networkType: NetworkType
|
||||
readonly blockWaitMsBeforeWarning?: number
|
||||
readonly docs: string
|
||||
readonly bridge?: string
|
||||
readonly explorer: string
|
||||
readonly infoLink: string
|
||||
readonly logoUrl: string
|
||||
readonly label: string
|
||||
readonly helpCenterUrl?: string
|
||||
readonly addNetworkInfo: AddNetworkInfo
|
||||
}
|
||||
|
||||
export interface L1ChainInfo extends BaseChainInfo {
|
||||
readonly networkType: NetworkType.L1
|
||||
}
|
||||
|
||||
export interface L2ChainInfo extends BaseChainInfo {
|
||||
readonly networkType: NetworkType.L2
|
||||
readonly bridge: string
|
||||
readonly statusPage?: string
|
||||
readonly defaultListUrl: string
|
||||
}
|
||||
|
||||
export type ChainInfo = { readonly [chainId: number]: L1ChainInfo | L2ChainInfo } & {
|
||||
export type ChainInfoMap = { readonly [chainId: number]: L1ChainInfo | L2ChainInfo } & {
|
||||
readonly [chainId in SupportedL2ChainId]: L2ChainInfo
|
||||
} &
|
||||
{ readonly [chainId in SupportedL1ChainId]: L1ChainInfo }
|
||||
|
||||
export const CHAIN_INFO: ChainInfo = {
|
||||
[SupportedChainId.ARBITRUM_ONE]: {
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://bridge.arbitrum.io/',
|
||||
defaultListUrl: ARBITRUM_LIST,
|
||||
docs: 'https://offchainlabs.com/',
|
||||
explorer: 'https://arbiscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/arbitrum/',
|
||||
label: 'Arbitrum',
|
||||
logoUrl: arbitrumLogoUrl,
|
||||
nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrls: ['https://arb1.arbitrum.io/rpc'],
|
||||
},
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: {
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://bridge.arbitrum.io/',
|
||||
defaultListUrl: ARBITRUM_LIST,
|
||||
docs: 'https://offchainlabs.com/',
|
||||
explorer: 'https://rinkeby-explorer.arbitrum.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/arbitrum/',
|
||||
label: 'Arbitrum Rinkeby',
|
||||
logoUrl: arbitrumLogoUrl,
|
||||
nativeCurrency: { name: 'Rinkeby ArbETH', symbol: 'rinkArbETH', decimals: 18 },
|
||||
rpcUrls: ['https://rinkeby.arbitrum.io/rpc'],
|
||||
},
|
||||
export const CHAIN_INFO: ChainInfoMap = {
|
||||
[SupportedChainId.MAINNET]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Ethereum',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.MAINNET],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.RINKEBY]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://rinkeby.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Rinkeby',
|
||||
nativeCurrency: { name: 'Rinkeby ETH', symbol: 'rinkETH', decimals: 18 },
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Rinkeby Ether', symbol: 'rETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.RINKEBY],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.ROPSTEN]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://ropsten.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Ropsten',
|
||||
nativeCurrency: { name: 'Ropsten ETH', symbol: 'ropETH', decimals: 18 },
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ropsten Ether', symbol: 'ropETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.ROPSTEN],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.KOVAN]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://kovan.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Kovan',
|
||||
nativeCurrency: { name: 'Kovan ETH', symbol: 'kovETH', decimals: 18 },
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Kovan Ether', symbol: 'kovETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.KOVAN],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.GOERLI]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://goerli.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Görli',
|
||||
nativeCurrency: { name: 'Görli ETH', symbol: 'görETH', decimals: 18 },
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Görli Ether', symbol: 'görETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.GOERLI],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.OPTIMISM]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`25m`,
|
||||
bridge: 'https://gateway.optimism.io/',
|
||||
bridge: 'https://gateway.optimism.io/?chainId=1',
|
||||
defaultListUrl: OPTIMISM_LIST,
|
||||
docs: 'https://optimism.io/',
|
||||
explorer: 'https://optimistic.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/optimism/',
|
||||
label: 'OΞ',
|
||||
label: 'Optimism',
|
||||
logoUrl: optimismLogoUrl,
|
||||
nativeCurrency: { name: 'Optimistic ETH', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrls: ['https://mainnet.optimism.io'],
|
||||
statusPage: 'https://optimism.io/status',
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrl: 'https://mainnet.optimism.io',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`25m`,
|
||||
bridge: 'https://gateway.optimism.io/',
|
||||
defaultListUrl: OPTIMISM_LIST,
|
||||
@ -157,12 +216,72 @@ export const CHAIN_INFO: ChainInfo = {
|
||||
explorer: 'https://optimistic.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/optimism/',
|
||||
label: 'Optimistic Kovan',
|
||||
rpcUrls: ['https://kovan.optimism.io'],
|
||||
logoUrl: optimismLogoUrl,
|
||||
nativeCurrency: { name: 'Optimistic kovETH', symbol: 'kovOpETH', decimals: 18 },
|
||||
statusPage: 'https://optimism.io/status',
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Optimistic Kovan Ether', symbol: 'kovOpETH', decimals: 18 },
|
||||
rpcUrl: 'https://kovan.optimism.io',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.ARBITRUM_ONE]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://bridge.arbitrum.io/',
|
||||
docs: 'https://offchainlabs.com/',
|
||||
explorer: 'https://arbiscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/arbitrum',
|
||||
label: 'Arbitrum',
|
||||
logoUrl: arbitrumLogoUrl,
|
||||
defaultListUrl: ARBITRUM_LIST,
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrl: 'https://arb1.arbitrum.io/rpc',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://bridge.arbitrum.io/',
|
||||
docs: 'https://offchainlabs.com/',
|
||||
explorer: 'https://rinkeby-explorer.arbitrum.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/arbitrum/',
|
||||
label: 'Arbitrum Rinkeby',
|
||||
logoUrl: arbitrumLogoUrl,
|
||||
defaultListUrl: ARBITRUM_LIST,
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Rinkeby Arbitrum Ether', symbol: 'rinkArbETH', decimals: 18 },
|
||||
rpcUrl: 'https://rinkeby.arbitrum.io/rpc',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.POLYGON]: {
|
||||
networkType: NetworkType.L1,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://wallet.polygon.technology/bridge',
|
||||
docs: 'https://polygon.io/',
|
||||
explorer: 'https://polygonscan.com/',
|
||||
infoLink: 'https://info.uniswap.org/#/polygon',
|
||||
label: 'Polygon',
|
||||
logoUrl: polygonMaticLogo,
|
||||
addNetworkInfo: {
|
||||
rpcUrl: 'https://polygon-rpc.com/',
|
||||
nativeCurrency: { name: 'Polygon Matic', symbol: 'MATIC', decimals: 18 },
|
||||
},
|
||||
},
|
||||
[SupportedChainId.POLYGON_MUMBAI]: {
|
||||
networkType: NetworkType.L1,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://wallet.polygon.technology/bridge',
|
||||
docs: 'https://polygon.io/',
|
||||
explorer: 'https://mumbai.polygonscan.com/',
|
||||
infoLink: 'https://info.uniswap.org/#/polygon',
|
||||
label: 'Polygon Mumbai',
|
||||
logoUrl: polygonMaticLogo,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Polygon Mumbai Matic', symbol: 'mMATIC', decimals: 18 },
|
||||
rpcUrl: 'https://rpc-endpoints.superfluid.dev/mumbai',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const ARBITRUM_HELP_CENTER_LINK = 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum'
|
||||
export const OPTIMISM_HELP_CENTER_LINK =
|
||||
'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ'
|
||||
|
@ -7,11 +7,12 @@ import {
|
||||
DAI,
|
||||
DAI_ARBITRUM_ONE,
|
||||
DAI_OPTIMISM,
|
||||
DAI_POLYGON,
|
||||
ETH2X_FLI,
|
||||
ExtendedEther,
|
||||
FEI,
|
||||
FRAX,
|
||||
FXS,
|
||||
nativeOnChain,
|
||||
renBTC,
|
||||
rETH2,
|
||||
sETH2,
|
||||
@ -20,13 +21,18 @@ import {
|
||||
USDC,
|
||||
USDC_ARBITRUM,
|
||||
USDC_OPTIMISM,
|
||||
USDC_POLYGON,
|
||||
USDT,
|
||||
USDT_ARBITRUM_ONE,
|
||||
USDT_OPTIMISM,
|
||||
USDT_POLYGON,
|
||||
WBTC,
|
||||
WBTC_ARBITRUM_ONE,
|
||||
WBTC_OPTIMISM,
|
||||
WETH9_EXTENDED,
|
||||
WBTC_POLYGON,
|
||||
WETH_POLYGON,
|
||||
WETH_POLYGON_MUMBAI,
|
||||
WRAPPED_NATIVE_CURRENCY,
|
||||
} from './tokens'
|
||||
|
||||
type ChainTokenList = {
|
||||
@ -37,21 +43,33 @@ type ChainCurrencyList = {
|
||||
readonly [chainId: number]: Currency[]
|
||||
}
|
||||
|
||||
const WETH_ONLY: ChainTokenList = Object.fromEntries(
|
||||
Object.entries(WETH9_EXTENDED).map(([key, value]) => [key, [value]])
|
||||
const WRAPPED_NATIVE_CURRENCIES_ONLY: ChainTokenList = Object.fromEntries(
|
||||
Object.entries(WRAPPED_NATIVE_CURRENCY).map(([key, value]) => [key, [value]])
|
||||
)
|
||||
|
||||
// used to construct intermediary pairs for trading
|
||||
export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = {
|
||||
...WETH_ONLY,
|
||||
[SupportedChainId.MAINNET]: [...WETH_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC],
|
||||
[SupportedChainId.OPTIMISM]: [...WETH_ONLY[SupportedChainId.OPTIMISM], DAI_OPTIMISM, USDT_OPTIMISM, WBTC_OPTIMISM],
|
||||
...WRAPPED_NATIVE_CURRENCIES_ONLY,
|
||||
[SupportedChainId.MAINNET]: [...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC],
|
||||
[SupportedChainId.OPTIMISM]: [
|
||||
...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.OPTIMISM],
|
||||
DAI_OPTIMISM,
|
||||
USDT_OPTIMISM,
|
||||
WBTC_OPTIMISM,
|
||||
],
|
||||
[SupportedChainId.ARBITRUM_ONE]: [
|
||||
...WETH_ONLY[SupportedChainId.ARBITRUM_ONE],
|
||||
...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.ARBITRUM_ONE],
|
||||
DAI_ARBITRUM_ONE,
|
||||
USDT_ARBITRUM_ONE,
|
||||
WBTC_ARBITRUM_ONE,
|
||||
],
|
||||
[SupportedChainId.POLYGON]: [
|
||||
...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.POLYGON],
|
||||
DAI_POLYGON,
|
||||
USDC_POLYGON,
|
||||
USDT_POLYGON,
|
||||
WETH_POLYGON,
|
||||
],
|
||||
}
|
||||
export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = {
|
||||
[SupportedChainId.MAINNET]: {
|
||||
@ -72,7 +90,7 @@ export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: To
|
||||
*/
|
||||
export const CUSTOM_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = {
|
||||
[SupportedChainId.MAINNET]: {
|
||||
[AMPL.address]: [DAI, WETH9_EXTENDED[SupportedChainId.MAINNET]],
|
||||
[AMPL.address]: [DAI, WRAPPED_NATIVE_CURRENCY[SupportedChainId.MAINNET]],
|
||||
},
|
||||
}
|
||||
|
||||
@ -81,49 +99,62 @@ export const CUSTOM_BASES: { [chainId: number]: { [tokenAddress: string]: Token[
|
||||
*/
|
||||
export const COMMON_BASES: ChainCurrencyList = {
|
||||
[SupportedChainId.MAINNET]: [
|
||||
ExtendedEther.onChain(SupportedChainId.MAINNET),
|
||||
nativeOnChain(SupportedChainId.MAINNET),
|
||||
DAI,
|
||||
USDC,
|
||||
USDT,
|
||||
WBTC,
|
||||
WETH9_EXTENDED[SupportedChainId.MAINNET],
|
||||
WRAPPED_NATIVE_CURRENCY[SupportedChainId.MAINNET],
|
||||
],
|
||||
[SupportedChainId.ROPSTEN]: [
|
||||
ExtendedEther.onChain(SupportedChainId.ROPSTEN),
|
||||
WETH9_EXTENDED[SupportedChainId.ROPSTEN],
|
||||
nativeOnChain(SupportedChainId.ROPSTEN),
|
||||
WRAPPED_NATIVE_CURRENCY[SupportedChainId.ROPSTEN],
|
||||
],
|
||||
[SupportedChainId.RINKEBY]: [
|
||||
ExtendedEther.onChain(SupportedChainId.RINKEBY),
|
||||
WETH9_EXTENDED[SupportedChainId.RINKEBY],
|
||||
nativeOnChain(SupportedChainId.RINKEBY),
|
||||
WRAPPED_NATIVE_CURRENCY[SupportedChainId.RINKEBY],
|
||||
],
|
||||
[SupportedChainId.GOERLI]: [ExtendedEther.onChain(SupportedChainId.GOERLI), WETH9_EXTENDED[SupportedChainId.GOERLI]],
|
||||
[SupportedChainId.KOVAN]: [ExtendedEther.onChain(SupportedChainId.KOVAN), WETH9_EXTENDED[SupportedChainId.KOVAN]],
|
||||
[SupportedChainId.GOERLI]: [nativeOnChain(SupportedChainId.GOERLI), WRAPPED_NATIVE_CURRENCY[SupportedChainId.GOERLI]],
|
||||
[SupportedChainId.KOVAN]: [nativeOnChain(SupportedChainId.KOVAN), WRAPPED_NATIVE_CURRENCY[SupportedChainId.KOVAN]],
|
||||
[SupportedChainId.ARBITRUM_ONE]: [
|
||||
ExtendedEther.onChain(SupportedChainId.ARBITRUM_ONE),
|
||||
nativeOnChain(SupportedChainId.ARBITRUM_ONE),
|
||||
DAI_ARBITRUM_ONE,
|
||||
USDC_ARBITRUM,
|
||||
USDT_ARBITRUM_ONE,
|
||||
WBTC_ARBITRUM_ONE,
|
||||
WETH9_EXTENDED[SupportedChainId.ARBITRUM_ONE],
|
||||
WRAPPED_NATIVE_CURRENCY[SupportedChainId.ARBITRUM_ONE],
|
||||
],
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: [
|
||||
ExtendedEther.onChain(SupportedChainId.ARBITRUM_RINKEBY),
|
||||
WETH9_EXTENDED[SupportedChainId.ARBITRUM_RINKEBY],
|
||||
nativeOnChain(SupportedChainId.ARBITRUM_RINKEBY),
|
||||
WRAPPED_NATIVE_CURRENCY[SupportedChainId.ARBITRUM_RINKEBY],
|
||||
],
|
||||
[SupportedChainId.OPTIMISM]: [
|
||||
ExtendedEther.onChain(SupportedChainId.OPTIMISM),
|
||||
nativeOnChain(SupportedChainId.OPTIMISM),
|
||||
DAI_OPTIMISM,
|
||||
USDC_OPTIMISM,
|
||||
USDT_OPTIMISM,
|
||||
WBTC_OPTIMISM,
|
||||
],
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: [ExtendedEther.onChain(SupportedChainId.OPTIMISTIC_KOVAN)],
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: [nativeOnChain(SupportedChainId.OPTIMISTIC_KOVAN)],
|
||||
[SupportedChainId.POLYGON]: [
|
||||
nativeOnChain(SupportedChainId.POLYGON),
|
||||
WETH_POLYGON,
|
||||
USDC_POLYGON,
|
||||
DAI_POLYGON,
|
||||
USDT_POLYGON,
|
||||
WBTC_POLYGON,
|
||||
],
|
||||
[SupportedChainId.POLYGON_MUMBAI]: [
|
||||
nativeOnChain(SupportedChainId.POLYGON_MUMBAI),
|
||||
WRAPPED_NATIVE_CURRENCY[SupportedChainId.POLYGON_MUMBAI],
|
||||
WETH_POLYGON_MUMBAI,
|
||||
],
|
||||
}
|
||||
|
||||
// used to construct the list of all pairs we consider by default in the frontend
|
||||
export const BASES_TO_TRACK_LIQUIDITY_FOR: ChainTokenList = {
|
||||
...WETH_ONLY,
|
||||
[SupportedChainId.MAINNET]: [...WETH_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC],
|
||||
...WRAPPED_NATIVE_CURRENCIES_ONLY,
|
||||
[SupportedChainId.MAINNET]: [...WRAPPED_NATIVE_CURRENCIES_ONLY[SupportedChainId.MAINNET], DAI, USDC, USDT, WBTC],
|
||||
}
|
||||
export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = {
|
||||
[SupportedChainId.MAINNET]: [
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Ether, Token, WETH9 } from '@uniswap/sdk-core'
|
||||
import { Currency, Ether, NativeCurrency, Token, WETH9 } from '@uniswap/sdk-core'
|
||||
|
||||
import { UNI_ADDRESS } from './addresses'
|
||||
import { SupportedChainId } from './chains'
|
||||
@ -45,6 +45,34 @@ export const USDC_ARBITRUM = new Token(
|
||||
'USDC',
|
||||
'USD//C'
|
||||
)
|
||||
export const USDC_POLYGON = new Token(
|
||||
SupportedChainId.POLYGON,
|
||||
'0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
|
||||
6,
|
||||
'USDC',
|
||||
'USD//C'
|
||||
)
|
||||
export const DAI_POLYGON = new Token(
|
||||
SupportedChainId.POLYGON,
|
||||
'0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063',
|
||||
18,
|
||||
'DAI',
|
||||
'Dai Stablecoin'
|
||||
)
|
||||
export const USDT_POLYGON = new Token(
|
||||
SupportedChainId.POLYGON,
|
||||
'0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
|
||||
6,
|
||||
'USDT',
|
||||
'Tether USD'
|
||||
)
|
||||
export const WBTC_POLYGON = new Token(
|
||||
SupportedChainId.POLYGON,
|
||||
'0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6',
|
||||
8,
|
||||
'WBTC',
|
||||
'Wrapped BTC'
|
||||
)
|
||||
export const USDC_OPTIMISM = new Token(
|
||||
SupportedChainId.OPTIMISM,
|
||||
'0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
|
||||
@ -157,6 +185,21 @@ export const SWISE = new Token(
|
||||
'SWISE',
|
||||
'StakeWise'
|
||||
)
|
||||
export const WETH_POLYGON_MUMBAI = new Token(
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
'0xa6fa4fb5f76172d178d61b04b0ecd319c5d1c0aa',
|
||||
18,
|
||||
'WETH',
|
||||
'Wrapped Ether'
|
||||
)
|
||||
|
||||
export const WETH_POLYGON = new Token(
|
||||
SupportedChainId.POLYGON,
|
||||
'0x7ceb23fd6bc0add59e62ac25578270cff1b9f619',
|
||||
18,
|
||||
'WETH',
|
||||
'Wrapped Ether'
|
||||
)
|
||||
export const UNI: { [chainId: number]: Token } = {
|
||||
[SupportedChainId.MAINNET]: new Token(SupportedChainId.MAINNET, UNI_ADDRESS[1], 18, 'UNI', 'Uniswap'),
|
||||
[SupportedChainId.RINKEBY]: new Token(SupportedChainId.RINKEBY, UNI_ADDRESS[4], 18, 'UNI', 'Uniswap'),
|
||||
@ -165,7 +208,7 @@ export const UNI: { [chainId: number]: Token } = {
|
||||
[SupportedChainId.KOVAN]: new Token(SupportedChainId.KOVAN, UNI_ADDRESS[42], 18, 'UNI', 'Uniswap'),
|
||||
}
|
||||
|
||||
export const WETH9_EXTENDED: { [chainId: number]: Token } = {
|
||||
export const WRAPPED_NATIVE_CURRENCY: { [chainId: number]: Token } = {
|
||||
...WETH9,
|
||||
[SupportedChainId.OPTIMISM]: new Token(
|
||||
SupportedChainId.OPTIMISM,
|
||||
@ -195,17 +238,61 @@ export const WETH9_EXTENDED: { [chainId: number]: Token } = {
|
||||
'WETH',
|
||||
'Wrapped Ether'
|
||||
),
|
||||
[SupportedChainId.POLYGON]: new Token(
|
||||
SupportedChainId.POLYGON,
|
||||
'0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
|
||||
18,
|
||||
'WMATIC',
|
||||
'Wrapped MATIC'
|
||||
),
|
||||
[SupportedChainId.POLYGON_MUMBAI]: new Token(
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
'0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889',
|
||||
18,
|
||||
'WMATIC',
|
||||
'Wrapped MATIC'
|
||||
),
|
||||
}
|
||||
|
||||
function isMatic(chainId: number): chainId is SupportedChainId.POLYGON | SupportedChainId.POLYGON_MUMBAI {
|
||||
return chainId === SupportedChainId.POLYGON_MUMBAI || chainId === SupportedChainId.POLYGON
|
||||
}
|
||||
|
||||
class MaticNativeCurrency extends NativeCurrency {
|
||||
equals(other: Currency): boolean {
|
||||
return other.isNative && other.chainId === this.chainId
|
||||
}
|
||||
|
||||
get wrapped(): Token {
|
||||
if (!isMatic(this.chainId)) throw new Error('Not matic')
|
||||
return WRAPPED_NATIVE_CURRENCY[this.chainId]
|
||||
}
|
||||
|
||||
public constructor(chainId: number) {
|
||||
if (!isMatic(chainId)) throw new Error('Not matic')
|
||||
super(chainId, 18, 'MATIC', 'Polygon Matic')
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtendedEther extends Ether {
|
||||
public get wrapped(): Token {
|
||||
if (this.chainId in WETH9_EXTENDED) return WETH9_EXTENDED[this.chainId]
|
||||
if (this.chainId in WRAPPED_NATIVE_CURRENCY) return WRAPPED_NATIVE_CURRENCY[this.chainId]
|
||||
throw new Error('Unsupported chain ID')
|
||||
}
|
||||
|
||||
private static _cachedEther: { [chainId: number]: ExtendedEther } = {}
|
||||
private static _cachedExtendedEther: { [chainId: number]: NativeCurrency } = {}
|
||||
|
||||
public static onChain(chainId: number): ExtendedEther {
|
||||
return this._cachedEther[chainId] ?? (this._cachedEther[chainId] = new ExtendedEther(chainId))
|
||||
return this._cachedExtendedEther[chainId] ?? (this._cachedExtendedEther[chainId] = new ExtendedEther(chainId))
|
||||
}
|
||||
}
|
||||
|
||||
const cachedNativeCurrency: { [chainId: number]: NativeCurrency } = {}
|
||||
export function nativeOnChain(chainId: number): NativeCurrency {
|
||||
return (
|
||||
cachedNativeCurrency[chainId] ??
|
||||
(cachedNativeCurrency[chainId] = isMatic(chainId)
|
||||
? new MaticNativeCurrency(chainId)
|
||||
: ExtendedEther.onChain(chainId))
|
||||
)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from '
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { createTokenFilterFunction } from '../components/SearchModal/filtering'
|
||||
import { ExtendedEther, WETH9_EXTENDED } from '../constants/tokens'
|
||||
import { nativeOnChain } from '../constants/tokens'
|
||||
import { useAllLists, useCombinedActiveList, useInactiveListUrls } from '../state/lists/hooks'
|
||||
import { WrappedTokenInfo } from '../state/lists/wrappedTokenInfo'
|
||||
import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
|
||||
@ -225,20 +225,28 @@ export function useToken(tokenAddress?: string | null): Token | undefined | null
|
||||
])
|
||||
}
|
||||
|
||||
export function useCurrency(currencyId: string | null | undefined): Currency | null | undefined {
|
||||
export function useNativeCurrency(): Currency {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const isETH = currencyId?.toUpperCase() === 'ETH'
|
||||
const token = useToken(isETH ? undefined : currencyId)
|
||||
const extendedEther = useMemo(
|
||||
return useMemo(
|
||||
() =>
|
||||
chainId
|
||||
? ExtendedEther.onChain(chainId)
|
||||
? nativeOnChain(chainId)
|
||||
: // display mainnet when not connected
|
||||
ExtendedEther.onChain(SupportedChainId.MAINNET),
|
||||
nativeOnChain(SupportedChainId.MAINNET),
|
||||
[chainId]
|
||||
)
|
||||
const weth = chainId ? WETH9_EXTENDED[chainId] : undefined
|
||||
if (currencyId === null || currencyId === undefined) return currencyId
|
||||
if (weth?.address?.toUpperCase() === currencyId?.toUpperCase()) return weth
|
||||
return isETH ? extendedEther : token
|
||||
}
|
||||
|
||||
export function useCurrency(currencyId: string | null | undefined): Currency | null | undefined {
|
||||
const nativeCurrency = useNativeCurrency()
|
||||
const isNative = Boolean(nativeCurrency && currencyId?.toUpperCase() === 'ETH')
|
||||
const token = useToken(isNative ? undefined : currencyId)
|
||||
|
||||
if (currencyId === null || currencyId === undefined) return currencyId
|
||||
|
||||
// this case so we use our builtin wrapped token instead of wrapped tokens on token lists
|
||||
const wrappedNative = nativeCurrency?.wrapped
|
||||
if (wrappedNative?.address?.toUpperCase() === currencyId?.toUpperCase()) return wrappedNative
|
||||
|
||||
return isNative ? nativeCurrency : token
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ import { V3Migrator } from 'types/v3/V3Migrator'
|
||||
import { getContract } from 'utils'
|
||||
|
||||
import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Erc721, Erc1155, Weth } from '../abis/types'
|
||||
import { UNI, WETH9_EXTENDED } from '../constants/tokens'
|
||||
import { UNI, WRAPPED_NATIVE_CURRENCY } from '../constants/tokens'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
// returns null on errors
|
||||
@ -74,7 +74,11 @@ export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: b
|
||||
|
||||
export function useWETHContract(withSignerIfPossible?: boolean) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
return useContract<Weth>(chainId ? WETH9_EXTENDED[chainId]?.address : undefined, WETH_ABI, withSignerIfPossible)
|
||||
return useContract<Weth>(
|
||||
chainId ? WRAPPED_NATIVE_CURRENCY[chainId]?.address : undefined,
|
||||
WETH_ABI,
|
||||
withSignerIfPossible
|
||||
)
|
||||
}
|
||||
|
||||
export function useERC721Contract(nftAddress?: string) {
|
||||
@ -113,7 +117,7 @@ export function useV2RouterContract(): Contract | null {
|
||||
return useContract(V2_ROUTER_ADDRESS, IUniswapV2Router02ABI, true)
|
||||
}
|
||||
|
||||
export function useMulticall2Contract() {
|
||||
export function useInterfaceMulticall() {
|
||||
return useContract<UniswapInterfaceMulticall>(MULTICALL_ADDRESS, MulticallABI, false) as UniswapInterfaceMulticall
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useMulticall2Contract } from './useContract'
|
||||
import { useInterfaceMulticall } from './useContract'
|
||||
|
||||
// gets the current timestamp from the blockchain
|
||||
export default function useCurrentBlockTimestamp(): BigNumber | undefined {
|
||||
const multicall = useMulticall2Contract()
|
||||
const multicall = useInterfaceMulticall()
|
||||
return useSingleCallResult(multicall, 'getCurrentBlockTimestamp')?.result?.[0]
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { useMemo } from 'react'
|
||||
import { InterfaceTrade } from 'state/routing/types'
|
||||
|
||||
import { useUserSlippageToleranceWithDefault } from '../state/user/hooks'
|
||||
import { useCurrency } from './Tokens'
|
||||
import { useNativeCurrency } from './Tokens'
|
||||
import useGasPrice from './useGasPrice'
|
||||
import useUSDCPrice, { useUSDCValue } from './useUSDCPrice'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
@ -35,19 +35,23 @@ export default function useSwapSlippageTolerance(
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const onL2 = chainId && L2_CHAIN_IDS.includes(chainId)
|
||||
const outputDollarValue = useUSDCValue(trade?.outputAmount)
|
||||
const ethGasPrice = useGasPrice()
|
||||
const nativeGasPrice = useGasPrice()
|
||||
|
||||
const gasEstimate = guesstimateGas(trade)
|
||||
const ether = useCurrency('ETH')
|
||||
const etherPrice = useUSDCPrice(ether ?? undefined)
|
||||
const nativeCurrency = useNativeCurrency()
|
||||
const nativeCurrencyPrice = useUSDCPrice(nativeCurrency ?? undefined)
|
||||
|
||||
const defaultSlippageTolerance = useMemo(() => {
|
||||
if (!trade || onL2) return ONE_TENTHS_PERCENT
|
||||
|
||||
const ethGasCost =
|
||||
ethGasPrice && typeof gasEstimate === 'number' ? JSBI.multiply(ethGasPrice, JSBI.BigInt(gasEstimate)) : undefined
|
||||
const nativeGasCost =
|
||||
nativeGasPrice && typeof gasEstimate === 'number'
|
||||
? JSBI.multiply(nativeGasPrice, JSBI.BigInt(gasEstimate))
|
||||
: undefined
|
||||
const dollarGasCost =
|
||||
ether && ethGasCost && etherPrice ? etherPrice.quote(CurrencyAmount.fromRawAmount(ether, ethGasCost)) : undefined
|
||||
nativeCurrency && nativeGasCost && nativeCurrencyPrice
|
||||
? nativeCurrencyPrice.quote(CurrencyAmount.fromRawAmount(nativeCurrency, nativeGasCost))
|
||||
: undefined
|
||||
|
||||
// if valid estimate from api and using api trade, use gas estimate from api
|
||||
// NOTE - dont use gas estimate for L2s yet - need to verify accuracy
|
||||
@ -68,7 +72,7 @@ export default function useSwapSlippageTolerance(
|
||||
}
|
||||
|
||||
return V3_SWAP_DEFAULT_SLIPPAGE
|
||||
}, [trade, onL2, ethGasPrice, gasEstimate, ether, etherPrice, chainId, outputDollarValue])
|
||||
}, [trade, onL2, nativeGasPrice, gasEstimate, nativeCurrency, nativeCurrencyPrice, chainId, outputDollarValue])
|
||||
|
||||
return useUserSlippageToleranceWithDefault(defaultSlippageTolerance)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { useMemo } from 'react'
|
||||
import { tryParseAmount } from 'state/swap/hooks'
|
||||
|
||||
import { SupportedChainId } from '../constants/chains'
|
||||
import { DAI_OPTIMISM, USDC, USDC_ARBITRUM } from '../constants/tokens'
|
||||
import { DAI_OPTIMISM, USDC, USDC_ARBITRUM, USDC_POLYGON } from '../constants/tokens'
|
||||
import { useBestV2Trade } from './useBestV2Trade'
|
||||
import { useClientSideV3Trade } from './useClientSideV3Trade'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
@ -14,6 +14,7 @@ export const STABLECOIN_AMOUNT_OUT: { [chainId: number]: CurrencyAmount<Token> }
|
||||
[SupportedChainId.MAINNET]: CurrencyAmount.fromRawAmount(USDC, 100_000e6),
|
||||
[SupportedChainId.ARBITRUM_ONE]: CurrencyAmount.fromRawAmount(USDC_ARBITRUM, 10_000e6),
|
||||
[SupportedChainId.OPTIMISM]: CurrencyAmount.fromRawAmount(DAI_OPTIMISM, 10_000e18),
|
||||
[SupportedChainId.POLYGON]: CurrencyAmount.fromRawAmount(USDC_POLYGON, 10_000e6),
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { WETH9_EXTENDED } from '../constants/tokens'
|
||||
import { WRAPPED_NATIVE_CURRENCY } from '../constants/tokens'
|
||||
import { tryParseAmount } from '../state/swap/hooks'
|
||||
import { TransactionType } from '../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../state/transactions/hooks'
|
||||
import { useCurrencyBalance } from '../state/wallet/hooks'
|
||||
import { useNativeCurrency } from './Tokens'
|
||||
import { useWETHContract } from './useContract'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
@ -16,6 +18,34 @@ export enum WrapType {
|
||||
}
|
||||
|
||||
const NOT_APPLICABLE = { wrapType: WrapType.NOT_APPLICABLE }
|
||||
|
||||
enum WrapInputError {
|
||||
NO_ERROR, // must be equal to 0 so all other errors are truthy
|
||||
ENTER_NATIVE_AMOUNT,
|
||||
ENTER_WRAPPED_AMOUNT,
|
||||
INSUFFICIENT_NATIVE_BALANCE,
|
||||
INSUFFICIENT_WRAPPED_BALANCE,
|
||||
}
|
||||
|
||||
export function WrapErrorText({ wrapInputError }: { wrapInputError: WrapInputError }) {
|
||||
const native = useNativeCurrency()
|
||||
const wrapped = native?.wrapped
|
||||
|
||||
switch (wrapInputError) {
|
||||
case WrapInputError.NO_ERROR:
|
||||
return null
|
||||
case WrapInputError.ENTER_NATIVE_AMOUNT:
|
||||
return <Trans>Enter {native?.symbol} amount</Trans>
|
||||
case WrapInputError.ENTER_WRAPPED_AMOUNT:
|
||||
return <Trans>Enter {wrapped?.symbol} amount</Trans>
|
||||
|
||||
case WrapInputError.INSUFFICIENT_NATIVE_BALANCE:
|
||||
return <Trans>Insufficient {native?.symbol} balance</Trans>
|
||||
case WrapInputError.INSUFFICIENT_WRAPPED_BALANCE:
|
||||
return <Trans>Insufficient {wrapped?.symbol} balance</Trans>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the selected input and output currency, return a wrap callback
|
||||
* @param inputCurrency the selected input currency
|
||||
@ -26,7 +56,7 @@ export default function useWrapCallback(
|
||||
inputCurrency: Currency | undefined | null,
|
||||
outputCurrency: Currency | undefined | null,
|
||||
typedValue: string | undefined
|
||||
): { wrapType: WrapType; execute?: undefined | (() => Promise<void>); inputError?: string } {
|
||||
): { wrapType: WrapType; execute?: undefined | (() => Promise<void>); inputError?: WrapInputError } {
|
||||
const { chainId, account } = useActiveWeb3React()
|
||||
const wethContract = useWETHContract()
|
||||
const balance = useCurrencyBalance(account ?? undefined, inputCurrency ?? undefined)
|
||||
@ -36,7 +66,7 @@ export default function useWrapCallback(
|
||||
|
||||
return useMemo(() => {
|
||||
if (!wethContract || !chainId || !inputCurrency || !outputCurrency) return NOT_APPLICABLE
|
||||
const weth = WETH9_EXTENDED[chainId]
|
||||
const weth = WRAPPED_NATIVE_CURRENCY[chainId]
|
||||
if (!weth) return NOT_APPLICABLE
|
||||
|
||||
const hasInputAmount = Boolean(inputAmount?.greaterThan('0'))
|
||||
@ -54,13 +84,18 @@ export default function useWrapCallback(
|
||||
type: TransactionType.WRAP,
|
||||
unwrapped: false,
|
||||
currencyAmountRaw: inputAmount?.quotient.toString(),
|
||||
chainId,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Could not deposit', error)
|
||||
}
|
||||
}
|
||||
: undefined,
|
||||
inputError: sufficientBalance ? undefined : hasInputAmount ? 'Insufficient ETH balance' : 'Enter ETH amount',
|
||||
inputError: sufficientBalance
|
||||
? undefined
|
||||
: hasInputAmount
|
||||
? WrapInputError.INSUFFICIENT_NATIVE_BALANCE
|
||||
: WrapInputError.ENTER_NATIVE_AMOUNT,
|
||||
}
|
||||
} else if (weth.equals(inputCurrency) && outputCurrency.isNative) {
|
||||
return {
|
||||
@ -74,13 +109,18 @@ export default function useWrapCallback(
|
||||
type: TransactionType.WRAP,
|
||||
unwrapped: true,
|
||||
currencyAmountRaw: inputAmount?.quotient.toString(),
|
||||
chainId,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Could not withdraw', error)
|
||||
}
|
||||
}
|
||||
: undefined,
|
||||
inputError: sufficientBalance ? undefined : hasInputAmount ? 'Insufficient WETH balance' : 'Enter WETH amount',
|
||||
inputError: sufficientBalance
|
||||
? undefined
|
||||
: hasInputAmount
|
||||
? WrapInputError.INSUFFICIENT_WRAPPED_BALANCE
|
||||
: WrapInputError.ENTER_WRAPPED_AMOUNT,
|
||||
}
|
||||
} else {
|
||||
return NOT_APPLICABLE
|
@ -71,3 +71,4 @@ ReactDOM.render(
|
||||
if (process.env.REACT_APP_SERVICE_WORKER !== 'false') {
|
||||
serviceWorkerRegistration.register()
|
||||
}
|
||||
export { INFURA_NETWORK_URLS } from './constants/chains'
|
||||
|
@ -35,7 +35,7 @@ import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
|
||||
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
|
||||
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from '../../constants/addresses'
|
||||
import { ZERO_PERCENT } from '../../constants/misc'
|
||||
import { WETH9_EXTENDED } from '../../constants/tokens'
|
||||
import { WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
|
||||
import { useCurrency } from '../../hooks/Tokens'
|
||||
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
|
||||
import { useArgentWalletContract } from '../../hooks/useArgentWalletContract'
|
||||
@ -315,10 +315,12 @@ export default function AddLiquidity({
|
||||
} else {
|
||||
// prevent weth + eth
|
||||
const isETHOrWETHNew =
|
||||
currencyIdNew === 'ETH' || (chainId !== undefined && currencyIdNew === WETH9_EXTENDED[chainId]?.address)
|
||||
currencyIdNew === 'ETH' ||
|
||||
(chainId !== undefined && currencyIdNew === WRAPPED_NATIVE_CURRENCY[chainId]?.address)
|
||||
const isETHOrWETHOther =
|
||||
currencyIdOther !== undefined &&
|
||||
(currencyIdOther === 'ETH' || (chainId !== undefined && currencyIdOther === WETH9_EXTENDED[chainId]?.address))
|
||||
(currencyIdOther === 'ETH' ||
|
||||
(chainId !== undefined && currencyIdOther === WRAPPED_NATIVE_CURRENCY[chainId]?.address))
|
||||
|
||||
if (isETHOrWETHNew && isETHOrWETHOther) {
|
||||
return [currencyIdNew, undefined]
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { Redirect, RouteComponentProps } from 'react-router-dom'
|
||||
|
||||
import { WETH9_EXTENDED } from '../../constants/tokens'
|
||||
import { WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
|
||||
import AddLiquidity from './index'
|
||||
|
||||
export function RedirectDuplicateTokenIds(
|
||||
@ -17,9 +17,9 @@ export function RedirectDuplicateTokenIds(
|
||||
|
||||
// prevent weth + eth
|
||||
const isETHOrWETHA =
|
||||
currencyIdA === 'ETH' || (chainId !== undefined && currencyIdA === WETH9_EXTENDED[chainId]?.address)
|
||||
currencyIdA === 'ETH' || (chainId !== undefined && currencyIdA === WRAPPED_NATIVE_CURRENCY[chainId]?.address)
|
||||
const isETHOrWETHB =
|
||||
currencyIdB === 'ETH' || (chainId !== undefined && currencyIdB === WETH9_EXTENDED[chainId]?.address)
|
||||
currencyIdB === 'ETH' || (chainId !== undefined && currencyIdB === WRAPPED_NATIVE_CURRENCY[chainId]?.address)
|
||||
|
||||
if (
|
||||
currencyIdA &&
|
||||
|
@ -21,7 +21,7 @@ import { MinimalPositionCard } from '../../components/PositionCard'
|
||||
import Row, { RowBetween, RowFlat } from '../../components/Row'
|
||||
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
|
||||
import { ZERO_PERCENT } from '../../constants/misc'
|
||||
import { WETH9_EXTENDED } from '../../constants/tokens'
|
||||
import { WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
|
||||
import { useCurrency } from '../../hooks/Tokens'
|
||||
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
|
||||
import { useV2RouterContract } from '../../hooks/useContract'
|
||||
@ -61,8 +61,8 @@ export default function AddLiquidity({
|
||||
|
||||
const oneCurrencyIsWETH = Boolean(
|
||||
chainId &&
|
||||
((currencyA && currencyA.equals(WETH9_EXTENDED[chainId])) ||
|
||||
(currencyB && currencyB.equals(WETH9_EXTENDED[chainId])))
|
||||
((currencyA && currencyA.equals(WRAPPED_NATIVE_CURRENCY[chainId])) ||
|
||||
(currencyB && currencyB.equals(WRAPPED_NATIVE_CURRENCY[chainId])))
|
||||
)
|
||||
|
||||
const toggleWalletModal = useWalletModalToggle() // toggle wallet when disconnected
|
||||
|
@ -36,7 +36,7 @@ import CurrencyLogo from '../../components/CurrencyLogo'
|
||||
import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
|
||||
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
|
||||
import { V2_FACTORY_ADDRESSES } from '../../constants/addresses'
|
||||
import { WETH9_EXTENDED } from '../../constants/tokens'
|
||||
import { WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
|
||||
import { useToken } from '../../hooks/Tokens'
|
||||
import { usePairContract, useV2MigratorContract } from '../../hooks/useContract'
|
||||
import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit'
|
||||
@ -592,10 +592,10 @@ function V2PairMigration({
|
||||
<ThemedText.Black fontSize={12}>
|
||||
<Trans>
|
||||
At least {formatCurrencyAmount(refund0, 4)}{' '}
|
||||
{token0.equals(WETH9_EXTENDED[chainId]) ? 'ETH' : token0.symbol} and{' '}
|
||||
{token0.equals(WRAPPED_NATIVE_CURRENCY[chainId]) ? 'ETH' : token0.symbol} and{' '}
|
||||
{formatCurrencyAmount(refund1, 4)}{' '}
|
||||
{token1.equals(WETH9_EXTENDED[chainId]) ? 'ETH' : token1.symbol} will be refunded to your wallet
|
||||
due to selected price range.
|
||||
{token1.equals(WRAPPED_NATIVE_CURRENCY[chainId]) ? 'ETH' : token1.symbol} will be refunded to your
|
||||
wallet due to selected price range.
|
||||
</Trans>
|
||||
</ThemedText.Black>
|
||||
) : null}
|
||||
|
@ -4,11 +4,10 @@ import { AutoColumn } from 'components/Column'
|
||||
import DowntimeWarning from 'components/DowntimeWarning'
|
||||
import { FlyoutAlignment, NewMenu } from 'components/Menu'
|
||||
import { SwapPoolTabs } from 'components/NavigationTabs'
|
||||
import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert'
|
||||
import { SingleRowNetworkAlert } from 'components/NetworkAlert/NetworkAlert'
|
||||
import PositionList from 'components/PositionList'
|
||||
import { RowBetween, RowFixed } from 'components/Row'
|
||||
import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
|
||||
import { L2_CHAIN_IDS } from 'constants/chains'
|
||||
import { useV3Positions } from 'hooks/useV3Positions'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { useContext } from 'react'
|
||||
@ -20,6 +19,7 @@ import styled, { ThemeContext } from 'styled-components/macro'
|
||||
import { HideSmall, ThemedText } from 'theme'
|
||||
import { PositionDetails } from 'types/position'
|
||||
|
||||
import { V2_FACTORY_ADDRESSES } from '../../constants/addresses'
|
||||
import CTACards from './CTACards'
|
||||
import { LoadingRows } from './styleds'
|
||||
|
||||
@ -128,6 +128,25 @@ const ResponsiveRow = styled(RowFixed)`
|
||||
`};
|
||||
`
|
||||
|
||||
function PositionsLoadingPlaceholder() {
|
||||
return (
|
||||
<LoadingRows>
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
</LoadingRows>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Pool() {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
const toggleWalletModal = useWalletModalToggle()
|
||||
@ -147,7 +166,7 @@ export default function Pool() {
|
||||
|
||||
const filteredPositions = [...openPositions, ...(userHideClosedPositions ? [] : closedPositions)]
|
||||
const showConnectAWallet = Boolean(!account)
|
||||
const showV2Features = !!chainId && !L2_CHAIN_IDS.includes(chainId)
|
||||
const showV2Features = Boolean(chainId && V2_FACTORY_ADDRESSES[chainId])
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
@ -224,27 +243,14 @@ export default function Pool() {
|
||||
</TitleRow>
|
||||
|
||||
<HideSmall>
|
||||
<NetworkAlert thin />
|
||||
<SingleRowNetworkAlert />
|
||||
<DowntimeWarning />
|
||||
<CTACards />
|
||||
</HideSmall>
|
||||
|
||||
<MainContentWrapper>
|
||||
{positionsLoading ? (
|
||||
<LoadingRows>
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
</LoadingRows>
|
||||
<PositionsLoadingPlaceholder />
|
||||
) : filteredPositions && filteredPositions.length > 0 ? (
|
||||
<PositionList positions={filteredPositions} />
|
||||
) : (
|
||||
|
@ -16,7 +16,7 @@ import { MinimalPositionCard } from '../../components/PositionCard'
|
||||
import Row from '../../components/Row'
|
||||
import CurrencySearchModal from '../../components/SearchModal/CurrencySearchModal'
|
||||
import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
|
||||
import { ExtendedEther } from '../../constants/tokens'
|
||||
import { nativeOnChain } from '../../constants/tokens'
|
||||
import { PairState, useV2Pair } from '../../hooks/useV2Pairs'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { usePairAdder } from '../../state/user/hooks'
|
||||
@ -44,7 +44,7 @@ export default function PoolFinder() {
|
||||
const [showSearch, setShowSearch] = useState<boolean>(false)
|
||||
const [activeField, setActiveField] = useState<number>(Fields.TOKEN1)
|
||||
|
||||
const [currency0, setCurrency0] = useState<Currency | null>(() => (chainId ? ExtendedEther.onChain(chainId) : null))
|
||||
const [currency0, setCurrency0] = useState<Currency | null>(() => (chainId ? nativeOnChain(chainId) : null))
|
||||
const [currency1, setCurrency1] = useState<Currency | null>(null)
|
||||
|
||||
const [pairState, pair] = useV2Pair(currency0 ?? undefined, currency1 ?? undefined)
|
||||
|
@ -32,7 +32,7 @@ import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
|
||||
import { WETH9_EXTENDED } from '../../constants/tokens'
|
||||
import { WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { calculateGasMargin } from '../../utils/calculateGasMargin'
|
||||
import { currencyId } from '../../utils/currencyId'
|
||||
@ -266,8 +266,8 @@ function Remove({ tokenId }: { tokenId: BigNumber }) {
|
||||
liquidityValue1?.currency &&
|
||||
(liquidityValue0.currency.isNative ||
|
||||
liquidityValue1.currency.isNative ||
|
||||
liquidityValue0.currency.wrapped.equals(WETH9_EXTENDED[liquidityValue0.currency.chainId]) ||
|
||||
liquidityValue1.currency.wrapped.equals(WETH9_EXTENDED[liquidityValue1.currency.chainId]))
|
||||
liquidityValue0.currency.wrapped.equals(WRAPPED_NATIVE_CURRENCY[liquidityValue0.currency.chainId]) ||
|
||||
liquidityValue1.currency.wrapped.equals(WRAPPED_NATIVE_CURRENCY[liquidityValue1.currency.chainId]))
|
||||
)
|
||||
return (
|
||||
<AutoColumn>
|
||||
|
@ -22,7 +22,7 @@ import Row, { RowBetween, RowFixed } from '../../components/Row'
|
||||
import Slider from '../../components/Slider'
|
||||
import { Dots } from '../../components/swap/styleds'
|
||||
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
|
||||
import { WETH9_EXTENDED } from '../../constants/tokens'
|
||||
import { WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
|
||||
import { useCurrency } from '../../hooks/Tokens'
|
||||
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
|
||||
import { usePairContract, useV2RouterContract } from '../../hooks/useContract'
|
||||
@ -388,8 +388,8 @@ export default function RemoveLiquidity({
|
||||
const oneCurrencyIsETH = currencyA?.isNative || currencyB?.isNative
|
||||
const oneCurrencyIsWETH = Boolean(
|
||||
chainId &&
|
||||
WETH9_EXTENDED[chainId] &&
|
||||
(currencyA?.equals(WETH9_EXTENDED[chainId]) || currencyB?.equals(WETH9_EXTENDED[chainId]))
|
||||
WRAPPED_NATIVE_CURRENCY[chainId] &&
|
||||
(currencyA?.equals(WRAPPED_NATIVE_CURRENCY[chainId]) || currencyB?.equals(WRAPPED_NATIVE_CURRENCY[chainId]))
|
||||
)
|
||||
|
||||
const handleSelectCurrencyA = useCallback(
|
||||
@ -532,17 +532,17 @@ export default function RemoveLiquidity({
|
||||
<RowBetween style={{ justifyContent: 'flex-end' }}>
|
||||
{oneCurrencyIsETH ? (
|
||||
<StyledInternalLink
|
||||
to={`/remove/v2/${currencyA?.isNative ? WETH9_EXTENDED[chainId].address : currencyIdA}/${
|
||||
currencyB?.isNative ? WETH9_EXTENDED[chainId].address : currencyIdB
|
||||
}`}
|
||||
to={`/remove/v2/${
|
||||
currencyA?.isNative ? WRAPPED_NATIVE_CURRENCY[chainId].address : currencyIdA
|
||||
}/${currencyB?.isNative ? WRAPPED_NATIVE_CURRENCY[chainId].address : currencyIdB}`}
|
||||
>
|
||||
Receive WETH
|
||||
</StyledInternalLink>
|
||||
) : oneCurrencyIsWETH ? (
|
||||
<StyledInternalLink
|
||||
to={`/remove/v2/${currencyA?.equals(WETH9_EXTENDED[chainId]) ? 'ETH' : currencyIdA}/${
|
||||
currencyB?.equals(WETH9_EXTENDED[chainId]) ? 'ETH' : currencyIdB
|
||||
}`}
|
||||
to={`/remove/v2/${
|
||||
currencyA?.equals(WRAPPED_NATIVE_CURRENCY[chainId]) ? 'ETH' : currencyIdA
|
||||
}/${currencyB?.equals(WRAPPED_NATIVE_CURRENCY[chainId]) ? 'ETH' : currencyIdB}`}
|
||||
>
|
||||
Receive ETH
|
||||
</StyledInternalLink>
|
||||
|
@ -14,7 +14,7 @@ import ReactGA from 'react-ga'
|
||||
import { RouteComponentProps } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
import { TradeState } from 'state/routing/types'
|
||||
import { ThemeContext } from 'styled-components/macro'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import AddressInputPanel from '../../components/AddressInputPanel'
|
||||
import { ButtonConfirmed, ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button'
|
||||
@ -38,7 +38,7 @@ import useIsArgentWallet from '../../hooks/useIsArgentWallet'
|
||||
import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported'
|
||||
import { useSwapCallback } from '../../hooks/useSwapCallback'
|
||||
import { useUSDCValue } from '../../hooks/useUSDCPrice'
|
||||
import useWrapCallback, { WrapType } from '../../hooks/useWrapCallback'
|
||||
import useWrapCallback, { WrapErrorText, WrapType } from '../../hooks/useWrapCallback'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useWalletModalToggle } from '../../state/application/hooks'
|
||||
import { Field } from '../../state/swap/actions'
|
||||
@ -55,6 +55,10 @@ import { maxAmountSpend } from '../../utils/maxAmountSpend'
|
||||
import { warningSeverity } from '../../utils/prices'
|
||||
import AppBody from '../AppBody'
|
||||
|
||||
const AlertWrapper = styled.div`
|
||||
max-width: 480px;
|
||||
`
|
||||
|
||||
export default function Swap({ history }: RouteComponentProps) {
|
||||
const { account } = useActiveWeb3React()
|
||||
const loadedUrlParams = useDefaultsFromURLSearch()
|
||||
@ -374,7 +378,9 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
onConfirm={handleConfirmTokenWarning}
|
||||
onDismiss={handleDismissTokenWarning}
|
||||
/>
|
||||
<NetworkAlert />
|
||||
<AlertWrapper>
|
||||
<NetworkAlert />
|
||||
</AlertWrapper>
|
||||
<AppBody>
|
||||
<SwapHeader allowedSlippage={allowedSlippage} />
|
||||
<Wrapper id="swap-page">
|
||||
@ -473,12 +479,13 @@ export default function Swap({ history }: RouteComponentProps) {
|
||||
</ButtonLight>
|
||||
) : showWrap ? (
|
||||
<ButtonPrimary disabled={Boolean(wrapInputError)} onClick={onWrap}>
|
||||
{wrapInputError ??
|
||||
(wrapType === WrapType.WRAP ? (
|
||||
<Trans>Wrap</Trans>
|
||||
) : wrapType === WrapType.UNWRAP ? (
|
||||
<Trans>Unwrap</Trans>
|
||||
) : null)}
|
||||
{wrapInputError ? (
|
||||
<WrapErrorText wrapInputError={wrapInputError} />
|
||||
) : wrapType === WrapType.WRAP ? (
|
||||
<Trans>Wrap</Trans>
|
||||
) : wrapType === WrapType.UNWRAP ? (
|
||||
<Trans>Unwrap</Trans>
|
||||
) : null}
|
||||
</ButtonPrimary>
|
||||
) : routeNotFound && userHasSpecifiedInputOutput && !routeIsLoading && !routeIsSyncing ? (
|
||||
<GreyCard style={{ textAlign: 'center' }}>
|
||||
|
@ -1,10 +1,5 @@
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
export const StandardPageWrapper = styled.div`
|
||||
padding-top: 160px;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
export const IframeBodyWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -19,7 +19,6 @@ describe('application reducer', () => {
|
||||
1: 3,
|
||||
},
|
||||
chainId: null,
|
||||
implements3085: false,
|
||||
openModal: null,
|
||||
popupList: [],
|
||||
})
|
||||
|
@ -1,11 +1,17 @@
|
||||
import { createSlice, nanoid } from '@reduxjs/toolkit'
|
||||
import { DEFAULT_TXN_DISMISS_MS } from 'constants/misc'
|
||||
|
||||
export type PopupContent = {
|
||||
txn: {
|
||||
hash: string
|
||||
}
|
||||
}
|
||||
import { SupportedChainId } from '../../constants/chains'
|
||||
|
||||
export type PopupContent =
|
||||
| {
|
||||
txn: {
|
||||
hash: string
|
||||
}
|
||||
}
|
||||
| {
|
||||
failedSwitchNetwork: SupportedChainId
|
||||
}
|
||||
|
||||
export enum ApplicationModal {
|
||||
WALLET,
|
||||
@ -26,7 +32,6 @@ type PopupList = Array<{ key: string; show: boolean; content: PopupContent; remo
|
||||
export interface ApplicationState {
|
||||
readonly blockNumber: { readonly [chainId: number]: number }
|
||||
readonly chainId: number | null
|
||||
readonly implements3085: boolean
|
||||
readonly openModal: ApplicationModal | null
|
||||
readonly popupList: PopupList
|
||||
}
|
||||
@ -34,7 +39,6 @@ export interface ApplicationState {
|
||||
const initialState: ApplicationState = {
|
||||
blockNumber: {},
|
||||
chainId: null,
|
||||
implements3085: false,
|
||||
openModal: null,
|
||||
popupList: [],
|
||||
}
|
||||
@ -75,12 +79,8 @@ const applicationSlice = createSlice({
|
||||
}
|
||||
})
|
||||
},
|
||||
setImplements3085(state, { payload: { implements3085 } }) {
|
||||
state.implements3085 = implements3085
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const { updateChainId, updateBlockNumber, setOpenModal, addPopup, removePopup, setImplements3085 } =
|
||||
applicationSlice.actions
|
||||
export const { updateChainId, updateBlockNumber, setOpenModal, addPopup, removePopup } = applicationSlice.actions
|
||||
export default applicationSlice.reducer
|
||||
|
@ -5,9 +5,8 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
import { api, CHAIN_TAG } from 'state/data/enhanced'
|
||||
import { useAppDispatch, useAppSelector } from 'state/hooks'
|
||||
import { supportedChainId } from 'utils/supportedChainId'
|
||||
import { switchToNetwork } from 'utils/switchToNetwork'
|
||||
|
||||
import { setImplements3085, updateBlockNumber, updateChainId } from './reducer'
|
||||
import { updateBlockNumber, updateChainId } from './reducer'
|
||||
|
||||
function useQueryCacheInvalidator() {
|
||||
const dispatch = useAppDispatch()
|
||||
@ -23,7 +22,7 @@ function useQueryCacheInvalidator() {
|
||||
}
|
||||
|
||||
export default function Updater(): null {
|
||||
const { account, chainId, library } = useActiveWeb3React()
|
||||
const { chainId, library } = useActiveWeb3React()
|
||||
const dispatch = useAppDispatch()
|
||||
const windowVisible = useIsWindowVisible()
|
||||
|
||||
@ -77,19 +76,5 @@ export default function Updater(): null {
|
||||
)
|
||||
}, [dispatch, debouncedState.chainId])
|
||||
|
||||
const implements3085 = useAppSelector((state) => state.application.implements3085)
|
||||
|
||||
useEffect(() => {
|
||||
if (!library?.provider?.request) {
|
||||
dispatch(setImplements3085({ implements3085: false }))
|
||||
} else if (account && !implements3085) {
|
||||
switchToNetwork({ library })
|
||||
.then((x) => x ?? dispatch(setImplements3085({ implements3085: true })))
|
||||
.catch(() => dispatch(setImplements3085({ implements3085: false })))
|
||||
} else if (!account && implements3085) {
|
||||
dispatch(setImplements3085({ implements3085: false }))
|
||||
}
|
||||
}, [account, dispatch, implements3085, library])
|
||||
|
||||
return null
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ const CHAIN_SUBGRAPH_URL: Record<number, string> = {
|
||||
[SupportedChainId.ARBITRUM_ONE]: 'https://api.thegraph.com/subgraphs/name/ianlapham/arbitrum-minimal',
|
||||
|
||||
[SupportedChainId.OPTIMISM]: 'https://api.thegraph.com/subgraphs/name/ianlapham/uniswap-optimism-dev',
|
||||
|
||||
[SupportedChainId.POLYGON]: 'https://api.thegraph.com/subgraphs/name/ianlapham/uniswap-v3-polygon',
|
||||
}
|
||||
|
||||
export const api = createApi({
|
||||
|
@ -194,14 +194,14 @@ export function useV3DerivedMintInfo(
|
||||
// check for invalid price input (converts to invalid ratio)
|
||||
const invalidPrice = useMemo(() => {
|
||||
const sqrtRatioX96 = price ? encodeSqrtRatioX96(price.numerator, price.denominator) : undefined
|
||||
const invalid =
|
||||
return (
|
||||
price &&
|
||||
sqrtRatioX96 &&
|
||||
!(
|
||||
JSBI.greaterThanOrEqual(sqrtRatioX96, TickMath.MIN_SQRT_RATIO) &&
|
||||
JSBI.lessThan(sqrtRatioX96, TickMath.MAX_SQRT_RATIO)
|
||||
)
|
||||
return invalid
|
||||
)
|
||||
}, [price])
|
||||
|
||||
// used for ratio calculation when pool not initialized
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useMulticall2Contract } from '../../hooks/useContract'
|
||||
import { useInterfaceMulticall } from '../../hooks/useContract'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useBlockNumber } from '../application/hooks'
|
||||
import { multicall } from './instance'
|
||||
@ -7,6 +7,6 @@ import { multicall } from './instance'
|
||||
export default function Updater() {
|
||||
const latestBlockNumber = useBlockNumber()
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const multicall2Contract = useMulticall2Contract()
|
||||
const multicall2Contract = useInterfaceMulticall()
|
||||
return <multicall.Updater chainId={chainId} latestBlockNumber={latestBlockNumber} contract={multicall2Contract} />
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { AlphaRouterParams, IMetric, MetricLoggerUnit, setGlobalMetric } from '@uniswap/smart-order-router'
|
||||
import { NETWORK_URLS } from 'connectors'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { INFURA_NETWORK_URLS, SupportedChainId } from 'constants/chains'
|
||||
import { providers } from 'ethers/lib/ethers'
|
||||
import ReactGA from 'react-ga'
|
||||
|
||||
@ -14,7 +13,7 @@ export type Dependencies = {
|
||||
export function buildDependencies(): Dependencies {
|
||||
const dependenciesByChain: Dependencies = {}
|
||||
for (const chainId of AUTO_ROUTER_SUPPORTED_CHAINS) {
|
||||
const provider = new providers.JsonRpcProvider(NETWORK_URLS[chainId])
|
||||
const provider = new providers.JsonRpcProvider(INFURA_NETWORK_URLS[chainId])
|
||||
|
||||
dependenciesByChain[chainId] = {
|
||||
chainId,
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { Ether, Token, TradeType } from '@uniswap/sdk-core'
|
||||
import { Token, TradeType } from '@uniswap/sdk-core'
|
||||
|
||||
import { nativeOnChain } from '../../constants/tokens'
|
||||
import { computeRoutes } from './utils'
|
||||
|
||||
const USDC = new Token(1, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC')
|
||||
const DAI = new Token(1, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 6, 'DAI')
|
||||
const MKR = new Token(1, '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', 6, 'MKR')
|
||||
|
||||
const ETH = Ether.onChain(1)
|
||||
const ETH = nativeOnChain(1)
|
||||
|
||||
// helper function to make amounts more readable
|
||||
const amount = (raw: TemplateStringsArray) => (parseInt(raw[0]) * 1e6).toString()
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Currency, CurrencyAmount, Ether, Token, TradeType } from '@uniswap/sdk-core'
|
||||
import { Currency, CurrencyAmount, Token, TradeType } from '@uniswap/sdk-core'
|
||||
import { Pair, Route as V2Route } from '@uniswap/v2-sdk'
|
||||
import { FeeAmount, Pool, Route as V3Route } from '@uniswap/v3-sdk'
|
||||
|
||||
import { nativeOnChain } from '../../constants/tokens'
|
||||
import { GetQuoteResult, InterfaceTrade, V2PoolInRoute, V3PoolInRoute } from './types'
|
||||
|
||||
/**
|
||||
@ -24,9 +25,9 @@ export function computeRoutes(
|
||||
if (parsedTokenIn.address !== currencyIn.wrapped.address) return undefined
|
||||
if (parsedTokenOut.address !== currencyOut.wrapped.address) return undefined
|
||||
|
||||
const parsedCurrencyIn = currencyIn.isNative ? Ether.onChain(currencyIn.chainId) : parsedTokenIn
|
||||
const parsedCurrencyIn = currencyIn.isNative ? nativeOnChain(currencyIn.chainId) : parsedTokenIn
|
||||
|
||||
const parsedCurrencyOut = currencyOut.isNative ? Ether.onChain(currencyOut.chainId) : parsedTokenOut
|
||||
const parsedCurrencyOut = currencyOut.isNative ? nativeOnChain(currencyOut.chainId) : parsedTokenOut
|
||||
|
||||
try {
|
||||
return quoteResult.route.map((route) => {
|
||||
|
@ -7,7 +7,7 @@ import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
|
||||
import JSBI from 'jsbi'
|
||||
import { ReactNode, useMemo } from 'react'
|
||||
|
||||
import { DAI, UNI, USDC, USDT, WBTC, WETH9_EXTENDED } from '../../constants/tokens'
|
||||
import { DAI, UNI, USDC, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { NEVER_RELOAD, useMultipleContractSingleData } from '../multicall/hooks'
|
||||
import { tryParseAmount } from '../swap/hooks'
|
||||
@ -26,19 +26,19 @@ export const STAKING_REWARDS_INFO: {
|
||||
} = {
|
||||
1: [
|
||||
{
|
||||
tokens: [WETH9_EXTENDED[1], DAI],
|
||||
tokens: [WRAPPED_NATIVE_CURRENCY[1], DAI],
|
||||
stakingRewardAddress: '0xa1484C3aa22a66C62b77E0AE78E15258bd0cB711',
|
||||
},
|
||||
{
|
||||
tokens: [WETH9_EXTENDED[1], USDC],
|
||||
tokens: [WRAPPED_NATIVE_CURRENCY[1], USDC],
|
||||
stakingRewardAddress: '0x7FBa4B8Dc5E7616e59622806932DBea72537A56b',
|
||||
},
|
||||
{
|
||||
tokens: [WETH9_EXTENDED[1], USDT],
|
||||
tokens: [WRAPPED_NATIVE_CURRENCY[1], USDT],
|
||||
stakingRewardAddress: '0x6C3e4cb2E96B01F4b866965A91ed4437839A121a',
|
||||
},
|
||||
{
|
||||
tokens: [WETH9_EXTENDED[1], WBTC],
|
||||
tokens: [WRAPPED_NATIVE_CURRENCY[1], WBTC],
|
||||
stakingRewardAddress: '0xCA35e32e7926b96A9988f61d510E038108d8068e',
|
||||
},
|
||||
],
|
||||
|
@ -95,6 +95,7 @@ export interface WrapTransactionInfo {
|
||||
type: TransactionType.WRAP
|
||||
unwrapped: boolean
|
||||
currencyAmountRaw: string
|
||||
chainId?: number
|
||||
}
|
||||
|
||||
export interface ClaimTransactionInfo {
|
||||
|
@ -15,13 +15,8 @@ export interface SerializedPair {
|
||||
}
|
||||
|
||||
export const updateMatchesDarkMode = createAction<{ matchesDarkMode: boolean }>('user/updateMatchesDarkMode')
|
||||
export const updateArbitrumAlphaAcknowledged = createAction<{ arbitrumAlphaAcknowledged: boolean }>(
|
||||
'user/updateArbitrumAlphaAcknowledged'
|
||||
)
|
||||
export const updateOptimismAlphaAcknowledged = createAction<{ optimismAlphaAcknowledged: boolean }>(
|
||||
'user/updateOptimismAlphaAcknowledged'
|
||||
)
|
||||
export const updateUserDarkMode = createAction<{ userDarkMode: boolean }>('user/updateUserDarkMode')
|
||||
export const acknowledgeNetworkAlert = createAction<{ chainId: number }>('user/acknowledgeNetworkAlert')
|
||||
export const updateUserExpertMode = createAction<{ userExpertMode: boolean }>('user/updateUserExpertMode')
|
||||
export const updateUserLocale = createAction<{ userLocale: SupportedLocale }>('user/updateUserLocale')
|
||||
export const updateUserClientSideRouter = createAction<{ userClientSideRouter: boolean }>(
|
||||
|
@ -14,14 +14,13 @@ import { useAllTokens } from '../../hooks/Tokens'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { AppState } from '../index'
|
||||
import {
|
||||
acknowledgeNetworkAlert,
|
||||
addSerializedPair,
|
||||
addSerializedToken,
|
||||
removeSerializedToken,
|
||||
SerializedPair,
|
||||
SerializedToken,
|
||||
updateArbitrumAlphaAcknowledged,
|
||||
updateHideClosedPositions,
|
||||
updateOptimismAlphaAcknowledged,
|
||||
updateUserClientSideRouter,
|
||||
updateUserDarkMode,
|
||||
updateUserDeadline,
|
||||
@ -339,22 +338,13 @@ export function useTrackedTokenPairs(): [Token, Token][] {
|
||||
}, [combinedList])
|
||||
}
|
||||
|
||||
export function useArbitrumAlphaAlert(): [boolean, (arbitrumAlphaAcknowledged: boolean) => void] {
|
||||
export function useNetworkAlertStatus(chainId: number | undefined): [boolean, () => void] {
|
||||
const dispatch = useAppDispatch()
|
||||
const arbitrumAlphaAcknowledged = useAppSelector(({ user }) => user.arbitrumAlphaAcknowledged)
|
||||
const setArbitrumAlphaAcknowledged = (arbitrumAlphaAcknowledged: boolean) => {
|
||||
dispatch(updateArbitrumAlphaAcknowledged({ arbitrumAlphaAcknowledged }))
|
||||
}
|
||||
const alertAcknowledged = useAppSelector(({ user }) => user.networkAlertsAcknowledged)
|
||||
const acknowledgeAlert = useCallback(() => {
|
||||
if (typeof chainId !== 'number') return
|
||||
dispatch(acknowledgeNetworkAlert({ chainId }))
|
||||
}, [chainId, dispatch])
|
||||
|
||||
return [arbitrumAlphaAcknowledged, setArbitrumAlphaAcknowledged]
|
||||
}
|
||||
|
||||
export function useOptimismAlphaAlert(): [boolean, (optimismAlphaAcknowledged: boolean) => void] {
|
||||
const dispatch = useAppDispatch()
|
||||
const optimismAlphaAcknowledged = useAppSelector(({ user }) => user.optimismAlphaAcknowledged)
|
||||
const setOptimismAlphaAcknowledged = (optimismAlphaAcknowledged: boolean) => {
|
||||
dispatch(updateOptimismAlphaAcknowledged({ optimismAlphaAcknowledged }))
|
||||
}
|
||||
|
||||
return [optimismAlphaAcknowledged, setOptimismAlphaAcknowledged]
|
||||
return [typeof chainId === 'number' ? alertAcknowledged[chainId] ?? false : false, acknowledgeAlert]
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
import { createReducer } from '@reduxjs/toolkit'
|
||||
import { SupportedLocale } from 'constants/locales'
|
||||
|
||||
import { SupportedChainId } from '../../constants/chains'
|
||||
import { DEFAULT_DEADLINE_FROM_NOW } from '../../constants/misc'
|
||||
import { updateVersion } from '../global/actions'
|
||||
import {
|
||||
acknowledgeNetworkAlert,
|
||||
addSerializedPair,
|
||||
addSerializedToken,
|
||||
removeSerializedPair,
|
||||
removeSerializedToken,
|
||||
SerializedPair,
|
||||
SerializedToken,
|
||||
updateArbitrumAlphaAcknowledged,
|
||||
updateHideClosedPositions,
|
||||
updateMatchesDarkMode,
|
||||
updateOptimismAlphaAcknowledged,
|
||||
updateUserClientSideRouter,
|
||||
updateUserDarkMode,
|
||||
updateUserDeadline,
|
||||
@ -25,13 +25,15 @@ import {
|
||||
const currentTimestamp = () => new Date().getTime()
|
||||
|
||||
export interface UserState {
|
||||
arbitrumAlphaAcknowledged: boolean
|
||||
// replaces the above two fields
|
||||
networkAlertsAcknowledged: {
|
||||
[chainId: number]: true
|
||||
}
|
||||
|
||||
// the timestamp of the last updateVersion action
|
||||
lastUpdateVersionTimestamp?: number
|
||||
|
||||
matchesDarkMode: boolean // whether the dark mode media query matches
|
||||
optimismAlphaAcknowledged: boolean
|
||||
|
||||
userDarkMode: boolean | null // the user's choice for dark mode or light mode
|
||||
userLocale: SupportedLocale | null
|
||||
@ -72,9 +74,8 @@ function pairKey(token0Address: string, token1Address: string) {
|
||||
}
|
||||
|
||||
export const initialState: UserState = {
|
||||
arbitrumAlphaAcknowledged: false,
|
||||
matchesDarkMode: false,
|
||||
optimismAlphaAcknowledged: false,
|
||||
networkAlertsAcknowledged: {},
|
||||
userDarkMode: null,
|
||||
userExpertMode: false,
|
||||
userLocale: null,
|
||||
@ -122,8 +123,20 @@ export default createReducer(initialState, (builder) =>
|
||||
state.userDeadline = DEFAULT_DEADLINE_FROM_NOW
|
||||
}
|
||||
|
||||
state.networkAlertsAcknowledged = state.networkAlertsAcknowledged ?? {}
|
||||
if ((state as unknown as { arbitrumAlphaAcknowledged: boolean }).arbitrumAlphaAcknowledged) {
|
||||
state.networkAlertsAcknowledged[SupportedChainId.ARBITRUM_ONE] = true
|
||||
}
|
||||
if ((state as unknown as { optimismAlphaAcknowledged: boolean }).optimismAlphaAcknowledged) {
|
||||
state.networkAlertsAcknowledged[SupportedChainId.OPTIMISM] = true
|
||||
}
|
||||
|
||||
state.lastUpdateVersionTimestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(acknowledgeNetworkAlert, (state, action) => {
|
||||
state.networkAlertsAcknowledged = state.networkAlertsAcknowledged ?? {}
|
||||
state.networkAlertsAcknowledged[action.payload.chainId] = true
|
||||
})
|
||||
.addCase(updateUserDarkMode, (state, action) => {
|
||||
state.userDarkMode = action.payload.userDarkMode
|
||||
state.timestamp = currentTimestamp()
|
||||
@ -132,12 +145,6 @@ export default createReducer(initialState, (builder) =>
|
||||
state.matchesDarkMode = action.payload.matchesDarkMode
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(updateArbitrumAlphaAcknowledged, (state, action) => {
|
||||
state.arbitrumAlphaAcknowledged = action.payload.arbitrumAlphaAcknowledged
|
||||
})
|
||||
.addCase(updateOptimismAlphaAcknowledged, (state, action) => {
|
||||
state.optimismAlphaAcknowledged = action.payload.optimismAlphaAcknowledged
|
||||
})
|
||||
.addCase(updateUserExpertMode, (state, action) => {
|
||||
state.userExpertMode = action.payload.userExpertMode
|
||||
state.timestamp = currentTimestamp()
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { Interface } from '@ethersproject/abi'
|
||||
import { Currency, CurrencyAmount, Ether, Token } from '@uniswap/sdk-core'
|
||||
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import ERC20ABI from 'abis/erc20.json'
|
||||
import { Erc20Interface } from 'abis/types/Erc20'
|
||||
import JSBI from 'jsbi'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { UNI } from '../../constants/tokens'
|
||||
import { nativeOnChain, UNI } from '../../constants/tokens'
|
||||
import { useAllTokens } from '../../hooks/Tokens'
|
||||
import { useMulticall2Contract } from '../../hooks/useContract'
|
||||
import { useInterfaceMulticall } from '../../hooks/useContract'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { isAddress } from '../../utils'
|
||||
import { useUserUnclaimedAmount } from '../claim/hooks'
|
||||
@ -16,38 +16,35 @@ import { useTotalUniEarned } from '../stake/hooks'
|
||||
/**
|
||||
* Returns a map of the given addresses to their eventually consistent ETH balances.
|
||||
*/
|
||||
export function useETHBalances(uncheckedAddresses?: (string | undefined)[]): {
|
||||
export function useNativeCurrencyBalances(uncheckedAddresses?: (string | undefined)[]): {
|
||||
[address: string]: CurrencyAmount<Currency> | undefined
|
||||
} {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const multicallContract = useMulticall2Contract()
|
||||
const multicallContract = useInterfaceMulticall()
|
||||
|
||||
const addresses: string[] = useMemo(
|
||||
const validAddressInputs: [string][] = useMemo(
|
||||
() =>
|
||||
uncheckedAddresses
|
||||
? uncheckedAddresses
|
||||
.map(isAddress)
|
||||
.filter((a): a is string => a !== false)
|
||||
.sort()
|
||||
.map((addr) => [addr])
|
||||
: [],
|
||||
[uncheckedAddresses]
|
||||
)
|
||||
|
||||
const results = useSingleContractMultipleData(
|
||||
multicallContract,
|
||||
'getEthBalance',
|
||||
addresses.map((address) => [address])
|
||||
)
|
||||
const results = useSingleContractMultipleData(multicallContract, 'getEthBalance', validAddressInputs)
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
addresses.reduce<{ [address: string]: CurrencyAmount<Currency> }>((memo, address, i) => {
|
||||
validAddressInputs.reduce<{ [address: string]: CurrencyAmount<Currency> }>((memo, [address], i) => {
|
||||
const value = results?.[i]?.result?.[0]
|
||||
if (value && chainId)
|
||||
memo[address] = CurrencyAmount.fromRawAmount(Ether.onChain(chainId), JSBI.BigInt(value.toString()))
|
||||
memo[address] = CurrencyAmount.fromRawAmount(nativeOnChain(chainId), JSBI.BigInt(value.toString()))
|
||||
return memo
|
||||
}, {}),
|
||||
[addresses, chainId, results]
|
||||
[validAddressInputs, chainId, results]
|
||||
)
|
||||
}
|
||||
|
||||
@ -120,7 +117,7 @@ export function useCurrencyBalances(
|
||||
|
||||
const tokenBalances = useTokenBalances(account, tokens)
|
||||
const containsETH: boolean = useMemo(() => currencies?.some((currency) => currency?.isNative) ?? false, [currencies])
|
||||
const ethBalance = useETHBalances(containsETH ? [account] : [])
|
||||
const ethBalance = useNativeCurrencyBalances(containsETH ? [account] : [])
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
|
@ -8,11 +8,13 @@ const initialStyles = {
|
||||
width: '200vw',
|
||||
height: '200vh',
|
||||
transform: 'translate(-50vw, -100vh)',
|
||||
backgroundBlendMode: '',
|
||||
}
|
||||
const backgroundResetStyles = {
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
transform: 'unset',
|
||||
backgroundBlendMode: '',
|
||||
}
|
||||
|
||||
type TargetBackgroundStyles = typeof initialStyles | typeof backgroundResetStyles
|
||||
@ -48,6 +50,16 @@ export default function RadialGradientByChainUpdater(): null {
|
||||
const optimismDarkGradient = 'radial-gradient(150% 100% at 50% 0%, #3E2E38 2%, #2C1F2D 53%, #1F2128 100%)'
|
||||
backgroundRadialGradientElement.style.background = darkMode ? optimismDarkGradient : optimismLightGradient
|
||||
break
|
||||
case SupportedChainId.POLYGON:
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
setBackground(backgroundResetStyles)
|
||||
const polygonLightGradient =
|
||||
'radial-gradient(153.32% 100% at 47.26% 0%, rgba(130, 71, 229, 0.0864) 0%, rgba(0, 41, 255, 0.06) 48.19%, rgba(0, 41, 255, 0.012) 100%), #FFFFFF'
|
||||
const polygonDarkGradient =
|
||||
'radial-gradient(150.6% 98.22% at 48.06% 0%, rgba(130, 71, 229, 0.6) 0%, rgba(200, 168, 255, 0) 100%), #1F2128'
|
||||
backgroundRadialGradientElement.style.background = darkMode ? polygonDarkGradient : polygonLightGradient
|
||||
backgroundRadialGradientElement.style.backgroundBlendMode = darkMode ? 'overlay,normal' : 'multiply,normal'
|
||||
break
|
||||
default:
|
||||
setBackground(initialStyles)
|
||||
backgroundRadialGradientElement.style.background = ''
|
||||
|
@ -1,35 +0,0 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { hexStripZeros } from '@ethersproject/bytes'
|
||||
import { Web3Provider } from '@ethersproject/providers'
|
||||
import { L1ChainInfo, L2ChainInfo, SupportedChainId } from 'constants/chains'
|
||||
|
||||
interface AddNetworkArguments {
|
||||
library: Web3Provider
|
||||
chainId: SupportedChainId
|
||||
info: L1ChainInfo | L2ChainInfo
|
||||
}
|
||||
|
||||
// provider.request returns Promise<any>, but wallet_switchEthereumChain must return null or throw
|
||||
// see https://github.com/rekmarks/EIPs/blob/3326-create/EIPS/eip-3326.md for more info on wallet_switchEthereumChain
|
||||
export async function addNetwork({ library, chainId, info }: AddNetworkArguments): Promise<null | void> {
|
||||
if (!library?.provider?.request) {
|
||||
return
|
||||
}
|
||||
const formattedChainId = hexStripZeros(BigNumber.from(chainId).toHexString())
|
||||
try {
|
||||
await library?.provider.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [
|
||||
{
|
||||
chainId: formattedChainId,
|
||||
chainName: info.label,
|
||||
rpcUrls: info.rpcUrls,
|
||||
nativeCurrency: info.nativeCurrency,
|
||||
blockExplorerUrls: [info.explorer],
|
||||
},
|
||||
],
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('error adding eth network: ', chainId, info, error)
|
||||
}
|
||||
}
|
9
src/utils/chains.tsx
Normal file
9
src/utils/chains.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import { CHAIN_INFO, NetworkType, SupportedL1ChainId, SupportedL2ChainId } from '../constants/chains'
|
||||
|
||||
export function isL1ChainId(chainId: number | undefined): chainId is SupportedL1ChainId {
|
||||
return typeof chainId === 'number' && CHAIN_INFO[chainId].networkType === NetworkType.L1
|
||||
}
|
||||
|
||||
export function isL2ChainId(chainId: number | undefined): chainId is SupportedL2ChainId {
|
||||
return typeof chainId === 'number' && CHAIN_INFO[chainId].networkType === NetworkType.L2
|
||||
}
|
@ -1,13 +1,19 @@
|
||||
import { L1_CHAIN_IDS, SupportedChainId } from '../constants/chains'
|
||||
import { SupportedChainId } from '../constants/chains'
|
||||
|
||||
const DEFAULT_NETWORKS = [
|
||||
SupportedChainId.MAINNET,
|
||||
SupportedChainId.ROPSTEN,
|
||||
SupportedChainId.RINKEBY,
|
||||
SupportedChainId.GOERLI,
|
||||
SupportedChainId.KOVAN,
|
||||
]
|
||||
|
||||
export function constructSameAddressMap<T extends string>(
|
||||
address: T,
|
||||
additionalNetworks: SupportedChainId[] = []
|
||||
): { [chainId: number]: T } {
|
||||
return (L1_CHAIN_IDS as readonly SupportedChainId[])
|
||||
.concat(additionalNetworks)
|
||||
.reduce<{ [chainId: number]: T }>((memo, chainId) => {
|
||||
memo[chainId] = address
|
||||
return memo
|
||||
}, {})
|
||||
return DEFAULT_NETWORKS.concat(additionalNetworks).reduce<{ [chainId: number]: T }>((memo, chainId) => {
|
||||
memo[chainId] = address
|
||||
return memo
|
||||
}, {})
|
||||
}
|
||||
|
@ -13,6 +13,12 @@ describe('#getExplorerLink', () => {
|
||||
it('unrecognized chain id defaults to mainnet', () => {
|
||||
expect(getExplorerLink(2, 'abc', ExplorerDataType.ADDRESS)).toEqual('https://etherscan.io/address/abc')
|
||||
})
|
||||
it('arbitrum', () => {
|
||||
expect(getExplorerLink(42161, 'abc', ExplorerDataType.ADDRESS)).toEqual('https://arbiscan.io/address/abc')
|
||||
})
|
||||
it('polygon', () => {
|
||||
expect(getExplorerLink(137, 'abc', ExplorerDataType.ADDRESS)).toEqual('https://polygonscan.com/address/abc')
|
||||
})
|
||||
it('ropsten', () => {
|
||||
expect(getExplorerLink(3, 'abc', ExplorerDataType.ADDRESS)).toEqual('https://ropsten.etherscan.io/address/abc')
|
||||
})
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { SupportedChainId } from '../constants/chains'
|
||||
|
||||
const ETHERSCAN_PREFIXES: { [chainId: number]: string } = {
|
||||
[SupportedChainId.MAINNET]: '',
|
||||
[SupportedChainId.ROPSTEN]: 'ropsten.',
|
||||
[SupportedChainId.RINKEBY]: 'rinkeby.',
|
||||
[SupportedChainId.GOERLI]: 'goerli.',
|
||||
[SupportedChainId.KOVAN]: 'kovan.',
|
||||
[SupportedChainId.OPTIMISM]: 'optimistic.',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: 'kovan-optimistic.',
|
||||
[SupportedChainId.MAINNET]: 'https://etherscan.io',
|
||||
[SupportedChainId.ROPSTEN]: 'https://ropsten.etherscan.io',
|
||||
[SupportedChainId.RINKEBY]: 'https://rinkeby.etherscan.io',
|
||||
[SupportedChainId.GOERLI]: 'https://goerli.etherscan.io',
|
||||
[SupportedChainId.KOVAN]: 'https://kovan.etherscan.io',
|
||||
[SupportedChainId.OPTIMISM]: 'https://optimistic.etherscan.io',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: 'https://kovan-optimistic.etherscan.io',
|
||||
[SupportedChainId.POLYGON_MUMBAI]: 'https://mumbai.polygonscan.com',
|
||||
[SupportedChainId.POLYGON]: 'https://polygonscan.com',
|
||||
}
|
||||
|
||||
export enum ExplorerDataType {
|
||||
@ -52,7 +54,7 @@ export function getExplorerLink(chainId: number, data: string, type: ExplorerDat
|
||||
}
|
||||
}
|
||||
|
||||
const prefix = `https://${ETHERSCAN_PREFIXES[chainId] ?? ''}etherscan.io`
|
||||
const prefix = ETHERSCAN_PREFIXES[chainId] ?? 'https://etherscan.io'
|
||||
|
||||
switch (type) {
|
||||
case ExplorerDataType.TRANSACTION:
|
||||
|
@ -3,11 +3,9 @@ import { hexStripZeros } from '@ethersproject/bytes'
|
||||
import { Web3Provider } from '@ethersproject/providers'
|
||||
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
|
||||
import { addNetwork } from './addNetwork'
|
||||
|
||||
interface SwitchNetworkArguments {
|
||||
library: Web3Provider
|
||||
chainId?: SupportedChainId
|
||||
chainId: SupportedChainId
|
||||
}
|
||||
|
||||
// provider.request returns Promise<any>, but wallet_switchEthereumChain must return null or throw
|
||||
@ -16,25 +14,39 @@ export async function switchToNetwork({ library, chainId }: SwitchNetworkArgumen
|
||||
if (!library?.provider?.request) {
|
||||
return
|
||||
}
|
||||
if (!chainId && library?.getNetwork) {
|
||||
;({ chainId } = await library.getNetwork())
|
||||
}
|
||||
const formattedChainId = hexStripZeros(BigNumber.from(chainId).toHexString())
|
||||
try {
|
||||
await library?.provider.request({
|
||||
await library.provider.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [{ chainId: formattedChainId }],
|
||||
})
|
||||
} catch (error) {
|
||||
// 4902 is the error code for attempting to switch to an unrecognized chainId
|
||||
if (error.code === 4902 && chainId !== undefined) {
|
||||
if (error.code === 4902) {
|
||||
const info = CHAIN_INFO[chainId]
|
||||
|
||||
// metamask (only known implementer) automatically switches after a network is added
|
||||
// the second call is done here because that behavior is not a part of the spec and cannot be relied upon in the future
|
||||
// metamask's behavior when switching to the current network is just to return null (a no-op)
|
||||
await addNetwork({ library, chainId, info })
|
||||
await switchToNetwork({ library, chainId })
|
||||
await library.provider.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [
|
||||
{
|
||||
chainId: formattedChainId,
|
||||
chainName: info.label,
|
||||
rpcUrls: [info.addNetworkInfo.rpcUrl],
|
||||
nativeCurrency: info.addNetworkInfo.nativeCurrency,
|
||||
blockExplorerUrls: [info.explorer],
|
||||
},
|
||||
],
|
||||
})
|
||||
const { chainId: chainIdAfterSwitch } = await library.getNetwork()
|
||||
if (chainIdAfterSwitch !== chainId) {
|
||||
// metamask (only known implementer) automatically switches after a network is added
|
||||
// the second call is done here because that behavior is not a part of the spec and cannot be relied upon in the future
|
||||
// metamask's behavior when switching to the current network is just to return null (a no-op)
|
||||
await library.provider.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [{ chainId: formattedChainId }],
|
||||
})
|
||||
}
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
|
||||
import { ExtendedEther, WETH9_EXTENDED } from '../constants/tokens'
|
||||
import { nativeOnChain, WRAPPED_NATIVE_CURRENCY } from '../constants/tokens'
|
||||
import { supportedChainId } from './supportedChainId'
|
||||
|
||||
export function unwrappedToken(currency: Currency): Currency {
|
||||
if (currency.isNative) return currency
|
||||
const formattedChainId = supportedChainId(currency.chainId)
|
||||
if (formattedChainId && currency.equals(WETH9_EXTENDED[formattedChainId]))
|
||||
return ExtendedEther.onChain(currency.chainId)
|
||||
if (formattedChainId && currency.equals(WRAPPED_NATIVE_CURRENCY[formattedChainId]))
|
||||
return nativeOnChain(currency.chainId)
|
||||
return currency
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user