import { ReactNode, useEffect, useState } from 'react' import { utils } from 'ethers' import { getChainById } from '../helpers' import { useEthers, useBlockNumber, useConfig, useUpdateConfig, useLocalStorage } from '../hooks' import multicallABI from '../constants/abi/MultiCall.json' import multicall2ABI from '../constants/abi/MultiCall2.json' import { deployContract } from '../helpers/contract' interface LocalMulticallProps { children: ReactNode } enum LocalMulticallState { Unknown, NonLocal, Deploying, Deployed, Error, } export function LocalMulticallProvider({ children }: LocalMulticallProps) { const updateConfig = useUpdateConfig() const { library, chainId } = useEthers() const [, setMulticallAddress, getCurrent] = useLocalStorage('local_multicall_address' + chainId) const { multicallAddresses, multicallVersion } = useConfig() const [localMulticallState, setLocalMulticallState] = useState(LocalMulticallState.Unknown) const [multicallBlockNumber, setMulticallBlockNumber] = useState() const blockNumber = useBlockNumber() useEffect(() => { if (!library || !chainId) { setLocalMulticallState(LocalMulticallState.Unknown) } else if (!getChainById(chainId)?.isLocalChain) { setLocalMulticallState(LocalMulticallState.NonLocal) } else if (multicallAddresses && multicallAddresses[chainId]) { setLocalMulticallState(LocalMulticallState.Deployed) } else if (localMulticallState !== LocalMulticallState.Deploying) { const checkDeployed = async () => { const multicallAddress = getCurrent() if (typeof multicallAddress === 'string' && utils.isAddress(multicallAddress)) { const multicallCode = await library.getCode(multicallAddress) if (multicallCode !== '0x') { updateConfig({ multicallAddresses: { [chainId]: multicallAddress } }) return } } const signer = library.getSigner() if (!signer) { setLocalMulticallState(LocalMulticallState.Error) return } setLocalMulticallState(LocalMulticallState.Deploying) const deployMulticall = async () => { try { const { contractAddress, blockNumber } = await deployContract( multicallVersion === 1 ? multicallABI : multicall2ABI, signer ) updateConfig({ multicallAddresses: { [chainId]: contractAddress } }) setMulticallAddress(contractAddress) setMulticallBlockNumber(blockNumber) setLocalMulticallState(LocalMulticallState.Deployed) } catch { setLocalMulticallState(LocalMulticallState.Error) } } void deployMulticall() } void checkDeployed() } }, [library, chainId]) const awaitingMulticallBlock = multicallBlockNumber && blockNumber && blockNumber < multicallBlockNumber if ( localMulticallState === LocalMulticallState.Deploying || (localMulticallState === LocalMulticallState.Deployed && awaitingMulticallBlock) ) { return
Deploying multicall...
} else if (localMulticallState === LocalMulticallState.Error) { return
Error deploying multicall contract
} else { return <>{children} } }