import { did } from '@metafam/utils'; import WalletConnectProvider from '@walletconnect/web3-provider'; import { providers } from 'ethers'; import { clearToken, clearWalletConnect, getTokenFromStore, setTokenInStore, } from 'lib/auth'; import React, { createContext, useCallback, useEffect, useRef, useState, } from 'react'; import Web3Modal from 'web3modal'; import { CONFIG } from '../config'; export type Web3ContextType = { provider: providers.Web3Provider | null; connectWeb3: () => Promise; disconnect: () => void; isConnecting: boolean; isConnected: boolean; address: string | null; authToken: string | null; }; export const Web3Context = createContext({ provider: null, connectWeb3: async () => {}, disconnect: () => undefined, isConnecting: false, isConnected: false, address: null, authToken: null, }); const providerOptions = { walletconnect: { package: WalletConnectProvider, options: { infuraId: CONFIG.infuraId, }, }, }; const web3Modal = typeof window !== 'undefined' && new Web3Modal({ network: 'mainnet', cacheProvider: true, providerOptions, }); async function getExistingAuth( ethersProvider: providers.Web3Provider, ): Promise { const token = getTokenFromStore(); if (!token) return null; try { await did.verifyToken(token, ethersProvider); return token; } catch (e) { clearToken(); return null; } } async function authenticateWallet( ethersProvider: providers.Web3Provider, ): Promise { const token = await did.createToken(ethersProvider); setTokenInStore(token); return token; } interface Web3ContextProviderOptions { children: React.ReactElement; resetUrqlClient?: () => void; } export const Web3ContextProvider: React.FC = ({ children, resetUrqlClient, }) => { const [provider, setProvider] = useState(null); const [isConnected, setIsConnected] = useState(false); const [isConnecting, setIsConnecting] = useState(false); const [address, setAddress] = useState(null); const [authToken, setAuthToken] = useState(null); const calledOnce = useRef(false); const disconnect = useCallback(() => { if (web3Modal === false) return; web3Modal.clearCachedProvider(); clearWalletConnect(); clearToken(); setAuthToken(null); setAddress(null); setProvider(null); setIsConnecting(false); setIsConnected(false); if (resetUrqlClient) resetUrqlClient(); }, [resetUrqlClient]); const connectWeb3 = useCallback(async () => { if (web3Modal === false) return; setIsConnecting(true); try { const web3Provider = await web3Modal.connect(); const ethersProvider = new providers.Web3Provider(web3Provider); const ethAddress = await ethersProvider.getSigner().getAddress(); let token: string | null = await getExistingAuth(ethersProvider); if (!token) { token = await authenticateWallet(ethersProvider); } setAddress(ethAddress); setProvider(ethersProvider); setAuthToken(token); setIsConnecting(false); setIsConnected(true); if (resetUrqlClient) resetUrqlClient(); } catch (error) { console.log(error); // eslint-disable-line no-console setIsConnecting(false); disconnect(); } }, [resetUrqlClient, disconnect]); useEffect(() => { if (calledOnce.current) return; calledOnce.current = true; if (web3Modal === false) return; if (web3Modal.cachedProvider) { connectWeb3().catch(() => undefined); } }, [connectWeb3]); return ( {children} ); };