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
This commit is contained in:
Zach Pomerantz 2022-04-05 10:45:21 -07:00 committed by GitHub
parent 99a084f230
commit de3a33dfcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 40 additions and 46 deletions

@ -41,7 +41,7 @@ export default function useAutoSlippageTolerance(
const gasEstimate = guesstimateGas(trade)
const nativeCurrency = useNativeCurrency()
const nativeCurrencyPrice = useUSDCPrice(nativeCurrency ?? undefined)
const nativeCurrencyPrice = useUSDCPrice((trade && nativeCurrency) ?? undefined)
return useMemo(() => {
if (!trade || onL2) return ONE_TENTHS_PERCENT

@ -8,10 +8,10 @@ import { usePendingTransactions } from 'lib/hooks/transactions'
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
import useHasFocus from 'lib/hooks/useHasFocus'
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 { SwapTransactionInfo, Transaction, TransactionType, WrapTransactionInfo } from 'lib/state/transactions'
import { useMemo, useState } from 'react'
import { useState } from 'react'
import Dialog from '../Dialog'
import Header from '../Header'
@ -63,32 +63,26 @@ export default function Swap(props: SwapProps) {
const pendingTxs = usePendingTransactions()
const displayTx = getTransactionFromMap(pendingTxs, displayTxHash)
const tokenList = useTokenList()
const onSupportedNetwork = useOnSupportedNetwork()
const isSwapSupported = useMemo(
() => Boolean(active && onSupportedNetwork && tokenList?.length),
[active, onSupportedNetwork, tokenList?.length]
)
const isDisabled = !(active && onSupportedNetwork)
const focused = useHasFocus(wrapper)
const isInteractive = Boolean(active && onSupportedNetwork)
return (
<>
<SwapPropValidator {...props} />
<Updaters {...props} disabled={!isSwapSupported} />
<Updaters {...props} disabled={isDisabled} />
<Header title={<Trans>Swap</Trans>}>
{active && <Wallet disabled={!account} onClick={props.onConnectWallet} />}
<Settings disabled={!isInteractive} />
<Settings disabled={isDisabled} />
</Header>
<div ref={setWrapper}>
<BoundaryProvider value={wrapper}>
<Input disabled={!isInteractive} focused={focused} />
<ReverseButton disabled={!isInteractive} />
<Output disabled={!isInteractive} focused={focused}>
<Input disabled={isDisabled} focused={focused} />
<ReverseButton disabled={isDisabled} />
<Output disabled={isDisabled} focused={focused}>
<Toolbar disabled={!active} />
<SwapButton disabled={!isSwapSupported} />
<SwapButton disabled={isDisabled} />
</Output>
</BoundaryProvider>
</div>

@ -43,10 +43,11 @@ function useComputeSwapInfo(): SwapInfo {
() => tryParseCurrencyAmount(amount, (isExactIn ? currencyIn : currencyOut) ?? undefined),
[amount, isExactIn, currencyIn, currencyOut]
)
const hasAmounts = currencyIn && currencyOut && parsedAmount
const trade = useBestTrade(
isExactIn ? TradeType.EXACT_INPUT : TradeType.EXACT_OUTPUT,
parsedAmount,
(isExactIn ? currencyOut : currencyIn) ?? undefined
hasAmounts ? parsedAmount : undefined,
hasAmounts ? (isExactIn ? currencyOut : currencyIn) : undefined
)
const amountIn = useMemo(
@ -111,7 +112,7 @@ const swapInfoAtom = atom<SwapInfo>({
export function SwapInfoUpdater() {
const setSwapInfo = useUpdateAtom(swapInfoAtom)
const swapInfo = useComputeSwapInfo()
useEffect(() => setSwapInfo(swapInfo), [swapInfo, setSwapInfo])
useEffect(() => setSwapInfo(swapInfo), [setSwapInfo, swapInfo])
return null
}

@ -1,32 +1,38 @@
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useDebounce from 'hooks/useDebounce'
import useIsWindowVisible from 'hooks/useIsWindowVisible'
import { atom } from 'jotai'
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 windowVisible = useIsWindowVisible()
const [state, setState] = useState<{ chainId?: number; block?: number }>({ chainId })
const setChainBlock = useUpdateAtom(chainBlockAtom)
const onBlock = useCallback(
(block: number) => {
setState((state) => {
if (state.chainId === chainId) {
if (typeof state.block !== 'number') return { chainId, block }
return { chainId, block: Math.max(block, state.block) }
setChainBlock((chainBlock) => {
if (chainBlock.chainId === chainId) {
if (chainBlock.block === block) return chainBlock
if (typeof chainBlock.block !== 'number') return { chainId, block }
return { chainId, block: Math.max(block, chainBlock.block) }
}
return state
return chainBlock
})
},
[chainId]
[chainId, setChainBlock]
)
useEffect(() => {
if (library && chainId && windowVisible) {
// 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
.getBlockNumber()
@ -41,30 +47,23 @@ function useBlock() {
}
}
return undefined
}, [chainId, library, onBlock, windowVisible])
const debouncedBlock = useDebounce(state.block, 100)
return state.block ? debouncedBlock : undefined
}, [chainId, library, onBlock, setChainBlock, windowVisible])
}
const blockAtom = atom<number | undefined>(undefined)
export function BlockUpdater() {
const setBlock = useUpdateAtom(blockAtom)
const block = useBlock()
useEffect(() => {
setBlock(block)
}, [block, setBlock])
useUpdateChainBlock()
return null
}
/** Requires that BlockUpdater be installed in the DOM tree. */
export default function useBlockNumber(): number | undefined {
const { chainId } = useActiveWeb3React()
const block = useAtomValue(blockAtom)
return chainId ? block : undefined
const { chainId: activeChainId } = useActiveWeb3React()
const { chainId, block } = useAtomValue(chainBlockAtom)
return activeChainId === chainId ? block : undefined
}
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)
export default multicall
export function MulticallUpdater() {
const latestBlockNumber = useBlockNumber()
const { chainId } = useActiveWeb3React()
const latestBlockNumber = useBlockNumber()
const contract = useInterfaceMulticall()
return <multicall.Updater chainId={chainId} latestBlockNumber={latestBlockNumber} contract={contract} />
}