use multicall2

This commit is contained in:
Moody Salem 2021-04-23 18:26:24 -05:00
parent 60d1f8743f
commit bff3811faf
No known key found for this signature in database
GPG Key ID: 8CB5CD10385138DB
7 changed files with 52 additions and 40 deletions

@ -10,7 +10,7 @@ import COINBASE_ICON_URL from '../assets/images/coinbaseWalletIcon.svg'
import FORTMATIC_ICON_URL from '../assets/images/fortmaticIcon.png'
import PORTIS_ICON_URL from '../assets/images/portisIcon.png'
export const MULTICALL_ADDRESSES: { [chainId in ChainId]: string } = {
export const MULTICALL2_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
[ChainId.KOVAN]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',

@ -26,7 +26,7 @@ import {
MERKLE_DISTRIBUTOR_ADDRESS,
V1_MIGRATOR_ADDRESS,
UNI,
MULTICALL_ADDRESSES,
MULTICALL2_ADDRESSES,
} from 'constants/index'
import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
import {
@ -40,6 +40,7 @@ import { TickLens, UniswapV3Factory, UniswapV3Pool } from 'types/v3'
import { NonfungiblePositionManager } from 'types/v3/NonfungiblePositionManager'
import { V3Migrator } from 'types/v3/V3Migrator'
import { getContract } from 'utils'
import { Multicall2 } from '../abis/types'
import { useActiveWeb3React } from './index'
// returns null on errors
@ -113,9 +114,9 @@ export function usePairContract(pairAddress?: string, withSignerIfPossible?: boo
return useContract(pairAddress, IUniswapV2PairABI, withSignerIfPossible)
}
export function useMulticallContract(): Contract | null {
export function useMulticall2Contract(): Multicall2 | null {
const { chainId } = useActiveWeb3React()
return useContract(chainId && MULTICALL_ADDRESSES[chainId], MULTICALL_ABI, false)
return useContract(chainId && MULTICALL2_ADDRESSES[chainId], MULTICALL_ABI, false) as Multicall2
}
export function useMerkleDistributorContract(): Contract | null {

@ -1,9 +1,9 @@
import { BigNumber } from 'ethers'
import { useSingleCallResult } from '../state/multicall/hooks'
import { useMulticallContract } from './useContract'
import { useMulticall2Contract } from './useContract'
// gets the current timestamp from the blockchain
export default function useCurrentBlockTimestamp(): BigNumber | undefined {
const multicall = useMulticallContract()
const multicall = useMulticall2Contract()
return useSingleCallResult(multicall, 'getCurrentBlockTimestamp')?.result?.[0]
}

@ -69,13 +69,13 @@ export function useV3Positions(account: string | null | undefined): UseV3Positio
const positionManager = useV3NFTPositionManagerContract()
const { loading: balanceLoading, error: balanceError, result: balanceResult } = useSingleCallResult(
positionManager ?? undefined,
positionManager,
'balanceOf',
[account ?? undefined]
)
// we don't expect any account balance to ever exceed the bounds of max safe int
const accountBalance: number | undefined = balanceResult?.[0] ? Number.parseInt(balanceResult[0]) : undefined
const accountBalance: number | undefined = balanceResult?.[0]?.toNumber()
const tokenIdsArgs = useMemo(() => {
if (accountBalance && account) {
@ -88,11 +88,7 @@ export function useV3Positions(account: string | null | undefined): UseV3Positio
return []
}, [account, accountBalance])
const tokenIdResults = useSingleContractMultipleData(
positionManager ?? undefined,
'tokenOfOwnerByIndex',
tokenIdsArgs
)
const tokenIdResults = useSingleContractMultipleData(positionManager, 'tokenOfOwnerByIndex', tokenIdsArgs)
const tokenIds = useMemo(() => {
if (account) {

@ -73,6 +73,7 @@ const ResponsiveButtonPrimary = styled(ButtonPrimary)`
width: 49%;
`};
`
const MainContentWrapper = styled.main`
background-color: ${({ theme }) => theme.bg0};
padding: 16px;
@ -80,17 +81,14 @@ const MainContentWrapper = styled.main`
display: flex;
flex-direction: column;
`
export default function Pool() {
const { account } = useActiveWeb3React()
const toggleWalletModal = useWalletModalToggle()
const { t } = useTranslation()
const theme = useContext(ThemeContext)
const { error, positions } = useV3Positions(account)
if (error) {
console.error('error fetching v3 positions', error)
}
const { positions } = useV3Positions(account)
const hasPositions = useMemo(() => Boolean(positions && positions.length > 0), [positions])
@ -131,6 +129,7 @@ export default function Pool() {
external: false,
})
}
return (
<>
<PageWrapper>

@ -1,8 +1,9 @@
import { Contract } from '@ethersproject/contracts'
import { BigNumber } from 'ethers'
import { useEffect, useMemo, useRef } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Multicall2 } from '../../abis/types'
import { useActiveWeb3React } from '../../hooks'
import { useMulticallContract } from '../../hooks/useContract'
import { useMulticall2Contract } from '../../hooks/useContract'
import useDebounce from '../../hooks/useDebounce'
import chunkArray from '../../utils/chunkArray'
import { CancelledError, retry, RetryableError } from '../../utils/retry'
@ -18,20 +19,24 @@ import {
/**
* Fetches a chunk of calls, enforcing a minimum block number constraint
* @param multicallContract multicall contract to fetch against
* @param multicall2Contract multicall contract to fetch against
* @param chunk chunk of calls to make
* @param minBlockNumber minimum block number of the result set
*/
async function fetchChunk(
multicallContract: Contract,
multicall2Contract: Multicall2,
chunk: Call[],
minBlockNumber: number
): Promise<{ results: string[]; blockNumber: number }> {
console.debug('Fetching chunk', multicallContract, chunk, minBlockNumber)
let resultsBlockNumber, returnData
): Promise<{
results: { success: boolean; returnData: string }[]
blockNumber: number
}> {
console.debug('Fetching chunk', multicall2Contract, chunk, minBlockNumber)
let resultsBlockNumber: BigNumber
let results: { success: boolean; returnData: string }[]
try {
;[resultsBlockNumber, returnData] = await multicallContract.callStatic.aggregate(
chunk.map((obj) => [obj.address, obj.callData])
;[resultsBlockNumber, , results] = await multicall2Contract.callStatic.blockAndAggregate(
chunk.map((obj) => ({ target: obj.address, callData: obj.callData }))
)
} catch (error) {
console.debug('Failed to fetch chunk inside retry', error)
@ -41,7 +46,7 @@ async function fetchChunk(
console.debug(`Fetched results for old block number: ${resultsBlockNumber.toString()} vs. ${minBlockNumber}`)
throw new RetryableError('Fetched for old block number')
}
return { results: returnData, blockNumber: resultsBlockNumber.toNumber() }
return { results, blockNumber: resultsBlockNumber.toNumber() }
}
/**
@ -116,7 +121,7 @@ export default function Updater(): null {
const debouncedListeners = useDebounce(state.callListeners, 100)
const latestBlockNumber = useBlockNumber()
const { chainId } = useActiveWeb3React()
const multicallContract = useMulticallContract()
const multicall2Contract = useMulticall2Contract()
const cancellations = useRef<{ blockNumber: number; cancellations: (() => void)[] }>()
const listeningKeys: { [callKey: string]: number } = useMemo(() => {
@ -132,7 +137,7 @@ export default function Updater(): null {
])
useEffect(() => {
if (!latestBlockNumber || !chainId || !multicallContract) return
if (!latestBlockNumber || !chainId || !multicall2Contract) return
const outdatedCallKeys: string[] = JSON.parse(serializedOutdatedCallKeys)
if (outdatedCallKeys.length === 0) return
@ -155,7 +160,7 @@ export default function Updater(): null {
cancellations.current = {
blockNumber: latestBlockNumber,
cancellations: chunkedCalls.map((chunk, index) => {
const { cancel, promise } = retry(() => fetchChunk(multicallContract, chunk, latestBlockNumber), {
const { cancel, promise } = retry(() => fetchChunk(multicall2Contract, chunk, latestBlockNumber), {
n: Infinity,
minWait: 2500,
maxWait: 3500,
@ -168,18 +173,29 @@ export default function Updater(): null {
const firstCallKeyIndex = chunkedCalls.slice(0, index).reduce<number>((memo, curr) => memo + curr.length, 0)
const lastCallKeyIndex = firstCallKeyIndex + returnData.length
const slice = outdatedCallKeys.slice(firstCallKeyIndex, lastCallKeyIndex)
dispatch(
updateMulticallResults({
chainId,
results: outdatedCallKeys
.slice(firstCallKeyIndex, lastCallKeyIndex)
.reduce<{ [callKey: string]: string | null }>((memo, callKey, i) => {
memo[callKey] = returnData[i] ?? null
return memo
}, {}),
results: slice.reduce<{ [callKey: string]: string | null }>((memo, callKey, i) => {
if (returnData[i].success) {
memo[callKey] = returnData[i].returnData ?? null
}
return memo
}, {}),
blockNumber: fetchBlockNumber,
})
)
// todo: dispatch an error for each call that failed, i.e. returnData[i].success === false
// dispatch(
// errorFetchingMulticallResults({
// calls: // todo: compute this,
// chainId,
// fetchingBlockNumber: latestBlockNumber,
// })
// )
})
.catch((error: any) => {
if (error instanceof CancelledError) {
@ -198,7 +214,7 @@ export default function Updater(): null {
return cancel
}),
}
}, [chainId, multicallContract, dispatch, serializedOutdatedCallKeys, latestBlockNumber])
}, [chainId, multicall2Contract, dispatch, serializedOutdatedCallKeys, latestBlockNumber])
return null
}

@ -3,7 +3,7 @@ import { JSBI } from '@uniswap/v2-sdk'
import { useMemo } from 'react'
import { useActiveWeb3React } from '../../hooks'
import { useAllTokens } from '../../hooks/Tokens'
import { useMulticallContract } from '../../hooks/useContract'
import { useMulticall2Contract } from '../../hooks/useContract'
import { isAddress } from '../../utils'
import { useUserUnclaimedAmount } from '../claim/hooks'
import { useMultipleContractSingleData, useSingleContractMultipleData } from '../multicall/hooks'
@ -18,7 +18,7 @@ import { Erc20Interface } from 'abis/types/Erc20'
export function useETHBalances(
uncheckedAddresses?: (string | undefined)[]
): { [address: string]: CurrencyAmount | undefined } {
const multicallContract = useMulticallContract()
const multicallContract = useMulticall2Contract()
const addresses: string[] = useMemo(
() =>