Skip to content

Commit

Permalink
Create Web3Auth instance with chain configuration for RainbowKit wall…
Browse files Browse the repository at this point in the history
…et adapter
  • Loading branch information
Royal-lobster committed Sep 6, 2024
1 parent e28ce91 commit 853a177
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 51 deletions.
5 changes: 5 additions & 0 deletions .changeset/hip-plants-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@everipedia/iq-login": patch
---

Returns the user from web3auth hook
21 changes: 13 additions & 8 deletions src/components/IqLoginProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ import { cookieToInitialState, WagmiProvider } from "wagmi";
import { polygon } from "wagmi/chains";
import { iqTestnet } from "../lib/data/iqTestnet";
import { iqWikiTheme } from "../lib/data/rainbowKitTheme";
import { rainbowWeb3AuthConnector } from "../lib/integrations/web3-auth-connector";
import {
rainbowWeb3AuthConnector,
createWeb3AuthInstance,
} from "../lib/integrations/web3-auth-connector";
import { structuralSharing } from "@wagmi/core/query";
import { Web3AuthProvider } from "./Web3AuthProvider";

if (!process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID) {
throw new Error("NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID is not set");
Expand All @@ -21,6 +25,7 @@ if (!process.env.NEXT_PUBLIC_IS_PRODUCTION) {

const chain =
process.env.NEXT_PUBLIC_IS_PRODUCTION === "true" ? polygon : iqTestnet;
const web3AuthInstance = createWeb3AuthInstance(chain);

export const defaultConfig = getDefaultConfig({
appName: "IQ.Wiki AI Editor",
Expand All @@ -33,7 +38,7 @@ export const defaultConfig = getDefaultConfig({
}).wallets,
{
groupName: "More",
wallets: [() => rainbowWeb3AuthConnector({ chain })],
wallets: [() => rainbowWeb3AuthConnector({ web3AuthInstance })],
},
],
multiInjectedProviderDiscovery: false,
Expand Down Expand Up @@ -72,17 +77,17 @@ export function IqLoginProvider({
children,
cookie,
}: Readonly<React.PropsWithChildren> & { cookie?: string }) {
// NOTE: Avoid useState when initializing the query client if you don't
// have a suspense boundary between this and the code that may
// suspend because React will throw away the client on the initial
// render if it suspends and there is no boundary
const queryClient = getQueryClient();

const initialStates = cookieToInitialState(defaultConfig, cookie);

return (
<QueryClientProvider client={queryClient}>
<WagmiProvider config={defaultConfig} initialState={initialStates}>
<RainbowKitProvider theme={iqWikiTheme}>{children}</RainbowKitProvider>
<RainbowKitProvider theme={iqWikiTheme}>
<Web3AuthProvider web3AuthInstance={web3AuthInstance}>
{children}
</Web3AuthProvider>
</RainbowKitProvider>
</WagmiProvider>
</QueryClientProvider>
);
Expand Down
36 changes: 36 additions & 0 deletions src/components/Web3AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { UserInfo } from "@web3auth/base";
import type { Web3Auth } from "@web3auth/modal";
import { createContext, useContext, useEffect, useState } from "react";

const Web3AuthContext = createContext<{
web3Auth: Web3Auth | null;
user: Partial<UserInfo> | null;
}>({ web3Auth: null, user: null });

export function Web3AuthProvider({
children,
web3AuthInstance,
}: { children: React.ReactNode; web3AuthInstance: Web3Auth }) {
const [user, setUser] = useState<Partial<UserInfo> | null>(null);

useEffect(() => {
const checkLoggedInUser = async () => {
if (web3AuthInstance.connected) {
const user = await web3AuthInstance.getUserInfo();
setUser(user);
}
};

checkLoggedInUser();
}, [web3AuthInstance]);

return (
<Web3AuthContext.Provider value={{ web3Auth: web3AuthInstance, user }}>
{children}
</Web3AuthContext.Provider>
);
}

export function useWeb3Auth() {
return useContext(Web3AuthContext);
}
27 changes: 25 additions & 2 deletions src/lib/hooks/useAuth.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, { useCallback } from "react";
import { useWalletClient } from "wagmi";
import { useWalletClient, useAccount } from "wagmi";
import { sign, verify } from "@everipedia/web3-signer";
import { create } from "zustand";
import { getCookie, deleteCookie, setCookie } from "cookies-next";
import { useWeb3Auth } from "../../components/Web3AuthProvider";
import type { UserInfo } from "@web3auth/base";

export const useTokenStore = create<{
token: string | null;
Expand All @@ -12,12 +14,27 @@ export const useTokenStore = create<{
setToken: (token) => set({ token }),
}));

/**
* A custom React hook that provides authentication functionality for a web3 application.
*
* This hook manages the user's authentication token, which is stored in a cookie and used to verify the user's identity. It also provides methods to generate a new token and fetch the stored token.
*
* The hook uses the `wagmi` library to interact with the user's wallet, and the `web3-signer` library to sign and verify the authentication token.
*
* @returns An object containing the following properties:
* - `token`: The user's authentication token, or `null` if no token is available.
* - `loading`: A boolean indicating whether the hook is currently loading or generating a new token.
* - `reSignToken`: A function that can be called to re-sign the user's token.
* - `error`: An error message, if an error occurred during token generation or retrieval.
* - `web3AuthUser`: The user's information from the `web3auth` library, or `null` if no user is available.
*/
export const useAuth = () => {
const { token, setToken } = useTokenStore((state) => state);
const [loading, setLoading] = React.useState<boolean>(false);
const [error, setError] = React.useState<string>();
const [isReSignToken, reSignToken] = React.useState<boolean>(false);
const { data: walletClient } = useWalletClient();
const { user: web3AuthUser } = useWeb3Auth();

const generateNewTokenAndStore = useCallback(async () => {
if (!walletClient) return;
Expand Down Expand Up @@ -70,5 +87,11 @@ export const useAuth = () => {
retrieveWeb3Token();
}, [isReSignToken, generateNewTokenAndStore, fetchStoredToken]);

return { token, loading, reSignToken, error };
return {
token,
loading,
reSignToken,
error,
web3AuthUser: web3AuthUser as Partial<UserInfo> | null, // For some reason, we are getting inference error from web3auth library, so we are casting explicitly
};
};
99 changes: 58 additions & 41 deletions src/lib/integrations/web3-auth-connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,18 @@ if (!process.env.NEXT_PUBLIC_WEB3_AUTH_CLIENT_ID) {

const clientId = process.env.NEXT_PUBLIC_WEB3_AUTH_CLIENT_ID;

/**
* Creates a RainbowKit wallet adapter for the Web3Auth integration.
*
* @param props - The props for creating the wallet adapter.
* @param props.web3AuthInstance - The Web3Auth instance to use for the wallet adapter.
* @returns - The RainbowKit wallet adapter.
*/
export const rainbowWeb3AuthConnector = ({
chain,
}: { chain: Chain }): Wallet => {
const name = "Web3 Auth";

const chainConfig = {
chainNamespace: Web3AuthBase.CHAIN_NAMESPACES.EIP155,
chainId: `0x${chain.id.toString(16)}`,
rpcTarget: chain.rpcUrls.default.http[0],
displayName: chain.name,
tickerName: chain.nativeCurrency?.name,
ticker: chain.nativeCurrency?.symbol,
blockExplorerUrl: chain.blockExplorers?.default.url[0] as string,
};

const privateKeyProvider =
new Web3AuthEthereumProvider.EthereumPrivateKeyProvider({
config: { chainConfig },
});

const web3AuthInstance = new Web3AuthModal.Web3Auth({
clientId,
chainConfig,
privateKeyProvider,
uiConfig: {
appName: name,
loginMethodsOrder: ["google", "discord", "twitter"],
defaultLanguage: "en",
modalZIndex: "2147483647",
logoLight: "https://web3auth.io/images/web3authlog.png",
logoDark: "https://web3auth.io/images/web3authlogodark.png",
uxMode: "redirect",
mode: "auto",
theme: {
primary: "#f93493",
onPrimary: "#ffffff",
},
},
web3AuthNetwork: Web3AuthBase.WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
enableLogging: true,
});

web3AuthInstance,
}: {
web3AuthInstance: Web3AuthModal.Web3Auth;
}): Wallet => {
const modalConfig = {
[Web3AuthBase.WALLET_ADAPTERS.OPENLOGIN]: {
label: "openlogin",
Expand All @@ -80,3 +50,50 @@ export const rainbowWeb3AuthConnector = ({
},
};
};

/**
* Creates a new Web3Auth instance with the specified chain configuration.
*
* @param chain - The chain configuration to use for the Web3Auth instance.
* @returns - A new Web3Auth instance with the specified configuration.
*/
export function createWeb3AuthInstance(chain: Chain) {
const chainConfig = {
chainNamespace: Web3AuthBase.CHAIN_NAMESPACES.EIP155,
chainId: `0x${chain.id.toString(16)}`,
rpcTarget: chain.rpcUrls.default.http[0],
displayName: chain.name,
tickerName: chain.nativeCurrency?.name,
ticker: chain.nativeCurrency?.symbol,
blockExplorerUrl: chain.blockExplorers?.default.url[0] as string,
};

const privateKeyProvider =
new Web3AuthEthereumProvider.EthereumPrivateKeyProvider({
config: { chainConfig },
});

const uiConfig: Web3AuthModal.Web3AuthOptions["uiConfig"] = {
appName: "IQ.Wiki AI Editor",
loginMethodsOrder: ["google", "discord", "twitter"],
defaultLanguage: "en",
modalZIndex: "2147483647",
logoLight: "https://web3auth.io/images/web3authlog.png",
logoDark: "https://web3auth.io/images/web3authlogodark.png",
uxMode: "redirect",
mode: "auto",
theme: {
primary: "#f93493",
onPrimary: "#ffffff",
},
};

return new Web3AuthModal.Web3Auth({
clientId,
chainConfig,
privateKeyProvider,
uiConfig,
web3AuthNetwork: Web3AuthBase.WEB3AUTH_NETWORK.SAPPHIRE_MAINNET,
enableLogging: true,
});
}

0 comments on commit 853a177

Please sign in to comment.