How to Integrate Smart Contracts into React DApp with ethersjs

Home » Technology » Blockchain » How to Integrate Smart Contracts into React DApp with ethersjs

If you are into building DApps, it is crucial to know how to integrate your application with smart contracts. Because most of the time your smart contract will be the heart of your DApp where the logic is written. Which makes your app decentralized. In this post, I will show you how to do it.

Prerequisite for DApp

I assume you have successfully deployed your smart contract in one of the Testnet or locally in the personal blockchain. Also, you have some basic knowledge of React web app development.

Smart Contract ABI in JSON file
Smart Contract Address
Metamask Wallet: In case you have deployed in a personal blockchain make sure to add the network and import the accounts inside the Metamask wallet.

Create DApp using React

Let’s create of DApp using react. You can use the following command to create a basic single-page application using it.

npx create-react-app nft-gallery
cd nft-gallery
npm start

You should be able to see your react web application inside the browser at http://localhost:3000. Do the necessary clean-up of the application and make sure you have blank space as output inside the browser.

Integrate Metamask Wallet with our Application

Let’s connect the Metamask wallet with our application. For that, we will make use of ethersjs library. You can install the ethersjs using the command given below.

npm install ethers

You can connect with the Metamask wallet using ethersjs as given below.

Modify the App.js file as given below. I have added the useEffect() hook to connect with the Metamask wallet.

import "./App.css";
import { useEffect, useState } from "react";
import { ethers } from "ethers";

function App() {
  const [address, setAddress] = useState("");
  const [provider, setProvider] = useState(null);

  //To connect with the metamask wallet
  useEffect(() => {
    const connectWallet = async () => {
      if (window.ethereum == null) {
        alert("Metamask not found");
        console.log("Metamask not found");
      } else {
        //You can use this providerInstance to just perform read operations
        const providerInstance = new ethers.BrowserProvider(window.ethereum);
        try {
          //You can use the signer to perform both read and write operations
          const signer = await providerInstance.getSigner();
          setProvider(signer);
          setAddress(await signer.getAddress());
        } catch (error) {
          console.log(error);
        }
      }
    };
    connectWallet();
    //Listen for accounts changed event
    window.ethereum.on("accountsChanged", (accounts) => {
      console.log(accounts);
      setAddress(accounts[0]); //current active account address will be at index 0
    });
    //Listen for network changed event
    window.ethereum.on("chainChanged", (chainId) => {
      console.log(chainId);
      connectWallet();
    });
  }, []);
  return (
    <div className="App">
      <p>Wallet Address: {address ? address : "wallet not connected"} </p>
    </div>
  );
}

export default App;

This is how your package.json file should look like.

{
  "name": "nft-gallery",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "ethers": "^6.13.2",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

There are two event listeners. It listens to account changes and network changes with the Metamask wallet. Whenever it happens I rerender my application with the updated wallet information.

Connect Application with Smart Contract on Blockchain

Finally, let’s connect our application with the smart contract that we have deployed. You can use the contract address “0xCa2eb4eE46063E3Fc7454D31d4dA674C5B8C24Ce”. It is a simple NFT smart contract developed using Openzeppelin ERC721 smart contract. Otherwise, you can use your smart contract address. The above smart contract is deployed into Sepolia Testnet.

The entire NFT project can be found at https://github.com/sumukus/gcit-nft-collection inside the main branch. In this post, I focus on how to connect the smart contract with our application. So I will explain how you can do it. Please refer to the code given below for it. There are two methods to get the name of the NFT collection and symbol with the method name() and symbol(). So we will make use of it to check whether the integration of the smart contract with the application is successful or not. You should see the name value as “Gcit NFT Collection” and the symbol value as “GCITNFT”. If you see it, your application can communicate with the smart contract in the Blockchain.

Modify the App.js file as given below. We have added two more useEffect() hooks to handle the smart contract connection and fetch data from the smart contract.

import "./App.css";
import { useEffect, useState } from "react";
import { ethers } from "ethers";

//Import ABI file and smart contract address
const smartContractABI = require("./contracts/SmartContractABI.json"); //Replace this with your own
const smartContractAddress = "0xCa2eb4eE46063E3Fc7454D31d4dA674C5B8C24Ce"; //Replace this with your own

function App() {
  const [address, setAddress] = useState(null);
  const [provider, setProvider] = useState(null);
  const [smartContractAPI, setSmartContractAPI] = useState(null);
  const [nftName, setNFTName] = useState(null);
  const [nftSymbol, setNFTSymbol] = useState(null);

  //To connect with the metamask wallet
  useEffect(() => {
    const connectWallet = async () => {
      if (window.ethereum == null) {
        alert("Metamask not found");
        console.log("Metamask not found");
      } else {
        //You can use this providerInstance to just perform read operations
        const providerInstance = new ethers.BrowserProvider(window.ethereum);
        try {
          //You can use the signer to perform both read and write operations
          const signer = await providerInstance.getSigner();
          setProvider(signer);
          setAddress(await signer.getAddress());
        } catch (error) {
          console.log(error);
        }
      }
    };
    connectWallet();
    //Listen for accounts changed event
    window.ethereum.on("accountsChanged", (accounts) => {
      console.log(accounts);
      setAddress(accounts[0]); //current active account address will be at index 0
    });
    //Listen for network changed event
    window.ethereum.on("chainChanged", (chainId) => {
      console.log(chainId);
      // To refetch the details when network changes
      connectWallet();
      setNFTName(null);
      setNFTSymbol(null);
    });
  }, []);

  //Connect with the smart contract
  useEffect(() => {
    if (provider) {
      try {
        setSmartContractAPI(
          new ethers.Contract(
            smartContractAddress,
            smartContractABI.abi, //If JSON file only contains ABI section, then you can directly use it
            provider
          )
        );
      } catch (error) {
        console.log(error);
      }
    }
  }, [provider]);

  //Lets fetch the NFT contract name and symbol
  useEffect(() => {
    const fetchNFTDetails = async () => {
      try {
        if (smartContractAPI) {
          setNFTName(await smartContractAPI.name());
          setNFTSymbol(await smartContractAPI.symbol());
        }
      } catch (error) {
        console.log(error);
      }
    };
    fetchNFTDetails();
  }, [smartContractAPI]);
  return (
    <div className="App">
      <p>Wallet Address: {address ? address : "wallet not connected"} </p>
      <br />
      <p>NFT Name: {nftName ? nftName : "Couldn't fetch NFT name"}</p>
      <p>NFT Symbol: {nftSymbol ? nftSymbol : "Couldn't fetch NFT symbol"}</p>
    </div>
  );
}

export default App;

This JSON file is generated using the Truffle suite. The JSON file contains more than ABI content. We can access the ABI using the “abi” key with the JSON file. If you want you can import the ABI section only. The JSON file can be found here. Create the “contracts” directory inside the “src” folder. Then create the “SmartContractABI.json” file and put the content of the JSON file[access here] inside this file. Otherwise, you can use your own smart contract JSON file. Only use the one I provided if you are using the above-given contract address(i.e.“0xCa2eb4eE46063E3Fc7454D31d4dA674C5B8C24Ce”).

Congratulations!… You have successfully created your first DApp using React, EthersJS, and Metamask wallet. But remember the auto-complete features for smart contracts methods are not enabled. Therefore, you have to know the names of the methods that are inside your smart contract to communicate with it.

Conclusion

To conclude, I showed how to make Dapp using React, EtherJS, and Metamask wallet. The core idea behind the DApp is powered by the Blockchain’s decentralized nature and the smart contract feature it provides. I hope this took you one step further in the work of DApp and web3 developer. If you have any questions, drop in the comment section. Change the network inside the Metamask wallet and observe the output.