import type { Networkish } from '@ethersproject/networks' import type { BaseProvider, Web3Provider } from '@ethersproject/providers' import type { Connector, Web3ReactStore } from '@web3-react/types' import type { Context, MutableRefObject, ReactNode } from 'react' import React, { createContext, useContext, useRef } from 'react' import type { Web3ReactHooks, Web3ReactPriorityHooks } from './hooks' import { getPriorityConnector } from './hooks' /** * @typeParam T - A type argument must only be provided if one or more of the connectors passed to Web3ReactProvider * is using `connector.customProvider`, in which case it must match every possible type of this * property, over all connectors. */ export type Web3ContextType = { connector: Connector chainId: ReturnType accounts: ReturnType isActivating: ReturnType account: ReturnType isActive: ReturnType provider: T | undefined ENSNames: ReturnType ENSName: ReturnType hooks: ReturnType } const Web3Context = createContext(undefined) /** * @param children - A React subtree that needs access to the context. * @param connectors - Two or more [connector, hooks(, store)] arrays, as returned from initializeConnector. * If modified in place without re-rendering the parent component, will result in an error. * @param connectorOverride - A connector whose state will be reflected in useWeb3React if set, overriding the * priority selection. * @param network - An optional argument passed along to `useSelectedProvider`. * @param lookupENS - A flag to enable/disable ENS lookups. */ export interface Web3ReactProviderProps { children: ReactNode connectors: [Connector, Web3ReactHooks][] | [Connector, Web3ReactHooks, Web3ReactStore][] connectorOverride?: Connector network?: Networkish lookupENS?: boolean } export function Web3ReactProvider({ children, connectors, connectorOverride, network, lookupENS = true, }: Web3ReactProviderProps) { const cachedConnectors: MutableRefObject = useRef(connectors) // because we're calling `getPriorityConnector` with these connectors, we need to ensure that they're not changing in place if ( connectors.length != cachedConnectors.current.length || connectors.some((connector, i) => { const cachedConnector = cachedConnectors.current[i] // because a "connector" is actually an array, we want to be sure to only perform an equality check on the actual Connector // class instance, to see if they're the same object return connector[0] !== cachedConnector[0] }) ) throw new Error( 'The connectors prop passed to Web3ReactProvider must be referentially static. If connectors is changing, try providing a key prop to Web3ReactProvider that changes every time connectors changes.' ) const hooks = getPriorityConnector(...connectors) const { usePriorityConnector, useSelectedChainId, useSelectedAccounts, useSelectedIsActivating, useSelectedAccount, useSelectedIsActive, useSelectedProvider, useSelectedENSNames, useSelectedENSName, } = hooks const priorityConnector = usePriorityConnector() const connector = connectorOverride ?? priorityConnector const chainId = useSelectedChainId(connector) const accounts = useSelectedAccounts(connector) const isActivating = useSelectedIsActivating(connector) const account = useSelectedAccount(connector) const isActive = useSelectedIsActive(connector) // note that we've omitted a generic type // in Web3ReactProvider, and thus can't pass T through to useSelectedProvider below. // this is because if we did so, the type of provider would include T, but that would // conflict because Web3Context can't take a generic. however, this isn't particularly // important, because useWeb3React (below) is manually typed const provider = useSelectedProvider(connector, network) const ENSNames = useSelectedENSNames(connector, lookupENS ? provider : undefined) const ENSName = useSelectedENSName(connector, lookupENS ? provider : undefined) return ( {children} ) } export function useWeb3React(): Web3ContextType { const context = useContext(Web3Context as Context | undefined>) if (!context) throw Error('useWeb3React can only be used within the Web3ReactProvider component') return context }