* Add loading spinners for buttons * Add navigation to home page * Add logo inside anchor tag
174 lines
4.4 KiB
TypeScript
174 lines
4.4 KiB
TypeScript
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<void>;
|
|
session: SessionTypes.Struct | null;
|
|
signClient: SignClient | undefined;
|
|
checkPersistedState: (client: SignClient) => Promise<void>;
|
|
disconnect: () => Promise<void>;
|
|
}
|
|
|
|
const walletConnectContext = createContext<ContextValue>({
|
|
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<SignClient>();
|
|
const [session, setSession] = useState<SessionTypes.Struct | null>(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 (
|
|
<>
|
|
<walletConnectContext.Provider
|
|
value={{
|
|
connect,
|
|
session,
|
|
signClient,
|
|
checkPersistedState,
|
|
disconnect,
|
|
}}
|
|
>
|
|
{children}
|
|
</walletConnectContext.Provider>
|
|
<SnackbarProvider />
|
|
</>
|
|
);
|
|
};
|
|
|
|
export const useWalletConnectContext = () => {
|
|
const { connect, session, signClient, checkPersistedState, disconnect } =
|
|
useContext(walletConnectContext);
|
|
|
|
return {
|
|
connect,
|
|
session,
|
|
signClient,
|
|
checkPersistedState,
|
|
disconnect,
|
|
};
|
|
};
|