import { createWeb3ReactStoreAndActions } from '@web3-react/store' import { Connector, Web3ReactState } from '@web3-react/types' import create, { UseStore } from 'zustand' import { useEffect, useMemo, useState } from 'react' import { Web3Provider } from '@ethersproject/providers' // https://stackoverflow.com/questions/24677592/generic-type-inference-with-class-argument/26696435#26696435 interface IConstructor { new (...args: any[]): T } export function initializeConnector( Connector: IConstructor, constructorArgs: any = [] // TODO: really unfortunate that we can't type this ): [T, UseStore] { const [store, actions] = createWeb3ReactStoreAndActions() const instance = new Connector(actions, ...constructorArgs) const useConnector = create(store) return [instance, useConnector] } const CHAIN_ID = (state: Web3ReactState) => state.chainId export function useChainId(useConnector: UseStore): Web3ReactState['chainId'] { return useConnector(CHAIN_ID) } const ACCOUNTS = (state: Web3ReactState) => state.accounts export function useAccounts(useConnector: UseStore): Web3ReactState['accounts'] { return useConnector(ACCOUNTS) } export function useAccount(useConnector: UseStore): string | undefined { return useConnector(ACCOUNTS)?.[0] } const ACTIVATING = (state: Web3ReactState) => state.activating export function useActivating(useConnector: UseStore): Web3ReactState['activating'] { return useConnector(ACTIVATING) } const ERROR = (state: Web3ReactState) => state.error export function useError(useConnector: UseStore): Web3ReactState['error'] { return useConnector(ERROR) } export function useProvider( connector: InstanceType, useConnector: UseStore ): Web3Provider | undefined { const chainId = useChainId(useConnector) const accounts = useAccounts(useConnector) return useMemo(() => { if (chainId && accounts && connector.provider) { return new Web3Provider(connector.provider) } }, [chainId, accounts, connector.provider]) } export function useENSNames( connector: InstanceType, useConnector: UseStore ): string[] | undefined { const provider = useProvider(connector, useConnector) const accounts = useAccounts(useConnector) const [ENSNames, setENSNames] = useState(undefined) useEffect(() => { if (provider && accounts?.length) { let stale = false Promise.all(accounts.map((account) => provider.lookupAddress(account))) .then((ENSNames) => { if (!stale) { setENSNames(ENSNames) } }) .catch((error) => { console.debug('Could not fetch ENS names', error) }) return () => { stale = true setENSNames(undefined) } } }, [provider, accounts]) return ENSNames } export function useENSName( connector: InstanceType, useConnector: UseStore ): string | undefined { const provider = useProvider(connector, useConnector) const account = useAccount(useConnector) const [ENSName, setENSName] = useState(undefined) useEffect(() => { if (provider && account) { let stale = false provider .lookupAddress(account) .then((ENSName) => { if (!stale) { setENSName(ENSName) } }) .catch((error) => { console.debug('Could not fetch ENS name', error) }) return () => { stale = true setENSName(undefined) } } }, [provider, account]) return ENSName }