import assert from "assert"; import React, { createContext, useContext, ReactNode, useState, useEffect, useCallback, useRef, } from "react"; import { enqueueSnackbar } from "notistack"; import { useNavigate } from "react-router-dom"; import SignClient from "@walletconnect/sign-client"; import { WalletConnectModal } from "@walletconnect/modal"; import { SessionTypes } from "@walletconnect/types"; import { getSdkError } from "@walletconnect/utils"; const PROJECT_ID = process.env.REACT_APP_WALLET_CONNECT_ID; assert(PROJECT_ID, "Wallet connect project id not provided"); interface ContextValue { connect: () => Promise; session: SessionTypes.Struct | null; isSessionLoading: boolean; signClient: SignClient | undefined; checkPersistedState: (client: SignClient) => Promise; disconnect: () => Promise; } const walletConnectContext = createContext({ connect: () => Promise.resolve(), session: null, isSessionLoading: true, signClient: undefined, checkPersistedState: () => Promise.resolve(), disconnect: () => Promise.resolve(), }); const web3Modal = new WalletConnectModal({ projectId: PROJECT_ID, chains: [`eip155:${process.env.REACT_APP_ETHEREUM_MAINNET_CHAIN_ID}`], }); export const WalletConnectProvider = ({ children, }: { children: ReactNode; }) => { const [signClient, setSignClient] = useState(); const [session, setSession] = useState(null); const [isSessionLoading, setIsSessionLoading] = useState(true); const isSignClientInitializing = useRef(false); const navigate = useNavigate(); const disconnect = useCallback(async () => { if (signClient && session) { await signClient.disconnect({ topic: session.topic, reason: getSdkError("USER_DISCONNECTED"), }); } setSession(null); setIsSessionLoading(false); }, [signClient, session]); const checkPersistedState = useCallback(async (client: SignClient) => { if (client.session.length) { const lastKeyIndex = client.session.keys.length - 1; const session = client.session.get(client.session.keys[lastKeyIndex]); setSession(session); } setIsSessionLoading(false); }, []); const subscribeToEvents = useCallback( async (client: SignClient) => { client.on("session_update", ({ topic, params }) => { const { namespaces } = params; const currentSession = client.session.get(topic); const updatedSession = { ...currentSession, namespaces }; setSession(updatedSession); setIsSessionLoading(false); }); client.on("session_delete", () => { setSession(null); setIsSessionLoading(false); navigate("/"); }); }, [navigate] ); const createClient = useCallback(async () => { isSignClientInitializing.current = true; const signClient = await SignClient.init({ projectId: PROJECT_ID, metadata: { name: "Testnet onboarding app", description: "Testnet onboarding app", url: process.env.REACT_APP_WALLET_META_URL!, icons: ["https://avatars.githubusercontent.com/u/92608123"], }, }); setSignClient(signClient); await subscribeToEvents(signClient); await checkPersistedState(signClient); isSignClientInitializing.current = false; }, [checkPersistedState, subscribeToEvents]); const connect = async () => { if (!signClient) { throw Error("SignClient does not exist"); } const proposalNamespace = { eip155: { methods: ["personal_sign"], chains: [`eip155:${process.env.REACT_APP_ETHEREUM_MAINNET_CHAIN_ID}`], events: [], }, cosmos: { methods: [ "cosmos_sendTransaction", ], chains: [`cosmos:${process.env.REACT_APP_LACONICD_CHAIN_ID}`], events: [], }, }; const { uri, approval } = await signClient.connect({ requiredNamespaces: proposalNamespace, }); if (uri) { web3Modal.openModal({ uri }); try { const session = await approval(); setSession(session); setIsSessionLoading(false); } catch (error) { enqueueSnackbar("User rejected pairing request", { variant: "error" }); console.log(error); } web3Modal.closeModal(); } }; useEffect(() => { if (!signClient && !isSignClientInitializing.current) { createClient(); } }, [signClient, createClient]); return ( {children} ); }; export const useWalletConnectContext = () => { const { connect, session, signClient, isSessionLoading, checkPersistedState, disconnect } = useContext(walletConnectContext); return { connect, session, isSessionLoading, signClient, checkPersistedState, disconnect, }; };