Commit bff3811f authored by Moody Salem's avatar Moody Salem

use multicall2

parent 60d1f874
......@@ -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 {
......
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>
......
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(
() =>
......
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