Commit 8ed6481f authored by Brendan Wong's avatar Brendan Wong Committed by GitHub

fix: display ens name/avatar on other chains (#6981)

* maintain ens info on other chains

* update ens hook

* feedback!

* initial test

* test progress

* e2e test?

* final feedback

* fix(lint): rm unused test var

* fix final bugs

* last lint issue

* fix: update catches

* fix: define catch handlers

* fix: use error in catch

* empty commit

---------
Co-authored-by: default avatarZach Pomerantz <zzmp@uniswap.org>
parent d160d1a9
...@@ -77,4 +77,36 @@ describe('Mini Portfolio account drawer', () => { ...@@ -77,4 +77,36 @@ describe('Mini Portfolio account drawer', () => {
}) })
}) })
}) })
it('fetches ENS name', () => {
cy.hardhat().then(() => {
const haydenAccount = '0x50EC05ADe8280758E2077fcBC08D878D4aef79C3'
const haydenENS = 'hayden.eth'
// Opens the account drawer
cy.get(getTestSelector('web3-status-connected')).click()
// Simulate wallet changing to Hayden's account
cy.window().then((win) => win.ethereum.emit('accountsChanged', [haydenAccount]))
// Hayden's ENS name should be shown
cy.contains(haydenENS).should('exist')
// Close account drawer
cy.get(getTestSelector('close-account-drawer')).click()
// Switch chain to Polygon
cy.get(getTestSelector('chain-selector')).eq(1).click()
cy.contains('Polygon').click()
//Reopen account drawer
cy.get(getTestSelector('web3-status-connected')).click()
// Simulate wallet changing to Hayden's account
cy.window().then((win) => win.ethereum.emit('accountsChanged', [haydenAccount]))
// Hayden's ENS name should be shown
cy.contains(haydenENS).should('exist')
})
})
}) })
...@@ -12,6 +12,7 @@ import { formatDelta } from 'components/Tokens/TokenDetails/PriceChart' ...@@ -12,6 +12,7 @@ import { formatDelta } from 'components/Tokens/TokenDetails/PriceChart'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
import { getConnection } from 'connection' import { getConnection } from 'connection'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes' import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import useENSName from 'hooks/useENSName'
import { useProfilePageState, useSellAsset, useWalletCollections } from 'nft/hooks' import { useProfilePageState, useSellAsset, useWalletCollections } from 'nft/hooks'
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable' import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
import { ProfilePageStateType } from 'nft/types' import { ProfilePageStateType } from 'nft/types'
...@@ -161,7 +162,8 @@ const LogOutCentered = styled(LogOut)` ...@@ -161,7 +162,8 @@ const LogOutCentered = styled(LogOut)`
` `
export default function AuthenticatedHeader({ account, openSettings }: { account: string; openSettings: () => void }) { export default function AuthenticatedHeader({ account, openSettings }: { account: string; openSettings: () => void }) {
const { connector, ENSName } = useWeb3React() const { connector } = useWeb3React()
const { ENSName } = useENSName(account)
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const navigate = useNavigate() const navigate = useNavigate()
const closeModal = useCloseModal() const closeModal = useCloseModal()
......
...@@ -7,6 +7,7 @@ import PrefetchBalancesWrapper from 'components/AccountDrawer/PrefetchBalancesWr ...@@ -7,6 +7,7 @@ import PrefetchBalancesWrapper from 'components/AccountDrawer/PrefetchBalancesWr
import Loader from 'components/Icons/LoadingSpinner' import Loader from 'components/Icons/LoadingSpinner'
import { IconWrapper } from 'components/Identicon/StatusIcon' import { IconWrapper } from 'components/Identicon/StatusIcon'
import { getConnection } from 'connection' import { getConnection } from 'connection'
import useENSName from 'hooks/useENSName'
import useLast from 'hooks/useLast' import useLast from 'hooks/useLast'
import { navSearchInputVisibleSize } from 'hooks/useScreenSize' import { navSearchInputVisibleSize } from 'hooks/useScreenSize'
import { Portal } from 'nft/components/common/Portal' import { Portal } from 'nft/components/common/Portal'
...@@ -135,7 +136,8 @@ const StyledConnectButton = styled.button` ...@@ -135,7 +136,8 @@ const StyledConnectButton = styled.button`
function Web3StatusInner() { function Web3StatusInner() {
const switchingChain = useAppSelector((state) => state.wallets.switchingChain) const switchingChain = useAppSelector((state) => state.wallets.switchingChain)
const ignoreWhileSwitchingChain = useCallback(() => !switchingChain, [switchingChain]) const ignoreWhileSwitchingChain = useCallback(() => !switchingChain, [switchingChain])
const { account, connector, ENSName } = useLast(useWeb3React(), ignoreWhileSwitchingChain) const { account, connector } = useLast(useWeb3React(), ignoreWhileSwitchingChain)
const { ENSName } = useENSName(account)
const connection = getConnection(connector) const connection = getConnection(connector)
const [, toggleAccountDrawer] = useAccountDrawer() const [, toggleAccountDrawer] = useAccountDrawer()
......
import { Contract } from '@ethersproject/contracts' import { Contract } from '@ethersproject/contracts'
import { import {
ARGENT_WALLET_DETECTOR_ADDRESS, ARGENT_WALLET_DETECTOR_ADDRESS,
ChainId,
ENS_REGISTRAR_ADDRESSES, ENS_REGISTRAR_ADDRESSES,
MULTICALL_ADDRESSES, MULTICALL_ADDRESSES,
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
...@@ -28,6 +29,7 @@ import ERC721_ABI from 'abis/erc721.json' ...@@ -28,6 +29,7 @@ import ERC721_ABI from 'abis/erc721.json'
import ERC1155_ABI from 'abis/erc1155.json' import ERC1155_ABI from 'abis/erc1155.json'
import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Erc721, Erc1155, Weth } from 'abis/types' import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Erc721, Erc1155, Weth } from 'abis/types'
import WETH_ABI from 'abis/weth.json' import WETH_ABI from 'abis/weth.json'
import { RPC_PROVIDERS } from 'constants/providers'
import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens' import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
import { useMemo } from 'react' import { useMemo } from 'react'
import { NonfungiblePositionManager, Quoter, QuoterV2, TickLens, UniswapInterfaceMulticall } from 'types/v3' import { NonfungiblePositionManager, Quoter, QuoterV2, TickLens, UniswapInterfaceMulticall } from 'types/v3'
...@@ -66,6 +68,23 @@ export function useContract<T extends Contract = Contract>( ...@@ -66,6 +68,23 @@ export function useContract<T extends Contract = Contract>(
}, [addressOrAddressMap, ABI, provider, chainId, withSignerIfPossible, account]) as T }, [addressOrAddressMap, ABI, provider, chainId, withSignerIfPossible, account]) as T
} }
function useMainnetContract<T extends Contract = Contract>(address: string | undefined, ABI: any): T | null {
const { chainId } = useWeb3React()
const isMainnet = chainId === ChainId.MAINNET
const contract = useContract(isMainnet ? address : undefined, ABI, false)
return useMemo(() => {
if (isMainnet) return contract
if (!address) return null
const provider = RPC_PROVIDERS[ChainId.MAINNET]
try {
return getContract(address, ABI, provider)
} catch (error) {
console.error('Failed to get mainnet contract', error)
return null
}
}, [address, ABI, contract, isMainnet]) as T
}
export function useV2MigratorContract() { export function useV2MigratorContract() {
return useContract<V3Migrator>(V3_MIGRATOR_ADDRESSES, V2MigratorABI, true) return useContract<V3Migrator>(V3_MIGRATOR_ADDRESSES, V2MigratorABI, true)
} }
...@@ -95,12 +114,12 @@ export function useArgentWalletDetectorContract() { ...@@ -95,12 +114,12 @@ export function useArgentWalletDetectorContract() {
return useContract<ArgentWalletDetector>(ARGENT_WALLET_DETECTOR_ADDRESS, ARGENT_WALLET_DETECTOR_ABI, false) return useContract<ArgentWalletDetector>(ARGENT_WALLET_DETECTOR_ADDRESS, ARGENT_WALLET_DETECTOR_ABI, false)
} }
export function useENSRegistrarContract(withSignerIfPossible?: boolean) { export function useENSRegistrarContract() {
return useContract<EnsRegistrar>(ENS_REGISTRAR_ADDRESSES, ENS_ABI, withSignerIfPossible) return useMainnetContract<EnsRegistrar>(ENS_REGISTRAR_ADDRESSES[ChainId.MAINNET], ENS_ABI)
} }
export function useENSResolverContract(address: string | undefined, withSignerIfPossible?: boolean) { export function useENSResolverContract(address: string | undefined) {
return useContract<EnsPublicResolver>(address, ENS_PUBLIC_RESOLVER_ABI, withSignerIfPossible) return useMainnetContract<EnsPublicResolver>(address, ENS_PUBLIC_RESOLVER_ABI)
} }
export function useBytes32TokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null { export function useBytes32TokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
...@@ -123,6 +142,13 @@ export function useInterfaceMulticall() { ...@@ -123,6 +142,13 @@ export function useInterfaceMulticall() {
return useContract<UniswapInterfaceMulticall>(MULTICALL_ADDRESSES, MulticallABI, false) as UniswapInterfaceMulticall return useContract<UniswapInterfaceMulticall>(MULTICALL_ADDRESSES, MulticallABI, false) as UniswapInterfaceMulticall
} }
export function useMainnetInterfaceMulticall() {
return useMainnetContract<UniswapInterfaceMulticall>(
MULTICALL_ADDRESSES[ChainId.MAINNET],
MulticallABI
) as UniswapInterfaceMulticall
}
export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean): NonfungiblePositionManager | null { export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean): NonfungiblePositionManager | null {
return useContract<NonfungiblePositionManager>( return useContract<NonfungiblePositionManager>(
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
......
import { namehash } from '@ethersproject/hash' import { namehash } from '@ethersproject/hash'
import { useSingleCallResult } from 'lib/hooks/multicall' import { NEVER_RELOAD, useMainnetSingleCallResult } from 'lib/hooks/multicall'
import { useMemo } from 'react' import { useMemo } from 'react'
import isZero from '../utils/isZero' import isZero from '../utils/isZero'
...@@ -12,14 +12,13 @@ import useDebounce from './useDebounce' ...@@ -12,14 +12,13 @@ import useDebounce from './useDebounce'
export default function useENSAddress(ensName?: string | null): { loading: boolean; address: string | null } { export default function useENSAddress(ensName?: string | null): { loading: boolean; address: string | null } {
const debouncedName = useDebounce(ensName, 200) const debouncedName = useDebounce(ensName, 200)
const ensNodeArgument = useMemo(() => [debouncedName ? namehash(debouncedName) : undefined], [debouncedName]) const ensNodeArgument = useMemo(() => [debouncedName ? namehash(debouncedName) : undefined], [debouncedName])
const registrarContract = useENSRegistrarContract(false) const registrarContract = useENSRegistrarContract()
const resolverAddress = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument) const resolverAddress = useMainnetSingleCallResult(registrarContract, 'resolver', ensNodeArgument, NEVER_RELOAD)
const resolverAddressResult = resolverAddress.result?.[0] const resolverAddressResult = resolverAddress.result?.[0]
const resolverContract = useENSResolverContract( const resolverContract = useENSResolverContract(
resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined, resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined
false
) )
const addr = useSingleCallResult(resolverContract, 'addr', ensNodeArgument) const addr = useMainnetSingleCallResult(resolverContract, 'addr', ensNodeArgument, NEVER_RELOAD)
const changed = debouncedName !== ensName const changed = debouncedName !== ensName
return useMemo( return useMemo(
......
...@@ -2,7 +2,7 @@ import { BigNumber } from '@ethersproject/bignumber' ...@@ -2,7 +2,7 @@ import { BigNumber } from '@ethersproject/bignumber'
import { hexZeroPad } from '@ethersproject/bytes' import { hexZeroPad } from '@ethersproject/bytes'
import { namehash } from '@ethersproject/hash' import { namehash } from '@ethersproject/hash'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { useSingleCallResult } from 'lib/hooks/multicall' import { NEVER_RELOAD, useMainnetSingleCallResult } from 'lib/hooks/multicall'
import uriToHttp from 'lib/utils/uriToHttp' import uriToHttp from 'lib/utils/uriToHttp'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
...@@ -49,14 +49,13 @@ export default function useENSAvatar( ...@@ -49,14 +49,13 @@ export default function useENSAvatar(
function useAvatarFromNode(node?: string): { avatar?: string; loading: boolean } { function useAvatarFromNode(node?: string): { avatar?: string; loading: boolean } {
const nodeArgument = useMemo(() => [node], [node]) const nodeArgument = useMemo(() => [node], [node])
const textArgument = useMemo(() => [node, 'avatar'], [node]) const textArgument = useMemo(() => [node, 'avatar'], [node])
const registrarContract = useENSRegistrarContract(false) const registrarContract = useENSRegistrarContract()
const resolverAddress = useSingleCallResult(registrarContract, 'resolver', nodeArgument) const resolverAddress = useMainnetSingleCallResult(registrarContract, 'resolver', nodeArgument, NEVER_RELOAD)
const resolverAddressResult = resolverAddress.result?.[0] const resolverAddressResult = resolverAddress.result?.[0]
const resolverContract = useENSResolverContract( const resolverContract = useENSResolverContract(
resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined, resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined
false
) )
const avatar = useSingleCallResult(resolverContract, 'text', textArgument) const avatar = useMainnetSingleCallResult(resolverContract, 'text', textArgument, NEVER_RELOAD)
return useMemo( return useMemo(
() => ({ () => ({
...@@ -113,8 +112,8 @@ function useERC721Uri( ...@@ -113,8 +112,8 @@ function useERC721Uri(
const idArgument = useMemo(() => [id], [id]) const idArgument = useMemo(() => [id], [id])
const { account } = useWeb3React() const { account } = useWeb3React()
const contract = useERC721Contract(contractAddress) const contract = useERC721Contract(contractAddress)
const owner = useSingleCallResult(contract, 'ownerOf', idArgument) const owner = useMainnetSingleCallResult(contract, 'ownerOf', idArgument, NEVER_RELOAD)
const uri = useSingleCallResult(contract, 'tokenURI', idArgument) const uri = useMainnetSingleCallResult(contract, 'tokenURI', idArgument, NEVER_RELOAD)
return useMemo( return useMemo(
() => ({ () => ({
uri: !enforceOwnership || account === owner.result?.[0] ? uri.result?.[0] : undefined, uri: !enforceOwnership || account === owner.result?.[0] ? uri.result?.[0] : undefined,
...@@ -133,8 +132,8 @@ function useERC1155Uri( ...@@ -133,8 +132,8 @@ function useERC1155Uri(
const idArgument = useMemo(() => [id], [id]) const idArgument = useMemo(() => [id], [id])
const accountArgument = useMemo(() => [account || '', id], [account, id]) const accountArgument = useMemo(() => [account || '', id], [account, id])
const contract = useERC1155Contract(contractAddress) const contract = useERC1155Contract(contractAddress)
const balance = useSingleCallResult(contract, 'balanceOf', accountArgument) const balance = useMainnetSingleCallResult(contract, 'balanceOf', accountArgument, NEVER_RELOAD)
const uri = useSingleCallResult(contract, 'uri', idArgument) const uri = useMainnetSingleCallResult(contract, 'uri', idArgument, NEVER_RELOAD)
return useMemo(() => { return useMemo(() => {
try { try {
// ERC-1155 allows a generic {id} in the URL, so prepare to replace if relevant, // ERC-1155 allows a generic {id} in the URL, so prepare to replace if relevant,
......
import { namehash } from '@ethersproject/hash' import { namehash } from '@ethersproject/hash'
import { useSingleCallResult } from 'lib/hooks/multicall' import { NEVER_RELOAD, useMainnetSingleCallResult } from 'lib/hooks/multicall'
import { useMemo } from 'react' import { useMemo } from 'react'
import isZero from '../utils/isZero' import isZero from '../utils/isZero'
...@@ -10,14 +10,13 @@ import { useENSRegistrarContract, useENSResolverContract } from './useContract' ...@@ -10,14 +10,13 @@ import { useENSRegistrarContract, useENSResolverContract } from './useContract'
*/ */
export default function useENSContentHash(ensName?: string | null): { loading: boolean; contenthash: string | null } { export default function useENSContentHash(ensName?: string | null): { loading: boolean; contenthash: string | null } {
const ensNodeArgument = useMemo(() => [ensName ? namehash(ensName) : undefined], [ensName]) const ensNodeArgument = useMemo(() => [ensName ? namehash(ensName) : undefined], [ensName])
const registrarContract = useENSRegistrarContract(false) const registrarContract = useENSRegistrarContract()
const resolverAddressResult = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument) const resolverAddressResult = useMainnetSingleCallResult(registrarContract, 'resolver', ensNodeArgument, NEVER_RELOAD)
const resolverAddress = resolverAddressResult.result?.[0] const resolverAddress = resolverAddressResult.result?.[0]
const resolverContract = useENSResolverContract( const resolverContract = useENSResolverContract(
resolverAddress && isZero(resolverAddress) ? undefined : resolverAddress, resolverAddress && isZero(resolverAddress) ? undefined : resolverAddress
false
) )
const contenthash = useSingleCallResult(resolverContract, 'contenthash', ensNodeArgument) const contenthash = useMainnetSingleCallResult(resolverContract, 'contenthash', ensNodeArgument, NEVER_RELOAD)
return useMemo( return useMemo(
() => ({ () => ({
......
import { namehash } from '@ethersproject/hash' import { namehash } from '@ethersproject/hash'
import { useSingleCallResult } from 'lib/hooks/multicall' import { NEVER_RELOAD, useMainnetSingleCallResult } from 'lib/hooks/multicall'
import { useMemo } from 'react' import { useMemo } from 'react'
import { isAddress } from '../utils' import { isAddress } from '../utils'
...@@ -18,14 +18,13 @@ export default function useENSName(address?: string): { ENSName: string | null; ...@@ -18,14 +18,13 @@ export default function useENSName(address?: string): { ENSName: string | null;
if (!debouncedAddress || !isAddress(debouncedAddress)) return [undefined] if (!debouncedAddress || !isAddress(debouncedAddress)) return [undefined]
return [namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`)] return [namehash(`${debouncedAddress.toLowerCase().substr(2)}.addr.reverse`)]
}, [debouncedAddress]) }, [debouncedAddress])
const registrarContract = useENSRegistrarContract(false) const registrarContract = useENSRegistrarContract()
const resolverAddress = useSingleCallResult(registrarContract, 'resolver', ensNodeArgument) const resolverAddress = useMainnetSingleCallResult(registrarContract, 'resolver', ensNodeArgument, NEVER_RELOAD)
const resolverAddressResult = resolverAddress.result?.[0] const resolverAddressResult = resolverAddress.result?.[0]
const resolverContract = useENSResolverContract( const resolverContract = useENSResolverContract(
resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined, resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined
false
) )
const nameCallRes = useSingleCallResult(resolverContract, 'name', ensNodeArgument) const nameCallRes = useMainnetSingleCallResult(resolverContract, 'name', ensNodeArgument, NEVER_RELOAD)
const name = nameCallRes.result?.[0] const name = nameCallRes.result?.[0]
// ENS does not enforce that an address owns a .eth domain before setting it as a reverse proxy // ENS does not enforce that an address owns a .eth domain before setting it as a reverse proxy
......
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import useBlockNumber from 'lib/hooks/useBlockNumber' import useBlockNumber, { useMainnetBlockNumber } from 'lib/hooks/useBlockNumber'
import multicall from 'lib/state/multicall' import multicall from 'lib/state/multicall'
import { SkipFirst } from 'types/tuple' import { SkipFirst } from 'types/tuple'
...@@ -22,6 +23,11 @@ export function useSingleCallResult(...args: SkipFirstTwoParams<typeof multicall ...@@ -22,6 +23,11 @@ export function useSingleCallResult(...args: SkipFirstTwoParams<typeof multicall
return multicall.hooks.useSingleCallResult(chainId, latestBlock, ...args) return multicall.hooks.useSingleCallResult(chainId, latestBlock, ...args)
} }
export function useMainnetSingleCallResult(...args: SkipFirstTwoParams<typeof multicall.hooks.useSingleCallResult>) {
const latestMainnetBlock = useMainnetBlockNumber()
return multicall.hooks.useSingleCallResult(ChainId.MAINNET, latestMainnetBlock, ...args)
}
export function useSingleContractMultipleData( export function useSingleContractMultipleData(
...args: SkipFirstTwoParams<typeof multicall.hooks.useSingleContractMultipleData> ...args: SkipFirstTwoParams<typeof multicall.hooks.useSingleContractMultipleData>
) { ) {
......
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { RPC_PROVIDERS } from 'constants/providers'
import useIsWindowVisible from 'hooks/useIsWindowVisible' import useIsWindowVisible from 'hooks/useIsWindowVisible'
import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react' import { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react'
...@@ -7,6 +9,7 @@ const BlockNumberContext = createContext< ...@@ -7,6 +9,7 @@ const BlockNumberContext = createContext<
| { | {
value?: number value?: number
fastForward(block: number): void fastForward(block: number): void
mainnetValue?: number
} }
| typeof MISSING_PROVIDER | typeof MISSING_PROVIDER
>(MISSING_PROVIDER) >(MISSING_PROVIDER)
...@@ -28,23 +31,34 @@ export function useFastForwardBlockNumber(): (block: number) => void { ...@@ -28,23 +31,34 @@ export function useFastForwardBlockNumber(): (block: number) => void {
return useBlockNumberContext().fastForward return useBlockNumberContext().fastForward
} }
export function useMainnetBlockNumber(): number | undefined {
return useBlockNumberContext().mainnetValue
}
export function BlockNumberProvider({ children }: { children: ReactNode }) { export function BlockNumberProvider({ children }: { children: ReactNode }) {
const { chainId: activeChainId, provider } = useWeb3React() const { chainId: activeChainId, provider } = useWeb3React()
const [{ chainId, block }, setChainBlock] = useState<{ chainId?: number; block?: number }>({ chainId: activeChainId }) const [{ chainId, block, mainnetBlock }, setChainBlock] = useState<{
chainId?: number
block?: number
mainnetBlock?: number
}>({
chainId: activeChainId,
})
const onBlock = useCallback( const onChainBlock = useCallback((chainId: number, block: number) => {
(block: number) => { setChainBlock((chainBlock) => {
setChainBlock((chainBlock) => { if (chainBlock.chainId === chainId) {
if (chainBlock.chainId === activeChainId) { if (!chainBlock.block || chainBlock.block < block) {
if (!chainBlock.block || chainBlock.block < block) { return { chainId, block, mainnetBlock: chainId === ChainId.MAINNET ? block : chainBlock.mainnetBlock }
return { chainId: activeChainId, block }
}
} }
return chainBlock } else if (chainId === ChainId.MAINNET) {
}) if (!chainBlock.mainnetBlock || chainBlock.mainnetBlock < block) {
}, return { ...chainBlock, mainnetBlock: block }
[activeChainId, setChainBlock] }
) }
return chainBlock
})
}, [])
const windowVisible = useIsWindowVisible() const windowVisible = useIsWindowVisible()
useEffect(() => { useEffect(() => {
...@@ -52,17 +66,22 @@ export function BlockNumberProvider({ children }: { children: ReactNode }) { ...@@ -52,17 +66,22 @@ export function BlockNumberProvider({ children }: { children: ReactNode }) {
if (provider && activeChainId && windowVisible) { if (provider && activeChainId && 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.
setChainBlock((chainBlock) => (chainBlock.chainId === activeChainId ? chainBlock : { chainId: activeChainId })) setChainBlock((chainBlock) =>
chainBlock.chainId === activeChainId
? chainBlock
: { chainId: activeChainId, mainnetBlock: chainBlock.mainnetBlock }
)
provider provider
.getBlockNumber() .getBlockNumber()
.then((block) => { .then((block) => {
if (!stale) onBlock(block) if (!stale) onChainBlock(activeChainId, block)
}) })
.catch((error) => { .catch((error) => {
console.error(`Failed to get block number for chainId ${activeChainId}`, error) console.error(`Failed to get block number for chainId ${activeChainId}`, error)
}) })
const onBlock = (block: number) => onChainBlock(activeChainId, block)
provider.on('block', onBlock) provider.on('block', onBlock)
return () => { return () => {
stale = true stale = true
...@@ -71,7 +90,19 @@ export function BlockNumberProvider({ children }: { children: ReactNode }) { ...@@ -71,7 +90,19 @@ export function BlockNumberProvider({ children }: { children: ReactNode }) {
} }
return void 0 return void 0
}, [activeChainId, provider, onBlock, setChainBlock, windowVisible]) }, [activeChainId, provider, windowVisible, onChainBlock])
useEffect(() => {
if (mainnetBlock === undefined) {
RPC_PROVIDERS[ChainId.MAINNET]
.getBlockNumber()
.then((block) => {
onChainBlock(ChainId.MAINNET, block)
})
// swallow errors - it's ok if this fails, as we'll try again if we activate mainnet
.catch(() => undefined)
}
}, [mainnetBlock, onChainBlock])
const value = useMemo( const value = useMemo(
() => ({ () => ({
...@@ -81,8 +112,9 @@ export function BlockNumberProvider({ children }: { children: ReactNode }) { ...@@ -81,8 +112,9 @@ export function BlockNumberProvider({ children }: { children: ReactNode }) {
setChainBlock({ chainId: activeChainId, block: update }) setChainBlock({ chainId: activeChainId, block: update })
} }
}, },
mainnetValue: mainnetBlock,
}), }),
[activeChainId, block, chainId] [activeChainId, block, chainId, mainnetBlock]
) )
return <BlockNumberContext.Provider value={value}>{children}</BlockNumberContext.Provider> return <BlockNumberContext.Provider value={value}>{children}</BlockNumberContext.Provider>
} }
import { createMulticall, ListenerOptions } from '@uniswap/redux-multicall' import { createMulticall, ListenerOptions } from '@uniswap/redux-multicall'
import { ChainId } from '@uniswap/sdk-core' import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core' import { useWeb3React } from '@web3-react/core'
import { useInterfaceMulticall } from 'hooks/useContract' import { useInterfaceMulticall, useMainnetInterfaceMulticall } from 'hooks/useContract'
import useBlockNumber from 'lib/hooks/useBlockNumber' import useBlockNumber, { useMainnetBlockNumber } from 'lib/hooks/useBlockNumber'
import { useMemo } from 'react' import { useMemo } from 'react'
const multicall = createMulticall() const multicall = createMulticall()
...@@ -33,20 +33,38 @@ function getBlocksPerFetchForChainId(chainId: number | undefined): number { ...@@ -33,20 +33,38 @@ function getBlocksPerFetchForChainId(chainId: number | undefined): number {
export function MulticallUpdater() { export function MulticallUpdater() {
const { chainId } = useWeb3React() const { chainId } = useWeb3React()
const latestBlockNumber = useBlockNumber() const latestBlockNumber = useBlockNumber()
const latestMainnetBlockNumber = useMainnetBlockNumber()
const contract = useInterfaceMulticall() const contract = useInterfaceMulticall()
const mainnetContract = useMainnetInterfaceMulticall()
const listenerOptions: ListenerOptions = useMemo( const listenerOptions: ListenerOptions = useMemo(
() => ({ () => ({
blocksPerFetch: getBlocksPerFetchForChainId(chainId), blocksPerFetch: getBlocksPerFetchForChainId(chainId),
}), }),
[chainId] [chainId]
) )
const mainnetListener: ListenerOptions = useMemo(
() => ({
blocksPerFetch: getBlocksPerFetchForChainId(ChainId.MAINNET),
}),
[]
)
return ( return (
<multicall.Updater <>
chainId={chainId} <multicall.Updater
latestBlockNumber={latestBlockNumber} chainId={ChainId.MAINNET}
contract={contract} latestBlockNumber={latestMainnetBlockNumber}
listenerOptions={listenerOptions} contract={mainnetContract}
/> listenerOptions={mainnetListener}
/>
{chainId !== ChainId.MAINNET && (
<multicall.Updater
chainId={chainId}
latestBlockNumber={latestBlockNumber}
contract={contract}
listenerOptions={listenerOptions}
/>
)}
</>
) )
} }
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