import assert from "assert"; import React, { createContext, useContext, ReactNode, useState, useEffect, useCallback, } from "react"; import { SnackbarProvider, 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; signClient: SignClient | undefined; checkPersistedState: (client: SignClient) => Promise; disconnect: () => Promise; } const walletConnectContext = createContext({ connect: () => Promise.resolve(), session: null, signClient: undefined, checkPersistedState: () => Promise.resolve(), disconnect: () => Promise.resolve(), }); const web3Modal = new WalletConnectModal({ projectId: PROJECT_ID, chains: ["eip155:1"], }); export const WalletConnectProvider = ({ children, }: { children: ReactNode; }) => { const [signClient, setSignClient] = useState(); const [session, setSession] = useState(null); const navigate = useNavigate() const disconnect = useCallback(async () => { if (signClient && session) { await signClient.disconnect({ topic: session.topic, reason: getSdkError("USER_DISCONNECTED"), }); } setSession(null); }, [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); } }, []); 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); }); client.on("session_delete", () => { setSession(null); navigate("/") }); }, [navigate] ); const createClient = useCallback(async () => { const signClient = await SignClient.init({ projectId: PROJECT_ID, metadata: { name: "Urbit onboarding app", description: "Urbit onboarding app", url: "localhost:3000", icons: ["https://avatars.githubusercontent.com/u/5237680?s=200&v=4"], }, }); setSignClient(signClient); await subscribeToEvents(signClient); await checkPersistedState(signClient); }, [checkPersistedState, subscribeToEvents]) const connect = async () => { if (!signClient) { throw Error("SignClient does not exist"); } const proposalNamespace = { eip155: { methods: ["personal_sign"], chains: ["eip155:1"], events: [], }, cosmos: { methods: ["cosmos_signDirect", "cosmos_signAmino"], chains: ["cosmos:cosmoshub-4"], events: [], }, }; const { uri, approval } = await signClient.connect({ requiredNamespaces: proposalNamespace, }); if (uri) { web3Modal.openModal({ uri }); try { const session = await approval(); setSession(session); } catch (error) { enqueueSnackbar("User rejected pairing request", { variant: "error" }); console.log(error); } web3Modal.closeModal(); } }; useEffect(() => { if (!signClient) { createClient(); } }, [signClient, createClient]); return ( <> {children} ); }; export const useWalletConnectContext = () => { const { connect, session, signClient, checkPersistedState, disconnect } = useContext(walletConnectContext); return { connect, session, signClient, checkPersistedState, disconnect, }; };