How to Connect a React DApp to MetaMask

How to Connect a React DApp to MetaMask

In this article, we will learn how to connect a React DApp to MetaMask and retrieve information such as the wallet address and wallet amount.

Introduction

I recently got into the Web3 space, and one of the first things I discovered were common blockchain technologies/terms like decentralized applications (DApps), MetaMask, and many others.

After becoming acquainted with these terms, I wanted to write blockchain-related code for the first time, and one of the first ideas that came to mind was how to connect a React application (now DApp) to MetaMask. At first, I thought this would be extremely difficult to implement, but after much research, it turned out to be a simple/fun thing to try out.

In this article, I will explain all of these terms/technologies so that a newbie can easily understand it, and then we will learn how to connect a React DApp to MetaMask and retrieve some information such as the wallet address and wallet amount.

You can check out the app live here ✨.

Definition of Technologies/Terms

This article is going to cover a lot, but let's start by understanding the fundamental terms and technologies that will be used in this guide.

What is Decentralized Application (DApp)?

A decentralized application (DApp) is a program that is run by many users on a decentralized network to eliminate any single point of failure. Smart contracts are typically visualized on a front-end and executed on a peer-to-peer network; in other words, there is usually a front-end, which could be a website containing HTML, CSS, and JS code, in our case a React application; this is what users interact with. And a backend to handle the core logic, such as smart contracts written in Solidity.

According to the definition above, the title should not be "how to connect a decentralized application..." because we are only learning how to connect metamask and not interacting with a smart contract, but this is a good first step toward creating our first decentralized application, so the title is appropriate.

What is MetaMask?

As one of the world's leading cryptocurrency wallets that can be used to interact with decentralized applications, MetaMask is a browser plugin that serves as an Ethereum wallet, allowing users to store Ether and other ERC-20 tokens.

Though it may appear complicated at first, MetaMask is one of the easiest Ethereum wallets and DApp browsers to use, and it can be set up in a matter of minutes. We can find a list of supported browsers and instructions for installing MetaMask on our preferred browser here.

In summary, all we have to do is download and install the official MetaMask browser extension/add-on; once installed, we should see a splash screen. To begin creating our Ethereum wallet with MetaMask, click the 'Get Started' button.

What is Ethers.JS?

Ethers.js is a free and open source JavaScript library that lets developers interact with the Ethereum blockchain. It is very similar to web3.js, but we will use ethers.js in this article. The library includes utility functions written in JavaScript and TypeScript, as well as all the features of an Ethereum wallet.

It is made up of several major modules, but for the purposes of this guide, we will only interact with Ethers. Provides abstract read-only access to the Ethereum blockchain and its status, which can be used to issue queries and send signed transactions.

So far, we've learned about the core technologies that will be used in this article; now, let's build our React application so that we can connect MetaMask to it using ethers.js.

Getting Started

First, we'll set up our React application using Create React App (CRA), which is a quick way to start building a new single-page application in React. But before we do that, it’s important to know some prerequisites to help us understand this article better.

These are the prerequisites:

  • A basic understanding of HTML, CSS, and JavaScript.
  • Some experience with or knowledge of React.
  • Node and npm or yarn installed on our machine
  • Fundamental understanding of how the terminal works

Let’s now proceed to create our React application. We will do this by running the following command in our terminal:

npx create-react-app react-metamask-ethers

Note: We can use any name of our choice when creating our application

Once that is successful, the next step would be to change the directory to the project and then start our server:

cd react-metamask-ethers

npm start

Install Ethers.js

After we've successfully created our React application, we'll install Ethers.js. We can do this in our project's folder using the following command:

npm install --save ethers

When we check our **package.json** file at this point, we will see that the ethers.js dependency have been successfully installed as seen in the object:

"dependencies": {
  "ethers": "^5.6.6",
  "react": "^18.1.0",
  "react-dom": "^18.1.0",
  "react-scripts": "5.0.1",
  "web-vitals": "^2.1.4"
},

Note: I removed some other dependencies from the Object while pasting it here and our version of ethers might be different from mine, which doesn’t matter.

Connecting React App to MetaMask

Connecting a React Application to MetaMask is simple with ethers.js, and we will use the useEffect() and useState() hooks to make the process easier. The procedure would be divided into three major sections:

How to Check If MetaMask is Installed

The first step is to see if MetaMask already exists in our browser, as this is a requirement for us to continue reading this article. If MetaMask is installed on our browser, we will be able to access the Ethereum object; otherwise, this indicates that MetaMask does not exist:

const { ethereum } = window;

We destructured, so we only have direct access to the Ethereum object. The best thing to do is to create a state and set it to true by default; if we don't have MetaMask installed on our browser, this state will change to false:

const [haveMetamask, sethaveMetamask] = useState(true);

const checkMetamaskAvailability = () => {
  if (!ethereum) {
    sethaveMetamask(false);
  }
  sethaveMetamask(true);
};

In the above function, we simply checked to see if MetaMask was installed, and if it was, we set the state to true; otherwise, we set it to false.

Note: We want this function to be triggered as soon as our app is staged, so we'll use the useEffect() hook:

import { useState, useEffect } from 'react';

function App() {
  const [haveMetamask, sethaveMetamask] = useState(true);

  useEffect(() => {
    const { ethereum } = window;
    const checkMetamaskAvailability = async () => {
      if (!ethereum) {
        sethaveMetamask(false);
      }
      sethaveMetamask(true);
    };
    checkMetamaskAvailability();
  }, []);

  return (
  // ...
  );
}

export default App;

Note: Within our application, we can now use the state to display a message instructing the user to install MetaMask or conditionals to avoid displaying the connect button.

How to Connect to MetaMask and Retrieve Wallet Address

The first step would be to create states that would store the account address and also inform us whether MetaMask has been connected:

const [isConnected, setIsConnected] = useState(false);
const [accountAddress, setAccountAddress] = useState('');

After that, we can write a function to handle this specific logic. The created function can be added to a button in our application and triggered when the button is clicked:

<button className="btn" onClick={connectWallet}>
  Connect
</button>

And then we can now create a function to handle the core logic:

const connectWallet = async () => {
  // ... Handle Logic
};

Within this function, we will first confirm that MetaMask has been installed, then request an available account, and finally connect react to MetaMask using the Ethereum provider API. This will also allow us to save the address of the connected account.

const connectWallet = async () => {
const { ethereum } = window;
  if (!ethereum) {
    sethaveMetamask(false);
  }

  const accounts = await ethereum.request({
    method: 'eth_requestAccounts',
  });
};

We can now refactor the above code to store the account address and handle errors with the try and catch block:

import { useState, useEffect } from 'react';

function App() {
  const [accountAddress, setAccountAddress] = useState('');
  const [isConnected, setIsConnected] = useState(false);

  const { ethereum } = window;

  const connectWallet = async () => {
    try {
      if (!ethereum) {
        sethaveMetamask(false);
      }
      const accounts = await ethereum.request({
        method: 'eth_requestAccounts',
      });
      setAccountAddress(accounts[0]);
      setIsConnected(true);
    } catch (error) {
      setIsConnected(false);
    }
  };
  return (
  // ...
  );
}

export default App;

Within our React application, we can now output the account address and use the isConnected value to display a success message.

How to Retrieve Wallet Balance Using Ethers.js

We will use the Ethers.js dependency for this so that we can use the getBalance() function on the Web3Provider with the wallet address as an argument. The function will return a BigNumber; we will use the utility function formatEther() to display it in ETH units in the user interface.

import { ethers } from 'ethers';
const provider = new ethers.providers.Web3Provider(window.ethereum);

const connectWallet = async () => {
  try {
    //...

    let balance = await provider.getBalance(accounts[0]);
    let bal = ethers.utils.formatEther(balance);

    setAccountBalance(bal);

  } catch (error) {
    setIsConnected(false);
  }
};

So far, we've been able to use ethers.js to check if MetaMask is installed, connect, get the wallet address, and get the wallet balance. We may have difficulty implementing this within our React application. Here is the source code for the demo I created, which properly depicts the entire process, as well as the code demonstrating the full implementation:

import { useState, useEffect } from 'react';
import { ethers } from 'ethers';

function App() {
  const [haveMetamask, sethaveMetamask] = useState(true);
  const [accountAddress, setAccountAddress] = useState('');
  const [accountBalance, setAccountBalance] = useState('');
  const [isConnected, setIsConnected] = useState(false);

  const { ethereum } = window;
  const provider = new ethers.providers.Web3Provider(window.ethereum);

  useEffect(() => {
    const { ethereum } = window;
    const checkMetamaskAvailability = async () => {
      if (!ethereum) {
        sethaveMetamask(false);
      }
      sethaveMetamask(true);
    };
    checkMetamaskAvailability();
  }, []);

  const connectWallet = async () => {
    try {
      if (!ethereum) {
        sethaveMetamask(false);
      }
      const accounts = await ethereum.request({
        method: 'eth_requestAccounts',
      });
      let balance = await provider.getBalance(accounts[0]);
      let bal = ethers.utils.formatEther(balance);
      setAccountAddress(accounts[0]);
      setAccountBalance(bal);
      setIsConnected(true);
    } catch (error) {
      setIsConnected(false);
    }
  };

  return (
    <div className="App">
      <header className="App-header">
        {haveMetamask ? (
          <div className="App-header">
            {isConnected ? (
              <div className="card">
                <div className="card-row">
                  <h3>Wallet Address:</h3>
                  <p>
                    {accountAddress.slice(0, 4)}...
                    {accountAddress.slice(38, 42)}
                  </p>
                </div>
                <div className="card-row">
                  <h3>Wallet Balance:</h3>
                  <p>{accountBalance}</p>
                </div>
              </div>
            ) : (
              <img src={logo} className="App-logo" alt="logo" />
            )}
            {isConnected ? (
              <p className="info">🎉 Connected Successfully</p>
            ) : (
              <button className="btn" onClick={connectWallet}>
                Connect
              </button>
            )}
          </div>
        ) : (
          <p>Please Install MataMask</p>
        )}
      </header>
    </div>
  );
}

export default App;

We can get the styles from the index.css file.

Conclusion

We learned how to connect a React application to MetaMask in this article, which is a good first step toward working with decentralized applications. The next step would be to learn how to create smart contracts and consume them within our decentralized applications.