Commit de3a33df authored by Zach Pomerantz's avatar Zach Pomerantz Committed by GitHub

fix: stale data edge cases (#3657)

* fix: stale chain block

* chore: simplify atom usage

* fix: support single-token chain

* fix: avoid extra rpcs

* chore: rename isDisabled

* fix: simplify useUSDCPrice

* fix: simplify useComputeSwapInfo

* chore: include type

* fix: guard hasAmounts
parent 99a084f2
...@@ -41,7 +41,7 @@ export default function useAutoSlippageTolerance( ...@@ -41,7 +41,7 @@ export default function useAutoSlippageTolerance(
const gasEstimate = guesstimateGas(trade) const gasEstimate = guesstimateGas(trade)
const nativeCurrency = useNativeCurrency() const nativeCurrency = useNativeCurrency()
const nativeCurrencyPrice = useUSDCPrice(nativeCurrency ?? undefined) const nativeCurrencyPrice = useUSDCPrice((trade && nativeCurrency) ?? undefined)
return useMemo(() => { return useMemo(() => {
if (!trade || onL2) return ONE_TENTHS_PERCENT if (!trade || onL2) return ONE_TENTHS_PERCENT
......
...@@ -8,10 +8,10 @@ import { usePendingTransactions } from 'lib/hooks/transactions' ...@@ -8,10 +8,10 @@ import { usePendingTransactions } from 'lib/hooks/transactions'
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React' import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
import useHasFocus from 'lib/hooks/useHasFocus' import useHasFocus from 'lib/hooks/useHasFocus'
import useOnSupportedNetwork from 'lib/hooks/useOnSupportedNetwork' import useOnSupportedNetwork from 'lib/hooks/useOnSupportedNetwork'
import useTokenList, { useSyncTokenList } from 'lib/hooks/useTokenList' import { useSyncTokenList } from 'lib/hooks/useTokenList'
import { displayTxHashAtom } from 'lib/state/swap' import { displayTxHashAtom } from 'lib/state/swap'
import { SwapTransactionInfo, Transaction, TransactionType, WrapTransactionInfo } from 'lib/state/transactions' import { SwapTransactionInfo, Transaction, TransactionType, WrapTransactionInfo } from 'lib/state/transactions'
import { useMemo, useState } from 'react' import { useState } from 'react'
import Dialog from '../Dialog' import Dialog from '../Dialog'
import Header from '../Header' import Header from '../Header'
...@@ -63,32 +63,26 @@ export default function Swap(props: SwapProps) { ...@@ -63,32 +63,26 @@ export default function Swap(props: SwapProps) {
const pendingTxs = usePendingTransactions() const pendingTxs = usePendingTransactions()
const displayTx = getTransactionFromMap(pendingTxs, displayTxHash) const displayTx = getTransactionFromMap(pendingTxs, displayTxHash)
const tokenList = useTokenList()
const onSupportedNetwork = useOnSupportedNetwork() const onSupportedNetwork = useOnSupportedNetwork()
const isSwapSupported = useMemo( const isDisabled = !(active && onSupportedNetwork)
() => Boolean(active && onSupportedNetwork && tokenList?.length),
[active, onSupportedNetwork, tokenList?.length]
)
const focused = useHasFocus(wrapper) const focused = useHasFocus(wrapper)
const isInteractive = Boolean(active && onSupportedNetwork)
return ( return (
<> <>
<SwapPropValidator {...props} /> <SwapPropValidator {...props} />
<Updaters {...props} disabled={!isSwapSupported} /> <Updaters {...props} disabled={isDisabled} />
<Header title={<Trans>Swap</Trans>}> <Header title={<Trans>Swap</Trans>}>
{active && <Wallet disabled={!account} onClick={props.onConnectWallet} />} {active && <Wallet disabled={!account} onClick={props.onConnectWallet} />}
<Settings disabled={!isInteractive} /> <Settings disabled={isDisabled} />
</Header> </Header>
<div ref={setWrapper}> <div ref={setWrapper}>
<BoundaryProvider value={wrapper}> <BoundaryProvider value={wrapper}>
<Input disabled={!isInteractive} focused={focused} /> <Input disabled={isDisabled} focused={focused} />
<ReverseButton disabled={!isInteractive} /> <ReverseButton disabled={isDisabled} />
<Output disabled={!isInteractive} focused={focused}> <Output disabled={isDisabled} focused={focused}>
<Toolbar disabled={!active} /> <Toolbar disabled={!active} />
<SwapButton disabled={!isSwapSupported} /> <SwapButton disabled={isDisabled} />
</Output> </Output>
</BoundaryProvider> </BoundaryProvider>
</div> </div>
......
...@@ -43,10 +43,11 @@ function useComputeSwapInfo(): SwapInfo { ...@@ -43,10 +43,11 @@ function useComputeSwapInfo(): SwapInfo {
() => tryParseCurrencyAmount(amount, (isExactIn ? currencyIn : currencyOut) ?? undefined), () => tryParseCurrencyAmount(amount, (isExactIn ? currencyIn : currencyOut) ?? undefined),
[amount, isExactIn, currencyIn, currencyOut] [amount, isExactIn, currencyIn, currencyOut]
) )
const hasAmounts = currencyIn && currencyOut && parsedAmount
const trade = useBestTrade( const trade = useBestTrade(
isExactIn ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT, isExactIn ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT,
parsedAmount, hasAmounts ? parsedAmount : undefined,
(isExactIn ? currencyOut : currencyIn) ?? undefined hasAmounts ? (isExactIn ? currencyOut : currencyIn) : undefined
) )
const amountIn = useMemo( const amountIn = useMemo(
...@@ -111,7 +112,7 @@ const swapInfoAtom = atom<SwapInfo>({ ...@@ -111,7 +112,7 @@ const swapInfoAtom = atom<SwapInfo>({
export function SwapInfoUpdater() { export function SwapInfoUpdater() {
const setSwapInfo = useUpdateAtom(swapInfoAtom) const setSwapInfo = useUpdateAtom(swapInfoAtom)
const swapInfo = useComputeSwapInfo() const swapInfo = useComputeSwapInfo()
useEffect(() => setSwapInfo(swapInfo), [swapInfo, setSwapInfo]) useEffect(() => setSwapInfo(swapInfo), [setSwapInfo, swapInfo])
return null return null
} }
......
import useActiveWeb3React from 'hooks/useActiveWeb3React' import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useDebounce from 'hooks/useDebounce'
import useIsWindowVisible from 'hooks/useIsWindowVisible' import useIsWindowVisible from 'hooks/useIsWindowVisible'
import { atom } from 'jotai' import { atom } from 'jotai'
import { useAtomValue, useUpdateAtom } from 'jotai/utils' import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect } from 'react'
function useBlock() { interface ChainBlock {
chainId?: number
block?: number
}
const chainBlockAtom = atom<ChainBlock>({})
function useUpdateChainBlock() {
const { chainId, library } = useActiveWeb3React() const { chainId, library } = useActiveWeb3React()
const windowVisible = useIsWindowVisible() const windowVisible = useIsWindowVisible()
const [state, setState] = useState<{ chainId?: number; block?: number }>({ chainId }) const setChainBlock = useUpdateAtom(chainBlockAtom)
const onBlock = useCallback( const onBlock = useCallback(
(block: number) => { (block: number) => {
setState((state) => { setChainBlock((chainBlock) => {
if (state.chainId === chainId) { if (chainBlock.chainId === chainId) {
if (typeof state.block !== 'number') return { chainId, block } if (chainBlock.block === block) return chainBlock
return { chainId, block: Math.max(block, state.block) } if (typeof chainBlock.block !== 'number') return { chainId, block }
return { chainId, block: Math.max(block, chainBlock.block) }
} }
return state return chainBlock
}) })
}, },
[chainId] [chainId, setChainBlock]
) )
useEffect(() => { useEffect(() => {
if (library && chainId && windowVisible) { if (library && chainId && windowVisible) {
// If chainId hasn't changed, don't clear the block. This prevents re-fetching still valid data. // If chainId hasn't changed, don't clear the block. This prevents re-fetching still valid data.
setState((state) => (state.chainId === chainId ? state : { chainId })) setChainBlock((chainBlock) => (chainBlock.chainId === chainId ? chainBlock : { chainId }))
library library
.getBlockNumber() .getBlockNumber()
...@@ -41,30 +47,23 @@ function useBlock() { ...@@ -41,30 +47,23 @@ function useBlock() {
} }
} }
return undefined return undefined
}, [chainId, library, onBlock, windowVisible]) }, [chainId, library, onBlock, setChainBlock, windowVisible])
const debouncedBlock = useDebounce(state.block, 100)
return state.block ? debouncedBlock : undefined
} }
const blockAtom = atom<number | undefined>(undefined)
export function BlockUpdater() { export function BlockUpdater() {
const setBlock = useUpdateAtom(blockAtom) useUpdateChainBlock()
const block = useBlock()
useEffect(() => {
setBlock(block)
}, [block, setBlock])
return null return null
} }
/** Requires that BlockUpdater be installed in the DOM tree. */ /** Requires that BlockUpdater be installed in the DOM tree. */
export default function useBlockNumber(): number | undefined { export default function useBlockNumber(): number | undefined {
const { chainId } = useActiveWeb3React() const { chainId: activeChainId } = useActiveWeb3React()
const block = useAtomValue(blockAtom) const { chainId, block } = useAtomValue(chainBlockAtom)
return chainId ? block : undefined return activeChainId === chainId ? block : undefined
} }
export function useFastForwardBlockNumber(): (block: number) => void { export function useFastForwardBlockNumber(): (block: number) => void {
return useUpdateAtom(blockAtom) const { chainId } = useActiveWeb3React()
const setChainBlock = useUpdateAtom(chainBlockAtom)
return useCallback((block: number) => setChainBlock({ chainId, block }), [chainId, setChainBlock])
} }
...@@ -11,8 +11,8 @@ export const store = createStore(reducer) ...@@ -11,8 +11,8 @@ export const store = createStore(reducer)
export default multicall export default multicall
export function MulticallUpdater() { export function MulticallUpdater() {
const latestBlockNumber = useBlockNumber()
const { chainId } = useActiveWeb3React() const { chainId } = useActiveWeb3React()
const latestBlockNumber = useBlockNumber()
const contract = useInterfaceMulticall() const contract = useInterfaceMulticall()
return <multicall.Updater chainId={chainId} latestBlockNumber={latestBlockNumber} contract={contract} /> return <multicall.Updater chainId={chainId} latestBlockNumber={latestBlockNumber} contract={contract} />
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment