fix: inadvertent merges/reverts (#2915)
* Revert "Revert "Merge branch 'main' of https://github.com/Uniswap/interface" (#2912)" This reverts commit 7d343dcfbdf75a2f91d28cefce84e4b1bace7b87. * Revert "deleted files" This reverts commit 097b8361d4c09afd3cb681c4622145c555ced884.
This commit is contained in:
parent
7d343dcfbd
commit
e81e8a8f71
@ -8,13 +8,33 @@
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"ignorePatterns": ["node_modules/**/*"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"src/types/v3",
|
||||
"src/abis/types",
|
||||
"src/locales/**/*.js",
|
||||
"src/locales/**/en-US.po",
|
||||
"src/state/data/generated.ts",
|
||||
"node_modules",
|
||||
"coverage",
|
||||
"build",
|
||||
"dist",
|
||||
".DS_Store",
|
||||
".env.local",
|
||||
".env.development.local",
|
||||
".env.test.local",
|
||||
".env.production.local",
|
||||
".idea/",
|
||||
".vscode/",
|
||||
"package-lock.json",
|
||||
"yarn.lock"
|
||||
],
|
||||
"extends": [
|
||||
"react-app",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
|
10
.github/workflows/lint.yml
vendored
10
.github/workflows/lint.yml
vendored
@ -11,7 +11,6 @@ on:
|
||||
jobs:
|
||||
run-linters:
|
||||
name: Run linters
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.repository_owner }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -39,10 +38,15 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Run linters
|
||||
- name: Run eslint w/ autofix
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.repository_owner }}
|
||||
uses: wearerequired/lint-action@36c7e6689e80d785d27a22f71d970f3a3b4fcb70
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
eslint: true
|
||||
eslint_extensions: js,jsx,ts,tsx,json
|
||||
eslint_args: "-c .eslintrc.json"
|
||||
auto_fix: true
|
||||
|
||||
- name: Run eslint
|
||||
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login != github.repository_owner }}
|
||||
run: yarn eslint .
|
||||
|
@ -74,6 +74,7 @@ class CustomizedBridge extends Eip1193Bridge {
|
||||
}
|
||||
|
||||
// sets up the injected provider to be a mock ethereum provider with the given mnemonic/index
|
||||
// eslint-disable-next-line no-undef
|
||||
Cypress.Commands.overwrite('visit', (original, url, options) => {
|
||||
return original(url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url, {
|
||||
...options,
|
||||
|
@ -1,4 +1,4 @@
|
||||
export default {
|
||||
const linguiConfig = {
|
||||
catalogs: [
|
||||
{
|
||||
path: '<rootDir>/src/locales/{locale}',
|
||||
@ -52,3 +52,5 @@ export default {
|
||||
runtimeConfigModule: ['@lingui/core', 'i18n'],
|
||||
sourceLocale: 'en-US',
|
||||
}
|
||||
|
||||
export default linguiConfig
|
||||
|
@ -152,12 +152,6 @@
|
||||
"bundle": "microbundle --tsconfig tsconfig.lib.json src/lib/index.tsx --format esm,cjs",
|
||||
"cosmos": "open http://localhost:5000 && cross-env FAST_REFRESH=false cosmos"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app",
|
||||
"ignorePatterns": [
|
||||
"node_modules"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
|
@ -80,7 +80,7 @@ function ClaimSummary({ info: { recipient, uniAmountRaw } }: { info: ClaimTransa
|
||||
)
|
||||
}
|
||||
|
||||
function SubmitProposalTransactionSummary({}: { info: SubmitProposalTransactionInfo }) {
|
||||
function SubmitProposalTransactionSummary(_: { info: SubmitProposalTransactionInfo }) {
|
||||
return <Trans>Submit new proposal</Trans>
|
||||
}
|
||||
|
||||
@ -147,13 +147,13 @@ function WrapSummary({ info: { currencyAmountRaw, unwrapped } }: { info: WrapTra
|
||||
}
|
||||
}
|
||||
|
||||
function DepositLiquidityStakingSummary({}: { info: DepositLiquidityStakingTransactionInfo }) {
|
||||
function DepositLiquidityStakingSummary(_: { info: DepositLiquidityStakingTransactionInfo }) {
|
||||
// not worth rendering the tokens since you can should no longer deposit liquidity in the staking contracts
|
||||
// todo: deprecate and delete the code paths that allow this, show user more information
|
||||
return <Trans>Deposit liquidity</Trans>
|
||||
}
|
||||
|
||||
function WithdrawLiquidityStakingSummary({}: { info: WithdrawLiquidityStakingTransactionInfo }) {
|
||||
function WithdrawLiquidityStakingSummary(_: { info: WithdrawLiquidityStakingTransactionInfo }) {
|
||||
return <Trans>Withdraw deposited liquidity</Trans>
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import { injected, portis, walletlink } from '../../connectors'
|
||||
import { SUPPORTED_WALLETS } from '../../constants/wallet'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { clearAllTransactions } from '../../state/transactions/actions'
|
||||
import { ExternalLink, LinkStyledButton, TYPE } from '../../theme'
|
||||
import { ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { ButtonSecondary } from '../Button'
|
||||
@ -378,9 +378,9 @@ export default function AccountDetails({
|
||||
{!!pendingTransactions.length || !!confirmedTransactions.length ? (
|
||||
<LowerSection>
|
||||
<AutoRow mb={'1rem'} style={{ justifyContent: 'space-between' }}>
|
||||
<TYPE.body>
|
||||
<ThemedText.Body>
|
||||
<Trans>Recent Transactions</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
<LinkStyledButton onClick={clearAllTransactionsCallback}>
|
||||
<Trans>(clear all)</Trans>
|
||||
</LinkStyledButton>
|
||||
@ -390,9 +390,9 @@ export default function AccountDetails({
|
||||
</LowerSection>
|
||||
) : (
|
||||
<LowerSection>
|
||||
<TYPE.body color={theme.text1}>
|
||||
<ThemedText.Body color={theme.text1}>
|
||||
<Trans>Your transactions will appear here...</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</LowerSection>
|
||||
)}
|
||||
</>
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { t } from '@lingui/macro'
|
||||
import { ReactNode, useCallback, useContext } from 'react'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import useENS from '../../hooks/useENS'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween } from '../Row'
|
||||
@ -107,9 +108,9 @@ export default function AddressInputPanel({
|
||||
<InputContainer>
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.black color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
<ThemedText.Black color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
{label ?? <Trans>Recipient</Trans>}
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
{address && chainId && (
|
||||
<ExternalLink
|
||||
href={getExplorerLink(chainId, name ?? address, ExplorerDataType.ADDRESS)}
|
||||
|
@ -4,7 +4,7 @@ import HoverInlineText from 'components/HoverInlineText'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { warningSeverity } from '../../utils/prices'
|
||||
|
||||
export function FiatValue({
|
||||
@ -25,7 +25,7 @@ export function FiatValue({
|
||||
}, [priceImpact, theme.green1, theme.red1, theme.text3, theme.yellow1])
|
||||
|
||||
return (
|
||||
<TYPE.body fontSize={14} color={fiatValue ? theme.text2 : theme.text4}>
|
||||
<ThemedText.Body fontSize={14} color={fiatValue ? theme.text2 : theme.text4}>
|
||||
{fiatValue ? (
|
||||
<Trans>
|
||||
~$ <HoverInlineText text={fiatValue?.toSignificant(6, { groupSeparator: ',' })} />
|
||||
@ -39,6 +39,6 @@ export function FiatValue({
|
||||
(<Trans>{priceImpact.multiply(-1).toSignificant(3)}%</Trans>)
|
||||
</span>
|
||||
) : null}
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useCurrencyBalance } from '../../state/wallet/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ButtonGray } from '../Button'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
import DoubleCurrencyLogo from '../DoubleLogo'
|
||||
@ -212,9 +212,9 @@ export default function CurrencyInputPanel({
|
||||
<FixedContainer>
|
||||
<AutoColumn gap="sm" justify="center">
|
||||
<Lock />
|
||||
<TYPE.label fontSize="12px" textAlign="center" padding="0 12px">
|
||||
<ThemedText.Label fontSize="12px" textAlign="center" padding="0 12px">
|
||||
<Trans>The market price is outside your specified price range. Single-asset deposit only.</Trans>
|
||||
</TYPE.label>
|
||||
</ThemedText.Label>
|
||||
</AutoColumn>
|
||||
</FixedContainer>
|
||||
)}
|
||||
@ -271,7 +271,7 @@ export default function CurrencyInputPanel({
|
||||
<RowBetween>
|
||||
{account ? (
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
<TYPE.body
|
||||
<ThemedText.Body
|
||||
onClick={onMax}
|
||||
color={theme.text2}
|
||||
fontWeight={400}
|
||||
@ -287,7 +287,7 @@ export default function CurrencyInputPanel({
|
||||
</Trans>
|
||||
)
|
||||
) : null}
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
{showMaxButton && selectedCurrencyBalance ? (
|
||||
<StyledBalanceMax onClick={onMax}>
|
||||
<Trans>(Max)</Trans>
|
||||
|
@ -4,7 +4,7 @@ import ReactGA from 'react-ga'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import store, { AppState } from '../../state'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import { userAgent } from '../../utils/userAgent'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { AutoRow } from '../Row'
|
||||
@ -47,6 +47,8 @@ type ErrorBoundaryState = {
|
||||
error: Error | null
|
||||
}
|
||||
|
||||
const IS_UNISWAP = window.location.hostname === 'app.uniswap.org'
|
||||
|
||||
export default class ErrorBoundary extends React.Component<unknown, ErrorBoundaryState> {
|
||||
constructor(props: unknown) {
|
||||
super(props)
|
||||
@ -67,6 +69,7 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
|
||||
|
||||
render() {
|
||||
const { error } = this.state
|
||||
|
||||
if (error !== null) {
|
||||
const encodedBody = encodeURIComponent(issueBody(error))
|
||||
return (
|
||||
@ -74,39 +77,41 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
|
||||
<BodyWrapper>
|
||||
<AutoColumn gap={'md'}>
|
||||
<SomethingWentWrongWrapper>
|
||||
<TYPE.label fontSize={24} fontWeight={600}>
|
||||
<ThemedText.Label fontSize={24} fontWeight={600}>
|
||||
<Trans>Something went wrong</Trans>
|
||||
</TYPE.label>
|
||||
</ThemedText.Label>
|
||||
</SomethingWentWrongWrapper>
|
||||
<CodeBlockWrapper>
|
||||
<code>
|
||||
<TYPE.main fontSize={10}>{error.stack}</TYPE.main>
|
||||
<ThemedText.Main fontSize={10}>{error.stack}</ThemedText.Main>
|
||||
</code>
|
||||
</CodeBlockWrapper>
|
||||
<AutoRow>
|
||||
<LinkWrapper>
|
||||
<ExternalLink
|
||||
id="create-github-issue-link"
|
||||
href={`https://github.com/Uniswap/uniswap-interface/issues/new?assignees=&labels=bug&body=${encodedBody}&title=${encodeURIComponent(
|
||||
`Crash report: \`${error.name}${error.message && `: ${error.message}`}\``
|
||||
)}`}
|
||||
target="_blank"
|
||||
>
|
||||
<TYPE.link fontSize={16}>
|
||||
<Trans>Create an issue on GitHub</Trans>
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
<LinkWrapper>
|
||||
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
|
||||
<TYPE.link fontSize={16}>
|
||||
<Trans>Get support on Discord</Trans>
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
</AutoRow>
|
||||
{IS_UNISWAP ? (
|
||||
<AutoRow>
|
||||
<LinkWrapper>
|
||||
<ExternalLink
|
||||
id="create-github-issue-link"
|
||||
href={`https://github.com/Uniswap/uniswap-interface/issues/new?assignees=&labels=bug&body=${encodedBody}&title=${encodeURIComponent(
|
||||
`Crash report: \`${error.name}${error.message && `: ${error.message}`}\``
|
||||
)}`}
|
||||
target="_blank"
|
||||
>
|
||||
<ThemedText.Link fontSize={16}>
|
||||
<Trans>Create an issue on GitHub</Trans>
|
||||
<span>↗</span>
|
||||
</ThemedText.Link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
<LinkWrapper>
|
||||
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
|
||||
<ThemedText.Link fontSize={16}>
|
||||
<Trans>Get support on Discord</Trans>
|
||||
<span>↗</span>
|
||||
</ThemedText.Link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
</AutoRow>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
</BodyWrapper>
|
||||
</FallbackWrapper>
|
||||
@ -121,7 +126,7 @@ function getRelevantState(): null | keyof AppState {
|
||||
if (!path.startsWith('#/')) {
|
||||
return null
|
||||
}
|
||||
const pieces = path.substring(2).split(/[\/\\?]/)
|
||||
const pieces = path.substring(2).split(/[/\\?]/)
|
||||
switch (pieces[0]) {
|
||||
case 'swap':
|
||||
return 'swap'
|
||||
|
@ -6,12 +6,12 @@ import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
|
||||
import { PoolState } from 'hooks/usePools'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { FeeTierPercentageBadge } from './FeeTierPercentageBadge'
|
||||
import { FEE_AMOUNT_DETAIL } from './shared'
|
||||
|
||||
const ResponsiveText = styled(TYPE.label)`
|
||||
const ResponsiveText = styled(ThemedText.Label)`
|
||||
line-height: 16px;
|
||||
font-size: 14px;
|
||||
|
||||
@ -37,9 +37,9 @@ export function FeeOption({ feeAmount, active, poolState, distributions, onClick
|
||||
<ResponsiveText>
|
||||
<Trans>{FEE_AMOUNT_DETAIL[feeAmount].label}%</Trans>
|
||||
</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
<ThemedText.Main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
{FEE_AMOUNT_DETAIL[feeAmount].description}
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
|
||||
{distributions && (
|
||||
|
@ -4,7 +4,7 @@ import Badge from 'components/Badge'
|
||||
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
|
||||
import { PoolState } from 'hooks/usePools'
|
||||
import React from 'react'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
export function FeeTierPercentageBadge({
|
||||
feeAmount,
|
||||
@ -17,7 +17,7 @@ export function FeeTierPercentageBadge({
|
||||
}) {
|
||||
return (
|
||||
<Badge>
|
||||
<TYPE.label fontSize={10}>
|
||||
<ThemedText.Label fontSize={10}>
|
||||
{!distributions || poolState === PoolState.NOT_EXISTS || poolState === PoolState.INVALID ? (
|
||||
<Trans>Not created</Trans>
|
||||
) : distributions[feeAmount] !== undefined ? (
|
||||
@ -25,7 +25,7 @@ export function FeeTierPercentageBadge({
|
||||
) : (
|
||||
<Trans>No data</Trans>
|
||||
)}
|
||||
</TYPE.label>
|
||||
</ThemedText.Label>
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import { Box } from 'rebass'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { FeeOption } from './FeeOption'
|
||||
import { FeeTierPercentageBadge } from './FeeTierPercentageBadge'
|
||||
@ -149,18 +149,18 @@ export default function FeeSelector({
|
||||
<AutoColumn id="add-liquidity-selected-fee">
|
||||
{!feeAmount ? (
|
||||
<>
|
||||
<TYPE.label>
|
||||
<ThemedText.Label>
|
||||
<Trans>Fee tier</Trans>
|
||||
</TYPE.label>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
</ThemedText.Label>
|
||||
<ThemedText.Main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
<Trans>The % you will earn in fees.</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TYPE.label className="selected-fee-label">
|
||||
<ThemedText.Label className="selected-fee-label">
|
||||
<Trans>{FEE_AMOUNT_DETAIL[feeAmount].label}% fee tier</Trans>
|
||||
</TYPE.label>
|
||||
</ThemedText.Label>
|
||||
<Box style={{ width: 'fit-content', marginTop: '8px' }} className="selected-fee-percentage">
|
||||
{distributions && (
|
||||
<FeeTierPercentageBadge
|
||||
|
@ -6,7 +6,7 @@ import ms from 'ms.macro'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useBlockNumber } from 'state/application/hooks'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { ExternalLink, TYPE } from 'theme'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
|
||||
|
||||
import { ChainConnectivityWarning } from './ChainConnectivityWarning'
|
||||
@ -25,7 +25,7 @@ const StyledPolling = styled.div<{ warning: boolean }>`
|
||||
display: none;
|
||||
`}
|
||||
`
|
||||
const StyledPollingNumber = styled(TYPE.small)<{ breathe: boolean; hovering: boolean }>`
|
||||
const StyledPollingNumber = styled(ThemedText.Small)<{ breathe: boolean; hovering: boolean }>`
|
||||
transition: opacity 0.25s ease;
|
||||
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.5)};
|
||||
:hover {
|
||||
|
@ -14,7 +14,7 @@ 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, TYPE, UniTokenAnimated } from '../../theme'
|
||||
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'
|
||||
@ -70,9 +70,9 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
|
||||
<CardNoise />
|
||||
<CardSection gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<ThemedText.White color="white">
|
||||
<Trans>Your UNI Breakdown</Trans>
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
<StyledClose stroke="white" onClick={() => setShowUniBalanceModal(false)} />
|
||||
</RowBetween>
|
||||
</CardSection>
|
||||
@ -82,29 +82,29 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
|
||||
<CardSection gap="sm">
|
||||
<AutoColumn gap="md" justify="center">
|
||||
<UniTokenAnimated width="48px" src={tokenLogo} />{' '}
|
||||
<TYPE.white fontSize={48} fontWeight={600} color="white">
|
||||
<ThemedText.White fontSize={48} fontWeight={600} color="white">
|
||||
{total?.toFixed(2, { groupSeparator: ',' })}
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
</AutoColumn>
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<ThemedText.White color="white">
|
||||
<Trans>Balance:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">{uniBalance?.toFixed(2, { groupSeparator: ',' })}</TYPE.white>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White color="white">{uniBalance?.toFixed(2, { groupSeparator: ',' })}</ThemedText.White>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<ThemedText.White color="white">
|
||||
<Trans>Unclaimed:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">
|
||||
</ThemedText.White>
|
||||
<ThemedText.White color="white">
|
||||
{uniToClaim?.toFixed(4, { groupSeparator: ',' })}{' '}
|
||||
{uniToClaim && uniToClaim.greaterThan('0') && (
|
||||
<StyledInternalLink onClick={() => setShowUniBalanceModal(false)} to="/uni">
|
||||
<Trans>(claim)</Trans>
|
||||
</StyledInternalLink>
|
||||
)}
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</CardSection>
|
||||
@ -114,22 +114,22 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
|
||||
<CardSection gap="sm">
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<ThemedText.White color="white">
|
||||
<Trans>UNI price:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">${uniPrice?.toFixed(2) ?? '-'}</TYPE.white>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White color="white">${uniPrice?.toFixed(2) ?? '-'}</ThemedText.White>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<ThemedText.White color="white">
|
||||
<Trans>UNI in circulation:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">{circulation?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White color="white">{circulation?.toFixed(0, { groupSeparator: ',' })}</ThemedText.White>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<ThemedText.White color="white">
|
||||
<Trans>Total Supply</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</ThemedText.White>
|
||||
</RowBetween>
|
||||
{uni && uni.chainId === 1 ? (
|
||||
<ExternalLink href={`${infoLink}/token/${uni.address}`}>
|
||||
|
@ -15,7 +15,7 @@ import styled from 'styled-components/macro'
|
||||
|
||||
import { ReactComponent as Logo } from '../../assets/svg/logo.svg'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import ClaimModal from '../claim/ClaimModal'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
import Menu from '../Menu'
|
||||
@ -309,7 +309,7 @@ export default function Header() {
|
||||
{availableClaim && !showClaimPopup && (
|
||||
<UNIWrapper onClick={toggleClaimModal}>
|
||||
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
|
||||
<TYPE.white padding="0 2px">
|
||||
<ThemedText.White padding="0 2px">
|
||||
{claimTxn && !claimTxn?.receipt ? (
|
||||
<Dots>
|
||||
<Trans>Claiming UNI</Trans>
|
||||
@ -317,7 +317,7 @@ export default function Header() {
|
||||
) : (
|
||||
<Trans>Claim UNI</Trans>
|
||||
)}
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
</UNIAmount>
|
||||
<CardNoise />
|
||||
</UNIWrapper>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import jazzicon from '@metamask/jazzicon'
|
||||
import useENSAvatar from 'hooks/useENSAvatar'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
@ -20,27 +20,29 @@ const StyledAvatar = styled.img`
|
||||
`
|
||||
|
||||
export default function Identicon() {
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const { account } = useActiveWeb3React()
|
||||
const { avatar } = useENSAvatar(account ?? undefined)
|
||||
const [fetchable, setFetchable] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
if ((!avatar || !fetchable) && account) {
|
||||
const icon = jazzicon(16, parseInt(account?.slice(2, 10), 16))
|
||||
const current = ref.current
|
||||
const icon = useMemo(() => account && jazzicon(16, parseInt(account.slice(2, 10), 16)), [account])
|
||||
const iconRef = useRef<HTMLDivElement>(null)
|
||||
useLayoutEffect(() => {
|
||||
const current = iconRef.current
|
||||
if (icon) {
|
||||
current?.appendChild(icon)
|
||||
return () => {
|
||||
current?.removeChild(icon)
|
||||
}
|
||||
}
|
||||
return
|
||||
}, [account, avatar, fetchable])
|
||||
}, [icon, iconRef])
|
||||
|
||||
return (
|
||||
<StyledIdenticon ref={ref}>
|
||||
{avatar && fetchable && (
|
||||
<StyledIdenticon>
|
||||
{avatar && fetchable ? (
|
||||
<StyledAvatar alt="avatar" src={avatar} onError={() => setFetchable(false)}></StyledAvatar>
|
||||
) : (
|
||||
<span ref={iconRef} />
|
||||
)}
|
||||
</StyledIdenticon>
|
||||
)
|
||||
|
@ -6,7 +6,7 @@ import { AutoColumn } from 'components/Column'
|
||||
import { ReactNode, useCallback, useEffect, useState } from 'react'
|
||||
import { Minus, Plus } from 'react-feather'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
|
||||
@ -57,13 +57,13 @@ const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
||||
`};
|
||||
`
|
||||
|
||||
const InputTitle = styled(TYPE.small)`
|
||||
const InputTitle = styled(ThemedText.Small)`
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
const ButtonLabel = styled(TYPE.white)<{ disabled: boolean }>`
|
||||
const ButtonLabel = styled(ThemedText.White)<{ disabled: boolean }>`
|
||||
color: ${({ theme, disabled }) => (disabled ? theme.text2 : theme.text1)} !important;
|
||||
`
|
||||
|
||||
|
@ -37,7 +37,7 @@ export const Area = ({
|
||||
.y0(yScale(0))(
|
||||
series.filter((d) => {
|
||||
const value = xScale(xValue(d))
|
||||
return value > 0 && value <= innerWidth
|
||||
return value > 0 && value <= window.innerWidth
|
||||
}) as Iterable<[number, number]>
|
||||
) ?? undefined
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import { batch } from 'react-redux'
|
||||
import { Bound } from 'state/mint/v3/actions'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { Chart } from './Chart'
|
||||
import { useDensityChartData } from './hooks'
|
||||
import { ZoomLevels } from './types'
|
||||
@ -58,9 +58,9 @@ function InfoBox({ message, icon }: { message?: ReactNode; icon: ReactNode }) {
|
||||
<ColumnCenter style={{ height: '100%', justifyContent: 'center' }}>
|
||||
{icon}
|
||||
{message && (
|
||||
<TYPE.mediumHeader padding={10} marginTop="20px" textAlign="center">
|
||||
<ThemedText.MediumHeader padding={10} marginTop="20px" textAlign="center">
|
||||
{message}
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
)}
|
||||
</ColumnCenter>
|
||||
)
|
||||
|
@ -5,7 +5,7 @@ import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import Circle from '../../assets/images/blue-loader.svg'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { CloseIcon, CustomLightSpinner, TYPE } from '../../theme'
|
||||
import { CloseIcon, CustomLightSpinner, ThemedText } from '../../theme'
|
||||
import { ExternalLink } from '../../theme/components'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { AutoColumn, ColumnCenter } from '../Column'
|
||||
@ -32,9 +32,9 @@ export function LoadingView({ children, onDismiss }: { children: any; onDismiss:
|
||||
</ConfirmedIcon>
|
||||
<AutoColumn gap="100px" justify={'center'}>
|
||||
{children}
|
||||
<TYPE.subHeader>
|
||||
<ThemedText.SubHeader>
|
||||
<Trans>Confirm this transaction in your wallet</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</AutoColumn>
|
||||
</ConfirmOrLoadingWrapper>
|
||||
)
|
||||
@ -68,9 +68,9 @@ export function SubmittedView({
|
||||
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
|
||||
style={{ marginLeft: '4px' }}
|
||||
>
|
||||
<TYPE.subHeader>
|
||||
<ThemedText.SubHeader>
|
||||
<Trans>View transaction on Explorer</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</ExternalLink>
|
||||
)}
|
||||
</AutoColumn>
|
||||
|
@ -10,7 +10,7 @@ import { useAppDispatch } from 'state/hooks'
|
||||
import { resetMintState } from 'state/mint/actions'
|
||||
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import Row, { RowBetween } from '../Row'
|
||||
import SettingsTab from '../Settings'
|
||||
@ -136,7 +136,7 @@ export function AddRemoveTabs({
|
||||
>
|
||||
<StyledArrowLeft stroke={theme.text2} />
|
||||
</StyledHistoryLink>
|
||||
<TYPE.mediumHeader
|
||||
<ThemedText.MediumHeader
|
||||
fontWeight={500}
|
||||
fontSize={20}
|
||||
style={{ flex: '1', margin: 'auto', textAlign: children ? 'start' : 'center' }}
|
||||
@ -148,7 +148,7 @@ export function AddRemoveTabs({
|
||||
) : (
|
||||
<Trans>Remove Liquidity</Trans>
|
||||
)}
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
<Box style={{ marginRight: '.5rem' }}>{children}</Box>
|
||||
<SettingsTab placeholderSlippage={defaultSlippage} />
|
||||
</RowBetween>
|
||||
|
@ -6,7 +6,6 @@ import ReactGA from 'react-ga'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
|
||||
import tokenLogo from '../../assets/images/token-logo.png'
|
||||
import { ButtonPrimary } from '../../components/Button'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import {
|
||||
useModalOpen,
|
||||
@ -16,7 +15,8 @@ import {
|
||||
} from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { CardBGImage, CardNoise } from '../earn/styled'
|
||||
|
||||
@ -98,10 +98,10 @@ export default function ClaimPopup() {
|
||||
<StyledClose stroke="white" onClick={toggleShowClaimPopup} />
|
||||
<AutoColumn style={{ padding: '2rem 0', zIndex: 10 }} justify="center">
|
||||
<UniToken width="48px" src={tokenLogo} />{' '}
|
||||
<TYPE.white style={{ marginTop: '1rem' }} fontSize={36} fontWeight={600}>
|
||||
<ThemedText.White style={{ marginTop: '1rem' }} fontSize={36} fontWeight={600}>
|
||||
{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI
|
||||
</TYPE.white>
|
||||
<TYPE.white style={{ paddingTop: '1.25rem', textAlign: 'center' }} fontWeight={600} color="white">
|
||||
</ThemedText.White>
|
||||
<ThemedText.White style={{ paddingTop: '1.25rem', textAlign: 'center' }} fontWeight={600} color="white">
|
||||
<span role="img" aria-label="party">
|
||||
🎉
|
||||
</span>{' '}
|
||||
@ -109,12 +109,12 @@ export default function ClaimPopup() {
|
||||
<span role="img" aria-label="party">
|
||||
🎉
|
||||
</span>
|
||||
</TYPE.white>
|
||||
<TYPE.subHeader style={{ paddingTop: '0.5rem', textAlign: 'center' }} color="white">
|
||||
</ThemedText.White>
|
||||
<ThemedText.SubHeader style={{ paddingTop: '0.5rem', textAlign: 'center' }} color="white">
|
||||
<Trans>
|
||||
Thanks for being part of the Uniswap community <Heart size={12} />
|
||||
</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</AutoColumn>
|
||||
<AutoColumn style={{ zIndex: 10 }} justify="center">
|
||||
<ButtonPrimary padding="8px" $borderRadius="8px" width={'fit-content'} onClick={handleToggleSelfClaimModal}>
|
||||
|
@ -4,8 +4,8 @@ import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useTransaction } from '../../state/transactions/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ExternalLink } from '../../theme/components'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { TransactionSummary } from '../AccountDetails/TransactionSummary'
|
||||
import { AutoColumn } from '../Column'
|
||||
@ -30,9 +30,9 @@ export default function TransactionPopup({ hash }: { hash: string }) {
|
||||
{success ? <CheckCircle color={theme.green1} size={24} /> : <AlertCircle color={theme.red1} size={24} />}
|
||||
</div>
|
||||
<AutoColumn gap="8px">
|
||||
<TYPE.body fontWeight={500}>
|
||||
<ThemedText.Body fontWeight={500}>
|
||||
<TransactionSummary info={tx.info} />
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
{chainId && (
|
||||
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
|
||||
View on Explorer
|
||||
|
@ -14,7 +14,7 @@ import { useColor } from '../../hooks/useColor'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import { currencyId } from '../../utils/currencyId'
|
||||
import { unwrappedToken } from '../../utils/unwrappedToken'
|
||||
import { ButtonEmpty, ButtonPrimary, ButtonSecondary } from '../Button'
|
||||
@ -142,7 +142,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
|
||||
</GreyCard>
|
||||
) : (
|
||||
<LightCard>
|
||||
<TYPE.subHeader style={{ textAlign: 'center' }}>
|
||||
<ThemedText.SubHeader style={{ textAlign: 'center' }}>
|
||||
<span role="img" aria-label="wizard-icon">
|
||||
⭐️
|
||||
</span>{' '}
|
||||
@ -150,7 +150,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
|
||||
By adding liquidity you'll earn 0.3% of all trades on this pair proportional to your share of the
|
||||
pool. Fees are added to the pool, accrue in real time and can be claimed by withdrawing your liquidity.
|
||||
</Trans>{' '}
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</LightCard>
|
||||
)}
|
||||
</>
|
||||
|
@ -13,7 +13,7 @@ import JSBI from 'jsbi'
|
||||
import { ReactNode, useCallback, useContext, useState } from 'react'
|
||||
import { Bound } from 'state/mint/v3/actions'
|
||||
import { ThemeContext } from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
import { formatTickPrice } from 'utils/formatTickPrice'
|
||||
import { unwrappedToken } from 'utils/unwrappedToken'
|
||||
|
||||
@ -70,9 +70,9 @@ export const PositionPreview = ({
|
||||
size={24}
|
||||
margin={true}
|
||||
/>
|
||||
<TYPE.label ml="10px" fontSize="24px">
|
||||
<ThemedText.Label ml="10px" fontSize="24px">
|
||||
{currency0?.symbol} / {currency1?.symbol}
|
||||
</TYPE.label>
|
||||
</ThemedText.Label>
|
||||
</RowFixed>
|
||||
<RangeBadge removed={removed} inRange={inRange} />
|
||||
</RowBetween>
|
||||
@ -82,36 +82,36 @@ export const PositionPreview = ({
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<CurrencyLogo currency={currency0} />
|
||||
<TYPE.label ml="8px">{currency0?.symbol}</TYPE.label>
|
||||
<ThemedText.Label ml="8px">{currency0?.symbol}</ThemedText.Label>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<TYPE.label mr="8px">{position.amount0.toSignificant(4)}</TYPE.label>
|
||||
<ThemedText.Label mr="8px">{position.amount0.toSignificant(4)}</ThemedText.Label>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<CurrencyLogo currency={currency1} />
|
||||
<TYPE.label ml="8px">{currency1?.symbol}</TYPE.label>
|
||||
<ThemedText.Label ml="8px">{currency1?.symbol}</ThemedText.Label>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<TYPE.label mr="8px">{position.amount1.toSignificant(4)}</TYPE.label>
|
||||
<ThemedText.Label mr="8px">{position.amount1.toSignificant(4)}</ThemedText.Label>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<Break />
|
||||
<RowBetween>
|
||||
<TYPE.label>
|
||||
<ThemedText.Label>
|
||||
<Trans>Fee Tier</Trans>
|
||||
</TYPE.label>
|
||||
<TYPE.label>
|
||||
</ThemedText.Label>
|
||||
<ThemedText.Label>
|
||||
<Trans>{position?.pool?.fee / 10000}%</Trans>
|
||||
</TYPE.label>
|
||||
</ThemedText.Label>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
{title ? <TYPE.main>{title}</TYPE.main> : <div />}
|
||||
{title ? <ThemedText.Main>{title}</ThemedText.Main> : <div />}
|
||||
<RateToggle
|
||||
currencyA={sorted ? currency0 : currency1}
|
||||
currencyB={sorted ? currency1 : currency0}
|
||||
@ -122,57 +122,57 @@ export const PositionPreview = ({
|
||||
<RowBetween>
|
||||
<LightCard width="48%" padding="8px">
|
||||
<AutoColumn gap="4px" justify="center">
|
||||
<TYPE.main fontSize="12px">
|
||||
<ThemedText.Main fontSize="12px">
|
||||
<Trans>Min Price</Trans>
|
||||
</TYPE.main>
|
||||
<TYPE.mediumHeader textAlign="center">{`${formatTickPrice(
|
||||
</ThemedText.Main>
|
||||
<ThemedText.MediumHeader textAlign="center">{`${formatTickPrice(
|
||||
priceLower,
|
||||
ticksAtLimit,
|
||||
Bound.LOWER
|
||||
)}`}</TYPE.mediumHeader>
|
||||
<TYPE.main textAlign="center" fontSize="12px">
|
||||
)}`}</ThemedText.MediumHeader>
|
||||
<ThemedText.Main textAlign="center" fontSize="12px">
|
||||
<Trans>
|
||||
{quoteCurrency.symbol} per {baseCurrency.symbol}
|
||||
</Trans>
|
||||
</TYPE.main>
|
||||
<TYPE.small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
|
||||
</ThemedText.Main>
|
||||
<ThemedText.Small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
|
||||
<Trans>Your position will be 100% composed of {baseCurrency?.symbol} at this price</Trans>
|
||||
</TYPE.small>
|
||||
</ThemedText.Small>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
|
||||
<LightCard width="48%" padding="8px">
|
||||
<AutoColumn gap="4px" justify="center">
|
||||
<TYPE.main fontSize="12px">
|
||||
<ThemedText.Main fontSize="12px">
|
||||
<Trans>Max Price</Trans>
|
||||
</TYPE.main>
|
||||
<TYPE.mediumHeader textAlign="center">{`${formatTickPrice(
|
||||
</ThemedText.Main>
|
||||
<ThemedText.MediumHeader textAlign="center">{`${formatTickPrice(
|
||||
priceUpper,
|
||||
ticksAtLimit,
|
||||
Bound.UPPER
|
||||
)}`}</TYPE.mediumHeader>
|
||||
<TYPE.main textAlign="center" fontSize="12px">
|
||||
)}`}</ThemedText.MediumHeader>
|
||||
<ThemedText.Main textAlign="center" fontSize="12px">
|
||||
<Trans>
|
||||
{quoteCurrency.symbol} per {baseCurrency.symbol}
|
||||
</Trans>
|
||||
</TYPE.main>
|
||||
<TYPE.small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
|
||||
</ThemedText.Main>
|
||||
<ThemedText.Small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
|
||||
<Trans>Your position will be 100% composed of {quoteCurrency?.symbol} at this price</Trans>
|
||||
</TYPE.small>
|
||||
</ThemedText.Small>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
</RowBetween>
|
||||
<LightCard padding="12px ">
|
||||
<AutoColumn gap="4px" justify="center">
|
||||
<TYPE.main fontSize="12px">
|
||||
<ThemedText.Main fontSize="12px">
|
||||
<Trans>Current price</Trans>
|
||||
</TYPE.main>
|
||||
<TYPE.mediumHeader>{`${price.toSignificant(5)} `}</TYPE.mediumHeader>
|
||||
<TYPE.main textAlign="center" fontSize="12px">
|
||||
</ThemedText.Main>
|
||||
<ThemedText.MediumHeader>{`${price.toSignificant(5)} `}</ThemedText.MediumHeader>
|
||||
<ThemedText.Main textAlign="center" fontSize="12px">
|
||||
<Trans>
|
||||
{quoteCurrency.symbol} per {baseCurrency.symbol}
|
||||
</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
</AutoColumn>
|
||||
|
@ -5,7 +5,7 @@ import { useEffect, useRef } from 'react'
|
||||
import { ArrowDown, Info, X } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, TYPE } from 'theme'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
import { isMobile } from 'utils/userAgent'
|
||||
|
||||
import { useModalOpen, useTogglePrivacyPolicy } from '../../state/application/hooks'
|
||||
@ -91,9 +91,9 @@ export function PrivacyPolicyModal() {
|
||||
<Modal isOpen={open} onDismiss={() => toggle()}>
|
||||
<AutoColumn gap="12px" ref={node as any}>
|
||||
<RowBetween padding="1rem 1rem 0.5rem 1rem">
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Legal & Privacy</Trans>
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
<HoverText onClick={() => toggle()}>
|
||||
<X size={24} />
|
||||
</HoverText>
|
||||
@ -122,9 +122,9 @@ export function PrivacyPolicy() {
|
||||
<RowBetween>
|
||||
<AutoRow gap="4px">
|
||||
<Info size={20} />
|
||||
<TYPE.main fontSize={14} color={'primaryText1'}>
|
||||
<ThemedText.Main fontSize={14} color={'primaryText1'}>
|
||||
<Trans>Uniswap Labs' Terms of Service</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</AutoRow>
|
||||
<StyledLinkOut size={20} />
|
||||
</RowBetween>
|
||||
@ -135,29 +135,29 @@ export function PrivacyPolicy() {
|
||||
<RowBetween>
|
||||
<AutoRow gap="4px">
|
||||
<Info size={20} />
|
||||
<TYPE.main fontSize={14} color={'primaryText1'}>
|
||||
<ThemedText.Main fontSize={14} color={'primaryText1'}>
|
||||
<Trans>Protocol Disclaimer</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</AutoRow>
|
||||
<StyledLinkOut size={20} />
|
||||
</RowBetween>
|
||||
</ExternalLink>
|
||||
</StyledExternalCard>
|
||||
</AutoColumn>
|
||||
<TYPE.main fontSize={14}>
|
||||
<ThemedText.Main fontSize={14}>
|
||||
<Trans>This app uses the following third-party APIs:</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
<AutoColumn gap="12px">
|
||||
{EXTERNAL_APIS.map(({ name, description }, i) => (
|
||||
<DarkGreyCard key={i}>
|
||||
<AutoColumn gap="8px">
|
||||
<AutoRow gap="4px">
|
||||
<Info size={18} />
|
||||
<TYPE.main fontSize={14} color={'text1'}>
|
||||
<ThemedText.Main fontSize={14} color={'text1'}>
|
||||
{name}
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</AutoRow>
|
||||
<TYPE.main fontSize={14}>{description}</TYPE.main>
|
||||
<ThemedText.Main fontSize={14}>{description}</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
</DarkGreyCard>
|
||||
))}
|
||||
|
@ -2,7 +2,7 @@ import { useContext } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
|
||||
const Wrapper = styled(AutoColumn)`
|
||||
@ -65,7 +65,7 @@ export default function ProgressCircles({ steps, disabled = false, ...rest }: Pr
|
||||
<Circle confirmed={step} disabled={disabled || (!steps[i - 1] && i !== 0)}>
|
||||
{step ? '✓' : i + 1 + '.'}
|
||||
</Circle>
|
||||
<TYPE.main color={theme.text4}>|</TYPE.main>
|
||||
<ThemedText.Main color={theme.text4}>|</ThemedText.Main>
|
||||
</CircleRow>
|
||||
)
|
||||
})}
|
||||
|
@ -4,7 +4,7 @@ import { AutoRow } from 'components/Row'
|
||||
import React from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
const Button = styled(ButtonOutlined).attrs(() => ({
|
||||
padding: '8px',
|
||||
@ -26,9 +26,9 @@ export default function PresetsButtons({ setFullRange }: { setFullRange: () => v
|
||||
})
|
||||
}}
|
||||
>
|
||||
<TYPE.body fontSize={12}>
|
||||
<ThemedText.Body fontSize={12}>
|
||||
<Trans>Full Range</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</Button>
|
||||
</AutoRow>
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ import Row, { AutoRow } from 'components/Row'
|
||||
import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList'
|
||||
import { Box } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { ReactComponent as DotLine } from '../../assets/svg/dot_line.svg'
|
||||
|
||||
@ -93,9 +93,9 @@ function Route({ percent, path }: { percent: RoutingDiagramEntry['percent']; pat
|
||||
<DotColor />
|
||||
</DottedLine>
|
||||
<OpaqueBadge>
|
||||
<TYPE.small fontSize={12} style={{ wordBreak: 'normal' }}>
|
||||
<ThemedText.Small fontSize={12} style={{ wordBreak: 'normal' }}>
|
||||
{percent.toSignificant(2)}%
|
||||
</TYPE.small>
|
||||
</ThemedText.Small>
|
||||
</OpaqueBadge>
|
||||
|
||||
<AutoRow gap="1px" width="100%" style={{ justifyContent: 'space-evenly', zIndex: 2 }}>
|
||||
@ -116,7 +116,7 @@ function Pool({ currency0, currency1, feeAmount }: { currency0: Currency; curren
|
||||
<Box margin="0 5px 0 10px">
|
||||
<DoubleCurrencyLogo currency0={tokenInfo1} currency1={tokenInfo0} size={20} />
|
||||
</Box>
|
||||
<TYPE.small fontSize={12}>{feeAmount / 10000}%</TYPE.small>
|
||||
<ThemedText.Small fontSize={12}>{feeAmount / 10000}%</ThemedText.Small>
|
||||
</PoolBadge>
|
||||
)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { Token } from '@uniswap/sdk-core'
|
||||
import { ButtonPrimary } from 'components/Button'
|
||||
import { AlertCircle, ArrowLeft } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CloseIcon, TYPE } from 'theme'
|
||||
import { CloseIcon, ThemedText } from 'theme'
|
||||
|
||||
import TokenImportCard from './TokenImportCard'
|
||||
|
||||
@ -22,7 +22,7 @@ const Button = styled(ButtonPrimary)`
|
||||
const Content = styled.div`
|
||||
padding: 1em;
|
||||
`
|
||||
const Copy = styled(TYPE.body)`
|
||||
const Copy = styled(ThemedText.Body)`
|
||||
text-align: center;
|
||||
margin: 0 2em 1em !important;
|
||||
font-weight: 400;
|
||||
@ -51,9 +51,9 @@ const BlockedToken = ({ onBack, onDismiss, blockedTokens }: BlockedTokenProps) =
|
||||
<Wrapper>
|
||||
<Header>
|
||||
{onBack ? <ArrowLeft style={{ cursor: 'pointer' }} onClick={onBack} /> : <div />}
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Token not supported</Trans>
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
{onDismiss ? <CloseIcon onClick={onDismiss} /> : <div />}
|
||||
</Header>
|
||||
<Icon />
|
||||
|
@ -14,7 +14,7 @@ import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useCombinedActiveList } from '../../state/lists/hooks'
|
||||
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
|
||||
import { useCurrencyBalance } from '../../state/wallet/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { isTokenOnList } from '../../utils'
|
||||
import Column from '../Column'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
@ -135,13 +135,13 @@ function CurrencyRow({
|
||||
<Text title={currency.name} fontWeight={500}>
|
||||
{currency.symbol}
|
||||
</Text>
|
||||
<TYPE.darkGray ml="0px" fontSize={'12px'} fontWeight={300}>
|
||||
<ThemedText.DarkGray ml="0px" fontSize={'12px'} fontWeight={300}>
|
||||
{!currency.isNative && !isOnSelectedList && customAdded ? (
|
||||
<Trans>{currency.name} • Added by user</Trans>
|
||||
) : (
|
||||
currency.name
|
||||
)}
|
||||
</TYPE.darkGray>
|
||||
</ThemedText.DarkGray>
|
||||
</Column>
|
||||
<TokenTags currency={currency} />
|
||||
{showCurrencyAmount && (
|
||||
@ -167,9 +167,9 @@ function BreakLineComponent({ style }: { style: CSSProperties }) {
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TokenListLogoWrapper src={TokenListLogo} />
|
||||
<TYPE.main ml="6px" fontSize="12px" color={theme.text1}>
|
||||
<ThemedText.Main ml="6px" fontSize="12px" color={theme.text1}>
|
||||
<Trans>Expanded results from inactive Token Lists</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</RowFixed>
|
||||
<QuestionHelper
|
||||
text={
|
||||
|
@ -16,7 +16,7 @@ import styled from 'styled-components/macro'
|
||||
import { ExtendedEther } from '../../constants/tokens'
|
||||
import { useAllTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ButtonText, CloseIcon, IconWrapper, TYPE } from '../../theme'
|
||||
import { ButtonText, CloseIcon, IconWrapper, ThemedText } from '../../theme'
|
||||
import { isAddress } from '../../utils'
|
||||
import Column from '../Column'
|
||||
import Row, { RowBetween, RowFixed } from '../Row'
|
||||
@ -224,9 +224,9 @@ export function CurrencySearch({
|
||||
</div>
|
||||
) : (
|
||||
<Column style={{ padding: '20px', height: '100%' }}>
|
||||
<TYPE.main color={theme.text3} textAlign="center" mb="20px">
|
||||
<ThemedText.Main color={theme.text3} textAlign="center" mb="20px">
|
||||
<Trans>No results found.</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</Column>
|
||||
)}
|
||||
<Footer>
|
||||
@ -236,9 +236,9 @@ export function CurrencySearch({
|
||||
<IconWrapper size="16px" marginRight="6px" stroke={theme.primaryText1}>
|
||||
<Edit />
|
||||
</IconWrapper>
|
||||
<TYPE.main color={theme.primaryText1}>
|
||||
<ThemedText.Main color={theme.primaryText1}>
|
||||
<Trans>Manage Token Lists</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</RowFixed>
|
||||
</ButtonText>
|
||||
</Row>
|
||||
|
@ -16,9 +16,9 @@ import { useAppDispatch } from 'state/hooks'
|
||||
import { enableList, removeList } from 'state/lists/actions'
|
||||
import { useAllLists } from 'state/lists/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CloseIcon, TYPE } from 'theme'
|
||||
import { CloseIcon, ThemedText } from 'theme'
|
||||
|
||||
import { ExternalLink } from '../../theme/components'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { CurrencyModalView } from './CurrencySearchModal'
|
||||
import { Checkbox, PaddedColumn, TextDot } from './styleds'
|
||||
|
||||
@ -81,9 +81,9 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
|
||||
<PaddedColumn gap="14px" style={{ width: '100%', flex: '1 1' }}>
|
||||
<RowBetween>
|
||||
<ArrowLeft style={{ cursor: 'pointer' }} onClick={() => setModalView(CurrencyModalView.manage)} />
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Import List</Trans>
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
<CloseIcon onClick={onDismiss} />
|
||||
</RowBetween>
|
||||
</PaddedColumn>
|
||||
@ -96,18 +96,18 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
|
||||
{list.logoURI && <ListLogo logoURI={list.logoURI} size="40px" />}
|
||||
<AutoColumn gap="sm" style={{ marginLeft: '20px' }}>
|
||||
<RowFixed>
|
||||
<TYPE.body fontWeight={600} mr="6px">
|
||||
<ThemedText.Body fontWeight={600} mr="6px">
|
||||
{list.name}
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
<TextDot />
|
||||
<TYPE.main fontSize={'16px'} ml="6px">
|
||||
<ThemedText.Main fontSize={'16px'} ml="6px">
|
||||
<Trans>{list.tokens.length} tokens</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</RowFixed>
|
||||
<ExternalLink href={`https://tokenlists.org/token-list?url=${listURL}`}>
|
||||
<TYPE.main fontSize={'12px'} color={theme.blue1}>
|
||||
<ThemedText.Main fontSize={'12px'} color={theme.blue1}>
|
||||
{listURL}
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</ExternalLink>
|
||||
</AutoColumn>
|
||||
</RowFixed>
|
||||
@ -116,22 +116,22 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
|
||||
<Card style={{ backgroundColor: transparentize(0.8, theme.red1) }}>
|
||||
<AutoColumn justify="center" style={{ textAlign: 'center', gap: '16px', marginBottom: '12px' }}>
|
||||
<AlertTriangle stroke={theme.red1} size={32} />
|
||||
<TYPE.body fontWeight={500} fontSize={20} color={theme.red1}>
|
||||
<ThemedText.Body fontWeight={500} fontSize={20} color={theme.red1}>
|
||||
<Trans>Import at your own risk</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
|
||||
<AutoColumn style={{ textAlign: 'center', gap: '16px', marginBottom: '12px' }}>
|
||||
<TYPE.body fontWeight={500} color={theme.red1}>
|
||||
<ThemedText.Body fontWeight={500} color={theme.red1}>
|
||||
<Trans>
|
||||
By adding this list you are implicitly trusting that the data is correct. Anyone can create a list,
|
||||
including creating fake versions of existing lists and lists that claim to represent projects that do
|
||||
not have one.
|
||||
</Trans>
|
||||
</TYPE.body>
|
||||
<TYPE.body fontWeight={600} color={theme.red1}>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.Body fontWeight={600} color={theme.red1}>
|
||||
<Trans>If you purchase a token from this list, you may not be able to sell it back.</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
<AutoRow justify="center" style={{ cursor: 'pointer' }} onClick={() => setConfirmed(!confirmed)}>
|
||||
<Checkbox
|
||||
@ -140,9 +140,9 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
|
||||
checked={confirmed}
|
||||
onChange={() => setConfirmed(!confirmed)}
|
||||
/>
|
||||
<TYPE.body ml="10px" fontSize="16px" color={theme.red1} fontWeight={500}>
|
||||
<ThemedText.Body ml="10px" fontSize="16px" color={theme.red1} fontWeight={500}>
|
||||
<Trans>I understand</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoRow>
|
||||
</Card>
|
||||
|
||||
@ -156,9 +156,9 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
|
||||
<Trans>Import</Trans>
|
||||
</ButtonPrimary>
|
||||
{addError ? (
|
||||
<TYPE.error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
|
||||
<ThemedText.Error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
|
||||
{addError}
|
||||
</TYPE.error>
|
||||
</ThemedText.Error>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
{/* </Card> */}
|
||||
|
@ -10,7 +10,7 @@ import useTheme from 'hooks/useTheme'
|
||||
import { CSSProperties } from 'react'
|
||||
import { CheckCircle } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
|
||||
|
||||
@ -67,16 +67,16 @@ export default function ImportRow({
|
||||
<CurrencyLogo currency={token} size={'24px'} style={{ opacity: dim ? '0.6' : '1' }} />
|
||||
<AutoColumn gap="4px" style={{ opacity: dim ? '0.6' : '1' }}>
|
||||
<AutoRow>
|
||||
<TYPE.body fontWeight={500}>{token.symbol}</TYPE.body>
|
||||
<TYPE.darkGray ml="8px" fontWeight={300}>
|
||||
<ThemedText.Body fontWeight={500}>{token.symbol}</ThemedText.Body>
|
||||
<ThemedText.DarkGray ml="8px" fontWeight={300}>
|
||||
<NameOverflow title={token.name}>{token.name}</NameOverflow>
|
||||
</TYPE.darkGray>
|
||||
</ThemedText.DarkGray>
|
||||
</AutoRow>
|
||||
{list && list.logoURI && (
|
||||
<RowFixed>
|
||||
<TYPE.small mr="4px" color={theme.text3}>
|
||||
<ThemedText.Small mr="4px" color={theme.text3}>
|
||||
<Trans>via {list.name} </Trans>
|
||||
</TYPE.small>
|
||||
</ThemedText.Small>
|
||||
<ListLogo logoURI={list.logoURI} size="12px" />
|
||||
</RowFixed>
|
||||
)}
|
||||
@ -97,9 +97,9 @@ export default function ImportRow({
|
||||
) : (
|
||||
<RowFixed style={{ minWidth: 'fit-content' }}>
|
||||
<CheckIcon />
|
||||
<TYPE.main color={theme.green1}>
|
||||
<ThemedText.Main color={theme.green1}>
|
||||
<Trans>Active</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</RowFixed>
|
||||
)}
|
||||
</TokenSection>
|
||||
|
@ -10,7 +10,7 @@ import useTheme from 'hooks/useTheme'
|
||||
import { AlertCircle, ArrowLeft } from 'react-feather'
|
||||
import { useAddUserToken } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CloseIcon, TYPE } from 'theme'
|
||||
import { CloseIcon, ThemedText } from 'theme'
|
||||
|
||||
import BlockedToken from './BlockedToken'
|
||||
import { PaddedColumn } from './styleds'
|
||||
@ -47,9 +47,9 @@ export function ImportToken(props: ImportProps) {
|
||||
<PaddedColumn gap="14px" style={{ width: '100%', flex: '1 1' }}>
|
||||
<RowBetween>
|
||||
{onBack ? <ArrowLeft style={{ cursor: 'pointer' }} onClick={onBack} /> : <div />}
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Plural value={tokens.length} one="Import token" other="Import tokens" />
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
{onDismiss ? <CloseIcon onClick={onDismiss} /> : <div />}
|
||||
</RowBetween>
|
||||
</PaddedColumn>
|
||||
@ -57,12 +57,12 @@ export function ImportToken(props: ImportProps) {
|
||||
<AutoColumn gap="md" style={{ marginBottom: '32px', padding: '1rem' }}>
|
||||
<AutoColumn justify="center" style={{ textAlign: 'center', gap: '16px', padding: '1rem' }}>
|
||||
<AlertCircle size={48} stroke={theme.text2} strokeWidth={1} />
|
||||
<TYPE.body fontWeight={400} fontSize={16}>
|
||||
<ThemedText.Body fontWeight={400} fontSize={16}>
|
||||
<Trans>
|
||||
This token doesn't appear on the active token list(s). Make sure this is the token that you want to
|
||||
trade.
|
||||
</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
{tokens.map((token) => (
|
||||
<TokenImportCard token={token} list={list} key={'import' + token.address} />
|
||||
|
@ -18,7 +18,7 @@ import useTheme from '../../hooks/useTheme'
|
||||
import useToggle from '../../hooks/useToggle'
|
||||
import { acceptListUpdate, disableList, enableList, removeList } from '../../state/lists/actions'
|
||||
import { useActiveListUrls, useAllLists, useIsListActive } from '../../state/lists/hooks'
|
||||
import { ExternalLink, IconWrapper, LinkStyledButton, TYPE } from '../../theme'
|
||||
import { ExternalLink, IconWrapper, LinkStyledButton, ThemedText } from '../../theme'
|
||||
import listVersionLabel from '../../utils/listVersionLabel'
|
||||
import { parseENSAddress } from '../../utils/parseENSAddress'
|
||||
import uriToHttp from '../../utils/uriToHttp'
|
||||
@ -75,7 +75,7 @@ const StyledTitleText = styled.div<{ active: boolean }>`
|
||||
color: ${({ theme, active }) => (active ? theme.white : theme.text2)};
|
||||
`
|
||||
|
||||
const StyledListUrlText = styled(TYPE.main)<{ active: boolean }>`
|
||||
const StyledListUrlText = styled(ThemedText.Main)<{ active: boolean }>`
|
||||
font-size: 12px;
|
||||
color: ${({ theme, active }) => (active ? theme.white : theme.text2)};
|
||||
`
|
||||
@ -361,9 +361,9 @@ export function ManageLists({
|
||||
/>
|
||||
</Row>
|
||||
{addError ? (
|
||||
<TYPE.error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
|
||||
<ThemedText.Error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
|
||||
{addError}
|
||||
</TYPE.error>
|
||||
</ThemedText.Error>
|
||||
) : null}
|
||||
</PaddedColumn>
|
||||
{tempList && (
|
||||
@ -373,10 +373,10 @@ export function ManageLists({
|
||||
<RowFixed>
|
||||
{tempList.logoURI && <ListLogo logoURI={tempList.logoURI} size="40px" />}
|
||||
<AutoColumn gap="4px" style={{ marginLeft: '20px' }}>
|
||||
<TYPE.body fontWeight={600}>{tempList.name}</TYPE.body>
|
||||
<TYPE.main fontSize={'12px'}>
|
||||
<ThemedText.Body fontWeight={600}>{tempList.name}</ThemedText.Body>
|
||||
<ThemedText.Main fontSize={'12px'}>
|
||||
<Trans>{tempList.tokens.length} tokens</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
</RowFixed>
|
||||
{isImported ? (
|
||||
@ -384,9 +384,9 @@ export function ManageLists({
|
||||
<IconWrapper stroke={theme.text2} size="16px" marginRight={'10px'}>
|
||||
<CheckCircle />
|
||||
</IconWrapper>
|
||||
<TYPE.body color={theme.text2}>
|
||||
<ThemedText.Body color={theme.text2}>
|
||||
<Trans>Loaded</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</RowFixed>
|
||||
) : (
|
||||
<ButtonPrimary
|
||||
|
@ -9,7 +9,7 @@ import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { RefObject, useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { useRemoveUserAddedToken, useUserAddedTokens } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ButtonText, ExternalLink, ExternalLinkIcon, TrashIcon, TYPE } from 'theme'
|
||||
import { ButtonText, ExternalLink, ExternalLinkIcon, ThemedText, TrashIcon } from 'theme'
|
||||
import { isAddress } from 'utils'
|
||||
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
@ -81,9 +81,9 @@ export default function ManageTokens({
|
||||
<RowFixed>
|
||||
<CurrencyLogo currency={token} size={'20px'} />
|
||||
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
|
||||
<TYPE.main ml={'10px'} fontWeight={600}>
|
||||
<ThemedText.Main ml={'10px'} fontWeight={600}>
|
||||
{token.symbol}
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</ExternalLink>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
@ -111,9 +111,9 @@ export default function ManageTokens({
|
||||
/>
|
||||
</Row>
|
||||
{searchQuery !== '' && !isAddressSearch && (
|
||||
<TYPE.error error={true}>
|
||||
<ThemedText.Error error={true}>
|
||||
<Trans>Enter valid token address</Trans>
|
||||
</TYPE.error>
|
||||
</ThemedText.Error>
|
||||
)}
|
||||
{searchToken && (
|
||||
<Card backgroundColor={theme.bg2} padding="10px 0">
|
||||
@ -129,14 +129,14 @@ export default function ManageTokens({
|
||||
<Separator />
|
||||
<PaddedColumn gap="lg" style={{ overflow: 'auto', marginBottom: '10px' }}>
|
||||
<RowBetween>
|
||||
<TYPE.main fontWeight={600}>
|
||||
<ThemedText.Main fontWeight={600}>
|
||||
<Trans>{userAddedTokens?.length} Custom Tokens</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
{userAddedTokens.length > 0 && (
|
||||
<ButtonText onClick={handleRemoveAll}>
|
||||
<TYPE.blue>
|
||||
<ThemedText.Blue>
|
||||
<Trans>Clear all</Trans>
|
||||
</TYPE.blue>
|
||||
</ThemedText.Blue>
|
||||
</ButtonText>
|
||||
)}
|
||||
</RowBetween>
|
||||
@ -144,9 +144,9 @@ export default function ManageTokens({
|
||||
</PaddedColumn>
|
||||
</Column>
|
||||
<Footer>
|
||||
<TYPE.darkGray>
|
||||
<ThemedText.DarkGray>
|
||||
<Trans>Tip: Custom tokens are stored locally in your browser</Trans>
|
||||
</TYPE.darkGray>
|
||||
</ThemedText.DarkGray>
|
||||
</Footer>
|
||||
</Wrapper>
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { transparentize } from 'polished'
|
||||
import { AlertCircle } from 'react-feather'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { ExternalLink, TYPE } from 'theme'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
|
||||
|
||||
const WarningWrapper = styled(Card)<{ highWarning: boolean }>`
|
||||
@ -19,7 +19,7 @@ const WarningWrapper = styled(Card)<{ highWarning: boolean }>`
|
||||
width: fit-content;
|
||||
`
|
||||
|
||||
const AddressText = styled(TYPE.blue)`
|
||||
const AddressText = styled(ThemedText.Blue)`
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
|
||||
@ -39,12 +39,12 @@ const TokenImportCard = ({ list, token }: TokenImportCardProps) => {
|
||||
<AutoColumn gap="10px" justify="center">
|
||||
<CurrencyLogo currency={token} size={'32px'} />
|
||||
<AutoColumn gap="4px" justify="center">
|
||||
<TYPE.body ml="8px" mr="8px" fontWeight={500} fontSize={20}>
|
||||
<ThemedText.Body ml="8px" mr="8px" fontWeight={500} fontSize={20}>
|
||||
{token.symbol}
|
||||
</TYPE.body>
|
||||
<TYPE.darkGray fontWeight={400} fontSize={14}>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.DarkGray fontWeight={400} fontSize={14}>
|
||||
{token.name}
|
||||
</TYPE.darkGray>
|
||||
</ThemedText.DarkGray>
|
||||
</AutoColumn>
|
||||
{chainId && (
|
||||
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
|
||||
@ -54,17 +54,17 @@ const TokenImportCard = ({ list, token }: TokenImportCardProps) => {
|
||||
{list !== undefined ? (
|
||||
<RowFixed>
|
||||
{list.logoURI && <ListLogo logoURI={list.logoURI} size="16px" />}
|
||||
<TYPE.small ml="6px" fontSize={14} color={theme.text3}>
|
||||
<ThemedText.Small ml="6px" fontSize={14} color={theme.text3}>
|
||||
<Trans>via {list.name} token list</Trans>
|
||||
</TYPE.small>
|
||||
</ThemedText.Small>
|
||||
</RowFixed>
|
||||
) : (
|
||||
<WarningWrapper $borderRadius="4px" padding="4px" highWarning={true}>
|
||||
<RowFixed>
|
||||
<AlertCircle stroke={theme.red1} size="10px" />
|
||||
<TYPE.body color={theme.red1} ml="4px" fontSize="10px" fontWeight={500}>
|
||||
<ThemedText.Body color={theme.red1} ml="4px" fontSize="10px" fontWeight={500}>
|
||||
<Trans>Unknown Source</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</RowFixed>
|
||||
</WarningWrapper>
|
||||
)}
|
||||
|
@ -13,7 +13,7 @@ import { useOnClickOutside } from '../../hooks/useOnClickOutside'
|
||||
import { useModalOpen, useToggleSettingsMenu } from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { useClientSideRouter, useExpertModeManager } from '../../state/user/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ButtonError } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
import Modal from '../Modal'
|
||||
@ -203,9 +203,9 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
|
||||
{chainId === SupportedChainId.MAINNET && (
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<ThemedText.Black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<Trans>Auto Router</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
<QuestionHelper
|
||||
text={<Trans>Use the Uniswap Labs API to get better pricing through a more efficient route.</Trans>}
|
||||
/>
|
||||
@ -226,9 +226,9 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
|
||||
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<ThemedText.Black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<Trans>Expert Mode</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
<QuestionHelper
|
||||
text={
|
||||
<Trans>Allow high price impact trades and skip the confirm screen. Use at your own risk.</Trans>
|
||||
|
@ -5,9 +5,9 @@ import styled from 'styled-components/macro'
|
||||
|
||||
import { DEFAULT_LOCALE, LOCALE_LABEL, SupportedLocale } from '../../constants/locales'
|
||||
import { navigatorLocale, useActiveLocale } from '../../hooks/useActiveLocale'
|
||||
import { StyledInternalLink, TYPE } from '../../theme'
|
||||
import { StyledInternalLink, ThemedText } from '../../theme'
|
||||
|
||||
const Container = styled(TYPE.small)`
|
||||
const Container = styled(ThemedText.Small)`
|
||||
opacity: 0.6;
|
||||
:hover {
|
||||
opacity: 1;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
|
||||
const Wrapper = styled.button<{ isActive?: boolean; activeElement?: boolean }>`
|
||||
border-radius: 20px;
|
||||
@ -25,7 +25,7 @@ const ToggleElement = styled.span<{ isActive?: boolean; bgColor?: string }>`
|
||||
}
|
||||
`
|
||||
|
||||
const StatusText = styled(TYPE.main)<{ isActive?: boolean }>`
|
||||
const StatusText = styled(ThemedText.Main)<{ isActive?: boolean }>`
|
||||
margin: 0 10px;
|
||||
width: 24px;
|
||||
color: ${({ theme, isActive }) => (isActive ? theme.text1 : theme.text3)};
|
||||
|
@ -13,7 +13,7 @@ import Circle from '../../assets/images/blue-loader.svg'
|
||||
import MetaMaskLogo from '../../assets/images/metamask.png'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { CloseIcon, CustomLightSpinner } from '../../theme/components'
|
||||
import { CloseIcon, CustomLightSpinner } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { TransactionSummary } from '../AccountDetails/TransactionSummary'
|
||||
import { ButtonLight, ButtonPrimary } from '../Button'
|
||||
@ -284,11 +284,11 @@ function L2Content({
|
||||
</Text>
|
||||
</ExternalLink>
|
||||
) : (
|
||||
<div style={{ height: '17px' }}></div>
|
||||
<div style={{ height: '17px' }} />
|
||||
)}
|
||||
<Text color={theme.text3} style={{ margin: '20px 0 0 0' }} fontSize={'14px'}>
|
||||
{!secondsToConfirm ? (
|
||||
<div style={{ height: '24px' }}></div>
|
||||
<div style={{ height: '24px' }} />
|
||||
) : (
|
||||
<div>
|
||||
<Trans>Transaction completed in </Trans>
|
||||
|
@ -8,7 +8,7 @@ import { useContext, useState } from 'react'
|
||||
import { useSetUserSlippageTolerance, useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import QuestionHelper from '../QuestionHelper'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
@ -160,9 +160,9 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
|
||||
<AutoColumn gap="md">
|
||||
<AutoColumn gap="sm">
|
||||
<RowFixed>
|
||||
<TYPE.black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<ThemedText.Black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<Trans>Slippage tolerance</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
<QuestionHelper
|
||||
text={
|
||||
<Trans>Your transaction will revert if the price changes unfavorably by more than this percentage.</Trans>
|
||||
@ -229,9 +229,9 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
|
||||
{showCustomDeadlineRow && (
|
||||
<AutoColumn gap="sm">
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
|
||||
<ThemedText.Black fontSize={14} fontWeight={400} color={theme.text2}>
|
||||
<Trans>Transaction deadline</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
<QuestionHelper
|
||||
text={<Trans>Your transaction will revert if it is pending for more than this period of time.</Trans>}
|
||||
/>
|
||||
@ -255,9 +255,9 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
|
||||
color={deadlineError ? 'red' : ''}
|
||||
/>
|
||||
</OptionCustom>
|
||||
<TYPE.body style={{ paddingLeft: '8px' }} fontSize={14}>
|
||||
<ThemedText.Body style={{ paddingLeft: '8px' }} fontSize={14}>
|
||||
<Trans>minutes</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</RowFixed>
|
||||
</AutoColumn>
|
||||
)}
|
||||
|
@ -19,7 +19,7 @@ import { SUPPORTED_WALLETS } from '../../constants/wallet'
|
||||
import usePrevious from '../../hooks/usePrevious'
|
||||
import { useModalOpen, useWalletModalToggle } from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import { isMobile } from '../../utils/userAgent'
|
||||
import AccountDetails from '../AccountDetails'
|
||||
import Card, { LightCard } from '../Card'
|
||||
@ -344,9 +344,9 @@ export default function WalletModal({
|
||||
<ArrowLeft />
|
||||
</HoverText>
|
||||
<Row justify="center">
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Legal & Privacy</Trans>
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
</Row>
|
||||
</HeaderRow>
|
||||
<PrivacyPolicy />
|
||||
@ -392,23 +392,23 @@ export default function WalletModal({
|
||||
<AutoColumn gap="16px">
|
||||
<LightCard>
|
||||
<AutoRow style={{ flexWrap: 'nowrap' }}>
|
||||
<TYPE.black fontSize={14}>
|
||||
<ThemedText.Black fontSize={14}>
|
||||
<Trans>
|
||||
By connecting a wallet, you agree to Uniswap Labs’{' '}
|
||||
<ExternalLink href="https://uniswap.org/terms-of-service/">Terms of Service</ExternalLink> and
|
||||
acknowledge that you have read and understand the Uniswap{' '}
|
||||
<ExternalLink href="https://uniswap.org/disclaimer/">Protocol Disclaimer</ExternalLink>.
|
||||
</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
</AutoRow>
|
||||
</LightCard>
|
||||
<LinkCard padding=".5rem" $borderRadius=".75rem" onClick={() => setWalletView(WALLET_VIEWS.LEGAL)}>
|
||||
<RowBetween>
|
||||
<AutoRow gap="4px">
|
||||
<Info size={20} />
|
||||
<TYPE.white fontSize={14}>
|
||||
<ThemedText.White fontSize={14}>
|
||||
<Trans>How this app uses APIs</Trans>
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
</AutoRow>
|
||||
<ArrowRight size={16} />
|
||||
</RowBetween>
|
||||
|
@ -11,7 +11,7 @@ import useENS from '../../hooks/useENS'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useClaimCallback, useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
|
||||
import { useIsTransactionPending } from '../../state/transactions/hooks'
|
||||
import { CloseIcon, CustomLightSpinner, ExternalLink, TYPE, UniTokenAnimated } from '../../theme'
|
||||
import { CloseIcon, CustomLightSpinner, ExternalLink, ThemedText, UniTokenAnimated } from '../../theme'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import AddressInputPanel from '../AddressInputPanel'
|
||||
@ -105,29 +105,29 @@ export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boole
|
||||
<CardNoise />
|
||||
<CardSection gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white fontWeight={500}>
|
||||
<ThemedText.White fontWeight={500}>
|
||||
<Trans>Claim UNI Token</Trans>
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
<CloseIcon onClick={wrappedOnDismiss} style={{ zIndex: 99 }} stroke="white" />
|
||||
</RowBetween>
|
||||
<TYPE.white fontWeight={700} fontSize={36}>
|
||||
<ThemedText.White fontWeight={700} fontSize={36}>
|
||||
<Trans>{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI</Trans>
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
</CardSection>
|
||||
<Break />
|
||||
</ModalUpper>
|
||||
<AutoColumn gap="md" style={{ padding: '1rem', paddingTop: '0' }} justify="center">
|
||||
<TYPE.subHeader fontWeight={500}>
|
||||
<ThemedText.SubHeader fontWeight={500}>
|
||||
<Trans>
|
||||
Enter an address to trigger a UNI claim. If the address has any claimable UNI it will be sent to them on
|
||||
submission.
|
||||
</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
<AddressInputPanel value={typed} onChange={handleRecipientType} />
|
||||
{parsedAddress && !hasAvailableClaim && (
|
||||
<TYPE.error error={true}>
|
||||
<ThemedText.Error error={true}>
|
||||
<Trans>Address has no available claim</Trans>
|
||||
</TYPE.error>
|
||||
</ThemedText.Error>
|
||||
)}
|
||||
<ButtonPrimary
|
||||
disabled={!isAddress(parsedAddress ?? '') || !hasAvailableClaim}
|
||||
@ -159,23 +159,23 @@ export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boole
|
||||
</ConfirmedIcon>
|
||||
<AutoColumn gap="100px" justify={'center'}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.largeHeader fontWeight={600} color="black">
|
||||
<ThemedText.LargeHeader fontWeight={600} color="black">
|
||||
{claimConfirmed ? <Trans>Claimed</Trans> : <Trans>Claiming</Trans>}
|
||||
</TYPE.largeHeader>
|
||||
</ThemedText.LargeHeader>
|
||||
{!claimConfirmed && (
|
||||
<Text fontSize={36} color={'#ff007a'} fontWeight={800}>
|
||||
<Trans>{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI</Trans>
|
||||
</Text>
|
||||
)}
|
||||
{parsedAddress && (
|
||||
<TYPE.largeHeader fontWeight={600} color="black">
|
||||
<ThemedText.LargeHeader fontWeight={600} color="black">
|
||||
<Trans>for {shortenAddress(parsedAddress)}</Trans>
|
||||
</TYPE.largeHeader>
|
||||
</ThemedText.LargeHeader>
|
||||
)}
|
||||
</AutoColumn>
|
||||
{claimConfirmed && (
|
||||
<>
|
||||
<TYPE.subHeader fontWeight={500} color="black">
|
||||
<ThemedText.SubHeader fontWeight={500} color="black">
|
||||
<span role="img" aria-label="party-hat">
|
||||
🎉{' '}
|
||||
</span>
|
||||
@ -183,13 +183,13 @@ export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boole
|
||||
<span role="img" aria-label="party-hat">
|
||||
🎉
|
||||
</span>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</>
|
||||
)}
|
||||
{attempting && !hash && (
|
||||
<TYPE.subHeader color="black">
|
||||
<ThemedText.SubHeader color="black">
|
||||
<Trans>Confirm this transaction in your wallet</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
)}
|
||||
{attempting && hash && !claimConfirmed && chainId && hash && (
|
||||
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)} style={{ zIndex: 99 }}>
|
||||
|
@ -13,7 +13,7 @@ import { useModalOpen, useToggleSelfClaimModal } from '../../state/application/h
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { useClaimCallback, useUserClaimData, useUserUnclaimedAmount } from '../../state/claim/hooks'
|
||||
import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
|
||||
import { CloseIcon, CustomLightSpinner, ExternalLink, TYPE, UniTokenAnimated } from '../../theme'
|
||||
import { CloseIcon, CustomLightSpinner, ExternalLink, ThemedText, UniTokenAnimated } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
import { AutoColumn, ColumnCenter } from '../Column'
|
||||
@ -100,63 +100,63 @@ export default function ClaimModal() {
|
||||
<CardNoise />
|
||||
<CardSection gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white fontWeight={500}>
|
||||
<ThemedText.White fontWeight={500}>
|
||||
<Trans>Claim UNI</Trans>
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
<CloseIcon onClick={toggleClaimModal} style={{ zIndex: 99 }} color="white" />
|
||||
</RowBetween>
|
||||
<TYPE.white fontWeight={700} fontSize={36}>
|
||||
<ThemedText.White fontWeight={700} fontSize={36}>
|
||||
<Trans>{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI</Trans>
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
</CardSection>
|
||||
<Break />
|
||||
<CardSection gap="sm">
|
||||
{userClaimData?.flags?.isSOCKS && (
|
||||
<RowBetween>
|
||||
<TYPE.subHeader color="white">SOCKS</TYPE.subHeader>
|
||||
<TYPE.subHeader color="white">
|
||||
<ThemedText.SubHeader color="white">SOCKS</ThemedText.SubHeader>
|
||||
<ThemedText.SubHeader color="white">
|
||||
<Trans>{SOCKS_AMOUNT} UNI</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</RowBetween>
|
||||
)}
|
||||
{userClaimData?.flags?.isLP &&
|
||||
unclaimedAmount &&
|
||||
JSBI.greaterThanOrEqual(unclaimedAmount.quotient, nonLPAmount) && (
|
||||
<RowBetween>
|
||||
<TYPE.subHeader color="white">
|
||||
<ThemedText.SubHeader color="white">
|
||||
<Trans>Liquidity</Trans>
|
||||
</TYPE.subHeader>
|
||||
<TYPE.subHeader color="white">
|
||||
</ThemedText.SubHeader>
|
||||
<ThemedText.SubHeader color="white">
|
||||
<Trans>
|
||||
{unclaimedAmount
|
||||
.subtract(CurrencyAmount.fromRawAmount(unclaimedAmount.currency, nonLPAmount))
|
||||
.toFixed(0, { groupSeparator: ',' })}{' '}
|
||||
UNI
|
||||
</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</RowBetween>
|
||||
)}
|
||||
{userClaimData?.flags?.isUser && (
|
||||
<RowBetween>
|
||||
<TYPE.subHeader color="white">
|
||||
<ThemedText.SubHeader color="white">
|
||||
<Trans>User</Trans>
|
||||
</TYPE.subHeader>
|
||||
<TYPE.subHeader color="white">
|
||||
</ThemedText.SubHeader>
|
||||
<ThemedText.SubHeader color="white">
|
||||
<Trans>{USER_AMOUNT} UNI</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</RowBetween>
|
||||
)}
|
||||
</CardSection>
|
||||
</ModalUpper>
|
||||
<AutoColumn gap="md" style={{ padding: '1rem', paddingTop: '0' }} justify="center">
|
||||
<TYPE.subHeader fontWeight={500}>
|
||||
<ThemedText.SubHeader fontWeight={500}>
|
||||
<Trans>
|
||||
As a member of the Uniswap community you may claim UNI to be used for voting and governance.
|
||||
<br />
|
||||
<br />
|
||||
<ExternalLink href="https://uniswap.org/blog/uni">Read more about UNI</ExternalLink>
|
||||
</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
<ButtonPrimary
|
||||
disabled={!isAddress(account ?? '')}
|
||||
padding="16px 16px"
|
||||
@ -187,9 +187,9 @@ export default function ClaimModal() {
|
||||
</ConfirmedIcon>
|
||||
<AutoColumn gap="100px" justify={'center'}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.largeHeader fontWeight={600} color="black">
|
||||
<ThemedText.LargeHeader fontWeight={600} color="black">
|
||||
{claimConfirmed ? <Trans>Claimed!</Trans> : <Trans>Claiming</Trans>}
|
||||
</TYPE.largeHeader>
|
||||
</ThemedText.LargeHeader>
|
||||
{!claimConfirmed && (
|
||||
<Text fontSize={36} color={'#ff007a'} fontWeight={800}>
|
||||
<Trans>{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI</Trans>
|
||||
@ -198,7 +198,7 @@ export default function ClaimModal() {
|
||||
</AutoColumn>
|
||||
{claimConfirmed && (
|
||||
<>
|
||||
<TYPE.subHeader fontWeight={500} color="black">
|
||||
<ThemedText.SubHeader fontWeight={500} color="black">
|
||||
<Trans>
|
||||
<span role="img" aria-label="party-hat">
|
||||
🎉{' '}
|
||||
@ -208,13 +208,13 @@ export default function ClaimModal() {
|
||||
🎉
|
||||
</span>
|
||||
</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</>
|
||||
)}
|
||||
{attempting && !claimSubmitted && (
|
||||
<TYPE.subHeader color="black">
|
||||
<ThemedText.SubHeader color="black">
|
||||
<Trans>Confirm this transaction in your wallet</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
)}
|
||||
{attempting && claimSubmitted && !claimConfirmed && chainId && claimTxn?.hash && (
|
||||
<ExternalLink
|
||||
|
@ -8,7 +8,7 @@ import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { StakingInfo } from '../../state/stake/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { CloseIcon, TYPE } from '../../theme'
|
||||
import { CloseIcon, ThemedText } from '../../theme'
|
||||
import { ButtonError } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
import Modal from '../Modal'
|
||||
@ -74,24 +74,24 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta
|
||||
{!attempting && !hash && (
|
||||
<ContentWrapper gap="lg">
|
||||
<RowBetween>
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Claim</Trans>
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
<CloseIcon onClick={wrappedOnDismiss} />
|
||||
</RowBetween>
|
||||
{stakingInfo?.earnedAmount && (
|
||||
<AutoColumn justify="center" gap="md">
|
||||
<TYPE.body fontWeight={600} fontSize={36}>
|
||||
<ThemedText.Body fontWeight={600} fontSize={36}>
|
||||
{stakingInfo?.earnedAmount?.toSignificant(6)}
|
||||
</TYPE.body>
|
||||
<TYPE.body>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.Body>
|
||||
<Trans>Unclaimed UNI</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
)}
|
||||
<TYPE.subHeader style={{ textAlign: 'center' }}>
|
||||
<ThemedText.SubHeader style={{ textAlign: 'center' }}>
|
||||
<Trans>When you claim without withdrawing your liquidity remains in the mining pool.</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
<ButtonError disabled={!!error} error={!!error && !!stakingInfo?.stakedAmount} onClick={onClaimReward}>
|
||||
{error ?? <Trans>Claim</Trans>}
|
||||
</ButtonError>
|
||||
@ -100,21 +100,21 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta
|
||||
{attempting && !hash && (
|
||||
<LoadingView onDismiss={wrappedOnDismiss}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.body fontSize={20}>
|
||||
<ThemedText.Body fontSize={20}>
|
||||
<Trans>Claiming {stakingInfo?.earnedAmount?.toSignificant(6)} UNI</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
</LoadingView>
|
||||
)}
|
||||
{hash && (
|
||||
<SubmittedView onDismiss={wrappedOnDismiss} hash={hash}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.largeHeader>
|
||||
<ThemedText.LargeHeader>
|
||||
<Trans>Transaction Submitted</Trans>
|
||||
</TYPE.largeHeader>
|
||||
<TYPE.body fontSize={20}>
|
||||
</ThemedText.LargeHeader>
|
||||
<ThemedText.Body fontSize={20}>
|
||||
<Trans>Claimed UNI!</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
</SubmittedView>
|
||||
)}
|
||||
|
@ -9,7 +9,7 @@ import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
import useUSDCPrice from '../../hooks/useUSDCPrice'
|
||||
import { useV2Pair } from '../../hooks/useV2Pairs'
|
||||
import { StakingInfo } from '../../state/stake/hooks'
|
||||
import { StyledInternalLink, TYPE } from '../../theme'
|
||||
import { StyledInternalLink, ThemedText } from '../../theme'
|
||||
import { currencyId } from '../../utils/currencyId'
|
||||
import { unwrappedToken } from '../../utils/unwrappedToken'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
@ -115,9 +115,9 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
|
||||
|
||||
<TopSection>
|
||||
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={24} />
|
||||
<TYPE.white fontWeight={600} fontSize={24} style={{ marginLeft: '8px' }}>
|
||||
<ThemedText.White fontWeight={600} fontSize={24} style={{ marginLeft: '8px' }}>
|
||||
{currency0.symbol}-{currency1.symbol}
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
|
||||
<StyledInternalLink to={`/uni/${currencyId(currency0)}/${currencyId(currency1)}`} style={{ width: '100%' }}>
|
||||
<ButtonPrimary padding="8px" $borderRadius="8px">
|
||||
@ -128,22 +128,22 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
|
||||
|
||||
<StatContainer>
|
||||
<RowBetween>
|
||||
<TYPE.white>
|
||||
<ThemedText.White>
|
||||
<Trans>Total deposited</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White>
|
||||
{valueOfTotalStakedAmountInUSDC ? (
|
||||
<Trans>${valueOfTotalStakedAmountInUSDC.toFixed(0, { groupSeparator: ',' })}</Trans>
|
||||
) : (
|
||||
<Trans>{valueOfTotalStakedAmountInWETH?.toSignificant(4, { groupSeparator: ',' }) ?? '-'} ETH</Trans>
|
||||
)}
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<TYPE.white>
|
||||
<ThemedText.White>
|
||||
<Trans>Pool rate</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white>
|
||||
</ThemedText.White>
|
||||
<ThemedText.White>
|
||||
{stakingInfo ? (
|
||||
stakingInfo.active ? (
|
||||
<Trans>
|
||||
@ -156,7 +156,7 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
</RowBetween>
|
||||
</StatContainer>
|
||||
|
||||
@ -164,13 +164,13 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
|
||||
<>
|
||||
<Break />
|
||||
<BottomSection showBackground={true}>
|
||||
<TYPE.black color={'white'} fontWeight={500}>
|
||||
<ThemedText.Black color={'white'} fontWeight={500}>
|
||||
<span>
|
||||
<Trans>Your rate</Trans>
|
||||
</span>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
|
||||
<TYPE.black style={{ textAlign: 'right' }} color={'white'} fontWeight={500}>
|
||||
<ThemedText.Black style={{ textAlign: 'right' }} color={'white'} fontWeight={500}>
|
||||
<span role="img" aria-label="wizard-icon" style={{ marginRight: '0.5rem' }}>
|
||||
⚡
|
||||
</span>
|
||||
@ -188,7 +188,7 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
</BottomSection>
|
||||
</>
|
||||
)}
|
||||
|
@ -13,7 +13,7 @@ import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { StakingInfo, useDerivedStakeInfo } from '../../state/stake/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { CloseIcon, TYPE } from '../../theme'
|
||||
import { CloseIcon, ThemedText } from '../../theme'
|
||||
import { formatCurrencyAmount } from '../../utils/formatCurrencyAmount'
|
||||
import { maxAmountSpend } from '../../utils/maxAmountSpend'
|
||||
import { ButtonConfirmed, ButtonError } from '../Button'
|
||||
@ -159,9 +159,9 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui
|
||||
{!attempting && !hash && (
|
||||
<ContentWrapper gap="lg">
|
||||
<RowBetween>
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Deposit</Trans>
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
<CloseIcon onClick={wrappedOnDismiss} />
|
||||
</RowBetween>
|
||||
<CurrencyInputPanel
|
||||
@ -178,19 +178,19 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui
|
||||
|
||||
<HypotheticalRewardRate dim={!hypotheticalRewardRate.greaterThan('0')}>
|
||||
<div>
|
||||
<TYPE.black fontWeight={600}>
|
||||
<ThemedText.Black fontWeight={600}>
|
||||
<Trans>Weekly Rewards</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
</div>
|
||||
|
||||
<TYPE.black>
|
||||
<ThemedText.Black>
|
||||
<Trans>
|
||||
{hypotheticalRewardRate
|
||||
.multiply((60 * 60 * 24 * 7).toString())
|
||||
.toSignificant(4, { groupSeparator: ',' })}{' '}
|
||||
UNI / week
|
||||
</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
</HypotheticalRewardRate>
|
||||
|
||||
<RowBetween>
|
||||
@ -216,24 +216,24 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui
|
||||
{attempting && !hash && (
|
||||
<LoadingView onDismiss={wrappedOnDismiss}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.largeHeader>
|
||||
<ThemedText.LargeHeader>
|
||||
<Trans>Depositing Liquidity</Trans>
|
||||
</TYPE.largeHeader>
|
||||
<TYPE.body fontSize={20}>
|
||||
</ThemedText.LargeHeader>
|
||||
<ThemedText.Body fontSize={20}>
|
||||
<Trans>{parsedAmount?.toSignificant(4)} UNI-V2</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
</LoadingView>
|
||||
)}
|
||||
{attempting && hash && (
|
||||
<SubmittedView onDismiss={wrappedOnDismiss} hash={hash}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.largeHeader>
|
||||
<ThemedText.LargeHeader>
|
||||
<Trans>Transaction Submitted</Trans>
|
||||
</TYPE.largeHeader>
|
||||
<TYPE.body fontSize={20}>
|
||||
</ThemedText.LargeHeader>
|
||||
<ThemedText.Body fontSize={20}>
|
||||
<Trans>Deposited {parsedAmount?.toSignificant(4)} UNI-V2</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
</SubmittedView>
|
||||
)}
|
||||
|
@ -8,7 +8,7 @@ import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { StakingInfo } from '../../state/stake/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { CloseIcon, TYPE } from '../../theme'
|
||||
import { CloseIcon, ThemedText } from '../../theme'
|
||||
import { ButtonError } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
import FormattedCurrencyAmount from '../FormattedCurrencyAmount'
|
||||
@ -76,34 +76,34 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki
|
||||
{!attempting && !hash && (
|
||||
<ContentWrapper gap="lg">
|
||||
<RowBetween>
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Withdraw</Trans>
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
<CloseIcon onClick={wrappedOndismiss} />
|
||||
</RowBetween>
|
||||
{stakingInfo?.stakedAmount && (
|
||||
<AutoColumn justify="center" gap="md">
|
||||
<TYPE.body fontWeight={600} fontSize={36}>
|
||||
<ThemedText.Body fontWeight={600} fontSize={36}>
|
||||
{<FormattedCurrencyAmount currencyAmount={stakingInfo.stakedAmount} />}
|
||||
</TYPE.body>
|
||||
<TYPE.body>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.Body>
|
||||
<Trans>Deposited liquidity:</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
)}
|
||||
{stakingInfo?.earnedAmount && (
|
||||
<AutoColumn justify="center" gap="md">
|
||||
<TYPE.body fontWeight={600} fontSize={36}>
|
||||
<ThemedText.Body fontWeight={600} fontSize={36}>
|
||||
{<FormattedCurrencyAmount currencyAmount={stakingInfo?.earnedAmount} />}
|
||||
</TYPE.body>
|
||||
<TYPE.body>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.Body>
|
||||
<Trans>Unclaimed UNI</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
)}
|
||||
<TYPE.subHeader style={{ textAlign: 'center' }}>
|
||||
<ThemedText.SubHeader style={{ textAlign: 'center' }}>
|
||||
<Trans>When you withdraw, your UNI is claimed and your liquidity is removed from the mining pool.</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
<ButtonError disabled={!!error} error={!!error && !!stakingInfo?.stakedAmount} onClick={onWithdraw}>
|
||||
{error ?? <Trans>Withdraw & Claim</Trans>}
|
||||
</ButtonError>
|
||||
@ -112,27 +112,27 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki
|
||||
{attempting && !hash && (
|
||||
<LoadingView onDismiss={wrappedOndismiss}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.body fontSize={20}>
|
||||
<ThemedText.Body fontSize={20}>
|
||||
<Trans>Withdrawing {stakingInfo?.stakedAmount?.toSignificant(4)} UNI-V2</Trans>
|
||||
</TYPE.body>
|
||||
<TYPE.body fontSize={20}>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.Body fontSize={20}>
|
||||
<Trans>Claiming {stakingInfo?.earnedAmount?.toSignificant(4)} UNI</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
</LoadingView>
|
||||
)}
|
||||
{hash && (
|
||||
<SubmittedView onDismiss={wrappedOndismiss} hash={hash}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.largeHeader>
|
||||
<ThemedText.LargeHeader>
|
||||
<Trans>Transaction Submitted</Trans>
|
||||
</TYPE.largeHeader>
|
||||
<TYPE.body fontSize={20}>
|
||||
</ThemedText.LargeHeader>
|
||||
<ThemedText.Body fontSize={20}>
|
||||
<Trans>Withdrew UNI-V2!</Trans>
|
||||
</TYPE.body>
|
||||
<TYPE.body fontSize={20}>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.Body fontSize={20}>
|
||||
<Trans>Claimed UNI!</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
</SubmittedView>
|
||||
)}
|
||||
|
@ -6,7 +6,7 @@ import { LoadingRows } from 'components/Loader/styled'
|
||||
import { useContext, useMemo } from 'react'
|
||||
import { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { computeRealizedLPFeePercent } from '../../utils/prices'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
@ -56,55 +56,55 @@ export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }:
|
||||
</TransactionDetailsLabel>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.subHeader color={theme.text1}>
|
||||
<ThemedText.SubHeader color={theme.text1}>
|
||||
<Trans>Liquidity Provider Fee</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</RowFixed>
|
||||
<TextWithLoadingPlaceholder syncing={syncing} width={65}>
|
||||
<TYPE.black textAlign="right" fontSize={14}>
|
||||
<ThemedText.Black textAlign="right" fontSize={14}>
|
||||
{realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${realizedLPFee.currency.symbol}` : '-'}
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
</TextWithLoadingPlaceholder>
|
||||
</RowBetween>
|
||||
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.subHeader color={theme.text1}>
|
||||
<ThemedText.SubHeader color={theme.text1}>
|
||||
<Trans>Price Impact</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</RowFixed>
|
||||
<TextWithLoadingPlaceholder syncing={syncing} width={50}>
|
||||
<TYPE.black textAlign="right" fontSize={14}>
|
||||
<ThemedText.Black textAlign="right" fontSize={14}>
|
||||
<FormattedPriceImpact priceImpact={priceImpact} />
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
</TextWithLoadingPlaceholder>
|
||||
</RowBetween>
|
||||
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.subHeader color={theme.text1}>
|
||||
<ThemedText.SubHeader color={theme.text1}>
|
||||
<Trans>Allowed Slippage</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</RowFixed>
|
||||
<TextWithLoadingPlaceholder syncing={syncing} width={45}>
|
||||
<TYPE.black textAlign="right" fontSize={14}>
|
||||
<ThemedText.Black textAlign="right" fontSize={14}>
|
||||
{allowedSlippage.toFixed(2)}%
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
</TextWithLoadingPlaceholder>
|
||||
</RowBetween>
|
||||
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.subHeader color={theme.text1}>
|
||||
<ThemedText.SubHeader color={theme.text1}>
|
||||
{trade.tradeType === TradeType.EXACT_INPUT ? <Trans>Minimum received</Trans> : <Trans>Maximum sent</Trans>}
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</RowFixed>
|
||||
<TextWithLoadingPlaceholder syncing={syncing} width={70}>
|
||||
<TYPE.black textAlign="right" fontSize={14}>
|
||||
<ThemedText.Black textAlign="right" fontSize={14}>
|
||||
{trade.tradeType === TradeType.EXACT_INPUT
|
||||
? `${trade.minimumAmountOut(allowedSlippage).toSignificant(6)} ${trade.outputAmount.currency.symbol}`
|
||||
: `${trade.maximumAmountIn(allowedSlippage).toSignificant(6)} ${trade.inputAmount.currency.symbol}`}
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
</TextWithLoadingPlaceholder>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useRoutingAPIEnabled } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { ReactComponent as AutoRouterIcon } from '../../assets/svg/auto_router.svg'
|
||||
import { ReactComponent as StaticRouterIcon } from '../../assets/svg/static_route.svg'
|
||||
@ -26,7 +26,7 @@ const StyledStaticRouterIcon = styled(StaticRouterIcon)`
|
||||
}
|
||||
`
|
||||
|
||||
const StyledAutoRouterLabel = styled(TYPE.black)`
|
||||
const StyledAutoRouterLabel = styled(ThemedText.Black)`
|
||||
line-height: 1rem;
|
||||
|
||||
/* fallback color */
|
||||
@ -51,8 +51,8 @@ export function AutoRouterLabel() {
|
||||
return routingAPIEnabled ? (
|
||||
<StyledAutoRouterLabel fontSize={14}>Auto Router</StyledAutoRouterLabel>
|
||||
) : (
|
||||
<TYPE.black fontSize={14}>
|
||||
<ThemedText.Black fontSize={14}>
|
||||
<Trans>Trade Route</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import SettingsTab from '../Settings'
|
||||
|
||||
@ -17,9 +17,9 @@ export default function SwapHeader({ allowedSlippage }: { allowedSlippage: Perce
|
||||
<StyledSwapHeader>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontWeight={500} fontSize={16} style={{ marginRight: '8px' }}>
|
||||
<ThemedText.Black fontWeight={500} fontSize={16} style={{ marginRight: '8px' }}>
|
||||
<Trans>Swap</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<SettingsTab placeholderSlippage={allowedSlippage} />
|
||||
|
@ -8,7 +8,7 @@ import { Text } from 'rebass'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { useUSDCValue } from '../../hooks/useUSDCPrice'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { isAddress, shortenAddress } from '../../utils'
|
||||
import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
@ -64,9 +64,9 @@ export default function SwapModalHeader({
|
||||
<LightCard padding="0.75rem 1rem">
|
||||
<AutoColumn gap={'8px'}>
|
||||
<RowBetween>
|
||||
<TYPE.body color={theme.text3} fontWeight={500} fontSize={14}>
|
||||
<ThemedText.Body color={theme.text3} fontWeight={500} fontSize={14}>
|
||||
<Trans>From</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
<FiatValue fiatValue={fiatValueInput} />
|
||||
</RowBetween>
|
||||
<RowBetween align="center">
|
||||
@ -94,15 +94,15 @@ export default function SwapModalHeader({
|
||||
<LightCard padding="0.75rem 1rem" style={{ marginBottom: '0.25rem' }}>
|
||||
<AutoColumn gap={'8px'}>
|
||||
<RowBetween>
|
||||
<TYPE.body color={theme.text3} fontWeight={500} fontSize={14}>
|
||||
<ThemedText.Body color={theme.text3} fontWeight={500} fontSize={14}>
|
||||
<Trans>To</Trans>
|
||||
</TYPE.body>
|
||||
<TYPE.body fontSize={14} color={theme.text3}>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.Body fontSize={14} color={theme.text3}>
|
||||
<FiatValue
|
||||
fiatValue={fiatValueOutput}
|
||||
priceImpact={computeFiatValuePriceImpact(fiatValueInput, fiatValueOutput)}
|
||||
/>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</RowBetween>
|
||||
<RowBetween align="flex-end">
|
||||
<RowFixed gap={'0px'}>
|
||||
@ -120,9 +120,9 @@ export default function SwapModalHeader({
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
<RowBetween style={{ marginTop: '0.25rem', padding: '0 1rem' }}>
|
||||
<TYPE.body color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
<ThemedText.Body color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
<Trans>Price</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
<TradePrice price={trade.executionPrice} showInverted={showInverted} setShowInverted={setShowInverted} />
|
||||
</RowBetween>
|
||||
|
||||
@ -135,9 +135,9 @@ export default function SwapModalHeader({
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<AlertTriangle size={20} style={{ marginRight: '8px', minWidth: 24 }} />
|
||||
<TYPE.main color={theme.primary1}>
|
||||
<ThemedText.Main color={theme.primary1}>
|
||||
<Trans>Price Updated</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</RowFixed>
|
||||
<ButtonPrimary
|
||||
style={{ padding: '.5rem', width: 'fit-content', fontSize: '0.825rem', borderRadius: '12px' }}
|
||||
@ -151,7 +151,7 @@ export default function SwapModalHeader({
|
||||
|
||||
<AutoColumn justify="flex-start" gap="sm" style={{ padding: '.75rem 1rem' }}>
|
||||
{trade.tradeType === TradeType.EXACT_INPUT ? (
|
||||
<TYPE.italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
|
||||
<ThemedText.Italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
|
||||
<Trans>
|
||||
Output is estimated. You will receive at least{' '}
|
||||
<b>
|
||||
@ -159,9 +159,9 @@ export default function SwapModalHeader({
|
||||
</b>{' '}
|
||||
or the transaction will revert.
|
||||
</Trans>
|
||||
</TYPE.italic>
|
||||
</ThemedText.Italic>
|
||||
) : (
|
||||
<TYPE.italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
|
||||
<ThemedText.Italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
|
||||
<Trans>
|
||||
Input is estimated. You will sell at most{' '}
|
||||
<b>
|
||||
@ -169,17 +169,17 @@ export default function SwapModalHeader({
|
||||
</b>{' '}
|
||||
or the transaction will revert.
|
||||
</Trans>
|
||||
</TYPE.italic>
|
||||
</ThemedText.Italic>
|
||||
)}
|
||||
</AutoColumn>
|
||||
{recipient !== null ? (
|
||||
<AutoColumn justify="flex-start" gap="sm" style={{ padding: '12px 0 0 0px' }}>
|
||||
<TYPE.main>
|
||||
<ThemedText.Main>
|
||||
<Trans>
|
||||
Output will be sent to{' '}
|
||||
<b title={recipient}>{isAddress(recipient) ? shortenAddress(recipient) : recipient}</b>
|
||||
</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
|
@ -11,7 +11,7 @@ import { Version } from 'hooks/useToggledVersion'
|
||||
import { memo } from 'react'
|
||||
import { useRoutingAPIEnabled } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
import { getTradeVersion } from 'utils/getTradeVersion'
|
||||
|
||||
import { AutoRouterLabel, AutoRouterLogo } from './RouterLabel'
|
||||
@ -46,9 +46,9 @@ export default memo(function SwapRoute({
|
||||
</LoadingRows>
|
||||
) : (
|
||||
<Badge>
|
||||
<TYPE.black fontSize={12}>
|
||||
<ThemedText.Black fontSize={12}>
|
||||
{getTradeVersion(trade) === Version.v2 ? <Trans>V2</Trans> : <Trans>V3</Trans>}
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
</Badge>
|
||||
)}
|
||||
</RowBetween>
|
||||
@ -65,9 +65,9 @@ export default memo(function SwapRoute({
|
||||
/>
|
||||
)}
|
||||
{routingAPIEnabled && (
|
||||
<TYPE.main fontSize={12} width={400}>
|
||||
<ThemedText.Main fontSize={12} width={400}>
|
||||
<Trans>This route optimizes your price by considering split routes, multiple hops, and gas costs.</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
)}
|
||||
</AutoColumn>
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ import useUSDCPrice from 'hooks/useUSDCPrice'
|
||||
import { useCallback, useContext } from 'react'
|
||||
import { Text } from 'rebass'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
interface TradePriceProps {
|
||||
price: Price<Currency, Currency>
|
||||
@ -49,9 +49,9 @@ export default function TradePrice({ price, showInverted, setShowInverted }: Tra
|
||||
{text}
|
||||
</Text>{' '}
|
||||
{usdcPrice && (
|
||||
<TYPE.darkGray>
|
||||
<ThemedText.DarkGray>
|
||||
<Trans>(${usdcPrice.toSignificant(6, { groupSeparator: ',' })})</Trans>
|
||||
</TYPE.darkGray>
|
||||
</ThemedText.DarkGray>
|
||||
)}
|
||||
</StyledPriceContainer>
|
||||
)
|
||||
|
@ -9,7 +9,7 @@ import { AutoRow, RowBetween } from 'components/Row'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CloseIcon, ExternalLink, TYPE, Z_INDEX } from 'theme'
|
||||
import { CloseIcon, ExternalLink, ThemedText, Z_INDEX } from 'theme'
|
||||
|
||||
import { useUnsupportedTokens } from '../../hooks/Tokens'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
@ -37,7 +37,7 @@ const StyledButtonEmpty = styled(ButtonEmpty)`
|
||||
text-decoration: none;
|
||||
`
|
||||
|
||||
const AddressText = styled(TYPE.blue)`
|
||||
const AddressText = styled(ThemedText.Blue)`
|
||||
font-size: 12px;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
@ -70,9 +70,9 @@ export default function UnsupportedCurrencyFooter({
|
||||
<Card padding="2rem">
|
||||
<AutoColumn gap="lg">
|
||||
<RowBetween>
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Unsupported Assets</Trans>
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
<CloseIcon onClick={() => setShowDetails(false)} />
|
||||
</RowBetween>
|
||||
{tokens.map((token) => {
|
||||
@ -84,7 +84,7 @@ export default function UnsupportedCurrencyFooter({
|
||||
<AutoColumn gap="10px">
|
||||
<AutoRow gap="5px" align="center">
|
||||
<CurrencyLogo currency={token} size={'24px'} />
|
||||
<TYPE.body fontWeight={500}>{token.symbol}</TYPE.body>
|
||||
<ThemedText.Body fontWeight={500}>{token.symbol}</ThemedText.Body>
|
||||
</AutoRow>
|
||||
{chainId && (
|
||||
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
|
||||
@ -97,20 +97,20 @@ export default function UnsupportedCurrencyFooter({
|
||||
)
|
||||
})}
|
||||
<AutoColumn gap="lg">
|
||||
<TYPE.body fontWeight={500}>
|
||||
<ThemedText.Body fontWeight={500}>
|
||||
<Trans>
|
||||
Some assets are not available through this interface because they may not work well with the smart
|
||||
contracts or we are unable to allow trading for legal reasons.
|
||||
</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
</AutoColumn>
|
||||
</Card>
|
||||
</Modal>
|
||||
<StyledButtonEmpty padding={'0'} onClick={() => setShowDetails(true)}>
|
||||
<TYPE.blue>
|
||||
<ThemedText.Blue>
|
||||
<Trans>Read more about unsupported assets</Trans>
|
||||
</TYPE.blue>
|
||||
</ThemedText.Blue>
|
||||
</StyledButtonEmpty>
|
||||
</DetailsFooter>
|
||||
)
|
||||
|
@ -5,8 +5,8 @@ import { ReactNode } from 'react'
|
||||
import { AlertTriangle } from 'react-feather'
|
||||
import { Text } from 'rebass'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
|
||||
import { ThemedText } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import TradePrice from './TradePrice'
|
||||
|
||||
@ -133,7 +133,7 @@ export const SwapShowAcceptChanges = styled(AutoColumn)`
|
||||
margin-top: 8px;
|
||||
`
|
||||
|
||||
export const TransactionDetailsLabel = styled(TYPE.black)`
|
||||
export const TransactionDetailsLabel = styled(ThemedText.Black)`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.bg2};
|
||||
padding-bottom: 0.5rem;
|
||||
`
|
||||
|
@ -10,7 +10,7 @@ import useENS from '../../hooks/useENS'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useDelegateCallback } from '../../state/governance/hooks'
|
||||
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import AddressInputPanel from '../AddressInputPanel'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
@ -95,23 +95,25 @@ export default function DelegateModal({ isOpen, onDismiss, title }: VoteModalPro
|
||||
<ContentWrapper gap="lg">
|
||||
<AutoColumn gap="lg" justify="center">
|
||||
<RowBetween>
|
||||
<TYPE.mediumHeader fontWeight={500}>{title}</TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader fontWeight={500}>{title}</ThemedText.MediumHeader>
|
||||
<StyledClosed stroke="black" onClick={wrappedOndismiss} />
|
||||
</RowBetween>
|
||||
<TYPE.body>
|
||||
<ThemedText.Body>
|
||||
<Trans>Earned UNI tokens represent voting shares in Uniswap governance.</Trans>
|
||||
</TYPE.body>
|
||||
<TYPE.body>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.Body>
|
||||
<Trans>You can either vote on each proposal yourself or delegate your votes to a third party.</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
{usingDelegate && <AddressInputPanel value={typed} onChange={handleRecipientType} />}
|
||||
<ButtonPrimary disabled={!isAddress(parsedAddress ?? '')} onClick={onDelegate}>
|
||||
<TYPE.mediumHeader color="white">
|
||||
<ThemedText.MediumHeader color="white">
|
||||
{usingDelegate ? <Trans>Delegate Votes</Trans> : <Trans>Self Delegate</Trans>}
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
</ButtonPrimary>
|
||||
<TextButton onClick={() => setUsingDelegate(!usingDelegate)}>
|
||||
<TYPE.blue>{usingDelegate ? <Trans>Remove Delegate</Trans> : <Trans>Add Delegate +</Trans>}</TYPE.blue>
|
||||
<ThemedText.Blue>
|
||||
{usingDelegate ? <Trans>Remove Delegate</Trans> : <Trans>Add Delegate +</Trans>}
|
||||
</ThemedText.Blue>
|
||||
</TextButton>
|
||||
</AutoColumn>
|
||||
</ContentWrapper>
|
||||
@ -119,20 +121,20 @@ export default function DelegateModal({ isOpen, onDismiss, title }: VoteModalPro
|
||||
{attempting && !hash && (
|
||||
<LoadingView onDismiss={wrappedOndismiss}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.largeHeader>
|
||||
<ThemedText.LargeHeader>
|
||||
{usingDelegate ? <Trans>Delegating votes</Trans> : <Trans>Unlocking Votes</Trans>}
|
||||
</TYPE.largeHeader>
|
||||
<TYPE.main fontSize={36}> {formatCurrencyAmount(uniBalance, 4)}</TYPE.main>
|
||||
</ThemedText.LargeHeader>
|
||||
<ThemedText.Main fontSize={36}> {formatCurrencyAmount(uniBalance, 4)}</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
</LoadingView>
|
||||
)}
|
||||
{hash && (
|
||||
<SubmittedView onDismiss={wrappedOndismiss} hash={hash}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.largeHeader>
|
||||
<ThemedText.LargeHeader>
|
||||
<Trans>Transaction Submitted</Trans>
|
||||
</TYPE.largeHeader>
|
||||
<TYPE.main fontSize={36}>{formatCurrencyAmount(uniBalance, 4)}</TYPE.main>
|
||||
</ThemedText.LargeHeader>
|
||||
<ThemedText.Main fontSize={36}>{formatCurrencyAmount(uniBalance, 4)}</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
</SubmittedView>
|
||||
)}
|
||||
|
@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
|
||||
import { L2_CHAIN_IDS } from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
const EmptyProposals = styled.div`
|
||||
border: 1px solid ${({ theme }) => theme.text4};
|
||||
@ -25,14 +25,14 @@ interface EmptyStateProps {
|
||||
}
|
||||
const EmptyState = ({ HeaderContent, SubHeaderContent }: EmptyStateProps) => (
|
||||
<EmptyProposals>
|
||||
<TYPE.body style={{ marginBottom: '8px' }}>
|
||||
<ThemedText.Body style={{ marginBottom: '8px' }}>
|
||||
<HeaderContent />
|
||||
</TYPE.body>
|
||||
<TYPE.subHeader>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.SubHeader>
|
||||
<Sub>
|
||||
<SubHeaderContent />
|
||||
</Sub>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</EmptyProposals>
|
||||
)
|
||||
|
||||
|
@ -8,8 +8,8 @@ import Circle from '../../assets/images/blue-loader.svg'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useUserVotes, useVoteCallback } from '../../state/governance/hooks'
|
||||
import { VoteOption } from '../../state/governance/types'
|
||||
import { CustomLightSpinner, TYPE } from '../../theme'
|
||||
import { ExternalLink } from '../../theme/components'
|
||||
import { CustomLightSpinner, ThemedText } from '../../theme'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
import { AutoColumn, ColumnCenter } from '../Column'
|
||||
@ -85,7 +85,7 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }:
|
||||
<ContentWrapper gap="lg">
|
||||
<AutoColumn gap="lg" justify="center">
|
||||
<RowBetween>
|
||||
<TYPE.mediumHeader fontWeight={500}>
|
||||
<ThemedText.MediumHeader fontWeight={500}>
|
||||
{voteOption === VoteOption.Against ? (
|
||||
<Trans>Vote against proposal {proposalId}</Trans>
|
||||
) : voteOption === VoteOption.For ? (
|
||||
@ -93,14 +93,14 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }:
|
||||
) : (
|
||||
<Trans>Vote to abstain on proposal {proposalId}</Trans>
|
||||
)}
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
<StyledClosed stroke="black" onClick={wrappedOndismiss} />
|
||||
</RowBetween>
|
||||
<TYPE.largeHeader>
|
||||
<ThemedText.LargeHeader>
|
||||
<Trans>{formatCurrencyAmount(availableVotes, 4)} Votes</Trans>
|
||||
</TYPE.largeHeader>
|
||||
</ThemedText.LargeHeader>
|
||||
<ButtonPrimary onClick={onVote}>
|
||||
<TYPE.mediumHeader color="white">
|
||||
<ThemedText.MediumHeader color="white">
|
||||
{voteOption === VoteOption.Against ? (
|
||||
<Trans>Vote against proposal {proposalId}</Trans>
|
||||
) : voteOption === VoteOption.For ? (
|
||||
@ -108,7 +108,7 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }:
|
||||
) : (
|
||||
<Trans>Vote to abstain on proposal {proposalId}</Trans>
|
||||
)}
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
</ButtonPrimary>
|
||||
</AutoColumn>
|
||||
</ContentWrapper>
|
||||
@ -124,13 +124,13 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }:
|
||||
</ConfirmedIcon>
|
||||
<AutoColumn gap="100px" justify={'center'}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.largeHeader>
|
||||
<ThemedText.LargeHeader>
|
||||
<Trans>Submitting Vote</Trans>
|
||||
</TYPE.largeHeader>
|
||||
</ThemedText.LargeHeader>
|
||||
</AutoColumn>
|
||||
<TYPE.subHeader>
|
||||
<ThemedText.SubHeader>
|
||||
<Trans>Confirm this transaction in your wallet</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</AutoColumn>
|
||||
</ConfirmOrLoadingWrapper>
|
||||
)}
|
||||
@ -145,18 +145,18 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }:
|
||||
</ConfirmedIcon>
|
||||
<AutoColumn gap="100px" justify={'center'}>
|
||||
<AutoColumn gap="12px" justify={'center'}>
|
||||
<TYPE.largeHeader>
|
||||
<ThemedText.LargeHeader>
|
||||
<Trans>Transaction Submitted</Trans>
|
||||
</TYPE.largeHeader>
|
||||
</ThemedText.LargeHeader>
|
||||
</AutoColumn>
|
||||
{chainId && (
|
||||
<ExternalLink
|
||||
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
|
||||
style={{ marginLeft: '4px' }}
|
||||
>
|
||||
<TYPE.subHeader>
|
||||
<ThemedText.SubHeader>
|
||||
<Trans>View transaction on Explorer</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</ExternalLink>
|
||||
)}
|
||||
</AutoColumn>
|
||||
|
@ -5,10 +5,10 @@ export const OVERLAY_READY = 'OVERLAY_READY'
|
||||
type FormaticSupportedChains = 1 | 3 | 4 | 42
|
||||
|
||||
const CHAIN_ID_NETWORK_ARGUMENT: { readonly [chainId in FormaticSupportedChains]: string | undefined } = {
|
||||
[1]: undefined,
|
||||
[3]: 'ropsten',
|
||||
[4]: 'rinkeby',
|
||||
[42]: 'kovan',
|
||||
1: undefined,
|
||||
3: 'ropsten',
|
||||
4: 'rinkeby',
|
||||
42: 'kovan',
|
||||
}
|
||||
|
||||
export class FortmaticConnector extends FortmaticConnectorCore {
|
||||
|
@ -19,5 +19,5 @@ export const DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS = 13
|
||||
|
||||
// Block time here is slightly higher (~1s) than average in order to avoid ongoing proposals past the displayed time
|
||||
export const AVERAGE_BLOCK_TIME_IN_SECS: { [chainId: number]: number } = {
|
||||
[1]: DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS,
|
||||
1: DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS,
|
||||
}
|
||||
|
@ -13,6 +13,9 @@ import {
|
||||
FRAX,
|
||||
FXS,
|
||||
renBTC,
|
||||
rETH2,
|
||||
sETH2,
|
||||
SWISE,
|
||||
TRIBE,
|
||||
USDC,
|
||||
USDC_ARBITRUM,
|
||||
@ -53,6 +56,8 @@ export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = {
|
||||
export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = {
|
||||
[SupportedChainId.MAINNET]: {
|
||||
'0xF16E4d813f4DcfDe4c5b44f305c908742De84eF0': [ETH2X_FLI],
|
||||
[rETH2.address]: [sETH2],
|
||||
[SWISE.address]: [sETH2],
|
||||
[FEI.address]: [TRIBE],
|
||||
[TRIBE.address]: [FEI],
|
||||
[FRAX.address]: [FXS],
|
||||
|
@ -136,6 +136,27 @@ export const ETH2X_FLI = new Token(
|
||||
'ETH2x-FLI',
|
||||
'ETH 2x Flexible Leverage Index'
|
||||
)
|
||||
export const sETH2 = new Token(
|
||||
SupportedChainId.MAINNET,
|
||||
'0xFe2e637202056d30016725477c5da089Ab0A043A',
|
||||
18,
|
||||
'sETH2',
|
||||
'StakeWise Staked ETH2'
|
||||
)
|
||||
export const rETH2 = new Token(
|
||||
SupportedChainId.MAINNET,
|
||||
'0x20BC832ca081b91433ff6c17f85701B6e92486c5',
|
||||
18,
|
||||
'rETH2',
|
||||
'StakeWise Reward ETH2'
|
||||
)
|
||||
export const SWISE = new Token(
|
||||
SupportedChainId.MAINNET,
|
||||
'0x48C3399719B582dD63eB5AADf12A40B4C3f52FA2',
|
||||
18,
|
||||
'SWISE',
|
||||
'StakeWise'
|
||||
)
|
||||
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'),
|
||||
|
244
src/hooks/Tokens.ts
Normal file
244
src/hooks/Tokens.ts
Normal file
@ -0,0 +1,244 @@
|
||||
import { arrayify } from '@ethersproject/bytes'
|
||||
import { parseBytes32String } from '@ethersproject/strings'
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from 'constants/chains'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { createTokenFilterFunction } from '../components/SearchModal/filtering'
|
||||
import { ExtendedEther, WETH9_EXTENDED } 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'
|
||||
import { useUserAddedTokens } from '../state/user/hooks'
|
||||
import { isAddress } from '../utils'
|
||||
import { TokenAddressMap, useUnsupportedTokenList } from './../state/lists/hooks'
|
||||
import { useBytes32TokenContract, useTokenContract } from './useContract'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
// reduce token map into standard address <-> Token mapping, optionally include user added tokens
|
||||
function useTokensFromMap(tokenMap: TokenAddressMap, includeUserAdded: boolean): { [address: string]: Token } {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const userAddedTokens = useUserAddedTokens()
|
||||
|
||||
return useMemo(() => {
|
||||
if (!chainId) return {}
|
||||
|
||||
// reduce to just tokens
|
||||
const mapWithoutUrls = Object.keys(tokenMap[chainId] ?? {}).reduce<{ [address: string]: Token }>(
|
||||
(newMap, address) => {
|
||||
newMap[address] = tokenMap[chainId][address].token
|
||||
return newMap
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
if (includeUserAdded) {
|
||||
return (
|
||||
userAddedTokens
|
||||
// reduce into all ALL_TOKENS filtered by the current chain
|
||||
.reduce<{ [address: string]: Token }>(
|
||||
(tokenMap, token) => {
|
||||
tokenMap[token.address] = token
|
||||
return tokenMap
|
||||
},
|
||||
// must make a copy because reduce modifies the map, and we do not
|
||||
// want to make a copy in every iteration
|
||||
{ ...mapWithoutUrls }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return mapWithoutUrls
|
||||
}, [chainId, userAddedTokens, tokenMap, includeUserAdded])
|
||||
}
|
||||
|
||||
export function useAllTokens(): { [address: string]: Token } {
|
||||
const allTokens = useCombinedActiveList()
|
||||
return useTokensFromMap(allTokens, true)
|
||||
}
|
||||
|
||||
type BridgeInfo = Record<
|
||||
SupportedChainId,
|
||||
{
|
||||
tokenAddress: string
|
||||
originBridgeAddress: string
|
||||
destBridgeAddress: string
|
||||
}
|
||||
>
|
||||
|
||||
export function useUnsupportedTokens(): { [address: string]: Token } {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const listsByUrl = useAllLists()
|
||||
const unsupportedTokensMap = useUnsupportedTokenList()
|
||||
const unsupportedTokens = useTokensFromMap(unsupportedTokensMap, false)
|
||||
|
||||
// checks the default L2 lists to see if `bridgeInfo` has an L1 address value that is unsupported
|
||||
const l2InferredBlockedTokens: typeof unsupportedTokens = useMemo(() => {
|
||||
if (!chainId || !L2_CHAIN_IDS.includes(chainId)) {
|
||||
return {}
|
||||
}
|
||||
|
||||
if (!listsByUrl) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const listUrl = CHAIN_INFO[chainId as SupportedL2ChainId].defaultListUrl
|
||||
const { current: list } = listsByUrl[listUrl]
|
||||
if (!list) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const unsupportedSet = new Set(Object.keys(unsupportedTokens))
|
||||
|
||||
return list.tokens.reduce((acc, tokenInfo) => {
|
||||
const bridgeInfo = tokenInfo.extensions?.bridgeInfo as unknown as BridgeInfo
|
||||
if (
|
||||
bridgeInfo &&
|
||||
bridgeInfo[SupportedChainId.MAINNET] &&
|
||||
bridgeInfo[SupportedChainId.MAINNET].tokenAddress &&
|
||||
unsupportedSet.has(bridgeInfo[SupportedChainId.MAINNET].tokenAddress)
|
||||
) {
|
||||
const address = bridgeInfo[SupportedChainId.MAINNET].tokenAddress
|
||||
// don't rely on decimals--it's possible that a token could be bridged w/ different decimals on the L2
|
||||
return { ...acc, [address]: new Token(SupportedChainId.MAINNET, address, tokenInfo.decimals) }
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
}, [chainId, listsByUrl, unsupportedTokens])
|
||||
|
||||
return { ...unsupportedTokens, ...l2InferredBlockedTokens }
|
||||
}
|
||||
|
||||
export function useSearchInactiveTokenLists(search: string | undefined, minResults = 10): WrappedTokenInfo[] {
|
||||
const lists = useAllLists()
|
||||
const inactiveUrls = useInactiveListUrls()
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const activeTokens = useAllTokens()
|
||||
return useMemo(() => {
|
||||
if (!search || search.trim().length === 0) return []
|
||||
const tokenFilter = createTokenFilterFunction(search)
|
||||
const result: WrappedTokenInfo[] = []
|
||||
const addressSet: { [address: string]: true } = {}
|
||||
for (const url of inactiveUrls) {
|
||||
const list = lists[url].current
|
||||
if (!list) continue
|
||||
for (const tokenInfo of list.tokens) {
|
||||
if (tokenInfo.chainId === chainId && tokenFilter(tokenInfo)) {
|
||||
const wrapped: WrappedTokenInfo = new WrappedTokenInfo(tokenInfo, list)
|
||||
if (!(wrapped.address in activeTokens) && !addressSet[wrapped.address]) {
|
||||
addressSet[wrapped.address] = true
|
||||
result.push(wrapped)
|
||||
if (result.length >= minResults) return result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}, [activeTokens, chainId, inactiveUrls, lists, minResults, search])
|
||||
}
|
||||
|
||||
export function useIsTokenActive(token: Token | undefined | null): boolean {
|
||||
const activeTokens = useAllTokens()
|
||||
|
||||
if (!activeTokens || !token) {
|
||||
return false
|
||||
}
|
||||
|
||||
return !!activeTokens[token.address]
|
||||
}
|
||||
|
||||
// Check if currency is included in custom list from user storage
|
||||
export function useIsUserAddedToken(currency: Currency | undefined | null): boolean {
|
||||
const userAddedTokens = useUserAddedTokens()
|
||||
|
||||
if (!currency) {
|
||||
return false
|
||||
}
|
||||
|
||||
return !!userAddedTokens.find((token) => currency.equals(token))
|
||||
}
|
||||
|
||||
// parse a name or symbol from a token response
|
||||
const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/
|
||||
|
||||
function parseStringOrBytes32(str: string | undefined, bytes32: string | undefined, defaultValue: string): string {
|
||||
return str && str.length > 0
|
||||
? str
|
||||
: // need to check for proper bytes string and valid terminator
|
||||
bytes32 && BYTES32_REGEX.test(bytes32) && arrayify(bytes32)[31] === 0
|
||||
? parseBytes32String(bytes32)
|
||||
: defaultValue
|
||||
}
|
||||
|
||||
// undefined if invalid or does not exist
|
||||
// null if loading or null was passed
|
||||
// otherwise returns the token
|
||||
export function useToken(tokenAddress?: string | null): Token | undefined | null {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const tokens = useAllTokens()
|
||||
|
||||
const address = isAddress(tokenAddress)
|
||||
|
||||
const tokenContract = useTokenContract(address ? address : undefined, false)
|
||||
const tokenContractBytes32 = useBytes32TokenContract(address ? address : undefined, false)
|
||||
const token: Token | undefined = address ? tokens[address] : undefined
|
||||
|
||||
const tokenName = useSingleCallResult(token ? undefined : tokenContract, 'name', undefined, NEVER_RELOAD)
|
||||
const tokenNameBytes32 = useSingleCallResult(
|
||||
token ? undefined : tokenContractBytes32,
|
||||
'name',
|
||||
undefined,
|
||||
NEVER_RELOAD
|
||||
)
|
||||
const symbol = useSingleCallResult(token ? undefined : tokenContract, 'symbol', undefined, NEVER_RELOAD)
|
||||
const symbolBytes32 = useSingleCallResult(token ? undefined : tokenContractBytes32, 'symbol', undefined, NEVER_RELOAD)
|
||||
const decimals = useSingleCallResult(token ? undefined : tokenContract, 'decimals', undefined, NEVER_RELOAD)
|
||||
|
||||
return useMemo(() => {
|
||||
if (token) return token
|
||||
if (tokenAddress === null) return null
|
||||
if (!chainId || !address) return undefined
|
||||
if (decimals.loading || symbol.loading || tokenName.loading) return null
|
||||
if (decimals.result) {
|
||||
return new Token(
|
||||
chainId,
|
||||
address,
|
||||
decimals.result[0],
|
||||
parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], 'UNKNOWN'),
|
||||
parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], 'Unknown Token')
|
||||
)
|
||||
}
|
||||
return undefined
|
||||
}, [
|
||||
address,
|
||||
chainId,
|
||||
decimals.loading,
|
||||
decimals.result,
|
||||
symbol.loading,
|
||||
symbol.result,
|
||||
symbolBytes32.result,
|
||||
token,
|
||||
tokenAddress,
|
||||
tokenName.loading,
|
||||
tokenName.result,
|
||||
tokenNameBytes32.result,
|
||||
])
|
||||
}
|
||||
|
||||
export function useCurrency(currencyId: string | null | undefined): Currency | null | undefined {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const isETH = currencyId?.toUpperCase() === 'ETH'
|
||||
const token = useToken(isETH ? undefined : currencyId)
|
||||
const extendedEther = useMemo(
|
||||
() =>
|
||||
chainId
|
||||
? ExtendedEther.onChain(chainId)
|
||||
: // display mainnet when not connected
|
||||
ExtendedEther.onChain(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
|
||||
}
|
56
src/hooks/useActiveLocale.ts
Normal file
56
src/hooks/useActiveLocale.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { DEFAULT_LOCALE, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
|
||||
import { useMemo } from 'react'
|
||||
import store from 'state'
|
||||
import { useUserLocale } from 'state/user/hooks'
|
||||
|
||||
import useParsedQueryString from './useParsedQueryString'
|
||||
import { parsedQueryString } from './useParsedQueryString'
|
||||
|
||||
/**
|
||||
* Given a locale string (e.g. from user agent), return the best match for corresponding SupportedLocale
|
||||
* @param maybeSupportedLocale the fuzzy locale identifier
|
||||
*/
|
||||
function parseLocale(maybeSupportedLocale: unknown): SupportedLocale | undefined {
|
||||
if (typeof maybeSupportedLocale !== 'string') return undefined
|
||||
const lowerMaybeSupportedLocale = maybeSupportedLocale.toLowerCase()
|
||||
return SUPPORTED_LOCALES.find(
|
||||
(locale) => locale.toLowerCase() === lowerMaybeSupportedLocale || locale.split('-')[0] === lowerMaybeSupportedLocale
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported locale read from the user agent (navigator)
|
||||
*/
|
||||
export function navigatorLocale(): SupportedLocale | undefined {
|
||||
if (!navigator.language) return undefined
|
||||
|
||||
const [language, region] = navigator.language.split('-')
|
||||
|
||||
if (region) {
|
||||
return parseLocale(`${language}-${region.toUpperCase()}`) ?? parseLocale(language)
|
||||
}
|
||||
|
||||
return parseLocale(language)
|
||||
}
|
||||
|
||||
function storeLocale(): SupportedLocale | undefined {
|
||||
return store.getState().user.userLocale ?? undefined
|
||||
}
|
||||
|
||||
export const initialLocale =
|
||||
parseLocale(parsedQueryString().lng) ?? storeLocale() ?? navigatorLocale() ?? DEFAULT_LOCALE
|
||||
|
||||
function useUrlLocale() {
|
||||
const parsed = useParsedQueryString()
|
||||
return parseLocale(parsed.lng)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently active locale, from a combination of user agent, query string, and user settings stored in redux
|
||||
* Stores the query string locale in redux (if set) to persist across sessions
|
||||
*/
|
||||
export function useActiveLocale(): SupportedLocale {
|
||||
const urlLocale = useUrlLocale()
|
||||
const userLocale = useUserLocale()
|
||||
return useMemo(() => urlLocale ?? userLocale ?? navigatorLocale() ?? DEFAULT_LOCALE, [urlLocale, userLocale])
|
||||
}
|
43
src/hooks/useAddTokenToMetamask.ts
Normal file
43
src/hooks/useAddTokenToMetamask.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
import { getTokenLogoURL } from './../components/CurrencyLogo/index'
|
||||
|
||||
export default function useAddTokenToMetamask(currencyToAdd: Currency | undefined): {
|
||||
addToken: () => void
|
||||
success: boolean | undefined
|
||||
} {
|
||||
const { library } = useActiveWeb3React()
|
||||
|
||||
const token: Token | undefined = currencyToAdd?.wrapped
|
||||
|
||||
const [success, setSuccess] = useState<boolean | undefined>()
|
||||
|
||||
const addToken = useCallback(() => {
|
||||
if (library && library.provider.isMetaMask && library.provider.request && token) {
|
||||
library.provider
|
||||
.request({
|
||||
method: 'wallet_watchAsset',
|
||||
params: {
|
||||
//@ts-ignore // need this for incorrect ethers provider type
|
||||
type: 'ERC20',
|
||||
options: {
|
||||
address: token.address,
|
||||
symbol: token.symbol,
|
||||
decimals: token.decimals,
|
||||
image: getTokenLogoURL(token.address),
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((success) => {
|
||||
setSuccess(success)
|
||||
})
|
||||
.catch(() => setSuccess(false))
|
||||
} else {
|
||||
setSuccess(false)
|
||||
}
|
||||
}, [library, token])
|
||||
|
||||
return { addToken, success }
|
||||
}
|
72
src/hooks/useAllCurrencyCombinations.ts
Normal file
72
src/hooks/useAllCurrencyCombinations.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { ADDITIONAL_BASES, BASES_TO_CHECK_TRADES_AGAINST, CUSTOM_BASES } from '../constants/routing'
|
||||
|
||||
export function useAllCurrencyCombinations(currencyA?: Currency, currencyB?: Currency): [Token, Token][] {
|
||||
const chainId = currencyA?.chainId
|
||||
|
||||
const [tokenA, tokenB] = chainId ? [currencyA?.wrapped, currencyB?.wrapped] : [undefined, undefined]
|
||||
|
||||
const bases: Token[] = useMemo(() => {
|
||||
if (!chainId || chainId !== tokenB?.chainId) return []
|
||||
|
||||
const common = BASES_TO_CHECK_TRADES_AGAINST[chainId] ?? []
|
||||
const additionalA = tokenA ? ADDITIONAL_BASES[chainId]?.[tokenA.address] ?? [] : []
|
||||
const additionalB = tokenB ? ADDITIONAL_BASES[chainId]?.[tokenB.address] ?? [] : []
|
||||
|
||||
return [...common, ...additionalA, ...additionalB]
|
||||
}, [chainId, tokenA, tokenB])
|
||||
|
||||
const basePairs: [Token, Token][] = useMemo(
|
||||
() =>
|
||||
bases
|
||||
.flatMap((base): [Token, Token][] => bases.map((otherBase) => [base, otherBase]))
|
||||
// though redundant with the first filter below, that expression runs more often, so this is probably worthwhile
|
||||
.filter(([t0, t1]) => !t0.equals(t1)),
|
||||
[bases]
|
||||
)
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
tokenA && tokenB
|
||||
? [
|
||||
// the direct pair
|
||||
[tokenA, tokenB] as [Token, Token],
|
||||
// token A against all bases
|
||||
...bases.map((base): [Token, Token] => [tokenA, base]),
|
||||
// token B against all bases
|
||||
...bases.map((base): [Token, Token] => [tokenB, base]),
|
||||
// each base against all bases
|
||||
...basePairs,
|
||||
]
|
||||
// filter out invalid pairs comprised of the same asset (e.g. WETH<>WETH)
|
||||
.filter(([t0, t1]) => !t0.equals(t1))
|
||||
// filter out duplicate pairs
|
||||
.filter(([t0, t1], i, otherPairs) => {
|
||||
// find the first index in the array at which there are the same 2 tokens as the current
|
||||
const firstIndexInOtherPairs = otherPairs.findIndex(([t0Other, t1Other]) => {
|
||||
return (t0.equals(t0Other) && t1.equals(t1Other)) || (t0.equals(t1Other) && t1.equals(t0Other))
|
||||
})
|
||||
// only accept the first occurrence of the same 2 tokens
|
||||
return firstIndexInOtherPairs === i
|
||||
})
|
||||
// optionally filter out some pairs for tokens with custom bases defined
|
||||
.filter(([tokenA, tokenB]) => {
|
||||
if (!chainId) return true
|
||||
const customBases = CUSTOM_BASES[chainId]
|
||||
|
||||
const customBasesA: Token[] | undefined = customBases?.[tokenA.address]
|
||||
const customBasesB: Token[] | undefined = customBases?.[tokenB.address]
|
||||
|
||||
if (!customBasesA && !customBasesB) return true
|
||||
|
||||
if (customBasesA && !customBasesA.find((base) => tokenB.equals(base))) return false
|
||||
if (customBasesB && !customBasesB.find((base) => tokenA.equals(base))) return false
|
||||
|
||||
return true
|
||||
})
|
||||
: [],
|
||||
[tokenA, tokenB, bases, basePairs, chainId]
|
||||
)
|
||||
}
|
75
src/hooks/useAllV3Routes.ts
Normal file
75
src/hooks/useAllV3Routes.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { Pool, Route } from '@uniswap/v3-sdk'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useV3SwapPools } from './useV3SwapPools'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
/**
|
||||
* Returns true if poolA is equivalent to poolB
|
||||
* @param poolA one of the two pools
|
||||
* @param poolB the other pool
|
||||
*/
|
||||
function poolEquals(poolA: Pool, poolB: Pool): boolean {
|
||||
return (
|
||||
poolA === poolB ||
|
||||
(poolA.token0.equals(poolB.token0) && poolA.token1.equals(poolB.token1) && poolA.fee === poolB.fee)
|
||||
)
|
||||
}
|
||||
|
||||
function computeAllRoutes(
|
||||
currencyIn: Currency,
|
||||
currencyOut: Currency,
|
||||
pools: Pool[],
|
||||
chainId: number,
|
||||
currentPath: Pool[] = [],
|
||||
allPaths: Route<Currency, Currency>[] = [],
|
||||
startCurrencyIn: Currency = currencyIn,
|
||||
maxHops = 2
|
||||
): Route<Currency, Currency>[] {
|
||||
const tokenIn = currencyIn?.wrapped
|
||||
const tokenOut = currencyOut?.wrapped
|
||||
if (!tokenIn || !tokenOut) throw new Error('Missing tokenIn/tokenOut')
|
||||
|
||||
for (const pool of pools) {
|
||||
if (!pool.involvesToken(tokenIn) || currentPath.find((pathPool) => poolEquals(pool, pathPool))) continue
|
||||
|
||||
const outputToken = pool.token0.equals(tokenIn) ? pool.token1 : pool.token0
|
||||
if (outputToken.equals(tokenOut)) {
|
||||
allPaths.push(new Route([...currentPath, pool], startCurrencyIn, currencyOut))
|
||||
} else if (maxHops > 1) {
|
||||
computeAllRoutes(
|
||||
outputToken,
|
||||
currencyOut,
|
||||
pools,
|
||||
chainId,
|
||||
[...currentPath, pool],
|
||||
allPaths,
|
||||
startCurrencyIn,
|
||||
maxHops - 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return allPaths
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the routes from an input currency to an output currency
|
||||
* @param currencyIn the input currency
|
||||
* @param currencyOut the output currency
|
||||
*/
|
||||
export function useAllV3Routes(
|
||||
currencyIn?: Currency,
|
||||
currencyOut?: Currency
|
||||
): { loading: boolean; routes: Route<Currency, Currency>[] } {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const { pools, loading: poolsLoading } = useV3SwapPools(currencyIn, currencyOut)
|
||||
|
||||
return useMemo(() => {
|
||||
if (poolsLoading || !chainId || !pools || !currencyIn || !currencyOut) return { loading: true, routes: [] }
|
||||
|
||||
const routes = computeAllRoutes(currencyIn, currencyOut, pools, chainId, [], [], currencyIn, 2)
|
||||
return { loading: false, routes }
|
||||
}, [chainId, currencyIn, currencyOut, pools, poolsLoading])
|
||||
}
|
22
src/hooks/useApeModeQueryParamReader.ts
Normal file
22
src/hooks/useApeModeQueryParamReader.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
|
||||
import { updateUserExpertMode } from '../state/user/actions'
|
||||
import useParsedQueryString from './useParsedQueryString'
|
||||
|
||||
export default function ApeModeQueryParamReader(): null {
|
||||
useApeModeQueryParamReader()
|
||||
return null
|
||||
}
|
||||
|
||||
function useApeModeQueryParamReader() {
|
||||
const dispatch = useAppDispatch()
|
||||
const { ape } = useParsedQueryString()
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof ape !== 'string') return
|
||||
if (ape === '' || ape.toLowerCase() === 'true') {
|
||||
dispatch(updateUserExpertMode({ userExpertMode: true }))
|
||||
}
|
||||
})
|
||||
}
|
15
src/hooks/useArgentWalletContract.ts
Normal file
15
src/hooks/useArgentWalletContract.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import ArgentWalletContractABI from '../abis/argent-wallet-contract.json'
|
||||
import { ArgentWalletContract } from '../abis/types'
|
||||
import { useContract } from './useContract'
|
||||
import useIsArgentWallet from './useIsArgentWallet'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
export function useArgentWalletContract(): ArgentWalletContract | null {
|
||||
const { account } = useActiveWeb3React()
|
||||
const isArgentWallet = useIsArgentWallet()
|
||||
return useContract(
|
||||
isArgentWallet ? account ?? undefined : undefined,
|
||||
ArgentWalletContractABI,
|
||||
true
|
||||
) as ArgentWalletContract
|
||||
}
|
87
src/hooks/useBestV2Trade.ts
Normal file
87
src/hooks/useBestV2Trade.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
|
||||
import { Pair, Trade } from '@uniswap/v2-sdk'
|
||||
import { useMemo } from 'react'
|
||||
import { isTradeBetter } from 'utils/isTradeBetter'
|
||||
|
||||
import { BETTER_TRADE_LESS_HOPS_THRESHOLD } from '../constants/misc'
|
||||
import { useAllCurrencyCombinations } from './useAllCurrencyCombinations'
|
||||
import { PairState, useV2Pairs } from './useV2Pairs'
|
||||
|
||||
function useAllCommonPairs(currencyA?: Currency, currencyB?: Currency): Pair[] {
|
||||
const allCurrencyCombinations = useAllCurrencyCombinations(currencyA, currencyB)
|
||||
|
||||
const allPairs = useV2Pairs(allCurrencyCombinations)
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
Object.values(
|
||||
allPairs
|
||||
// filter out invalid pairs
|
||||
.filter((result): result is [PairState.EXISTS, Pair] => Boolean(result[0] === PairState.EXISTS && result[1]))
|
||||
.map(([, pair]) => pair)
|
||||
),
|
||||
[allPairs]
|
||||
)
|
||||
}
|
||||
|
||||
const MAX_HOPS = 3
|
||||
|
||||
/**
|
||||
* Returns the best v2 trade for a desired swap
|
||||
* @param tradeType whether the swap is an exact in/out
|
||||
* @param amountSpecified the exact amount to swap in/out
|
||||
* @param otherCurrency the desired output/payment currency
|
||||
*/
|
||||
export function useBestV2Trade(
|
||||
tradeType: TradeType.EXACT_INPUT | TradeType.EXACT_OUTPUT,
|
||||
amountSpecified?: CurrencyAmount<Currency>,
|
||||
otherCurrency?: Currency,
|
||||
{ maxHops = MAX_HOPS } = {}
|
||||
): Trade<Currency, Currency, TradeType.EXACT_INPUT | TradeType.EXACT_OUTPUT> | null {
|
||||
const [currencyIn, currencyOut] = useMemo(
|
||||
() =>
|
||||
tradeType === TradeType.EXACT_INPUT
|
||||
? [amountSpecified?.currency, otherCurrency]
|
||||
: [otherCurrency, amountSpecified?.currency],
|
||||
[tradeType, amountSpecified, otherCurrency]
|
||||
)
|
||||
const allowedPairs = useAllCommonPairs(currencyIn, currencyOut)
|
||||
|
||||
return useMemo(() => {
|
||||
if (amountSpecified && currencyIn && currencyOut && allowedPairs.length > 0) {
|
||||
if (maxHops === 1) {
|
||||
const options = { maxHops: 1, maxNumResults: 1 }
|
||||
if (tradeType === TradeType.EXACT_INPUT) {
|
||||
const amountIn = amountSpecified
|
||||
return Trade.bestTradeExactIn(allowedPairs, amountIn, currencyOut, options)[0] ?? null
|
||||
} else {
|
||||
const amountOut = amountSpecified
|
||||
return Trade.bestTradeExactOut(allowedPairs, currencyIn, amountOut, options)[0] ?? null
|
||||
}
|
||||
}
|
||||
|
||||
// search through trades with varying hops, find best trade out of them
|
||||
let bestTradeSoFar: Trade<Currency, Currency, TradeType.EXACT_INPUT | TradeType.EXACT_OUTPUT> | null = null
|
||||
for (let i = 1; i <= maxHops; i++) {
|
||||
const options = { maxHops: i, maxNumResults: 1 }
|
||||
let currentTrade: Trade<Currency, Currency, TradeType.EXACT_INPUT | TradeType.EXACT_OUTPUT> | null
|
||||
|
||||
if (tradeType === TradeType.EXACT_INPUT) {
|
||||
const amountIn = amountSpecified
|
||||
currentTrade = Trade.bestTradeExactIn(allowedPairs, amountIn, currencyOut, options)[0] ?? null
|
||||
} else {
|
||||
const amountOut = amountSpecified
|
||||
currentTrade = Trade.bestTradeExactOut(allowedPairs, currencyIn, amountOut, options)[0] ?? null
|
||||
}
|
||||
|
||||
// if current trade is best yet, save it
|
||||
if (isTradeBetter(bestTradeSoFar, currentTrade, BETTER_TRADE_LESS_HOPS_THRESHOLD)) {
|
||||
bestTradeSoFar = currentTrade
|
||||
}
|
||||
}
|
||||
return bestTradeSoFar
|
||||
}
|
||||
|
||||
return null
|
||||
}, [tradeType, amountSpecified, currencyIn, currencyOut, allowedPairs, maxHops])
|
||||
}
|
108
src/hooks/useColor.ts
Normal file
108
src/hooks/useColor.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import { Token } from '@uniswap/sdk-core'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import Vibrant from 'node-vibrant/lib/bundle'
|
||||
import { shade } from 'polished'
|
||||
import { useLayoutEffect, useState } from 'react'
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
import uriToHttp from 'utils/uriToHttp'
|
||||
import { hex } from 'wcag-contrast'
|
||||
|
||||
function URIForEthToken(address: string) {
|
||||
return `https://raw.githubusercontent.com/uniswap/assets/master/blockchains/ethereum/assets/${address}/logo.png`
|
||||
}
|
||||
|
||||
async function getColorFromToken(token: Token): Promise<string | null> {
|
||||
if (!(token instanceof WrappedTokenInfo)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const wrappedToken = token as WrappedTokenInfo
|
||||
const { address } = wrappedToken
|
||||
let { logoURI } = wrappedToken
|
||||
if (!logoURI) {
|
||||
if (token.chainId !== SupportedChainId.MAINNET) {
|
||||
return null
|
||||
} else {
|
||||
logoURI = URIForEthToken(address)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return await getColorFromUriPath(logoURI)
|
||||
} catch (e) {
|
||||
if (logoURI === URIForEthToken(address)) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
logoURI = URIForEthToken(address)
|
||||
return await getColorFromUriPath(logoURI)
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
async function getColorFromUriPath(uri: string): Promise<string | null> {
|
||||
const formattedPath = uriToHttp(uri)[0]
|
||||
|
||||
const palette = await Vibrant.from(formattedPath).getPalette()
|
||||
if (!palette?.Vibrant) {
|
||||
return null
|
||||
}
|
||||
|
||||
let detectedHex = palette.Vibrant.hex
|
||||
let AAscore = hex(detectedHex, '#FFF')
|
||||
while (AAscore < 3) {
|
||||
detectedHex = shade(0.005, detectedHex)
|
||||
AAscore = hex(detectedHex, '#FFF')
|
||||
}
|
||||
|
||||
return detectedHex
|
||||
}
|
||||
|
||||
export function useColor(token?: Token) {
|
||||
const [color, setColor] = useState('#2172E5')
|
||||
|
||||
useLayoutEffect(() => {
|
||||
let stale = false
|
||||
|
||||
if (token) {
|
||||
getColorFromToken(token).then((tokenColor) => {
|
||||
if (!stale && tokenColor !== null) {
|
||||
setColor(tokenColor)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
stale = true
|
||||
setColor('#2172E5')
|
||||
}
|
||||
}, [token])
|
||||
|
||||
return color
|
||||
}
|
||||
|
||||
export function useListColor(listImageUri?: string) {
|
||||
const [color, setColor] = useState('#2172E5')
|
||||
|
||||
useLayoutEffect(() => {
|
||||
let stale = false
|
||||
|
||||
if (listImageUri) {
|
||||
getColorFromUriPath(listImageUri).then((color) => {
|
||||
if (!stale && color !== null) {
|
||||
setColor(color)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return () => {
|
||||
stale = true
|
||||
setColor('#2172E5')
|
||||
}
|
||||
}, [listImageUri])
|
||||
|
||||
return color
|
||||
}
|
157
src/hooks/useContract.ts
Normal file
157
src/hooks/useContract.ts
Normal file
@ -0,0 +1,157 @@
|
||||
import { Contract } from '@ethersproject/contracts'
|
||||
import { abi as GOVERNANCE_ABI } from '@uniswap/governance/build/GovernorAlpha.json'
|
||||
import { abi as UNI_ABI } from '@uniswap/governance/build/Uni.json'
|
||||
import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
|
||||
import { abi as MERKLE_DISTRIBUTOR_ABI } from '@uniswap/merkle-distributor/build/MerkleDistributor.json'
|
||||
import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
|
||||
import { abi as IUniswapV2Router02ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router02.json'
|
||||
import { abi as QuoterABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/Quoter.sol/Quoter.json'
|
||||
import { abi as MulticallABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json'
|
||||
import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
|
||||
import { abi as V2MigratorABI } from '@uniswap/v3-periphery/artifacts/contracts/V3Migrator.sol/V3Migrator.json'
|
||||
import ARGENT_WALLET_DETECTOR_ABI from 'abis/argent-wallet-detector.json'
|
||||
import EIP_2612 from 'abis/eip_2612.json'
|
||||
import ENS_PUBLIC_RESOLVER_ABI from 'abis/ens-public-resolver.json'
|
||||
import ENS_ABI from 'abis/ens-registrar.json'
|
||||
import ERC20_ABI from 'abis/erc20.json'
|
||||
import ERC20_BYTES32_ABI from 'abis/erc20_bytes32.json'
|
||||
import ERC721_ABI from 'abis/erc721.json'
|
||||
import ERC1155_ABI from 'abis/erc1155.json'
|
||||
import GOVERNOR_BRAVO_ABI from 'abis/governor-bravo.json'
|
||||
import WETH_ABI from 'abis/weth.json'
|
||||
import {
|
||||
ARGENT_WALLET_DETECTOR_ADDRESS,
|
||||
ENS_REGISTRAR_ADDRESSES,
|
||||
GOVERNANCE_ALPHA_V0_ADDRESSES,
|
||||
GOVERNANCE_ALPHA_V1_ADDRESSES,
|
||||
GOVERNANCE_BRAVO_ADDRESSES,
|
||||
MERKLE_DISTRIBUTOR_ADDRESS,
|
||||
MULTICALL_ADDRESS,
|
||||
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
|
||||
QUOTER_ADDRESSES,
|
||||
V2_ROUTER_ADDRESS,
|
||||
V3_MIGRATOR_ADDRESSES,
|
||||
} from 'constants/addresses'
|
||||
import { useMemo } from 'react'
|
||||
import { NonfungiblePositionManager, Quoter, UniswapInterfaceMulticall } from 'types/v3'
|
||||
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 { useActiveWeb3React } from './web3'
|
||||
|
||||
// returns null on errors
|
||||
export function useContract<T extends Contract = Contract>(
|
||||
addressOrAddressMap: string | { [chainId: number]: string } | undefined,
|
||||
ABI: any,
|
||||
withSignerIfPossible = true
|
||||
): T | null {
|
||||
const { library, account, chainId } = useActiveWeb3React()
|
||||
|
||||
return useMemo(() => {
|
||||
if (!addressOrAddressMap || !ABI || !library || !chainId) return null
|
||||
let address: string | undefined
|
||||
if (typeof addressOrAddressMap === 'string') address = addressOrAddressMap
|
||||
else address = addressOrAddressMap[chainId]
|
||||
if (!address) return null
|
||||
try {
|
||||
return getContract(address, ABI, library, withSignerIfPossible && account ? account : undefined)
|
||||
} catch (error) {
|
||||
console.error('Failed to get contract', error)
|
||||
return null
|
||||
}
|
||||
}, [addressOrAddressMap, ABI, library, chainId, withSignerIfPossible, account]) as T
|
||||
}
|
||||
|
||||
export function useV2MigratorContract() {
|
||||
return useContract<V3Migrator>(V3_MIGRATOR_ADDRESSES, V2MigratorABI, true)
|
||||
}
|
||||
|
||||
export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean) {
|
||||
return useContract<Erc20>(tokenAddress, ERC20_ABI, withSignerIfPossible)
|
||||
}
|
||||
|
||||
export function useWETHContract(withSignerIfPossible?: boolean) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
return useContract<Weth>(chainId ? WETH9_EXTENDED[chainId]?.address : undefined, WETH_ABI, withSignerIfPossible)
|
||||
}
|
||||
|
||||
export function useERC721Contract(nftAddress?: string) {
|
||||
return useContract<Erc721>(nftAddress, ERC721_ABI, false)
|
||||
}
|
||||
|
||||
export function useERC1155Contract(nftAddress?: string) {
|
||||
return useContract<Erc1155>(nftAddress, ERC1155_ABI, false)
|
||||
}
|
||||
|
||||
export function useArgentWalletDetectorContract() {
|
||||
return useContract<ArgentWalletDetector>(ARGENT_WALLET_DETECTOR_ADDRESS, ARGENT_WALLET_DETECTOR_ABI, false)
|
||||
}
|
||||
|
||||
export function useENSRegistrarContract(withSignerIfPossible?: boolean) {
|
||||
return useContract<EnsRegistrar>(ENS_REGISTRAR_ADDRESSES, ENS_ABI, withSignerIfPossible)
|
||||
}
|
||||
|
||||
export function useENSResolverContract(address: string | undefined, withSignerIfPossible?: boolean) {
|
||||
return useContract<EnsPublicResolver>(address, ENS_PUBLIC_RESOLVER_ABI, withSignerIfPossible)
|
||||
}
|
||||
|
||||
export function useBytes32TokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
|
||||
return useContract(tokenAddress, ERC20_BYTES32_ABI, withSignerIfPossible)
|
||||
}
|
||||
|
||||
export function useEIP2612Contract(tokenAddress?: string): Contract | null {
|
||||
return useContract(tokenAddress, EIP_2612, false)
|
||||
}
|
||||
|
||||
export function usePairContract(pairAddress?: string, withSignerIfPossible?: boolean): Contract | null {
|
||||
return useContract(pairAddress, IUniswapV2PairABI, withSignerIfPossible)
|
||||
}
|
||||
|
||||
export function useV2RouterContract(): Contract | null {
|
||||
return useContract(V2_ROUTER_ADDRESS, IUniswapV2Router02ABI, true)
|
||||
}
|
||||
|
||||
export function useMulticall2Contract() {
|
||||
return useContract<UniswapInterfaceMulticall>(MULTICALL_ADDRESS, MulticallABI, false) as UniswapInterfaceMulticall
|
||||
}
|
||||
|
||||
export function useMerkleDistributorContract() {
|
||||
return useContract(MERKLE_DISTRIBUTOR_ADDRESS, MERKLE_DISTRIBUTOR_ABI, true)
|
||||
}
|
||||
|
||||
export function useGovernanceV0Contract(): Contract | null {
|
||||
return useContract(GOVERNANCE_ALPHA_V0_ADDRESSES, GOVERNANCE_ABI, false)
|
||||
}
|
||||
|
||||
export function useGovernanceV1Contract(): Contract | null {
|
||||
return useContract(GOVERNANCE_ALPHA_V1_ADDRESSES, GOVERNANCE_ABI, false)
|
||||
}
|
||||
|
||||
export function useGovernanceBravoContract(): Contract | null {
|
||||
return useContract(GOVERNANCE_BRAVO_ADDRESSES, GOVERNOR_BRAVO_ABI, true)
|
||||
}
|
||||
|
||||
export const useLatestGovernanceContract = useGovernanceBravoContract
|
||||
|
||||
export function useUniContract() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
return useContract(chainId ? UNI[chainId]?.address : undefined, UNI_ABI, true)
|
||||
}
|
||||
|
||||
export function useStakingContract(stakingAddress?: string, withSignerIfPossible?: boolean) {
|
||||
return useContract(stakingAddress, STAKING_REWARDS_ABI, withSignerIfPossible)
|
||||
}
|
||||
|
||||
export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean): NonfungiblePositionManager | null {
|
||||
return useContract<NonfungiblePositionManager>(
|
||||
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
|
||||
NFTPositionManagerABI,
|
||||
withSignerIfPossible
|
||||
)
|
||||
}
|
||||
|
||||
export function useV3Quoter() {
|
||||
return useContract<Quoter>(QUOTER_ADDRESSES, QuoterABI)
|
||||
}
|
26
src/hooks/useCopyClipboard.ts
Normal file
26
src/hooks/useCopyClipboard.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import copy from 'copy-to-clipboard'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
export default function useCopyClipboard(timeout = 500): [boolean, (toCopy: string) => void] {
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
|
||||
const staticCopy = useCallback((text) => {
|
||||
const didCopy = copy(text)
|
||||
setIsCopied(didCopy)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (isCopied) {
|
||||
const hide = setTimeout(() => {
|
||||
setIsCopied(false)
|
||||
}, timeout)
|
||||
|
||||
return () => {
|
||||
clearTimeout(hide)
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}, [isCopied, setIsCopied, timeout])
|
||||
|
||||
return [isCopied, staticCopy]
|
||||
}
|
10
src/hooks/useCurrentBlockTimestamp.ts
Normal file
10
src/hooks/useCurrentBlockTimestamp.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useMulticall2Contract } from './useContract'
|
||||
|
||||
// gets the current timestamp from the blockchain
|
||||
export default function useCurrentBlockTimestamp(): BigNumber | undefined {
|
||||
const multicall = useMulticall2Contract()
|
||||
return useSingleCallResult(multicall, 'getCurrentBlockTimestamp')?.result?.[0]
|
||||
}
|
22
src/hooks/useDebounce.ts
Normal file
22
src/hooks/useDebounce.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
// modified from https://usehooks.com/useDebounce/
|
||||
export default function useDebounce<T>(value: T, delay: number): T {
|
||||
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
||||
|
||||
useEffect(() => {
|
||||
// Update debounced value after delay
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(value)
|
||||
}, delay)
|
||||
|
||||
// Cancel the timeout if value changes (also on delay change or unmount)
|
||||
// This is how we prevent debounced value from updating if value is changed ...
|
||||
// .. within the delay period. Timeout gets cleared and restarted.
|
||||
return () => {
|
||||
clearTimeout(handler)
|
||||
}
|
||||
}, [value, delay])
|
||||
|
||||
return debouncedValue
|
||||
}
|
40
src/hooks/useDebouncedChangeHandler.tsx
Normal file
40
src/hooks/useDebouncedChangeHandler.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
/**
|
||||
* Easy way to debounce the handling of a rapidly changing value, e.g. a changing slider input
|
||||
* @param value value that is rapidly changing
|
||||
* @param onChange change handler that should receive the debounced updates to the value
|
||||
* @param debouncedMs how long we should wait for changes to be applied
|
||||
*/
|
||||
export default function useDebouncedChangeHandler<T>(
|
||||
value: T,
|
||||
onChange: (newValue: T) => void,
|
||||
debouncedMs = 100
|
||||
): [T, (value: T) => void] {
|
||||
const [inner, setInner] = useState<T>(() => value)
|
||||
const timer = useRef<ReturnType<typeof setTimeout>>()
|
||||
|
||||
const onChangeInner = useCallback(
|
||||
(newValue: T) => {
|
||||
setInner(newValue)
|
||||
if (timer.current) {
|
||||
clearTimeout(timer.current)
|
||||
}
|
||||
timer.current = setTimeout(() => {
|
||||
onChange(newValue)
|
||||
timer.current = undefined
|
||||
}, debouncedMs)
|
||||
},
|
||||
[debouncedMs, onChange]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (timer.current) {
|
||||
clearTimeout(timer.current)
|
||||
timer.current = undefined
|
||||
}
|
||||
setInner(value)
|
||||
}, [value])
|
||||
|
||||
return [inner, onChangeInner]
|
||||
}
|
31
src/hooks/useDerivedPositionInfo.ts
Normal file
31
src/hooks/useDerivedPositionInfo.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { Pool, Position } from '@uniswap/v3-sdk'
|
||||
import { usePool } from 'hooks/usePools'
|
||||
import { PositionDetails } from 'types/position'
|
||||
|
||||
import { useCurrency } from './Tokens'
|
||||
|
||||
export function useDerivedPositionInfo(positionDetails: PositionDetails | undefined): {
|
||||
position: Position | undefined
|
||||
pool: Pool | undefined
|
||||
} {
|
||||
const currency0 = useCurrency(positionDetails?.token0)
|
||||
const currency1 = useCurrency(positionDetails?.token1)
|
||||
|
||||
// construct pool data
|
||||
const [, pool] = usePool(currency0 ?? undefined, currency1 ?? undefined, positionDetails?.fee)
|
||||
|
||||
let position = undefined
|
||||
if (pool && positionDetails) {
|
||||
position = new Position({
|
||||
pool,
|
||||
liquidity: positionDetails.liquidity.toString(),
|
||||
tickLower: positionDetails.tickLower,
|
||||
tickUpper: positionDetails.tickUpper,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
position,
|
||||
pool: pool ?? undefined,
|
||||
}
|
||||
}
|
23
src/hooks/useENS.ts
Normal file
23
src/hooks/useENS.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { isAddress } from '../utils'
|
||||
import useENSAddress from './useENSAddress'
|
||||
import useENSName from './useENSName'
|
||||
|
||||
/**
|
||||
* Given a name or address, does a lookup to resolve to an address and name
|
||||
* @param nameOrAddress ENS name or address
|
||||
*/
|
||||
export default function useENS(nameOrAddress?: string | null): {
|
||||
loading: boolean
|
||||
address: string | null
|
||||
name: string | null
|
||||
} {
|
||||
const validated = isAddress(nameOrAddress)
|
||||
const reverseLookup = useENSName(validated ? validated : undefined)
|
||||
const lookup = useENSAddress(nameOrAddress)
|
||||
|
||||
return {
|
||||
loading: reverseLookup.loading || lookup.loading,
|
||||
address: validated ? validated : lookup.address,
|
||||
name: reverseLookup.ENSName ? reverseLookup.ENSName : !validated && lookup.address ? nameOrAddress || null : null,
|
||||
}
|
||||
}
|
36
src/hooks/useENSAddress.ts
Normal file
36
src/hooks/useENSAddress.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { namehash } from '@ethersproject/hash'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import isZero from '../utils/isZero'
|
||||
import { useENSRegistrarContract, useENSResolverContract } from './useContract'
|
||||
import useDebounce from './useDebounce'
|
||||
|
||||
/**
|
||||
* Does a lookup for an ENS name to find its address.
|
||||
*/
|
||||
export default function useENSAddress(ensName?: string | null): { loading: boolean; address: string | null } {
|
||||
const debouncedName = useDebounce(ensName, 200)
|
||||
const ensNodeArgument = useMemo(() => {
|
||||
if (!debouncedName) return [undefined]
|
||||
try {
|
||||
return debouncedName ? [namehash(debouncedName)] : [undefined]
|
||||
} catch (error) {
|
||||
return [undefined]
|
||||
}
|
||||
}, [debouncedName])
|
||||
const registrarContract = useENSRegistrarContract(false)
|
||||
const resolverAddress = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument)
|
||||
const resolverAddressResult = resolverAddress.result?.[0]
|
||||
const resolverContract = useENSResolverContract(
|
||||
resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined,
|
||||
false
|
||||
)
|
||||
const addr = useSingleCallResult(resolverContract, 'addr', ensNodeArgument)
|
||||
|
||||
const changed = debouncedName !== ensName
|
||||
return {
|
||||
address: changed ? null : addr.result?.[0] ?? null,
|
||||
loading: changed || resolverAddress.loading || addr.loading,
|
||||
}
|
||||
}
|
131
src/hooks/useENSAvatar.ts
Normal file
131
src/hooks/useENSAvatar.ts
Normal file
@ -0,0 +1,131 @@
|
||||
import { namehash } from '@ethersproject/hash'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import uriToHttp from 'utils/uriToHttp'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { isAddress } from '../utils'
|
||||
import isZero from '../utils/isZero'
|
||||
import { useENSRegistrarContract, useENSResolverContract, useERC721Contract, useERC1155Contract } from './useContract'
|
||||
import useDebounce from './useDebounce'
|
||||
import useENSName from './useENSName'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
/**
|
||||
* Returns the ENS avatar URI, if available.
|
||||
* Spec: https://gist.github.com/Arachnid/9db60bd75277969ee1689c8742b75182.
|
||||
*/
|
||||
export default function useENSAvatar(
|
||||
address?: string,
|
||||
enforceOwnership = true
|
||||
): { avatar: string | null; loading: boolean } {
|
||||
const debouncedAddress = useDebounce(address, 200)
|
||||
const node = useMemo(() => {
|
||||
if (!debouncedAddress || !isAddress(debouncedAddress)) return undefined
|
||||
try {
|
||||
return debouncedAddress ? namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`) : undefined
|
||||
} catch (error) {
|
||||
return undefined
|
||||
}
|
||||
}, [debouncedAddress])
|
||||
|
||||
const addressAvatar = useAvatarFromNode(node)
|
||||
const nameAvatar = useAvatarFromNode(namehash(useENSName(address).ENSName ?? ''))
|
||||
let avatar = addressAvatar.avatar || nameAvatar.avatar
|
||||
|
||||
const nftAvatar = useAvatarFromNFT(avatar, enforceOwnership)
|
||||
avatar = nftAvatar.avatar || avatar
|
||||
|
||||
const http = avatar && uriToHttp(avatar)[0]
|
||||
|
||||
const changed = debouncedAddress !== address
|
||||
return {
|
||||
avatar: changed ? null : http ?? null,
|
||||
loading: changed || addressAvatar.loading || nameAvatar.loading || nftAvatar.loading,
|
||||
}
|
||||
}
|
||||
|
||||
function useAvatarFromNode(node?: string): { avatar?: string; loading: boolean } {
|
||||
const nodeArgument = useMemo(() => [node], [node])
|
||||
const textArgument = useMemo(() => [node, 'avatar'], [node])
|
||||
const registrarContract = useENSRegistrarContract(false)
|
||||
const resolverAddress = useSingleCallResult(registrarContract, 'resolver', nodeArgument)
|
||||
const resolverAddressResult = resolverAddress.result?.[0]
|
||||
const resolverContract = useENSResolverContract(
|
||||
resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined,
|
||||
false
|
||||
)
|
||||
const avatar = useSingleCallResult(resolverContract, 'text', textArgument)
|
||||
|
||||
return {
|
||||
avatar: avatar.result?.[0],
|
||||
loading: resolverAddress.loading || avatar.loading,
|
||||
}
|
||||
}
|
||||
|
||||
function useAvatarFromNFT(nftUri = '', enforceOwnership: boolean): { avatar?: string; loading: boolean } {
|
||||
const parts = nftUri.toLowerCase().split(':')
|
||||
const protocol = parts[0]
|
||||
// ignore the chain from eip155
|
||||
// TODO: when we are able, pull only from the specified chain
|
||||
const [, erc] = parts[1]?.split('/') ?? []
|
||||
const [contractAddress, id] = parts[2]?.split('/') ?? []
|
||||
const isERC721 = protocol === 'eip155' && erc === 'erc721'
|
||||
const isERC1155 = protocol === 'eip155' && erc === 'erc1155'
|
||||
const erc721 = useERC721Uri(isERC721 ? contractAddress : undefined, id, enforceOwnership)
|
||||
const erc1155 = useERC1155Uri(isERC1155 ? contractAddress : undefined, id, enforceOwnership)
|
||||
const uri = erc721.uri || erc1155.uri
|
||||
const http = uri && uriToHttp(uri)[0]
|
||||
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [avatar, setAvatar] = useState(undefined)
|
||||
useEffect(() => {
|
||||
setAvatar(undefined)
|
||||
if (http) {
|
||||
setLoading(true)
|
||||
fetch(http)
|
||||
.then((res) => res.json())
|
||||
.then(({ image }) => {
|
||||
setAvatar(image)
|
||||
})
|
||||
.catch((e) => console.warn(e))
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
}, [http])
|
||||
|
||||
return { avatar, loading: erc721.loading || erc1155.loading || loading }
|
||||
}
|
||||
|
||||
function useERC721Uri(
|
||||
contractAddress: string | undefined,
|
||||
id: string | undefined,
|
||||
enforceOwnership: boolean
|
||||
): { uri?: string; loading: boolean } {
|
||||
const idArgument = useMemo(() => [id], [id])
|
||||
const { account } = useActiveWeb3React()
|
||||
const contract = useERC721Contract(contractAddress)
|
||||
const owner = useSingleCallResult(contract, 'ownerOf', idArgument)
|
||||
const uri = useSingleCallResult(contract, 'tokenURI', idArgument)
|
||||
return {
|
||||
uri: !enforceOwnership || account === owner.result?.[0] ? uri.result?.[0] : undefined,
|
||||
loading: owner.loading || uri.loading,
|
||||
}
|
||||
}
|
||||
|
||||
function useERC1155Uri(
|
||||
contractAddress: string | undefined,
|
||||
id: string | undefined,
|
||||
enforceOwnership: boolean
|
||||
): { uri?: string; loading: boolean } {
|
||||
const { account } = useActiveWeb3React()
|
||||
const idArgument = useMemo(() => [id], [id])
|
||||
const accountArgument = useMemo(() => [account || '', id], [account, id])
|
||||
const contract = useERC1155Contract(contractAddress)
|
||||
const balance = useSingleCallResult(contract, 'balanceOf', accountArgument)
|
||||
const uri = useSingleCallResult(contract, 'uri', idArgument)
|
||||
return {
|
||||
uri: !enforceOwnership || balance.result?.[0] > 0 ? uri.result?.[0] : undefined,
|
||||
loading: balance.loading || uri.loading,
|
||||
}
|
||||
}
|
33
src/hooks/useENSContentHash.ts
Normal file
33
src/hooks/useENSContentHash.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { namehash } from '@ethersproject/hash'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import isZero from '../utils/isZero'
|
||||
import { useENSRegistrarContract, useENSResolverContract } from './useContract'
|
||||
|
||||
/**
|
||||
* Does a lookup for an ENS name to find its contenthash.
|
||||
*/
|
||||
export default function useENSContentHash(ensName?: string | null): { loading: boolean; contenthash: string | null } {
|
||||
const ensNodeArgument = useMemo(() => {
|
||||
if (!ensName) return [undefined]
|
||||
try {
|
||||
return ensName ? [namehash(ensName)] : [undefined]
|
||||
} catch (error) {
|
||||
return [undefined]
|
||||
}
|
||||
}, [ensName])
|
||||
const registrarContract = useENSRegistrarContract(false)
|
||||
const resolverAddressResult = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument)
|
||||
const resolverAddress = resolverAddressResult.result?.[0]
|
||||
const resolverContract = useENSResolverContract(
|
||||
resolverAddress && isZero(resolverAddress) ? undefined : resolverAddress,
|
||||
false
|
||||
)
|
||||
const contenthash = useSingleCallResult(resolverContract, 'contenthash', ensNodeArgument)
|
||||
|
||||
return {
|
||||
contenthash: contenthash.result?.[0] ?? null,
|
||||
loading: resolverAddressResult.loading || contenthash.loading,
|
||||
}
|
||||
}
|
38
src/hooks/useENSName.ts
Normal file
38
src/hooks/useENSName.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { namehash } from '@ethersproject/hash'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { isAddress } from '../utils'
|
||||
import isZero from '../utils/isZero'
|
||||
import { useENSRegistrarContract, useENSResolverContract } from './useContract'
|
||||
import useDebounce from './useDebounce'
|
||||
|
||||
/**
|
||||
* Does a reverse lookup for an address to find its ENS name.
|
||||
* Note this is not the same as looking up an ENS name to find an address.
|
||||
*/
|
||||
export default function useENSName(address?: string): { ENSName: string | null; loading: boolean } {
|
||||
const debouncedAddress = useDebounce(address, 200)
|
||||
const ensNodeArgument = useMemo(() => {
|
||||
if (!debouncedAddress || !isAddress(debouncedAddress)) return [undefined]
|
||||
try {
|
||||
return debouncedAddress ? [namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`)] : [undefined]
|
||||
} catch (error) {
|
||||
return [undefined]
|
||||
}
|
||||
}, [debouncedAddress])
|
||||
const registrarContract = useENSRegistrarContract(false)
|
||||
const resolverAddress = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument)
|
||||
const resolverAddressResult = resolverAddress.result?.[0]
|
||||
const resolverContract = useENSResolverContract(
|
||||
resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined,
|
||||
false
|
||||
)
|
||||
const name = useSingleCallResult(resolverContract, 'name', ensNodeArgument)
|
||||
|
||||
const changed = debouncedAddress !== address
|
||||
return {
|
||||
ENSName: changed ? null : name.result?.[0] ?? null,
|
||||
loading: changed || resolverAddress.loading || name.loading,
|
||||
}
|
||||
}
|
@ -34,23 +34,23 @@ const PERMITTABLE_TOKENS: {
|
||||
[checksummedTokenAddress: string]: PermitInfo
|
||||
}
|
||||
} = {
|
||||
[1]: {
|
||||
1: {
|
||||
[USDC.address]: { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
|
||||
[DAI.address]: { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' },
|
||||
[UNI[1].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
|
||||
},
|
||||
[4]: {
|
||||
['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735']: { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' },
|
||||
4: {
|
||||
'0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735': { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' },
|
||||
[UNI[4].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
|
||||
},
|
||||
[3]: {
|
||||
3: {
|
||||
[UNI[3].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
|
||||
['0x07865c6E87B9F70255377e024ace6630C1Eaa37F']: { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
|
||||
'0x07865c6E87B9F70255377e024ace6630C1Eaa37F': { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
|
||||
},
|
||||
[5]: {
|
||||
5: {
|
||||
[UNI[5].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
|
||||
},
|
||||
[42]: {
|
||||
42: {
|
||||
[UNI[42].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
|
||||
},
|
||||
}
|
||||
|
188
src/hooks/useFeeTierDistribution.ts
Normal file
188
src/hooks/useFeeTierDistribution.ts
Normal file
@ -0,0 +1,188 @@
|
||||
import { skipToken } from '@reduxjs/toolkit/query/react'
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import ms from 'ms.macro'
|
||||
import { useMemo } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import { useBlockNumber } from 'state/application/hooks'
|
||||
import { useFeeTierDistributionQuery } from 'state/data/enhanced'
|
||||
import { FeeTierDistributionQuery } from 'state/data/generated'
|
||||
|
||||
import { PoolState, usePool } from './usePools'
|
||||
|
||||
// maximum number of blocks past which we consider the data stale
|
||||
const MAX_DATA_BLOCK_AGE = 20
|
||||
|
||||
interface FeeTierDistribution {
|
||||
isLoading: boolean
|
||||
isError: boolean
|
||||
largestUsageFeeTier?: FeeAmount | undefined
|
||||
|
||||
// distributions as percentages of overall liquidity
|
||||
distributions?: Record<FeeAmount, number | undefined>
|
||||
}
|
||||
|
||||
export function useFeeTierDistribution(
|
||||
currencyA: Currency | undefined,
|
||||
currencyB: Currency | undefined
|
||||
): FeeTierDistribution {
|
||||
const { isFetching, isLoading, isUninitialized, isError, distributions } = usePoolTVL(
|
||||
currencyA?.wrapped,
|
||||
currencyB?.wrapped
|
||||
)
|
||||
|
||||
// fetch all pool states to determine pool state
|
||||
const [poolStateVeryLow] = usePool(currencyA, currencyB, FeeAmount.LOWEST)
|
||||
const [poolStateLow] = usePool(currencyA, currencyB, FeeAmount.LOW)
|
||||
const [poolStateMedium] = usePool(currencyA, currencyB, FeeAmount.MEDIUM)
|
||||
const [poolStateHigh] = usePool(currencyA, currencyB, FeeAmount.HIGH)
|
||||
|
||||
return useMemo(() => {
|
||||
if (isLoading || isFetching || isUninitialized || isError || !distributions) {
|
||||
return {
|
||||
isLoading: isLoading || isFetching || !isUninitialized,
|
||||
isError,
|
||||
distributions,
|
||||
}
|
||||
}
|
||||
|
||||
const largestUsageFeeTier = Object.keys(distributions)
|
||||
.map((d) => Number(d))
|
||||
.filter((d: FeeAmount) => distributions[d] !== 0 && distributions[d] !== undefined)
|
||||
.reduce((a: FeeAmount, b: FeeAmount) => ((distributions[a] ?? 0) > (distributions[b] ?? 0) ? a : b), -1)
|
||||
|
||||
const percentages =
|
||||
!isLoading &&
|
||||
!isError &&
|
||||
distributions &&
|
||||
poolStateVeryLow !== PoolState.LOADING &&
|
||||
poolStateLow !== PoolState.LOADING &&
|
||||
poolStateMedium !== PoolState.LOADING &&
|
||||
poolStateHigh !== PoolState.LOADING
|
||||
? {
|
||||
[FeeAmount.LOWEST]:
|
||||
poolStateVeryLow === PoolState.EXISTS ? (distributions[FeeAmount.LOWEST] ?? 0) * 100 : undefined,
|
||||
[FeeAmount.LOW]: poolStateLow === PoolState.EXISTS ? (distributions[FeeAmount.LOW] ?? 0) * 100 : undefined,
|
||||
[FeeAmount.MEDIUM]:
|
||||
poolStateMedium === PoolState.EXISTS ? (distributions[FeeAmount.MEDIUM] ?? 0) * 100 : undefined,
|
||||
[FeeAmount.HIGH]:
|
||||
poolStateHigh === PoolState.EXISTS ? (distributions[FeeAmount.HIGH] ?? 0) * 100 : undefined,
|
||||
}
|
||||
: undefined
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
isError,
|
||||
distributions: percentages,
|
||||
largestUsageFeeTier: largestUsageFeeTier === -1 ? undefined : largestUsageFeeTier,
|
||||
}
|
||||
}, [
|
||||
isLoading,
|
||||
isFetching,
|
||||
isUninitialized,
|
||||
isError,
|
||||
distributions,
|
||||
poolStateVeryLow,
|
||||
poolStateLow,
|
||||
poolStateMedium,
|
||||
poolStateHigh,
|
||||
])
|
||||
}
|
||||
|
||||
function usePoolTVL(token0: Token | undefined, token1: Token | undefined) {
|
||||
const latestBlock = useBlockNumber()
|
||||
|
||||
const { isLoading, isFetching, isUninitialized, isError, data } = useFeeTierDistributionQuery(
|
||||
token0 && token1 ? { token0: token0.address.toLowerCase(), token1: token1.address.toLowerCase() } : skipToken,
|
||||
{
|
||||
pollingInterval: ms`30s`,
|
||||
}
|
||||
)
|
||||
|
||||
const { asToken0, asToken1, _meta } = (data as FeeTierDistributionQuery) ?? {}
|
||||
|
||||
return useMemo(() => {
|
||||
if (!latestBlock || !_meta || !asToken0 || !asToken1) {
|
||||
return {
|
||||
isLoading,
|
||||
isFetching,
|
||||
isUninitialized,
|
||||
isError,
|
||||
}
|
||||
}
|
||||
|
||||
if (latestBlock - (_meta?.block?.number ?? 0) > MAX_DATA_BLOCK_AGE) {
|
||||
ReactGA.exception({
|
||||
description: `Graph stale (latest block: ${latestBlock})`,
|
||||
})
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
isFetching,
|
||||
isUninitialized,
|
||||
isError,
|
||||
}
|
||||
}
|
||||
|
||||
const all = asToken0.concat(asToken1)
|
||||
|
||||
// sum tvl for token0 and token1 by fee tier
|
||||
const tvlByFeeTier = all.reduce<{ [feeAmount: number]: [number | undefined, number | undefined] }>(
|
||||
(acc, value) => {
|
||||
acc[value.feeTier][0] = (acc[value.feeTier][0] ?? 0) + Number(value.totalValueLockedToken0)
|
||||
acc[value.feeTier][1] = (acc[value.feeTier][1] ?? 0) + Number(value.totalValueLockedToken1)
|
||||
return acc
|
||||
},
|
||||
{
|
||||
[FeeAmount.LOWEST]: [undefined, undefined],
|
||||
[FeeAmount.LOW]: [undefined, undefined],
|
||||
[FeeAmount.MEDIUM]: [undefined, undefined],
|
||||
[FeeAmount.HIGH]: [undefined, undefined],
|
||||
} as Record<FeeAmount, [number | undefined, number | undefined]>
|
||||
)
|
||||
|
||||
// sum total tvl for token0 and token1
|
||||
const [sumToken0Tvl, sumToken1Tvl] = Object.values(tvlByFeeTier).reduce(
|
||||
(acc: [number, number], value) => {
|
||||
acc[0] += value[0] ?? 0
|
||||
acc[1] += value[1] ?? 0
|
||||
return acc
|
||||
},
|
||||
[0, 0]
|
||||
)
|
||||
|
||||
// returns undefined if both tvl0 and tvl1 are undefined (pool not created)
|
||||
const mean = (tvl0: number | undefined, sumTvl0: number, tvl1: number | undefined, sumTvl1: number) =>
|
||||
tvl0 === undefined && tvl1 === undefined ? undefined : ((tvl0 ?? 0) + (tvl1 ?? 0)) / (sumTvl0 + sumTvl1) || 0
|
||||
|
||||
const distributions: Record<FeeAmount, number | undefined> = {
|
||||
[FeeAmount.LOWEST]: mean(
|
||||
tvlByFeeTier[FeeAmount.LOWEST][0],
|
||||
sumToken0Tvl,
|
||||
tvlByFeeTier[FeeAmount.LOWEST][1],
|
||||
sumToken1Tvl
|
||||
),
|
||||
[FeeAmount.LOW]: mean(tvlByFeeTier[FeeAmount.LOW][0], sumToken0Tvl, tvlByFeeTier[FeeAmount.LOW][1], sumToken1Tvl),
|
||||
[FeeAmount.MEDIUM]: mean(
|
||||
tvlByFeeTier[FeeAmount.MEDIUM][0],
|
||||
sumToken0Tvl,
|
||||
tvlByFeeTier[FeeAmount.MEDIUM][1],
|
||||
sumToken1Tvl
|
||||
),
|
||||
[FeeAmount.HIGH]: mean(
|
||||
tvlByFeeTier[FeeAmount.HIGH][0],
|
||||
sumToken0Tvl,
|
||||
tvlByFeeTier[FeeAmount.HIGH][1],
|
||||
sumToken1Tvl
|
||||
),
|
||||
}
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
isFetching,
|
||||
isUninitialized,
|
||||
isError,
|
||||
distributions,
|
||||
}
|
||||
}, [_meta, asToken0, asToken1, isLoading, isError, isFetching, isUninitialized, latestBlock])
|
||||
}
|
49
src/hooks/useFetchListCallback.ts
Normal file
49
src/hooks/useFetchListCallback.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { nanoid } from '@reduxjs/toolkit'
|
||||
import { TokenList } from '@uniswap/token-lists'
|
||||
import { useCallback } from 'react'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
|
||||
import { getNetworkLibrary } from '../connectors'
|
||||
import { fetchTokenList } from '../state/lists/actions'
|
||||
import getTokenList from '../utils/getTokenList'
|
||||
import resolveENSContentHash from '../utils/resolveENSContentHash'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
export function useFetchListCallback(): (listUrl: string, sendDispatch?: boolean) => Promise<TokenList> {
|
||||
const { chainId, library } = useActiveWeb3React()
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const ensResolver = useCallback(
|
||||
async (ensName: string) => {
|
||||
if (!library || chainId !== 1) {
|
||||
const networkLibrary = getNetworkLibrary()
|
||||
const network = await networkLibrary.getNetwork()
|
||||
if (networkLibrary && network.chainId === 1) {
|
||||
return resolveENSContentHash(ensName, networkLibrary)
|
||||
}
|
||||
throw new Error('Could not construct mainnet ENS resolver')
|
||||
}
|
||||
return resolveENSContentHash(ensName, library)
|
||||
},
|
||||
[chainId, library]
|
||||
)
|
||||
|
||||
// note: prevent dispatch if using for list search or unsupported list
|
||||
return useCallback(
|
||||
async (listUrl: string, sendDispatch = true) => {
|
||||
const requestId = nanoid()
|
||||
sendDispatch && dispatch(fetchTokenList.pending({ requestId, url: listUrl }))
|
||||
return getTokenList(listUrl, ensResolver)
|
||||
.then((tokenList) => {
|
||||
sendDispatch && dispatch(fetchTokenList.fulfilled({ url: listUrl, tokenList, requestId }))
|
||||
return tokenList
|
||||
})
|
||||
.catch((error) => {
|
||||
console.debug(`Failed to get list at url ${listUrl}`, error)
|
||||
sendDispatch && dispatch(fetchTokenList.rejected({ url: listUrl, requestId, errorMessage: error.message }))
|
||||
throw error
|
||||
})
|
||||
},
|
||||
[dispatch, ensResolver]
|
||||
)
|
||||
}
|
26
src/hooks/useGasPrice.ts
Normal file
26
src/hooks/useGasPrice.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import JSBI from 'jsbi'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useContract } from './useContract'
|
||||
import useENSAddress from './useENSAddress'
|
||||
|
||||
const CHAIN_DATA_ABI = [
|
||||
{
|
||||
inputs: [],
|
||||
name: 'latestAnswer',
|
||||
outputs: [{ internalType: 'int256', name: '', type: 'int256' }],
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* Returns the price of 1 gas in WEI for the currently selected network using the chainlink fast gas price oracle
|
||||
*/
|
||||
export default function useGasPrice(): JSBI | undefined {
|
||||
const { address } = useENSAddress('fast-gas-gwei.data.eth')
|
||||
const contract = useContract(address ?? undefined, CHAIN_DATA_ABI, false)
|
||||
|
||||
const resultStr = useSingleCallResult(contract, 'latestAnswer').result?.[0]?.toString()
|
||||
return typeof resultStr === 'string' ? JSBI.BigInt(resultStr) : undefined
|
||||
}
|
18
src/hooks/useHttpLocations.ts
Normal file
18
src/hooks/useHttpLocations.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import contenthashToUri from '../utils/contenthashToUri'
|
||||
import { parseENSAddress } from '../utils/parseENSAddress'
|
||||
import uriToHttp from '../utils/uriToHttp'
|
||||
import useENSContentHash from './useENSContentHash'
|
||||
|
||||
export default function useHttpLocations(uri: string | undefined): string[] {
|
||||
const ens = useMemo(() => (uri ? parseENSAddress(uri) : undefined), [uri])
|
||||
const resolvedContentHash = useENSContentHash(ens?.ensName)
|
||||
return useMemo(() => {
|
||||
if (ens) {
|
||||
return resolvedContentHash.contenthash ? uriToHttp(contenthashToUri(resolvedContentHash.contenthash)) : []
|
||||
} else {
|
||||
return uri ? uriToHttp(uri) : []
|
||||
}
|
||||
}, [ens, resolvedContentHash.contenthash, uri])
|
||||
}
|
25
src/hooks/useInterval.ts
Normal file
25
src/hooks/useInterval.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
export default function useInterval(callback: () => void, delay: null | number, leading = true) {
|
||||
const savedCallback = useRef<() => void>()
|
||||
|
||||
// Remember the latest callback.
|
||||
useEffect(() => {
|
||||
savedCallback.current = callback
|
||||
}, [callback])
|
||||
|
||||
// Set up the interval.
|
||||
useEffect(() => {
|
||||
function tick() {
|
||||
const current = savedCallback.current
|
||||
current && current()
|
||||
}
|
||||
|
||||
if (delay !== null) {
|
||||
if (leading) tick()
|
||||
const id = setInterval(tick, delay)
|
||||
return () => clearInterval(id)
|
||||
}
|
||||
return undefined
|
||||
}, [delay, leading])
|
||||
}
|
13
src/hooks/useIsArgentWallet.ts
Normal file
13
src/hooks/useIsArgentWallet.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useArgentWalletDetectorContract } from './useContract'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
export default function useIsArgentWallet(): boolean {
|
||||
const { account } = useActiveWeb3React()
|
||||
const argentWalletDetector = useArgentWalletDetectorContract()
|
||||
const inputs = useMemo(() => [account ?? undefined], [account])
|
||||
const call = useSingleCallResult(argentWalletDetector, 'isArgentWallet', inputs, NEVER_RELOAD)
|
||||
return call?.result?.[0] ?? false
|
||||
}
|
21
src/hooks/useIsSwapUnsupported.ts
Normal file
21
src/hooks/useIsSwapUnsupported.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useUnsupportedTokens } from './Tokens'
|
||||
|
||||
/**
|
||||
* Returns true if the input currency or output currency cannot be traded in the interface
|
||||
* @param currencyIn the input currency to check
|
||||
* @param currencyOut the output currency to check
|
||||
*/
|
||||
export function useIsSwapUnsupported(currencyIn?: Currency | null, currencyOut?: Currency | null): boolean {
|
||||
const unsupportedTokens = useUnsupportedTokens()
|
||||
return useMemo(() => {
|
||||
if (!unsupportedTokens) {
|
||||
return false
|
||||
}
|
||||
const currencyInUnsupported = Boolean(currencyIn?.isToken && unsupportedTokens[currencyIn.address])
|
||||
const currencyOutUnsupported = Boolean(currencyOut?.isToken && unsupportedTokens[currencyOut.address])
|
||||
return currencyInUnsupported || currencyOutUnsupported
|
||||
}, [currencyIn, currencyOut, unsupportedTokens])
|
||||
}
|
23
src/hooks/useIsTickAtLimit.ts
Normal file
23
src/hooks/useIsTickAtLimit.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { FeeAmount, nearestUsableTick, TICK_SPACINGS, TickMath } from '@uniswap/v3-sdk'
|
||||
import { useMemo } from 'react'
|
||||
import { Bound } from 'state/mint/v3/actions'
|
||||
|
||||
export default function useIsTickAtLimit(
|
||||
feeAmount: FeeAmount | undefined,
|
||||
tickLower: number | undefined,
|
||||
tickUpper: number | undefined
|
||||
) {
|
||||
return useMemo(
|
||||
() => ({
|
||||
[Bound.LOWER]:
|
||||
feeAmount && tickLower
|
||||
? tickLower === nearestUsableTick(TickMath.MIN_TICK, TICK_SPACINGS[feeAmount as FeeAmount])
|
||||
: undefined,
|
||||
[Bound.UPPER]:
|
||||
feeAmount && tickUpper
|
||||
? tickUpper === nearestUsableTick(TickMath.MAX_TICK, TICK_SPACINGS[feeAmount as FeeAmount])
|
||||
: undefined,
|
||||
}),
|
||||
[feeAmount, tickLower, tickUpper]
|
||||
)
|
||||
}
|
28
src/hooks/useIsWindowVisible.ts
Normal file
28
src/hooks/useIsWindowVisible.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
const VISIBILITY_STATE_SUPPORTED = 'visibilityState' in document
|
||||
|
||||
function isWindowVisible() {
|
||||
return !VISIBILITY_STATE_SUPPORTED || document.visibilityState !== 'hidden'
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the window is currently visible to the user.
|
||||
*/
|
||||
export default function useIsWindowVisible(): boolean {
|
||||
const [focused, setFocused] = useState<boolean>(isWindowVisible())
|
||||
const listener = useCallback(() => {
|
||||
setFocused(isWindowVisible())
|
||||
}, [setFocused])
|
||||
|
||||
useEffect(() => {
|
||||
if (!VISIBILITY_STATE_SUPPORTED) return undefined
|
||||
|
||||
document.addEventListener('visibilitychange', listener)
|
||||
return () => {
|
||||
document.removeEventListener('visibilitychange', listener)
|
||||
}
|
||||
}, [listener])
|
||||
|
||||
return focused
|
||||
}
|
21
src/hooks/useLast.ts
Normal file
21
src/hooks/useLast.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
/**
|
||||
* Returns the last value of type T that passes a filter function
|
||||
* @param value changing value
|
||||
* @param filterFn function that determines whether a given value should be considered for the last value
|
||||
*/
|
||||
export default function useLast<T>(
|
||||
value: T | undefined | null,
|
||||
filterFn?: (value: T | null | undefined) => boolean
|
||||
): T | null | undefined {
|
||||
const [last, setLast] = useState<T | null | undefined>(filterFn && filterFn(value) ? value : undefined)
|
||||
useEffect(() => {
|
||||
setLast((last) => {
|
||||
const shouldUse: boolean = filterFn ? filterFn(value) : true
|
||||
if (shouldUse) return value
|
||||
return last
|
||||
})
|
||||
}, [filterFn, value])
|
||||
return last
|
||||
}
|
38
src/hooks/useLocationLinkProps.ts
Normal file
38
src/hooks/useLocationLinkProps.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { SupportedLocale } from 'constants/locales'
|
||||
import { LocationDescriptor } from 'history'
|
||||
import useParsedQueryString from 'hooks/useParsedQueryString'
|
||||
import { stringify } from 'qs'
|
||||
import { useMemo } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
import { useActiveLocale } from './useActiveLocale'
|
||||
|
||||
export function useLocationLinkProps(locale: SupportedLocale | null): {
|
||||
to?: LocationDescriptor
|
||||
onClick?: () => void
|
||||
} {
|
||||
const location = useLocation()
|
||||
const qs = useParsedQueryString()
|
||||
const activeLocale = useActiveLocale()
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
!locale
|
||||
? {}
|
||||
: {
|
||||
to: {
|
||||
...location,
|
||||
search: stringify({ ...qs, lng: locale }),
|
||||
},
|
||||
onClick: () => {
|
||||
ReactGA.event({
|
||||
category: 'Localization',
|
||||
action: 'Switch Locale',
|
||||
label: `${activeLocale} -> ${locale}`,
|
||||
})
|
||||
},
|
||||
},
|
||||
[location, qs, activeLocale, locale]
|
||||
)
|
||||
}
|
14
src/hooks/useMachineTime.ts
Normal file
14
src/hooks/useMachineTime.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
import useInterval from './useInterval'
|
||||
|
||||
const useMachineTimeMs = (updateInterval: number): number => {
|
||||
const [now, setNow] = useState(Date.now())
|
||||
|
||||
useInterval(() => {
|
||||
setNow(Date.now())
|
||||
}, updateInterval)
|
||||
return now
|
||||
}
|
||||
|
||||
export default useMachineTimeMs
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user