From 78db838290d870a78e9d01c4d994cd5ca78ed5ca Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 11 Jan 2023 18:59:46 -0800 Subject: [PATCH 1/2] fix: type multicall usage --- src/hooks/multicall.ts | 53 ++++++++++++++++++++-------- src/hooks/useArgentWalletContract.ts | 4 +-- src/hooks/useContract.ts | 12 +++---- src/hooks/useCurrencyBalance.ts | 4 +-- src/hooks/usePermitAllowance.ts | 4 +-- src/hooks/useTokenAllowance.ts | 6 +--- 6 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/hooks/multicall.ts b/src/hooks/multicall.ts index f2ce869e3..42b7e37d9 100644 --- a/src/hooks/multicall.ts +++ b/src/hooks/multicall.ts @@ -1,4 +1,7 @@ +import { Contract } from '@ethersproject/contracts' +import { CallState as CallStateBase } from '@uniswap/redux-multicall' import { useWeb3React } from '@web3-react/core' +import { Interface } from 'ethers/lib/utils' import useBlockNumber from 'hooks/useBlockNumber' import multicall from 'state/multicall' @@ -7,33 +10,53 @@ export { NEVER_RELOAD } from '@uniswap/redux-multicall' // re-export for conveni // Create wrappers for hooks so consumers don't need to get latest block themselves -type MulticallParams any> = - Parameters extends [any, any, ...infer P] ? P : never +interface CallState extends Omit { + result: C['functions'][M] extends (...args: any) => Promise ? T : never +} + +type MultipleContractParams< + M extends string, + T extends ( + chainId: number | undefined, + latestBlock: number | undefined, + addresses: (string | undefined)[], + contractInterface: Interface, + methodName: M, + ...args: any + ) => any +> = Parameters extends [any, any, ...infer P] ? P : never -export function useMultipleContractSingleData( - ...args: MulticallParams +export function useMultipleContractSingleData( + ...args: MultipleContractParams ) { const { chainId, latestBlock } = useCallContext() - return multicall.hooks.useMultipleContractSingleData(chainId, latestBlock, ...args) + return multicall.hooks.useMultipleContractSingleData(chainId, latestBlock, ...args) as CallState[] } -export function useSingleCallResult(...args: MulticallParams) { - const { chainId, latestBlock } = useCallContext() - return multicall.hooks.useSingleCallResult(chainId, latestBlock, ...args) -} +type SingleContractParams< + C extends Contract, + M extends string, + T extends ( + chainId: number | undefined, + latestBlock: number | undefined, + contract: C | null | undefined, + methodName: M, + ...args: any + ) => any +> = Parameters extends [any, any, ...infer P] ? P : never -export function useSingleContractMultipleData( - ...args: MulticallParams +export function useSingleCallResult( + ...args: SingleContractParams ) { const { chainId, latestBlock } = useCallContext() - return multicall.hooks.useSingleContractMultipleData(chainId, latestBlock, ...args) + return multicall.hooks.useSingleCallResult(chainId, latestBlock, ...args) as CallState } -export function useSingleContractWithCallData( - ...args: MulticallParams +export function useSingleContractMultipleData( + ...args: SingleContractParams ) { const { chainId, latestBlock } = useCallContext() - return multicall.hooks.useSingleContractWithCallData(chainId, latestBlock, ...args) + return multicall.hooks.useSingleContractMultipleData(chainId, latestBlock, ...args) as CallState[] } function useCallContext() { diff --git a/src/hooks/useArgentWalletContract.ts b/src/hooks/useArgentWalletContract.ts index c407b120f..3361015f7 100644 --- a/src/hooks/useArgentWalletContract.ts +++ b/src/hooks/useArgentWalletContract.ts @@ -8,9 +8,9 @@ import useIsArgentWallet from './useIsArgentWallet' export function useArgentWalletContract(): ArgentWalletContract | null { const { account } = useWeb3React() const isArgentWallet = useIsArgentWallet() - return useContract( + return useContract( isArgentWallet ? account ?? undefined : undefined, ArgentWalletContractABI, true - ) as ArgentWalletContract + ) } diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index bdcbea43c..9442c9008 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -7,7 +7,7 @@ import ENS_PUBLIC_RESOLVER_ABI from 'abis/ens-public-resolver.json' import ENS_ABI from 'abis/ens-registrar.json' import ERC20_ABI from 'abis/erc20.json' import ERC20_BYTES32_ABI from 'abis/erc20_bytes32.json' -import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Weth } from 'abis/types' +import { ArgentWalletDetector, Eip2612, EnsPublicResolver, EnsRegistrar, Erc20, Erc20Bytes32, Weth } from 'abis/types' import WETH_ABI from 'abis/weth.json' import { ARGENT_WALLET_DETECTOR_ADDRESS, ENS_REGISTRAR_ADDRESSES, MULTICALL_ADDRESS } from 'constants/addresses' import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens' @@ -18,7 +18,7 @@ import { getContract } from 'utils' const { abi: MulticallABI } = UniswapInterfaceMulticallJson // returns null on errors -export function useContract( +export function useContract( addressOrAddressMap: string | { [chainId: number]: string } | undefined, ABI: any, withSignerIfPossible = true @@ -65,12 +65,12 @@ export function useENSResolverContract(address: string | undefined, withSignerIf return useContract(address, ENS_PUBLIC_RESOLVER_ABI, withSignerIfPossible) } -export function useBytes32TokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null { - return useContract(tokenAddress, ERC20_BYTES32_ABI, withSignerIfPossible) +export function useBytes32TokenContract(tokenAddress?: string, withSignerIfPossible?: boolean) { + return useContract(tokenAddress, ERC20_BYTES32_ABI, withSignerIfPossible) } -export function useEIP2612Contract(tokenAddress?: string): Contract | null { - return useContract(tokenAddress, EIP_2612, false) +export function useEIP2612Contract(tokenAddress?: string) { + return useContract(tokenAddress, EIP_2612, false) } export function useInterfaceMulticall() { diff --git a/src/hooks/useCurrencyBalance.ts b/src/hooks/useCurrencyBalance.ts index c06b8a2ce..5fb4cf6f8 100644 --- a/src/hooks/useCurrencyBalance.ts +++ b/src/hooks/useCurrencyBalance.ts @@ -2,7 +2,7 @@ import { Interface } from '@ethersproject/abi' import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core' import { useWeb3React } from '@web3-react/core' import ERC20ABI from 'abis/erc20.json' -import { Erc20Interface } from 'abis/types/Erc20' +import { Erc20, Erc20Interface } from 'abis/types/Erc20' import { nativeOnChain } from 'constants/tokens' import { useMultipleContractSingleData, useSingleContractMultipleData } from 'hooks/multicall' import { useInterfaceMulticall } from 'hooks/useContract' @@ -61,7 +61,7 @@ export function useTokenBalancesWithLoadingIndicator( ) const validatedTokenAddresses = useMemo(() => validatedTokens.map((vt) => vt.address), [validatedTokens]) - const balances = useMultipleContractSingleData( + const balances = useMultipleContractSingleData( validatedTokenAddresses, ERC20Interface, 'balanceOf', diff --git a/src/hooks/usePermitAllowance.ts b/src/hooks/usePermitAllowance.ts index 84fe50ea3..58bd169ce 100644 --- a/src/hooks/usePermitAllowance.ts +++ b/src/hooks/usePermitAllowance.ts @@ -22,9 +22,7 @@ export function usePermitAllowance(token?: Token, owner?: string, spender?: stri // If there is no allowance yet, re-check next observed block. // This guarantees that the permitAllowance is synced upon submission and updated upon being synced. const [blocksPerFetch, setBlocksPerFetch] = useState<1>() - const result = useSingleCallResult(contract, 'allowance', inputs, { - blocksPerFetch, - }).result as Awaited> | undefined + const result = useSingleCallResult(contract, 'allowance', inputs, { blocksPerFetch }).result const rawAmount = result?.amount.toString() // convert to a string before using in a hook, to avoid spurious rerenders const allowance = useMemo( diff --git a/src/hooks/useTokenAllowance.ts b/src/hooks/useTokenAllowance.ts index f6d8944bf..c0e58b117 100644 --- a/src/hooks/useTokenAllowance.ts +++ b/src/hooks/useTokenAllowance.ts @@ -1,6 +1,5 @@ import { BigNumberish } from '@ethersproject/bignumber' import { CurrencyAmount, MaxUint256, Token } from '@uniswap/sdk-core' -import { Erc20 } from 'abis/types' import { useSingleCallResult } from 'hooks/multicall' import { useTokenContract } from 'hooks/useContract' import { useCallback, useEffect, useMemo, useState } from 'react' @@ -21,10 +20,7 @@ export function useTokenAllowance( // If there is no allowance yet, re-check next observed block. // This guarantees that the tokenAllowance is marked isSyncing upon approval and updated upon being synced. const [blocksPerFetch, setBlocksPerFetch] = useState<1>() - const { result, syncing: isSyncing } = useSingleCallResult(contract, 'allowance', inputs, { blocksPerFetch }) as { - result: Awaited> | undefined - syncing: boolean - } + const { result, syncing: isSyncing } = useSingleCallResult(contract, 'allowance', inputs, { blocksPerFetch }) const rawAmount = result?.toString() // convert to a string before using in a hook, to avoid spurious rerenders const allowance = useMemo( From de47c16950e42b7c3fef722ef985004d3705d835 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 11 Jan 2023 19:11:22 -0800 Subject: [PATCH 2/2] fix: type result as | undefined --- src/hooks/multicall.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/multicall.ts b/src/hooks/multicall.ts index 42b7e37d9..c02c9e7e6 100644 --- a/src/hooks/multicall.ts +++ b/src/hooks/multicall.ts @@ -11,7 +11,7 @@ export { NEVER_RELOAD } from '@uniswap/redux-multicall' // re-export for conveni // Create wrappers for hooks so consumers don't need to get latest block themselves interface CallState extends Omit { - result: C['functions'][M] extends (...args: any) => Promise ? T : never + result: C['functions'][M] extends (...args: any) => Promise ? T | undefined : never } type MultipleContractParams<