How to add Web3 Authentication using NEXT JS & MetaMask?

How to add Web3 Authentication using NEXT JS & MetaMask?

Hello friends, today in this post we will work on web3 authentication using NEXT JS and MetaMask without using any libraries. We will not only connect to MetaMask but also dynamically update app states when we switch accounts or disconnect the accounts. We will be using context API for handling state of the application. Let’s get started.

Find the demo for what we will build today.

Pre-requisites:

Install the MetaMask chrome extension and set up your account.

https://metamask.io/

That’s it, now we are good to proceed with our app.

Step 1: Create Next JS App and install ethers

npx create-next-app metamask-connect-ethers

To run the app

yarn dev

or

npm run dev

Step2: Code

Let's understand how we are going to implement this and what functions we required. We need ethereum from the window object. If ethereum is not present inside the window object then metamask is not installed. Using this Object we can request for accounts connection and also register for account change events so that we can listen for account updates and update our app state.

Create a new folder context and inside that create a file AppContext.js. Let’s create an AppContext using React.createContext(). Create a functional component named AppProvider which will have two state variables one for storing the account address and the other for storing any error. Finally we will return AppContext.Provider with the values such as account, error, etc so that we call it using useContext. Also make sure you wrap your Component under _app.js with this AppProvider component.

To prevent error such as window not defined let’s add a condition typeof window is “undefined” or not. Inside AppProvider component create a connectWallet function inside that we will check if ethereum object exists if not we will setError to “Metamask not installed”. If ethereum exists then we need to call the request method with “eth_requestAccounts” it will return a list of accounts and we will store it in the account state.

// context/AppContext.js
import React, { createContext, useEffect, useState } from "react";
const { ethereum } = typeof window !== "undefined" ? window : {};
export const AppContext = createContext();
const AppProvider = ({ children }) => {
  const [account, setAccount] = useState("");
  const [error, setError] = useState("");

  const checkEthereumExists = () => {
    if (!ethereum) {
      setError("Please Install MetaMask.");
      return false;
    }
    return true;
  };

  const connectWallet = async () => {
    setError("");
    if (checkEthereumExists()) {
      try {
        const accounts = await ethereum.request({
          method: "eth_requestAccounts",
        });
        console.log(accounts);
        setAccount(accounts[0]);
      } catch (err) {
        setError(err.message);
      }
    }
  };

  return (
    <AppContext.Provider
      value={{ account, connectWallet, error }}
    >
      {children}
    </AppContext.Provider>
  );
};
export default AppProvider;
//_app.js
import AppProvider from "../context/AppContext";
import "../styles/globals.css";

function MyApp({ Component, pageProps }) {
  return (
    <AppProvider>
      <Component {...pageProps} />
    </AppProvider>
  );
}

export default MyApp;

In our index.js let’s create a button to call the connectWallet function. After that, you can reload the app and if you click on the “connect” button we can see our metamask pop-up and ask for permission to connect.

import { useContext } from "react";
import { AppContext } from "../context/AppContext";

export default function Home() {
  const { account, connectWallet, error } = useContext(AppContext);
  return (
    <div className="container">
      <div className="box">
        <h2>
          MetaMask <span className="block">Connect.</span>
        </h2>

        {account ? (
          <div className="account-box">
            <p className="shadow-border">{account}</p>
          </div>
        ) : (
          <button className="btn shadow-border" onClick={connectWallet}>
            Connect
          </button>
        )}
        {error && <p className={`error shadow-border`}>{`Error: ${error}`}</p>}
      </div>
    </div>
  );
}

So next step we need to show the account address if already connected whenever the app loads, we will create a function getConnectedAccount then we can check for ethereum exists then we need to call the request method with “eth_accounts” it will return a list of accounts and we will store it in the account state. Add a useEffect which will call the getConnectedAccounts and also register an event accountsChanged. Also removeListener when component unmounts.


import React, { createContext, useEffect, useState } from "react";

export const AppContext = createContext();

const { ethereum } = typeof window !== "undefined" ? window : {};

const AppProvider = ({ children }) => {
  const [account, setAccount] = useState("");
  const [error, setError] = useState("");

  const checkEthereumExists = () => {
    if (!ethereum) {
      setError("Please Install MetaMask.");
      return false;
    }
    return true;
  };

  const getConnectedAccounts = async () => {
    setError("");
    try {
      const accounts = await ethereum.request({
        method: "eth_accounts",
      });
      console.log(accounts);
      setAccount(accounts[0]);
    } catch (err) {
      setError(err.message);
    }
  };

  const connectWallet = async () => {
    setError("");
    if (checkEthereumExists()) {
      try {
        const accounts = await ethereum.request({
          method: "eth_requestAccounts",
        });
        console.log(accounts);
        setAccount(accounts[0]);
      } catch (err) {
        setError(err.message);
      }
    }
  };

  useEffect(() => {
    if (checkEthereumExists()) {
      ethereum.on("accountsChanged", getConnectedAccounts);
      getConnectedAccounts();
    }
    return () => {
      ethereum.removeListener("accountsChanged", getConnectedAccounts);
    };
  }, []);

  return (
    <AppContext.Provider
      value={{ account, connectWallet, error }}
    >
      {children}
    </AppContext.Provider>
  );
};

export default AppProvider;

Now lets test it by disconnecting and switching the accounts.

Thanks for reading this post, if you found this post helpful please support through your comments and claps. Will be back with an amazing content on Web3. Stay tuned.

Find the code on my website

NEXT JS MetaMask Authentication

If you are facing any issues please contact us from our contact section.

Contact Us | CodeWithMarish

Also please don’t forget to subscribe to our youtube channel codewithmarish for all web development-related challenges.

Code With Marish | Youtube

Posted with ❤️ from somewhere on the Earth