Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99ab581a87 | ||
|
|
fc571d0f63 | ||
|
|
2de43a8cdb | ||
|
|
5383436c88 | ||
|
|
521f3aae04 | ||
|
|
9318c1204b | ||
|
|
5055695b9b | ||
|
|
ae8c0377de | ||
|
|
8eaf1f4964 | ||
|
|
f717bf4a49 | ||
|
|
dcbd4e475d | ||
|
|
b704bdac94 | ||
|
|
00d3df95c0 | ||
|
|
251b8b703a | ||
|
|
ef8432437d |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@uniswap/widgets",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"description": "Uniswap Interface",
|
||||
"homepage": ".",
|
||||
"files": [
|
||||
|
||||
@@ -1,49 +1,58 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import React from 'react'
|
||||
import useCopyClipboard from 'hooks/useCopyClipboard'
|
||||
import React, { useCallback } from 'react'
|
||||
import { CheckCircle, Copy } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import useCopyClipboard from '../../hooks/useCopyClipboard'
|
||||
import { LinkStyledButton } from '../../theme'
|
||||
import { LinkStyledButton } from 'theme'
|
||||
|
||||
const CopyIcon = styled(LinkStyledButton)`
|
||||
color: ${({ theme }) => theme.text3};
|
||||
color: ${({ color, theme }) => color || theme.text3};
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
font-size: 0.825rem;
|
||||
font-size: 12px;
|
||||
:hover,
|
||||
:active,
|
||||
:focus {
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ color, theme }) => color || theme.text2};
|
||||
}
|
||||
`
|
||||
const TransactionStatusText = styled.span`
|
||||
margin-left: 0.25rem;
|
||||
font-size: 0.825rem;
|
||||
font-size: 12px;
|
||||
${({ theme }) => theme.flexRowNoWrap};
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
export default function CopyHelper(props: { toCopy: string; children?: React.ReactNode }) {
|
||||
interface BaseProps {
|
||||
toCopy: string
|
||||
color?: string
|
||||
}
|
||||
export type CopyHelperProps = BaseProps & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof BaseProps>
|
||||
|
||||
export default function CopyHelper({ color, toCopy, children }: CopyHelperProps) {
|
||||
const [isCopied, setCopied] = useCopyClipboard()
|
||||
const copy = useCallback(() => {
|
||||
setCopied(toCopy)
|
||||
}, [toCopy, setCopied])
|
||||
|
||||
return (
|
||||
<CopyIcon onClick={() => setCopied(props.toCopy)}>
|
||||
<CopyIcon onClick={copy} color={color}>
|
||||
{isCopied ? '' : children}
|
||||
|
||||
{isCopied ? (
|
||||
<TransactionStatusText>
|
||||
<CheckCircle size={'16'} />
|
||||
<CheckCircle size={'12'} />
|
||||
<TransactionStatusText>
|
||||
<Trans>Copied</Trans>
|
||||
</TransactionStatusText>
|
||||
</TransactionStatusText>
|
||||
) : (
|
||||
<TransactionStatusText>
|
||||
<Copy size={'16'} />
|
||||
<Copy size={'12'} />
|
||||
</TransactionStatusText>
|
||||
)}
|
||||
{isCopied ? '' : props.children}
|
||||
</CopyIcon>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
VoteTransactionInfo,
|
||||
WithdrawLiquidityStakingTransactionInfo,
|
||||
WrapTransactionInfo,
|
||||
} from '../../state/transactions/actions'
|
||||
} from '../../state/transactions/types'
|
||||
|
||||
function formatAmount(amountRaw: string, decimals: number, sigFigs: number): string {
|
||||
return new Fraction(amountRaw, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(decimals))).toSignificant(sigFigs)
|
||||
|
||||
@@ -10,7 +10,7 @@ import { AbstractConnector } from 'web3-react-abstract-connector'
|
||||
import { ReactComponent as Close } from '../../assets/images/x.svg'
|
||||
import { injected, walletlink } from '../../connectors'
|
||||
import { SUPPORTED_WALLETS } from '../../constants/wallet'
|
||||
import { clearAllTransactions } from '../../state/transactions/actions'
|
||||
import { clearAllTransactions } from '../../state/transactions/reducer'
|
||||
import { ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Trans } from '@lingui/macro'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { ReactNode, useMemo } from 'react'
|
||||
|
||||
// SDN OFAC addresses
|
||||
const BLOCKED_ADDRESSES: string[] = [
|
||||
'0x7Db418b5D567A4e0E8c59Ad71BE1FcE48f3E6107',
|
||||
'0x72a5843cc08275C8171E582972Aa4fDa8C397B2A',
|
||||
@@ -24,16 +23,18 @@ const BLOCKED_ADDRESSES: string[] = [
|
||||
'0x6acdfba02d390b97ac2b2d42a63e85293bcc1',
|
||||
'0x48549a34ae37b12f6a30566245176994e17c6',
|
||||
'0x5512d943ed1f7c8a43f3435c85f7ab68b30121',
|
||||
'0xc455f7fd3e0e12afd51fba5c106909934d8a0e',
|
||||
'0x3cbded43efdaf0fc77b9c55f6fc9988fcc9b757d',
|
||||
'0xC455f7fd3e0e12afd51fba5c106909934D8A0e4a',
|
||||
'0x3CBdeD43EFdAf0FC77b9C55F6fC9988fCC9b757d',
|
||||
'0x67d40EE1A85bf4a4Bb7Ffae16De985e8427B6b45',
|
||||
'0x6f1ca141a28907f78ebaa64fb83a9088b02a8352',
|
||||
'0x6acdfba02d390b97ac2b2d42a63e85293bcc160e',
|
||||
'0x6F1cA141A28907F78Ebaa64fb83A9088b02A8352',
|
||||
'0x6aCDFBA02D390b97Ac2b2d42A63E85293BCc160e',
|
||||
'0x48549a34ae37b12f6a30566245176994e17c6b4a',
|
||||
'0x5512d943ed1f7c8a43f3435c85f7ab68b30121b0',
|
||||
'0xc455f7fd3e0e12afd51fba5c106909934d8a0e4a',
|
||||
'0xC455f7fd3e0e12afd51fba5c106909934D8A0e4a',
|
||||
'0x629e7Da20197a5429d30da36E77d06CdF796b71A',
|
||||
'0x7FF9cFad3877F21d41Da833E2F775dB0569eE3D9',
|
||||
'0x098B716B8Aaf21512996dC57EB0615e2383E2f96',
|
||||
'0xfEC8A60023265364D066a1212fDE3930F6Ae8da7',
|
||||
]
|
||||
|
||||
export default function Blocklist({ children }: { children: ReactNode }) {
|
||||
|
||||
55
src/components/ConnectedAccountBlocked/index.tsx
Normal file
55
src/components/ConnectedAccountBlocked/index.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import CopyHelper from 'components/AccountDetails/Copy'
|
||||
import Column from 'components/Column'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
|
||||
import Modal from '../Modal'
|
||||
|
||||
const ContentWrapper = styled(Column)`
|
||||
align-items: center;
|
||||
margin: 32px;
|
||||
text-align: center;
|
||||
`
|
||||
const WarningIcon = styled(AlertOctagon)`
|
||||
min-height: 22px;
|
||||
min-width: 22px;
|
||||
color: ${({ theme }) => theme.warning};
|
||||
`
|
||||
|
||||
interface ConnectedAccountBlockedProps {
|
||||
account: string | null | undefined
|
||||
isOpen: boolean
|
||||
}
|
||||
|
||||
export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedProps) {
|
||||
const theme = useTheme()
|
||||
return (
|
||||
<Modal isOpen={props.isOpen} onDismiss={Function.prototype()}>
|
||||
<ContentWrapper>
|
||||
<WarningIcon />
|
||||
<ThemedText.LargeHeader lineHeight={2} marginBottom={1} marginTop={1}>
|
||||
<Trans>Blocked Address</Trans>
|
||||
</ThemedText.LargeHeader>
|
||||
<ThemedText.DarkGray fontSize={12} marginBottom={12}>
|
||||
{props.account}
|
||||
</ThemedText.DarkGray>
|
||||
<ThemedText.Main fontSize={14} marginBottom={12}>
|
||||
<Trans>This address is blocked on the Uniswap Labs interface because it is associated with one or more</Trans>{' '}
|
||||
<ExternalLink href="https://help.uniswap.org/en/articles/6149816">
|
||||
<Trans>blocked activities</Trans>
|
||||
</ExternalLink>
|
||||
.
|
||||
</ThemedText.Main>
|
||||
<ThemedText.Main fontSize={12}>
|
||||
<Trans>If you believe this is an error, please send an email including your address to </Trans>{' '}
|
||||
</ThemedText.Main>
|
||||
<CopyHelper toCopy="compliance@uniswap.org" color={theme.primary1}>
|
||||
compliance@uniswap.org.
|
||||
</CopyHelper>
|
||||
</ContentWrapper>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
@@ -146,7 +146,7 @@ const BridgeLabel = ({ chainId }: { chainId: SupportedChainId }) => {
|
||||
return <Trans>Arbitrum Bridge</Trans>
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
return <Trans>Optimism Gateway</Trans>
|
||||
return <Trans>Optimism Bridge</Trans>
|
||||
case SupportedChainId.POLYGON:
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
return <Trans>Polygon Bridge</Trans>
|
||||
|
||||
@@ -18,6 +18,7 @@ export const TooltipContainer = styled.div`
|
||||
|
||||
interface TooltipProps extends Omit<PopoverProps, 'content'> {
|
||||
text: ReactNode
|
||||
disableHover?: boolean // disable the hover and content display
|
||||
}
|
||||
|
||||
interface TooltipContentProps extends Omit<PopoverProps, 'content'> {
|
||||
@@ -29,19 +30,20 @@ interface TooltipContentProps extends Omit<PopoverProps, 'content'> {
|
||||
}
|
||||
|
||||
export default function Tooltip({ text, ...rest }: TooltipProps) {
|
||||
return <Popover content={<TooltipContainer>{text}</TooltipContainer>} {...rest} />
|
||||
return <Popover content={text && <TooltipContainer>{text}</TooltipContainer>} {...rest} />
|
||||
}
|
||||
|
||||
function TooltipContent({ content, wrap = false, ...rest }: TooltipContentProps) {
|
||||
return <Popover content={wrap ? <TooltipContainer>{content}</TooltipContainer> : content} {...rest} />
|
||||
}
|
||||
|
||||
export function MouseoverTooltip({ children, ...rest }: Omit<TooltipProps, 'show'>) {
|
||||
/** Standard text tooltip. */
|
||||
export function MouseoverTooltip({ text, disableHover, children, ...rest }: Omit<TooltipProps, 'show'>) {
|
||||
const [show, setShow] = useState(false)
|
||||
const open = useCallback(() => setShow(true), [setShow])
|
||||
const close = useCallback(() => setShow(false), [setShow])
|
||||
return (
|
||||
<Tooltip {...rest} show={show}>
|
||||
<Tooltip {...rest} show={show} text={disableHover ? null : text}>
|
||||
<div onMouseEnter={open} onMouseLeave={close}>
|
||||
{children}
|
||||
</div>
|
||||
@@ -49,6 +51,7 @@ export function MouseoverTooltip({ children, ...rest }: Omit<TooltipProps, 'show
|
||||
)
|
||||
}
|
||||
|
||||
/** Tooltip that displays custom content. */
|
||||
export function MouseoverTooltipContent({
|
||||
content,
|
||||
children,
|
||||
|
||||
23
src/components/TopLevelModals/index.tsx
Normal file
23
src/components/TopLevelModals/index.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import AddressClaimModal from 'components/claim/AddressClaimModal'
|
||||
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
|
||||
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useModalOpen, useToggleModal } from 'state/application/hooks'
|
||||
import { ApplicationModal } from 'state/application/reducer'
|
||||
|
||||
export default function TopLevelModals() {
|
||||
const addressClaimOpen = useModalOpen(ApplicationModal.ADDRESS_CLAIM)
|
||||
const addressClaimToggle = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
|
||||
|
||||
const blockedAccountModalOpen = useModalOpen(ApplicationModal.BLOCKED_ACCOUNT)
|
||||
const { account } = useActiveWeb3React()
|
||||
|
||||
useAccountRiskCheck(account)
|
||||
const open = Boolean(blockedAccountModalOpen && account)
|
||||
return (
|
||||
<>
|
||||
<AddressClaimModal isOpen={addressClaimOpen} onDismiss={addressClaimToggle} />
|
||||
<ConnectedAccountBlocked account={account} isOpen={open} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import { Trans } from '@lingui/macro'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { PrivacyPolicy } from 'components/PrivacyPolicy'
|
||||
import Row, { AutoRow, RowBetween } from 'components/Row'
|
||||
import { useWalletConnectMonitoringEventCallback } from 'hooks/useMonitoringEventCallback'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ArrowLeft, ArrowRight, Info } from 'react-feather'
|
||||
import ReactGA from 'react-ga4'
|
||||
@@ -151,8 +150,6 @@ export default function WalletModal({
|
||||
|
||||
const previousAccount = usePrevious(account)
|
||||
|
||||
const logMonitoringEvent = useWalletConnectMonitoringEventCallback()
|
||||
|
||||
// close on connection, when logged out before
|
||||
useEffect(() => {
|
||||
if (account && !previousAccount && walletModalOpen) {
|
||||
@@ -200,18 +197,13 @@ export default function WalletModal({
|
||||
}
|
||||
|
||||
connector &&
|
||||
activate(connector, undefined, true)
|
||||
.then(async () => {
|
||||
const walletAddress = await connector.getAccount()
|
||||
logMonitoringEvent({ walletAddress })
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error instanceof UnsupportedChainIdError) {
|
||||
activate(connector) // a little janky...can't use setError because the connector isn't set
|
||||
} else {
|
||||
setPendingError(true)
|
||||
}
|
||||
})
|
||||
activate(connector, undefined, true).catch((error) => {
|
||||
if (error instanceof UnsupportedChainIdError) {
|
||||
activate(connector) // a little janky...can't use setError because the connector isn't set
|
||||
} else {
|
||||
setPendingError(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// close wallet modal if fortmatic modal is active
|
||||
|
||||
@@ -13,7 +13,7 @@ import useENSName from '../../hooks/useENSName'
|
||||
import { useHasSocks } from '../../hooks/useSocksBalance'
|
||||
import { useWalletModalToggle } from '../../state/application/hooks'
|
||||
import { isTransactionRecent, useAllTransactions } from '../../state/transactions/hooks'
|
||||
import { TransactionDetails } from '../../state/transactions/reducer'
|
||||
import { TransactionDetails } from '../../state/transactions/types'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { ButtonSecondary } from '../Button'
|
||||
import StatusIcon from '../Identicon/StatusIcon'
|
||||
|
||||
@@ -7,8 +7,8 @@ import styled from 'styled-components/macro'
|
||||
|
||||
import { useContract } from '../../hooks/useContract'
|
||||
import { StakingInfo } from '../../state/stake/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { TransactionType } from '../../state/transactions/types'
|
||||
import { CloseIcon, ThemedText } from '../../theme'
|
||||
import { ButtonError } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
|
||||
@@ -12,8 +12,8 @@ import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallbac
|
||||
import { useContract, usePairContract, useV2RouterContract } from '../../hooks/useContract'
|
||||
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
|
||||
import { StakingInfo, useDerivedStakeInfo } from '../../state/stake/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { TransactionType } from '../../state/transactions/types'
|
||||
import { CloseIcon, ThemedText } from '../../theme'
|
||||
import { formatCurrencyAmount } from '../../utils/formatCurrencyAmount'
|
||||
import { maxAmountSpend } from '../../utils/maxAmountSpend'
|
||||
|
||||
@@ -7,8 +7,8 @@ import styled from 'styled-components/macro'
|
||||
|
||||
import { useContract } from '../../hooks/useContract'
|
||||
import { StakingInfo } from '../../state/stake/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { TransactionType } from '../../state/transactions/types'
|
||||
import { CloseIcon, ThemedText } from '../../theme'
|
||||
import { ButtonError } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
|
||||
@@ -4,6 +4,7 @@ import Card from 'components/Card'
|
||||
import { LoadingRows } from 'components/Loader/styled'
|
||||
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
||||
import { useContext, useMemo } from 'react'
|
||||
import { InterfaceTrade } from 'state/routing/types'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
@@ -12,6 +13,7 @@ import { Separator, ThemedText } from '../../theme'
|
||||
import { computeRealizedLPFeePercent } from '../../utils/prices'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import { MouseoverTooltip } from '../Tooltip'
|
||||
import FormattedPriceImpact from './FormattedPriceImpact'
|
||||
|
||||
const StyledCard = styled(Card)`
|
||||
@@ -23,6 +25,7 @@ interface AdvancedSwapDetailsProps {
|
||||
allowedSlippage: Percent
|
||||
syncing?: boolean
|
||||
hideRouteDiagram?: boolean
|
||||
hideInfoTooltips?: boolean
|
||||
}
|
||||
|
||||
function TextWithLoadingPlaceholder({
|
||||
@@ -43,9 +46,15 @@ function TextWithLoadingPlaceholder({
|
||||
)
|
||||
}
|
||||
|
||||
export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }: AdvancedSwapDetailsProps) {
|
||||
export function AdvancedSwapDetails({
|
||||
trade,
|
||||
allowedSlippage,
|
||||
syncing = false,
|
||||
hideInfoTooltips = false,
|
||||
}: AdvancedSwapDetailsProps) {
|
||||
const theme = useContext(ThemeContext)
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const nativeCurrency = useNativeCurrency()
|
||||
|
||||
const { expectedOutputAmount, priceImpact } = useMemo(() => {
|
||||
if (!trade) return { expectedOutputAmount: undefined, priceImpact: undefined }
|
||||
@@ -60,9 +69,19 @@ export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }:
|
||||
<AutoColumn gap="8px">
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<ThemedText.SubHeader color={theme.text1}>
|
||||
<Trans>Expected Output</Trans>
|
||||
</ThemedText.SubHeader>
|
||||
<MouseoverTooltip
|
||||
text={
|
||||
<Trans>
|
||||
The amount you expect to receive at the current market price. You may receive less or more if the
|
||||
market price changes while your transaction is pending.
|
||||
</Trans>
|
||||
}
|
||||
disableHover={hideInfoTooltips}
|
||||
>
|
||||
<ThemedText.SubHeader color={theme.text1}>
|
||||
<Trans>Expected Output</Trans>
|
||||
</ThemedText.SubHeader>
|
||||
</MouseoverTooltip>
|
||||
</RowFixed>
|
||||
<TextWithLoadingPlaceholder syncing={syncing} width={65}>
|
||||
<ThemedText.Black textAlign="right" fontSize={14}>
|
||||
@@ -74,9 +93,14 @@ export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }:
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<ThemedText.SubHeader color={theme.text1}>
|
||||
<Trans>Price Impact</Trans>
|
||||
</ThemedText.SubHeader>
|
||||
<MouseoverTooltip
|
||||
text={<Trans>The impact your trade has on the market price of this pool.</Trans>}
|
||||
disableHover={hideInfoTooltips}
|
||||
>
|
||||
<ThemedText.SubHeader color={theme.text1}>
|
||||
<Trans>Price Impact</Trans>
|
||||
</ThemedText.SubHeader>
|
||||
</MouseoverTooltip>
|
||||
</RowFixed>
|
||||
<TextWithLoadingPlaceholder syncing={syncing} width={50}>
|
||||
<ThemedText.Black textAlign="right" fontSize={14}>
|
||||
@@ -87,14 +111,24 @@ export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }:
|
||||
<Separator />
|
||||
<RowBetween>
|
||||
<RowFixed style={{ marginRight: '20px' }}>
|
||||
<ThemedText.SubHeader color={theme.text3}>
|
||||
{trade.tradeType === TradeType.EXACT_INPUT ? (
|
||||
<Trans>Minimum received</Trans>
|
||||
) : (
|
||||
<Trans>Maximum sent</Trans>
|
||||
)}{' '}
|
||||
<Trans>after slippage</Trans> ({allowedSlippage.toFixed(2)}%)
|
||||
</ThemedText.SubHeader>
|
||||
<MouseoverTooltip
|
||||
text={
|
||||
<Trans>
|
||||
The minimum amount you are guaranteed to receive. If the price slips any further, your transaction
|
||||
will revert.
|
||||
</Trans>
|
||||
}
|
||||
disableHover={hideInfoTooltips}
|
||||
>
|
||||
<ThemedText.SubHeader color={theme.text3}>
|
||||
{trade.tradeType === TradeType.EXACT_INPUT ? (
|
||||
<Trans>Minimum received</Trans>
|
||||
) : (
|
||||
<Trans>Maximum sent</Trans>
|
||||
)}{' '}
|
||||
<Trans>after slippage</Trans> ({allowedSlippage.toFixed(2)}%)
|
||||
</ThemedText.SubHeader>
|
||||
</MouseoverTooltip>
|
||||
</RowFixed>
|
||||
<TextWithLoadingPlaceholder syncing={syncing} width={70}>
|
||||
<ThemedText.Black textAlign="right" fontSize={14} color={theme.text3}>
|
||||
@@ -106,9 +140,18 @@ export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }:
|
||||
</RowBetween>
|
||||
{!trade?.gasUseEstimateUSD || !chainId || !SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId) ? null : (
|
||||
<RowBetween>
|
||||
<ThemedText.SubHeader color={theme.text3}>
|
||||
<Trans>Network Fee</Trans>
|
||||
</ThemedText.SubHeader>
|
||||
<MouseoverTooltip
|
||||
text={
|
||||
<Trans>
|
||||
The fee paid to miners who process your transaction. This must be paid in {nativeCurrency.symbol}.
|
||||
</Trans>
|
||||
}
|
||||
disableHover={hideInfoTooltips}
|
||||
>
|
||||
<ThemedText.SubHeader color={theme.text3}>
|
||||
<Trans>Network Fee</Trans>
|
||||
</ThemedText.SubHeader>
|
||||
</MouseoverTooltip>
|
||||
<TextWithLoadingPlaceholder syncing={syncing} width={50}>
|
||||
<ThemedText.Black textAlign="right" fontSize={14} color={theme.text3}>
|
||||
~${trade.gasUseEstimateUSD.toFixed(2)}
|
||||
|
||||
@@ -147,7 +147,12 @@ export default function SwapDetailsDropdown({
|
||||
content={
|
||||
<ResponsiveTooltipContainer origin="top right" style={{ padding: '0' }}>
|
||||
<Card padding="12px">
|
||||
<AdvancedSwapDetails trade={trade} allowedSlippage={allowedSlippage} syncing={syncing} />
|
||||
<AdvancedSwapDetails
|
||||
trade={trade}
|
||||
allowedSlippage={allowedSlippage}
|
||||
syncing={syncing}
|
||||
hideInfoTooltips={true}
|
||||
/>
|
||||
</Card>
|
||||
</ResponsiveTooltipContainer>
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ export const CHAIN_INFO: ChainInfoMap = {
|
||||
[SupportedChainId.OPTIMISM]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`25m`,
|
||||
bridge: 'https://gateway.optimism.io/?chainId=1',
|
||||
bridge: 'https://app.optimism.io/bridge',
|
||||
defaultListUrl: OPTIMISM_LIST,
|
||||
docs: 'https://optimism.io/',
|
||||
explorer: 'https://optimistic.etherscan.io/',
|
||||
@@ -108,7 +108,7 @@ export const CHAIN_INFO: ChainInfoMap = {
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`25m`,
|
||||
bridge: 'https://gateway.optimism.io/',
|
||||
bridge: 'https://app.optimism.io/bridge',
|
||||
defaultListUrl: OPTIMISM_LIST,
|
||||
docs: 'https://optimism.io/',
|
||||
explorer: 'https://optimistic.etherscan.io/',
|
||||
|
||||
33
src/hooks/useAccountRiskCheck.ts
Normal file
33
src/hooks/useAccountRiskCheck.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useEffect } from 'react'
|
||||
import ReactGA from 'react-ga4'
|
||||
import { ApplicationModal, setOpenModal } from 'state/application/reducer'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
|
||||
export default function useAccountRiskCheck(account: string | null | undefined) {
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
useEffect(() => {
|
||||
if (account && window.location.hostname === 'app.uniswap.org') {
|
||||
const headers = new Headers({ 'Content-Type': 'application/json' })
|
||||
fetch('https://screening-worker.uniswap.workers.dev', {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({ address: account }),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data.block) {
|
||||
dispatch(setOpenModal(ApplicationModal.BLOCKED_ACCOUNT))
|
||||
ReactGA.event({
|
||||
category: 'Address Screening',
|
||||
action: 'blocked',
|
||||
label: account,
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch(setOpenModal(null))
|
||||
})
|
||||
}
|
||||
}, [account, dispatch])
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
|
||||
import { updateUserExpertMode } from '../state/user/actions'
|
||||
import { updateUserExpertMode } from '../state/user/reducer'
|
||||
import useParsedQueryString from './useParsedQueryString'
|
||||
|
||||
export default function ApeModeQueryParamReader(): null {
|
||||
|
||||
@@ -6,8 +6,8 @@ import useSwapApproval, { useSwapApprovalOptimizedTrade } from 'lib/hooks/swap/u
|
||||
import { ApprovalState, useApproval } from 'lib/hooks/useApproval'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import { TransactionType } from '../state/transactions/actions'
|
||||
import { useHasPendingApproval, useTransactionAdder } from '../state/transactions/hooks'
|
||||
import { TransactionType } from '../state/transactions/types'
|
||||
export { ApprovalState } from 'lib/hooks/useApproval'
|
||||
|
||||
function useGetAndTrackApproval(getApproval: ReturnType<typeof useApproval>[1]) {
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { initializeApp } from 'firebase/app'
|
||||
import { getDatabase, push, ref } from 'firebase/database'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useCallback } from 'react'
|
||||
import { TransactionInfo, TransactionType } from 'state/transactions/actions'
|
||||
|
||||
type PartialTransactionResponse = Pick<TransactionResponse, 'hash' | 'v' | 'r' | 's'>
|
||||
|
||||
const SUPPORTED_TRANSACTION_TYPES = [
|
||||
TransactionType.ADD_LIQUIDITY_V2_POOL,
|
||||
TransactionType.ADD_LIQUIDITY_V3_POOL,
|
||||
TransactionType.CREATE_V3_POOL,
|
||||
TransactionType.REMOVE_LIQUIDITY_V3,
|
||||
TransactionType.SWAP,
|
||||
]
|
||||
|
||||
const FIREBASE_API_KEY = process.env.REACT_APP_FIREBASE_KEY
|
||||
const firebaseEnabled = typeof FIREBASE_API_KEY !== 'undefined'
|
||||
if (firebaseEnabled) initializeFirebase()
|
||||
|
||||
function useMonitoringEventCallback() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
|
||||
return useCallback(
|
||||
async function log(
|
||||
type: string,
|
||||
{
|
||||
transactionResponse,
|
||||
walletAddress,
|
||||
}: { transactionResponse: PartialTransactionResponse; walletAddress: string | undefined }
|
||||
) {
|
||||
if (!firebaseEnabled) return
|
||||
|
||||
const db = getDatabase()
|
||||
|
||||
if (!walletAddress) {
|
||||
console.debug('Wallet address required to log monitoring events.')
|
||||
return
|
||||
}
|
||||
try {
|
||||
push(ref(db, 'trm'), {
|
||||
chainId,
|
||||
origin: window.location.origin,
|
||||
timestamp: Date.now(),
|
||||
tx: transactionResponse,
|
||||
type,
|
||||
walletAddress,
|
||||
})
|
||||
} catch (e) {
|
||||
console.debug('Error adding document: ', e)
|
||||
}
|
||||
},
|
||||
[chainId]
|
||||
)
|
||||
}
|
||||
|
||||
export function useTransactionMonitoringEventCallback() {
|
||||
const { account } = useActiveWeb3React()
|
||||
const log = useMonitoringEventCallback()
|
||||
|
||||
return useCallback(
|
||||
(info: TransactionInfo, transactionResponse: TransactionResponse) => {
|
||||
if (SUPPORTED_TRANSACTION_TYPES.includes(info.type)) {
|
||||
log(TransactionType[info.type], {
|
||||
transactionResponse: (({ hash, v, r, s }: PartialTransactionResponse) => ({ hash, v, r, s }))(
|
||||
transactionResponse
|
||||
),
|
||||
walletAddress: account ?? undefined,
|
||||
})
|
||||
}
|
||||
},
|
||||
[account, log]
|
||||
)
|
||||
}
|
||||
|
||||
export function useWalletConnectMonitoringEventCallback() {
|
||||
const log = useMonitoringEventCallback()
|
||||
|
||||
return useCallback(
|
||||
(walletAddress) => {
|
||||
log('WALLET_CONNECTED', { transactionResponse: { hash: '', r: '', s: '', v: -1 }, walletAddress })
|
||||
},
|
||||
[log]
|
||||
)
|
||||
}
|
||||
|
||||
function initializeFirebase() {
|
||||
initializeApp({
|
||||
apiKey: process.env.REACT_APP_FIREBASE_KEY,
|
||||
authDomain: 'interface-monitoring.firebaseapp.com',
|
||||
databaseURL: 'https://interface-monitoring-default-rtdb.firebaseio.com',
|
||||
projectId: 'interface-monitoring',
|
||||
storageBucket: 'interface-monitoring.appspot.com',
|
||||
messagingSenderId: '968187720053',
|
||||
appId: '1:968187720053:web:acedf72dce629d470be33c',
|
||||
})
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { SwapCallbackState, useSwapCallback as useLibSwapCallBack } from 'lib/hooks/swap/useSwapCallback'
|
||||
import { ReactNode, useMemo } from 'react'
|
||||
|
||||
import { TransactionType } from '../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../state/transactions/hooks'
|
||||
import { TransactionType } from '../state/transactions/types'
|
||||
import { currencyId } from '../utils/currencyId'
|
||||
import useENS from './useENS'
|
||||
import { SignatureData } from './useERC20Permit'
|
||||
|
||||
@@ -6,8 +6,8 @@ import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { WRAPPED_NATIVE_CURRENCY } from '../constants/tokens'
|
||||
import { TransactionType } from '../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../state/transactions/hooks'
|
||||
import { TransactionType } from '../state/transactions/types'
|
||||
import { useCurrencyBalance } from '../state/wallet/hooks'
|
||||
import { useWETHContract } from './useContract'
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { tokens } from '@uniswap/default-token-list'
|
||||
import { TokenInfo } from '@uniswap/token-lists'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { DAI, USDC_MAINNET } from 'constants/tokens'
|
||||
import { useUpdateAtom } from 'jotai/utils'
|
||||
import { TokenListProvider } from 'lib/hooks/useTokenList'
|
||||
import { useEffect } from 'react'
|
||||
import { useSelect, useValue } from 'react-cosmos/fixture'
|
||||
|
||||
@@ -56,16 +60,47 @@ function Fixture() {
|
||||
})
|
||||
const [defaultOutputAmount] = useValue('defaultOutputAmount', { defaultValue: 0 })
|
||||
|
||||
const tokenListNameMap: Record<string, TokenInfo[] | string> = {
|
||||
'default list': tokens,
|
||||
'mainnet only': tokens.filter((token) => SupportedChainId.MAINNET === token.chainId),
|
||||
'arbitrum only': [
|
||||
{
|
||||
logoURI: 'https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734',
|
||||
chainId: 42161,
|
||||
address: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
|
||||
name: 'Dai Stablecoin',
|
||||
symbol: 'DAI',
|
||||
decimals: 18,
|
||||
},
|
||||
{
|
||||
logoURI: 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389',
|
||||
chainId: 42161,
|
||||
address: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8',
|
||||
name: 'USD Coin (Arb1)',
|
||||
symbol: 'USDC',
|
||||
decimals: 6,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const tokenListOptions = Object.keys(tokenListNameMap)
|
||||
const [tokenListName] = useSelect('tokenList', {
|
||||
options: tokenListOptions,
|
||||
defaultValue: tokenListOptions[0],
|
||||
})
|
||||
|
||||
return (
|
||||
<Swap
|
||||
convenienceFee={convenienceFee}
|
||||
convenienceFeeRecipient={convenienceFeeRecipient}
|
||||
defaultInputTokenAddress={optionsToAddressMap[defaultInputToken]}
|
||||
defaultInputAmount={defaultInputAmount}
|
||||
defaultOutputTokenAddress={optionsToAddressMap[defaultOutputToken]}
|
||||
defaultOutputAmount={defaultOutputAmount}
|
||||
onConnectWallet={() => console.log('onConnectWallet')} // this handler is included as a test of functionality, but only logs
|
||||
/>
|
||||
<TokenListProvider list={tokenListNameMap[tokenListName]}>
|
||||
<Swap
|
||||
convenienceFee={convenienceFee}
|
||||
convenienceFeeRecipient={convenienceFeeRecipient}
|
||||
defaultInputTokenAddress={optionsToAddressMap[defaultInputToken]}
|
||||
defaultInputAmount={defaultInputAmount}
|
||||
defaultOutputTokenAddress={optionsToAddressMap[defaultOutputToken]}
|
||||
defaultOutputAmount={defaultOutputAmount}
|
||||
onConnectWallet={() => console.log('onConnectWallet')} // this handler is included as a test of functionality, but only logs
|
||||
/>
|
||||
</TokenListProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'setimmediate'
|
||||
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import TokenSelect from 'lib/components/TokenSelect'
|
||||
import { loadingTransitionCss } from 'lib/css/loading'
|
||||
import styled, { keyframes, ThemedText } from 'lib/theme'
|
||||
import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
@@ -10,7 +11,6 @@ import Button from '../Button'
|
||||
import Column from '../Column'
|
||||
import { DecimalInput } from '../Input'
|
||||
import Row from '../Row'
|
||||
import TokenSelect from '../TokenSelect'
|
||||
|
||||
const TokenInputRow = styled(Row)`
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
@@ -86,7 +86,7 @@ export function WrapCurrency({ inputCurrency, outputCurrency }: { inputCurrency:
|
||||
const Text = useCallback(
|
||||
() => (
|
||||
<Trans>
|
||||
Convert {inputCurrency.symbol} to {outputCurrency.symbol} with no slippage
|
||||
Convert {inputCurrency.symbol} to {outputCurrency.symbol}
|
||||
</Trans>
|
||||
),
|
||||
[inputCurrency.symbol, outputCurrency.symbol]
|
||||
|
||||
@@ -17,21 +17,22 @@ function TokenImg({ token, ...rest }: TokenImgProps) {
|
||||
// Use the wrapped token info so that it includes the logoURI.
|
||||
const tokenInfo = useToken(token.isToken ? token.wrapped.address : undefined) ?? token
|
||||
|
||||
// TODO(zzmp): TokenImg takes a frame to switch.
|
||||
const srcs = useCurrencyLogoURIs(tokenInfo)
|
||||
|
||||
const [attempt, setAttempt] = useState(0)
|
||||
const onError = useCallback((e) => {
|
||||
if (e.target.src) badSrcs.add(e.target.src)
|
||||
setAttempt((attempt) => ++attempt)
|
||||
}, [])
|
||||
|
||||
const src = useMemo(() => {
|
||||
// Trigger a re-render when an error occurs.
|
||||
void attempt
|
||||
|
||||
return srcs.find((src) => !badSrcs.has(src))
|
||||
}, [attempt, srcs])
|
||||
const onError = useCallback(
|
||||
(e) => {
|
||||
if (src) badSrcs.add(src)
|
||||
setAttempt((attempt) => ++attempt)
|
||||
},
|
||||
[src]
|
||||
)
|
||||
|
||||
if (!src) return <MissingToken color="secondary" {...rest} />
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export default function Fixture() {
|
||||
return (
|
||||
<Modal color="module">
|
||||
<TokenListProvider list={DEFAULT_TOKEN_LIST.tokens}>
|
||||
<TokenSelectDialog onSelect={() => void 0} />
|
||||
<TokenSelectDialog onSelect={() => void 0} onClose={() => void 0} />
|
||||
</TokenListProvider>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { HelpCircle } from 'lib/icons'
|
||||
import styled, { css, ThemedText } from 'lib/theme'
|
||||
|
||||
import Column from '../Column'
|
||||
|
||||
const HelpCircleIcon = styled(HelpCircle)`
|
||||
height: 64px;
|
||||
margin-bottom: 12px;
|
||||
stroke: ${({ theme }) => theme.secondary};
|
||||
width: 64px;
|
||||
`
|
||||
|
||||
const wrapperCss = css`
|
||||
display: flex;
|
||||
height: 80%;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
export default function NoTokensAvailableOnNetwork() {
|
||||
return (
|
||||
<Column align="center" justify="center" css={wrapperCss}>
|
||||
<HelpCircleIcon />
|
||||
<ThemedText.Body1 color="primary">
|
||||
<Trans>No tokens are available on this network. Please switch to another network.</Trans>
|
||||
</ThemedText.Body1>
|
||||
</Column>
|
||||
)
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { Header as DialogHeader } from 'lib/components/Dialog'
|
||||
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
|
||||
import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance'
|
||||
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
||||
import useTokenList, { useIsTokenListLoaded, useQueryCurrencies } from 'lib/hooks/useTokenList'
|
||||
import useTokenList, { useIsTokenListLoaded, useQueryTokens } from 'lib/hooks/useTokenList'
|
||||
import styled, { ThemedText } from 'lib/theme'
|
||||
import { ElementRef, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { currencyId } from 'utils/currencyId'
|
||||
|
||||
import Column from '../Column'
|
||||
import Dialog, { Header } from '../Dialog'
|
||||
import Dialog from '../Dialog'
|
||||
import { inputCss, StringInput } from '../Input'
|
||||
import Row from '../Row'
|
||||
import Rule from '../Rule'
|
||||
import TokenBase from './TokenBase'
|
||||
import NoTokensAvailableOnNetwork from './NoTokensAvailableOnNetwork'
|
||||
import TokenButton from './TokenButton'
|
||||
import TokenOptions from './TokenOptions'
|
||||
import TokenOptionsSkeleton from './TokenOptionsSkeleton'
|
||||
@@ -42,12 +42,13 @@ function useAreBalancesLoaded(): boolean {
|
||||
interface TokenSelectDialogProps {
|
||||
value?: Currency
|
||||
onSelect: (token: Currency) => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export function TokenSelectDialog({ value, onSelect }: TokenSelectDialogProps) {
|
||||
export function TokenSelectDialog({ value, onSelect, onClose }: TokenSelectDialogProps) {
|
||||
const [query, setQuery] = useState('')
|
||||
const queriedTokens = useQueryCurrencies(query)
|
||||
const tokens = useMemo(() => queriedTokens?.filter((token) => token !== value), [queriedTokens, value])
|
||||
const list = useTokenList()
|
||||
const tokens = useQueryTokens(query, list)
|
||||
|
||||
const isTokenListLoaded = useIsTokenListLoaded()
|
||||
const areBalancesLoaded = useAreBalancesLoaded()
|
||||
@@ -65,16 +66,26 @@ export function TokenSelectDialog({ value, onSelect }: TokenSelectDialogProps) {
|
||||
[query, areBalancesLoaded, isTokenListLoaded]
|
||||
)
|
||||
|
||||
const baseTokens: Currency[] = [] // TODO(zzmp): Add base tokens to token list functionality
|
||||
|
||||
const input = useRef<HTMLInputElement>(null)
|
||||
useEffect(() => input.current?.focus({ preventScroll: true }), [input])
|
||||
|
||||
const [options, setOptions] = useState<ElementRef<typeof TokenOptions> | null>(null)
|
||||
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const listHasTokens = useMemo(() => list.some((token) => token.chainId === chainId), [chainId, list])
|
||||
|
||||
if (!listHasTokens && isLoaded) {
|
||||
return (
|
||||
<Dialog color="module" onClose={onClose}>
|
||||
<DialogHeader title={<Trans>Select a token</Trans>} />
|
||||
<NoTokensAvailableOnNetwork />
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header title={<Trans>Select a token</Trans>} />
|
||||
<Dialog color="module" onClose={onClose}>
|
||||
<DialogHeader title={<Trans>Select a token</Trans>} />
|
||||
<Column gap={0.75}>
|
||||
<Row pad={0.75} grow>
|
||||
<ThemedText.Body1>
|
||||
@@ -88,13 +99,6 @@ export function TokenSelectDialog({ value, onSelect }: TokenSelectDialogProps) {
|
||||
/>
|
||||
</ThemedText.Body1>
|
||||
</Row>
|
||||
{Boolean(baseTokens.length) && (
|
||||
<Row pad={0.75} gap={0.25} justify="flex-start" flex>
|
||||
{baseTokens.map((token) => (
|
||||
<TokenBase value={token} onClick={onSelect} key={currencyId(token)} />
|
||||
))}
|
||||
</Row>
|
||||
)}
|
||||
<Rule padded />
|
||||
</Column>
|
||||
{isLoaded ? (
|
||||
@@ -112,7 +116,7 @@ export function TokenSelectDialog({ value, onSelect }: TokenSelectDialogProps) {
|
||||
) : (
|
||||
<TokenOptionsSkeleton />
|
||||
)}
|
||||
</>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -138,11 +142,7 @@ export default memo(function TokenSelect({ value, collapsed, disabled, onSelect
|
||||
return (
|
||||
<>
|
||||
<TokenButton value={value} collapsed={collapsed} disabled={disabled} onClick={onOpen} />
|
||||
{open && (
|
||||
<Dialog color="module" onClose={() => setOpen(false)}>
|
||||
<TokenSelectDialog value={value} onSelect={selectAndClose} />
|
||||
</Dialog>
|
||||
)}
|
||||
{open && <TokenSelectDialog value={value} onSelect={selectAndClose} onClose={() => setOpen(false)} />}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NativeCurrency, Token } from '@uniswap/sdk-core'
|
||||
import { Token } from '@uniswap/sdk-core'
|
||||
import { TokenInfo, TokenList } from '@uniswap/token-lists'
|
||||
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
|
||||
import resolveENSContentHash from 'lib/utils/resolveENSContentHash'
|
||||
@@ -6,10 +6,11 @@ import { createContext, PropsWithChildren, useCallback, useContext, useEffect, u
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
|
||||
import fetchTokenList from './fetchTokenList'
|
||||
import { useQueryTokens } from './querying'
|
||||
import { ChainTokenMap, tokensToChainTokenMap } from './utils'
|
||||
import { validateTokens } from './validateTokenList'
|
||||
|
||||
export { useQueryTokens } from './useQueryTokens'
|
||||
|
||||
export const DEFAULT_TOKEN_LIST = 'https://gateway.ipfs.io/ipns/tokens.uniswap.org'
|
||||
|
||||
const MISSING_PROVIDER = Symbol()
|
||||
@@ -52,10 +53,6 @@ export function useTokenMap(): TokenMap {
|
||||
}, [tokenMap])
|
||||
}
|
||||
|
||||
export function useQueryCurrencies(query = ''): (WrappedTokenInfo | NativeCurrency)[] {
|
||||
return useQueryTokens(query, useTokenList())
|
||||
}
|
||||
|
||||
export function TokenListProvider({
|
||||
list = DEFAULT_TOKEN_LIST,
|
||||
children,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
|
||||
import { FeeAmount, NonfungiblePositionManager } from '@uniswap/v3-sdk'
|
||||
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useParsedQueryString from 'hooks/useParsedQueryString'
|
||||
import { useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { AlertTriangle } from 'react-feather'
|
||||
import ReactGA from 'react-ga4'
|
||||
@@ -47,8 +48,8 @@ import { useUSDCValue } from '../../hooks/useUSDCPrice'
|
||||
import { useV3PositionFromTokenId } from '../../hooks/useV3Positions'
|
||||
import { useWalletModalToggle } from '../../state/application/hooks'
|
||||
import { Bound, Field } from '../../state/mint/v3/actions'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { TransactionType } from '../../state/transactions/types'
|
||||
import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import approveAmountCalldata from '../../utils/approveAmountCalldata'
|
||||
@@ -86,6 +87,7 @@ export default function AddLiquidity({
|
||||
const expertMode = useIsExpertMode()
|
||||
const addTransaction = useTransactionAdder()
|
||||
const positionManager = useV3NFTPositionManagerContract()
|
||||
const parsedQs = useParsedQueryString()
|
||||
|
||||
// check for existing position if tokenId in url
|
||||
const { position: existingPositionDetails, loading: positionLoading } = useV3PositionFromTokenId(
|
||||
@@ -107,7 +109,8 @@ export default function AddLiquidity({
|
||||
baseCurrency && currencyB && baseCurrency.wrapped.equals(currencyB.wrapped) ? undefined : currencyB
|
||||
|
||||
// mint state
|
||||
const { independentField, typedValue, startPriceTypedValue } = useV3MintState()
|
||||
const { independentField, typedValue, startPriceTypedValue, rightRangeTypedValue, leftRangeTypedValue } =
|
||||
useV3MintState()
|
||||
|
||||
const {
|
||||
pool,
|
||||
@@ -150,6 +153,24 @@ export default function AddLiquidity({
|
||||
|
||||
useEffect(() => setShowCapitalEfficiencyWarning(false), [baseCurrency, quoteCurrency, feeAmount])
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
typeof parsedQs.minPrice === 'string' &&
|
||||
parsedQs.minPrice !== leftRangeTypedValue &&
|
||||
!isNaN(parsedQs.minPrice as any)
|
||||
) {
|
||||
onLeftRangeInput(parsedQs.minPrice)
|
||||
}
|
||||
|
||||
if (
|
||||
typeof parsedQs.maxPrice === 'string' &&
|
||||
parsedQs.maxPrice !== rightRangeTypedValue &&
|
||||
!isNaN(parsedQs.maxPrice as any)
|
||||
) {
|
||||
onRightRangeInput(parsedQs.maxPrice)
|
||||
}
|
||||
}, [parsedQs, rightRangeTypedValue, leftRangeTypedValue, onRightRangeInput, onLeftRangeInput])
|
||||
|
||||
// txn values
|
||||
const deadline = useTransactionDeadline() // custom from users settings
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ import { PairState } from '../../hooks/useV2Pairs'
|
||||
import { useWalletModalToggle } from '../../state/application/hooks'
|
||||
import { Field } from '../../state/mint/actions'
|
||||
import { useDerivedMintInfo, useMintActionHandlers, useMintState } from '../../state/mint/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { TransactionType } from '../../state/transactions/types'
|
||||
import { useIsExpertMode, useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { calculateGasMargin } from '../../utils/calculateGasMargin'
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import Loader from 'components/Loader'
|
||||
import TopLevelModals from 'components/TopLevelModals'
|
||||
import ApeModeQueryParamReader from 'hooks/useApeModeQueryParamReader'
|
||||
import { lazy, Suspense } from 'react'
|
||||
import { Redirect, Route, Switch } from 'react-router-dom'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import GoogleAnalyticsReporter from '../components/analytics/GoogleAnalyticsReporter'
|
||||
import AddressClaimModal from '../components/claim/AddressClaimModal'
|
||||
import ErrorBoundary from '../components/ErrorBoundary'
|
||||
import Header from '../components/Header'
|
||||
import Polling from '../components/Header/Polling'
|
||||
import Popups from '../components/Popups'
|
||||
import Web3ReactManager from '../components/Web3ReactManager'
|
||||
import { useModalOpen, useToggleModal } from '../state/application/hooks'
|
||||
import { ApplicationModal } from '../state/application/reducer'
|
||||
import DarkModeQueryParamReader from '../theme/DarkModeQueryParamReader'
|
||||
import AddLiquidity from './AddLiquidity'
|
||||
import { RedirectDuplicateTokenIds } from './AddLiquidity/redirects'
|
||||
@@ -65,12 +63,6 @@ const Marginer = styled.div`
|
||||
margin-top: 5rem;
|
||||
`
|
||||
|
||||
function TopLevelModals() {
|
||||
const open = useModalOpen(ApplicationModal.ADDRESS_CLAIM)
|
||||
const toggle = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
|
||||
return <AddressClaimModal isOpen={open} onDismiss={toggle} />
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
|
||||
@@ -44,7 +44,7 @@ import { useToken } from '../../hooks/Tokens'
|
||||
import { usePairContract, useV2MigratorContract } from '../../hooks/useContract'
|
||||
import useIsArgentWallet from '../../hooks/useIsArgentWallet'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { TransactionType } from '../../state/transactions/types'
|
||||
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { BackArrow, ExternalLink, ThemedText } from '../../theme'
|
||||
import { isAddress } from '../../utils'
|
||||
|
||||
@@ -42,7 +42,7 @@ import RateToggle from '../../components/RateToggle'
|
||||
import { SwitchLocaleLink } from '../../components/SwitchLocaleLink'
|
||||
import { usePositionTokenURI } from '../../hooks/usePositionTokenURI'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { TransactionType } from '../../state/transactions/types'
|
||||
import { calculateGasMargin } from '../../utils/calculateGasMargin'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { LoadingRows } from './styleds'
|
||||
|
||||
@@ -34,7 +34,7 @@ import { ThemedText } from 'theme'
|
||||
|
||||
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
|
||||
import { WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { TransactionType } from '../../state/transactions/types'
|
||||
import { calculateGasMargin } from '../../utils/calculateGasMargin'
|
||||
import { currencyId } from '../../utils/currencyId'
|
||||
import AppBody from '../AppBody'
|
||||
|
||||
@@ -33,8 +33,8 @@ import useTransactionDeadline from '../../hooks/useTransactionDeadline'
|
||||
import { useWalletModalToggle } from '../../state/application/hooks'
|
||||
import { Field } from '../../state/burn/actions'
|
||||
import { useBurnActionHandlers, useBurnState, useDerivedBurnInfo } from '../../state/burn/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
import { TransactionType } from '../../state/transactions/types'
|
||||
import { useUserSlippageToleranceWithDefault } from '../../state/user/hooks'
|
||||
import { StyledInternalLink, ThemedText } from '../../theme'
|
||||
import { calculateGasMargin } from '../../utils/calculateGasMargin'
|
||||
|
||||
@@ -14,17 +14,18 @@ export type PopupContent =
|
||||
}
|
||||
|
||||
export enum ApplicationModal {
|
||||
WALLET,
|
||||
SETTINGS,
|
||||
SELF_CLAIM,
|
||||
ADDRESS_CLAIM,
|
||||
BLOCKED_ACCOUNT,
|
||||
DELEGATE,
|
||||
CLAIM_POPUP,
|
||||
MENU,
|
||||
DELEGATE,
|
||||
VOTE,
|
||||
POOL_OVERVIEW_OPTIONS,
|
||||
NETWORK_SELECTOR,
|
||||
POOL_OVERVIEW_OPTIONS,
|
||||
PRIVACY_POLICY,
|
||||
SELF_CLAIM,
|
||||
SETTINGS,
|
||||
VOTE,
|
||||
WALLET,
|
||||
}
|
||||
|
||||
type PopupList = Array<{ key: string; show: boolean; content: PopupContent; removeAfterMs: number | null }>
|
||||
|
||||
@@ -11,8 +11,8 @@ import { UNI } from '../../constants/tokens'
|
||||
import { useContract } from '../../hooks/useContract'
|
||||
import { isAddress } from '../../utils'
|
||||
import { calculateGasMargin } from '../../utils/calculateGasMargin'
|
||||
import { TransactionType } from '../transactions/actions'
|
||||
import { useTransactionAdder } from '../transactions/hooks'
|
||||
import { TransactionType } from '../transactions/types'
|
||||
|
||||
function useMerkleDistributorContract() {
|
||||
return useContract(MERKLE_DISTRIBUTOR_ADDRESS, MERKLE_DISTRIBUTOR_ABI, true)
|
||||
|
||||
@@ -32,8 +32,8 @@ import {
|
||||
} from '../../constants/proposals'
|
||||
import { UNI } from '../../constants/tokens'
|
||||
import { useLogs } from '../logs/hooks'
|
||||
import { TransactionType } from '../transactions/actions'
|
||||
import { useTransactionAdder } from '../transactions/hooks'
|
||||
import { TransactionType } from '../transactions/types'
|
||||
import { VoteOption } from './types'
|
||||
|
||||
function useGovernanceV0Contract(): Contract | null {
|
||||
|
||||
@@ -16,8 +16,10 @@ import { usePool } from 'hooks/usePools'
|
||||
import JSBI from 'jsbi'
|
||||
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
||||
import { ReactNode, useCallback, useMemo } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { useAppDispatch, useAppSelector } from 'state/hooks'
|
||||
import { getTickToPrice } from 'utils/getTickToPrice'
|
||||
import { replaceURLParam } from 'utils/routes'
|
||||
|
||||
import { BIG_INT_ZERO } from '../../../constants/misc'
|
||||
import { PoolState } from '../../../hooks/usePools'
|
||||
@@ -46,6 +48,7 @@ export function useV3MintActionHandlers(noLiquidity: boolean | undefined): {
|
||||
onStartPriceInput: (typedValue: string) => void
|
||||
} {
|
||||
const dispatch = useAppDispatch()
|
||||
const history = useHistory()
|
||||
|
||||
const onFieldAInput = useCallback(
|
||||
(typedValue: string) => {
|
||||
@@ -64,15 +67,17 @@ export function useV3MintActionHandlers(noLiquidity: boolean | undefined): {
|
||||
const onLeftRangeInput = useCallback(
|
||||
(typedValue: string) => {
|
||||
dispatch(typeLeftRangeInput({ typedValue }))
|
||||
history.replace({ search: replaceURLParam(history.location.search, 'minPrice', typedValue) })
|
||||
},
|
||||
[dispatch]
|
||||
[dispatch, history]
|
||||
)
|
||||
|
||||
const onRightRangeInput = useCallback(
|
||||
(typedValue: string) => {
|
||||
dispatch(typeRightRangeInput({ typedValue }))
|
||||
history.replace({ search: replaceURLParam(history.location.search, 'maxPrice', typedValue) })
|
||||
},
|
||||
[dispatch]
|
||||
[dispatch, history]
|
||||
)
|
||||
|
||||
const onStartPriceInput = useCallback(
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { Token } from '@uniswap/sdk-core'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useTransactionMonitoringEventCallback } from 'hooks/useMonitoringEventCallback'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useAppDispatch, useAppSelector } from 'state/hooks'
|
||||
|
||||
import { addTransaction, TransactionInfo, TransactionType } from './actions'
|
||||
import { TransactionDetails } from './reducer'
|
||||
import { addTransaction } from './reducer'
|
||||
import { TransactionDetails, TransactionInfo, TransactionType } from './types'
|
||||
|
||||
// helper that can take a ethers library transaction response and add it to the list of transactions
|
||||
export function useTransactionAdder(): (response: TransactionResponse, info: TransactionInfo) => void {
|
||||
const { chainId, account } = useActiveWeb3React()
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const logMonitoringEvent = useTransactionMonitoringEventCallback()
|
||||
|
||||
return useCallback(
|
||||
(response: TransactionResponse, info: TransactionInfo) => {
|
||||
if (!account) return
|
||||
@@ -25,10 +22,8 @@ export function useTransactionAdder(): (response: TransactionResponse, info: Tra
|
||||
throw Error('No transaction hash found.')
|
||||
}
|
||||
dispatch(addTransaction({ hash, from: account, info, chainId }))
|
||||
|
||||
logMonitoringEvent(info, response)
|
||||
},
|
||||
[account, chainId, dispatch, logMonitoringEvent]
|
||||
[account, chainId, dispatch]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { createStore, Store } from 'redux'
|
||||
|
||||
import { updateVersion } from '../global/actions'
|
||||
import {
|
||||
import reducer, {
|
||||
addTransaction,
|
||||
checkedTransaction,
|
||||
clearAllTransactions,
|
||||
finalizeTransaction,
|
||||
TransactionType,
|
||||
} from './actions'
|
||||
import reducer, { initialState, TransactionState } from './reducer'
|
||||
initialState,
|
||||
TransactionState,
|
||||
} from './reducer'
|
||||
import { TransactionType } from './types'
|
||||
|
||||
describe('transaction reducer', () => {
|
||||
let store: Store<TransactionState>
|
||||
|
||||
@@ -1,27 +1,10 @@
|
||||
import { createReducer } from '@reduxjs/toolkit'
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
|
||||
import { updateVersion } from '../global/actions'
|
||||
import {
|
||||
addTransaction,
|
||||
checkedTransaction,
|
||||
clearAllTransactions,
|
||||
finalizeTransaction,
|
||||
SerializableTransactionReceipt,
|
||||
TransactionInfo,
|
||||
} from './actions'
|
||||
import { TransactionDetails } from './types'
|
||||
|
||||
const now = () => new Date().getTime()
|
||||
|
||||
export interface TransactionDetails {
|
||||
hash: string
|
||||
receipt?: SerializableTransactionReceipt
|
||||
lastCheckedBlockNumber?: number
|
||||
addedTime: number
|
||||
confirmedTime?: number
|
||||
from: string
|
||||
info: TransactionInfo
|
||||
}
|
||||
|
||||
export interface TransactionState {
|
||||
[chainId: number]: {
|
||||
[txHash: string]: TransactionDetails
|
||||
@@ -30,9 +13,44 @@ export interface TransactionState {
|
||||
|
||||
export const initialState: TransactionState = {}
|
||||
|
||||
export default createReducer(initialState, (builder) =>
|
||||
builder
|
||||
.addCase(updateVersion, (transactions) => {
|
||||
const transactionSlice = createSlice({
|
||||
name: 'transactions',
|
||||
initialState,
|
||||
reducers: {
|
||||
addTransaction(transactions, { payload: { chainId, from, hash, info } }) {
|
||||
if (transactions[chainId]?.[hash]) {
|
||||
throw Error('Attempted to add existing transaction.')
|
||||
}
|
||||
const txs = transactions[chainId] ?? {}
|
||||
txs[hash] = { hash, info, from, addedTime: now() }
|
||||
transactions[chainId] = txs
|
||||
},
|
||||
clearAllTransactions(transactions, { payload: { chainId } }) {
|
||||
if (!transactions[chainId]) return
|
||||
transactions[chainId] = {}
|
||||
},
|
||||
checkedTransaction(transactions, { payload: { chainId, hash, blockNumber } }) {
|
||||
const tx = transactions[chainId]?.[hash]
|
||||
if (!tx) {
|
||||
return
|
||||
}
|
||||
if (!tx.lastCheckedBlockNumber) {
|
||||
tx.lastCheckedBlockNumber = blockNumber
|
||||
} else {
|
||||
tx.lastCheckedBlockNumber = Math.max(blockNumber, tx.lastCheckedBlockNumber)
|
||||
}
|
||||
},
|
||||
finalizeTransaction(transactions, { payload: { hash, chainId, receipt } }) {
|
||||
const tx = transactions[chainId]?.[hash]
|
||||
if (!tx) {
|
||||
return
|
||||
}
|
||||
tx.receipt = receipt
|
||||
tx.confirmedTime = now()
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(updateVersion, (transactions) => {
|
||||
// in case there are any transactions in the store with the old format, remove them
|
||||
Object.keys(transactions).forEach((chainId) => {
|
||||
const chainTransactions = transactions[chainId as unknown as number]
|
||||
@@ -44,35 +62,9 @@ export default createReducer(initialState, (builder) =>
|
||||
})
|
||||
})
|
||||
})
|
||||
.addCase(addTransaction, (transactions, { payload: { chainId, from, hash, info } }) => {
|
||||
if (transactions[chainId]?.[hash]) {
|
||||
throw Error('Attempted to add existing transaction.')
|
||||
}
|
||||
const txs = transactions[chainId] ?? {}
|
||||
txs[hash] = { hash, info, from, addedTime: now() }
|
||||
transactions[chainId] = txs
|
||||
})
|
||||
.addCase(clearAllTransactions, (transactions, { payload: { chainId } }) => {
|
||||
if (!transactions[chainId]) return
|
||||
transactions[chainId] = {}
|
||||
})
|
||||
.addCase(checkedTransaction, (transactions, { payload: { chainId, hash, blockNumber } }) => {
|
||||
const tx = transactions[chainId]?.[hash]
|
||||
if (!tx) {
|
||||
return
|
||||
}
|
||||
if (!tx.lastCheckedBlockNumber) {
|
||||
tx.lastCheckedBlockNumber = blockNumber
|
||||
} else {
|
||||
tx.lastCheckedBlockNumber = Math.max(blockNumber, tx.lastCheckedBlockNumber)
|
||||
}
|
||||
})
|
||||
.addCase(finalizeTransaction, (transactions, { payload: { hash, chainId, receipt } }) => {
|
||||
const tx = transactions[chainId]?.[hash]
|
||||
if (!tx) {
|
||||
return
|
||||
}
|
||||
tx.receipt = receipt
|
||||
tx.confirmedTime = now()
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export const { addTransaction, clearAllTransactions, checkedTransaction, finalizeTransaction } =
|
||||
transactionSlice.actions
|
||||
export default transactionSlice.reducer
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { createAction } from '@reduxjs/toolkit'
|
||||
import { TradeType } from '@uniswap/sdk-core'
|
||||
|
||||
import { VoteOption } from '../governance/types'
|
||||
|
||||
export interface SerializableTransactionReceipt {
|
||||
interface SerializableTransactionReceipt {
|
||||
to: string
|
||||
from: string
|
||||
contractAddress: string
|
||||
@@ -171,20 +170,12 @@ export type TransactionInfo =
|
||||
| RemoveLiquidityV3TransactionInfo
|
||||
| SubmitProposalTransactionInfo
|
||||
|
||||
export const addTransaction = createAction<{
|
||||
chainId: number
|
||||
export interface TransactionDetails {
|
||||
hash: string
|
||||
receipt?: SerializableTransactionReceipt
|
||||
lastCheckedBlockNumber?: number
|
||||
addedTime: number
|
||||
confirmedTime?: number
|
||||
from: string
|
||||
info: TransactionInfo
|
||||
}>('transactions/addTransaction')
|
||||
export const clearAllTransactions = createAction<{ chainId: number }>('transactions/clearAllTransactions')
|
||||
export const finalizeTransaction = createAction<{
|
||||
chainId: number
|
||||
hash: string
|
||||
receipt: SerializableTransactionReceipt
|
||||
}>('transactions/finalizeTransaction')
|
||||
export const checkedTransaction = createAction<{
|
||||
chainId: number
|
||||
hash: string
|
||||
blockNumber: number
|
||||
}>('transactions/checkedTransaction')
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { useAppDispatch, useAppSelector } from 'state/hooks'
|
||||
|
||||
import { L2_CHAIN_IDS } from '../../constants/chains'
|
||||
import { useAddPopup } from '../application/hooks'
|
||||
import { checkedTransaction, finalizeTransaction } from './actions'
|
||||
import { checkedTransaction, finalizeTransaction } from './reducer'
|
||||
|
||||
export default function Updater() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { createAction } from '@reduxjs/toolkit'
|
||||
import { SupportedLocale } from 'constants/locales'
|
||||
|
||||
export interface SerializedToken {
|
||||
chainId: number
|
||||
address: string
|
||||
decimals: number
|
||||
symbol?: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export interface SerializedPair {
|
||||
token0: SerializedToken
|
||||
token1: SerializedToken
|
||||
}
|
||||
|
||||
export const updateMatchesDarkMode = createAction<{ matchesDarkMode: boolean }>('user/updateMatchesDarkMode')
|
||||
export const updateUserDarkMode = createAction<{ userDarkMode: boolean }>('user/updateUserDarkMode')
|
||||
export const updateUserExpertMode = createAction<{ userExpertMode: boolean }>('user/updateUserExpertMode')
|
||||
export const updateUserLocale = createAction<{ userLocale: SupportedLocale }>('user/updateUserLocale')
|
||||
export const updateShowSurveyPopup = createAction<{ showSurveyPopup: boolean }>('user/updateShowSurveyPopup')
|
||||
export const updateShowDonationLink = createAction<{ showDonationLink: boolean }>('user/updateShowDonationLink')
|
||||
export const updateUserClientSideRouter = createAction<{ userClientSideRouter: boolean }>(
|
||||
'user/updateUserClientSideRouter'
|
||||
)
|
||||
export const updateHideClosedPositions = createAction<{ userHideClosedPositions: boolean }>('user/hideClosedPositions')
|
||||
export const updateUserSlippageTolerance = createAction<{ userSlippageTolerance: number | 'auto' }>(
|
||||
'user/updateUserSlippageTolerance'
|
||||
)
|
||||
export const updateUserDeadline = createAction<{ userDeadline: number }>('user/updateUserDeadline')
|
||||
export const addSerializedToken = createAction<{ serializedToken: SerializedToken }>('user/addSerializedToken')
|
||||
export const removeSerializedToken = createAction<{ chainId: number; address: string }>('user/removeSerializedToken')
|
||||
export const addSerializedPair = createAction<{ serializedPair: SerializedPair }>('user/addSerializedPair')
|
||||
export const removeSerializedPair =
|
||||
createAction<{ chainId: number; tokenAAddress: string; tokenBAddress: string }>('user/removeSerializedPair')
|
||||
@@ -18,8 +18,6 @@ import {
|
||||
addSerializedPair,
|
||||
addSerializedToken,
|
||||
removeSerializedToken,
|
||||
SerializedPair,
|
||||
SerializedToken,
|
||||
updateHideClosedPositions,
|
||||
updateShowDonationLink,
|
||||
updateShowSurveyPopup,
|
||||
@@ -29,7 +27,8 @@ import {
|
||||
updateUserExpertMode,
|
||||
updateUserLocale,
|
||||
updateUserSlippageTolerance,
|
||||
} from './actions'
|
||||
} from './reducer'
|
||||
import { SerializedPair, SerializedToken } from './types'
|
||||
|
||||
function serializeToken(token: Token): SerializedToken {
|
||||
return {
|
||||
|
||||
@@ -1,26 +1,9 @@
|
||||
import { createReducer } from '@reduxjs/toolkit'
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
import { SupportedLocale } from 'constants/locales'
|
||||
|
||||
import { DEFAULT_DEADLINE_FROM_NOW } from '../../constants/misc'
|
||||
import { updateVersion } from '../global/actions'
|
||||
import {
|
||||
addSerializedPair,
|
||||
addSerializedToken,
|
||||
removeSerializedPair,
|
||||
removeSerializedToken,
|
||||
SerializedPair,
|
||||
SerializedToken,
|
||||
updateHideClosedPositions,
|
||||
updateMatchesDarkMode,
|
||||
updateShowDonationLink,
|
||||
updateShowSurveyPopup,
|
||||
updateUserClientSideRouter,
|
||||
updateUserDarkMode,
|
||||
updateUserDeadline,
|
||||
updateUserExpertMode,
|
||||
updateUserLocale,
|
||||
updateUserSlippageTolerance,
|
||||
} from './actions'
|
||||
import { SerializedPair, SerializedToken } from './types'
|
||||
|
||||
const currentTimestamp = () => new Date().getTime()
|
||||
|
||||
@@ -91,9 +74,84 @@ export const initialState: UserState = {
|
||||
showDonationLink: true,
|
||||
}
|
||||
|
||||
export default createReducer(initialState, (builder) =>
|
||||
builder
|
||||
.addCase(updateVersion, (state) => {
|
||||
const userSlice = createSlice({
|
||||
name: 'user',
|
||||
initialState,
|
||||
reducers: {
|
||||
updateUserDarkMode(state, action) {
|
||||
state.userDarkMode = action.payload.userDarkMode
|
||||
state.timestamp = currentTimestamp()
|
||||
},
|
||||
updateMatchesDarkMode(state, action) {
|
||||
state.matchesDarkMode = action.payload.matchesDarkMode
|
||||
state.timestamp = currentTimestamp()
|
||||
},
|
||||
updateUserExpertMode(state, action) {
|
||||
state.userExpertMode = action.payload.userExpertMode
|
||||
state.timestamp = currentTimestamp()
|
||||
},
|
||||
updateUserLocale(state, action) {
|
||||
state.userLocale = action.payload.userLocale
|
||||
state.timestamp = currentTimestamp()
|
||||
},
|
||||
updateUserSlippageTolerance(state, action) {
|
||||
state.userSlippageTolerance = action.payload.userSlippageTolerance
|
||||
state.timestamp = currentTimestamp()
|
||||
},
|
||||
updateUserDeadline(state, action) {
|
||||
state.userDeadline = action.payload.userDeadline
|
||||
state.timestamp = currentTimestamp()
|
||||
},
|
||||
updateUserClientSideRouter(state, action) {
|
||||
state.userClientSideRouter = action.payload.userClientSideRouter
|
||||
},
|
||||
updateHideClosedPositions(state, action) {
|
||||
state.userHideClosedPositions = action.payload.userHideClosedPositions
|
||||
},
|
||||
updateShowSurveyPopup(state, action) {
|
||||
state.showSurveyPopup = action.payload.showSurveyPopup
|
||||
},
|
||||
updateShowDonationLink(state, action) {
|
||||
state.showDonationLink = action.payload.showDonationLink
|
||||
},
|
||||
addSerializedToken(state, { payload: { serializedToken } }) {
|
||||
if (!state.tokens) {
|
||||
state.tokens = {}
|
||||
}
|
||||
state.tokens[serializedToken.chainId] = state.tokens[serializedToken.chainId] || {}
|
||||
state.tokens[serializedToken.chainId][serializedToken.address] = serializedToken
|
||||
state.timestamp = currentTimestamp()
|
||||
},
|
||||
removeSerializedToken(state, { payload: { address, chainId } }) {
|
||||
if (!state.tokens) {
|
||||
state.tokens = {}
|
||||
}
|
||||
state.tokens[chainId] = state.tokens[chainId] || {}
|
||||
delete state.tokens[chainId][address]
|
||||
state.timestamp = currentTimestamp()
|
||||
},
|
||||
addSerializedPair(state, { payload: { serializedPair } }) {
|
||||
if (
|
||||
serializedPair.token0.chainId === serializedPair.token1.chainId &&
|
||||
serializedPair.token0.address !== serializedPair.token1.address
|
||||
) {
|
||||
const chainId = serializedPair.token0.chainId
|
||||
state.pairs[chainId] = state.pairs[chainId] || {}
|
||||
state.pairs[chainId][pairKey(serializedPair.token0.address, serializedPair.token1.address)] = serializedPair
|
||||
}
|
||||
state.timestamp = currentTimestamp()
|
||||
},
|
||||
removeSerializedPair(state, { payload: { chainId, tokenAAddress, tokenBAddress } }) {
|
||||
if (state.pairs[chainId]) {
|
||||
// just delete both keys if either exists
|
||||
delete state.pairs[chainId][pairKey(tokenAAddress, tokenBAddress)]
|
||||
delete state.pairs[chainId][pairKey(tokenBAddress, tokenAAddress)]
|
||||
}
|
||||
state.timestamp = currentTimestamp()
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(updateVersion, (state) => {
|
||||
// slippage isnt being tracked in local storage, reset to default
|
||||
// noinspection SuspiciousTypeOfGuard
|
||||
if (
|
||||
@@ -126,75 +184,23 @@ export default createReducer(initialState, (builder) =>
|
||||
|
||||
state.lastUpdateVersionTimestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(updateUserDarkMode, (state, action) => {
|
||||
state.userDarkMode = action.payload.userDarkMode
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(updateMatchesDarkMode, (state, action) => {
|
||||
state.matchesDarkMode = action.payload.matchesDarkMode
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(updateUserExpertMode, (state, action) => {
|
||||
state.userExpertMode = action.payload.userExpertMode
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(updateUserLocale, (state, action) => {
|
||||
state.userLocale = action.payload.userLocale
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(updateUserSlippageTolerance, (state, action) => {
|
||||
state.userSlippageTolerance = action.payload.userSlippageTolerance
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(updateUserDeadline, (state, action) => {
|
||||
state.userDeadline = action.payload.userDeadline
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(updateUserClientSideRouter, (state, action) => {
|
||||
state.userClientSideRouter = action.payload.userClientSideRouter
|
||||
})
|
||||
.addCase(updateHideClosedPositions, (state, action) => {
|
||||
state.userHideClosedPositions = action.payload.userHideClosedPositions
|
||||
})
|
||||
.addCase(updateShowSurveyPopup, (state, action) => {
|
||||
state.showSurveyPopup = action.payload.showSurveyPopup
|
||||
})
|
||||
.addCase(updateShowDonationLink, (state, action) => {
|
||||
state.showDonationLink = action.payload.showDonationLink
|
||||
})
|
||||
.addCase(addSerializedToken, (state, { payload: { serializedToken } }) => {
|
||||
if (!state.tokens) {
|
||||
state.tokens = {}
|
||||
}
|
||||
state.tokens[serializedToken.chainId] = state.tokens[serializedToken.chainId] || {}
|
||||
state.tokens[serializedToken.chainId][serializedToken.address] = serializedToken
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(removeSerializedToken, (state, { payload: { address, chainId } }) => {
|
||||
if (!state.tokens) {
|
||||
state.tokens = {}
|
||||
}
|
||||
state.tokens[chainId] = state.tokens[chainId] || {}
|
||||
delete state.tokens[chainId][address]
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(addSerializedPair, (state, { payload: { serializedPair } }) => {
|
||||
if (
|
||||
serializedPair.token0.chainId === serializedPair.token1.chainId &&
|
||||
serializedPair.token0.address !== serializedPair.token1.address
|
||||
) {
|
||||
const chainId = serializedPair.token0.chainId
|
||||
state.pairs[chainId] = state.pairs[chainId] || {}
|
||||
state.pairs[chainId][pairKey(serializedPair.token0.address, serializedPair.token1.address)] = serializedPair
|
||||
}
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
.addCase(removeSerializedPair, (state, { payload: { chainId, tokenAAddress, tokenBAddress } }) => {
|
||||
if (state.pairs[chainId]) {
|
||||
// just delete both keys if either exists
|
||||
delete state.pairs[chainId][pairKey(tokenAAddress, tokenBAddress)]
|
||||
delete state.pairs[chainId][pairKey(tokenBAddress, tokenAAddress)]
|
||||
}
|
||||
state.timestamp = currentTimestamp()
|
||||
})
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export const {
|
||||
addSerializedPair,
|
||||
addSerializedToken,
|
||||
removeSerializedPair,
|
||||
removeSerializedToken,
|
||||
updateHideClosedPositions,
|
||||
updateMatchesDarkMode,
|
||||
updateShowDonationLink,
|
||||
updateShowSurveyPopup,
|
||||
updateUserClientSideRouter,
|
||||
updateUserDarkMode,
|
||||
updateUserDeadline,
|
||||
updateUserExpertMode,
|
||||
updateUserLocale,
|
||||
updateUserSlippageTolerance,
|
||||
} = userSlice.actions
|
||||
export default userSlice.reducer
|
||||
|
||||
12
src/state/user/types.ts
Normal file
12
src/state/user/types.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export interface SerializedToken {
|
||||
chainId: number
|
||||
address: string
|
||||
decimals: number
|
||||
symbol?: string
|
||||
name?: string
|
||||
}
|
||||
|
||||
export interface SerializedPair {
|
||||
token0: SerializedToken
|
||||
token1: SerializedToken
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
|
||||
import { updateMatchesDarkMode } from './actions'
|
||||
import { updateMatchesDarkMode } from './reducer'
|
||||
|
||||
export default function Updater(): null {
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useEffect } from 'react'
|
||||
import { RouteComponentProps } from 'react-router-dom'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
|
||||
import { updateUserDarkMode } from '../state/user/actions'
|
||||
import { updateUserDarkMode } from '../state/user/reducer'
|
||||
|
||||
export default function DarkModeQueryParamReader({ location: { search } }: RouteComponentProps): null {
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
Reference in New Issue
Block a user