How to Build Your DApp Using The Modern Ethereum Tech Stack: Hardhat and EthersJs

December 29, 2021

Overview

When building a smart contract on the Ethereum blockchain, new developers tend to reach out to tools like truffle and web3.js in building their smart contracts. This tutorial will look at how to use Hardhat and Ether.js, which are now becoming the standard in building smart contracts.

Goals


In this tutorial, we will create, run, compile and deploy the smart contracts made using the Hardhat Ethereum development environment.

Prerequisites:

  • Node.js installed.
  • A little solidity knowledge
  • Working knowledge of React

Tools we will be using:

  • Hardhat - Hardhat is the EVM we will run our solidity contracts on locally. It is similar to truffle, so you do not have to worry about coming from a truffle background.
  • React - Our smart contract needs a UI, and the React framework will serve as the UI framework when we build the smart contract.
  • Ethers.js - The Ethers.js library allows us to interact with the data we have in the Ethereum blockchain and connect our frontend to the smart contract, which is very important when connecting to dApps.

Getting started with the project

Here we will look at how to set up our project and some essential packages we need to install. To initialize and configure our project, we need to create a directory to house our dApp. Since we will be using React as our UI library, we can use create-react-app to initialize a template project.

getting started with the project

Copy
npx create-react-app moDappTut

Then we will incrementally add more packages and build up our dApp. Next, we will install Hardhat, Ethers.js, and other important libraries to create our dApp. Below is the list of packages we will install.

getting started with the project

Copy
npm install ethers hardhat @nomiclabs/hardhat-waffle ethereum-waffle @nomiclabs/hardhat-ethers chai

Looking at the section above, we already know what the Ethers and Hardhat package will be used for. We also have supporting packages from @nomiclabs that allow smooth development flow when building our dApp; then, there is the chai library for testing our smart contracts. In the next section, we will be creating our sample Hardhat project.

Set Up Hardhat

To create a basic setup with all the Hardhat configurations we need, we run the npx hardhat command in our terminal.

set up hardhat

Copy
npx hardhat

Select the following settings when prompted

  • What do you want to do? Select Create a basic sample project
  • Hardhat Project Root  Press Enter to set current directory as root
  • Do you want to add a .gitignore? (Y/n) n



We can now create a basic sample project, setting the default Hardhat config in our current directory moDappTut. In our editor, we see that the Hardhat config, test, script file, and contract folder have been added to our project.

NOTE: You can carry out different configurations in your project through the hardhat config file, like changing the default network to deploy to, or customizing the paths for your tests or artifacts. More about hardhat configurations can be found here. Let us dive into creating our smart contract in the next section.

Building Our Contract

In this section, we will analyze the default contract provided by Hardhat and also create the custom token we will use for transactions later in the tutorial.
We can see an already written contract Hardhat provided to us in the Greeter.sol file in our contracts folder.

In the Greeter contract, we have two functions. One of the functions returns the greeting while the other function sets a new greeting that will change the value of the current greeting. We will be adding a new contract to our contract folder. The contract will be a token contract named MDToken.

building our contract

Copy
//MDToken.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
contract Token {
    string public name = "Madfinger Token";
    string public symbol = "MHT";
    // The fixed amount of tokens stored in an unsigned integer type variable.
    uint256 public totalSupply = 1000000;
    // An address type variable is used to store ethereum accounts.
    address public owner;
    // A mapping is a key/value map. Here we store each account balance.
    mapping(address => uint256) balances;
    /**
     * Contract initialization.
     *
     * The `constructor` is executed only once when the contract is created.
     * The `public` modifier makes a function callable from outside the contract.
     */
    constructor() {
        // The totalSupply is assigned to transaction sender, which is the account
        // that is deploying the contract.
        balances[msg.sender] = totalSupply;
        owner = msg.sender;
    }
    /**
     * A function to transfer tokens.
     *
     * The `external` modifier makes a function *only* callable from outside
     * the contract.
     */
    function transfer(address to, uint256 amount) external {
        // Check if the transaction sender has enough tokens.
        // If `require`'s first argument evaluates to `false` then the
        // transaction will revert.
        require(balances[msg.sender] >= amount, "Not enough tokens");
        // Transfer the amount.
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
    /**
     * Read only function to retrieve the token balance of a given account.
     *
     * The `view` modifier indicates that it doesn't modify the contract's
     * state, which allows us to call it without executing a transaction.
     */
    function balanceOf(address account) external view returns (uint256) {
        return balances[account];
    }
}

In our MDToken contract above, we have created a token that we can use for transactions, as well as sending and receiving the token once it is deployed. If you are still new to Solidity, there are comments explaining what each code does in our contract above. The next thing we need to do is deploy our contract.

Deploying Our Contract

In this section, we will deploy our token to a local node provided by Hardhat. To get the local node running, all we have to do is run the command:

deploying our contract

Copy
npx hardhat node

After running the node, Hardhat provides us with 20 accounts with fake funds that we can use for transactions. Here is how our terminal looks:


From the image above you can see our local test network is running on http://127.0.0.1:8545/.

And each account has a wallet account ID and a private key. We will be using those to connect to MetaMask later on.

In our scripts folder, create a deploy.js file where we write our script for deploying the contract. Hardhat provides us with a sample script sample-script.js for deploying the Greeter contract. We can adapt the contract to our taste and deploy the MDToken contract. Here is what our deploy script looks like:

deploying our contract

Copy
//deploy.js
const hre = require("hardhat");
async function main() {
    // ethers is avaialble in the global scope
    const [deployer] = await hre.ethers.getSigners();
    console.log(
      "Deploying the contracts with the account:",
      await deployer.getAddress()
    );

    console.log("Account balance:", (await deployer.getBalance()).toString());

    const Token = await hre.ethers.getContractFactory("Token");
    const token = await Token.deploy();
    await token.deployed();

    console.log("Token address:", token.address);

  }

  main()
    .then(() => process.exit(0))
    .catch((error) => {
      console.error(error);
      process.exit(1);
    });

When we deploy our contract, Hardhat automatically creates an artifact folder and stores it in the root folder of our project directory. This artifact folder contains all the info for our smart contract that is needed to connect to our frontend. We want the artifacts folder to be in our src folder so we can configure this from our hardhat config file.

deploying our contract

Copy
//hardhat.config.js
module.exports = {
  paths: {
    artifacts: './src/artifacts',
  },
  solidity: "0.8.4",
};

Now to finally deploy our contract we run the command

deploying our contract

Copy
npx hardhat run --network localhost scripts/deploy.js

Our contract is now deployed on the test network, and we can see from our terminal information about the deployment. The first account provided by Hardhat is the account we have deployed the contract with, and a gas fee is charged for deploying this contract
Now that our contract has been deployed let us connect our MetaMask account to it.


Now that our contract has been deployed, let us connect our MetaMask account to it.

Connecting To MetaMask

This section will go through the steps of setting up a MetaMask account and adding our local Hardhat network.
Install the MetaMask extension and set up your account. To set up our MetaMask account, we have to create a new wallet and backup our phrase. After setting up our MetaMask wallet, if we click on the MetaMask icon, we see we are on the Ethereum Mainnet. What we want to do is connect our local Hardhat network to MetaMask. We can do this by:

  1. Make sure you set your test networks to be visible. Navigate to your MetaMask settings, go to advanced settings and set show test network to be on.
  2. Go to your settings and select network settings, then click on add new network.
  3. Fill in the required information and save, and you are now set up with the local Hardhat network.

Below is a screenshot of the information you need to provide to set up your network



Now let us import the account we used to deploy our Token contract. Click on Import Account and paste in your private key from the first account provided by Hardhat used to deploy your contract. You will notice that the 10000 ethers given to us have been reduced a little due to the gas fee we paid to deploy our contract. Now it’s time to finally connect the smart contract we created to our React frontend.

Connecting the Smart Contract to our React frontend

In this section, we will run our React application locally, connect our contract with the ethers library and write code to test everything is working perfectly.
First, start the react application by running:

connecting the smart contract to our react frontend

Copy
npm start

To make our dApp complete, we need to connect our smart contract to our React frontend. First, we import the ethers library to communicate with our smart contract data, then useState from React to manage our application state. We need to import the TokenArtifacts ABI, which contains the contract data we will need, then set our token address to the address our contract is deployed to, which is the contract address.

We use react useState to store three variables in the state:

  • tokenData: This is an object that stores information about the token contract, which is the name and symbol
  • amount: we will use amount to store the amount of MDToken we will be sending to another address
  • userAccountId: this stores the user's account address we will be sending our MDToken to. The amount and userAccountId will be set from the form input we provide to the user in the UI.

connecting the smart contract to our react frontend

Copy
// src/app.js
import './App.css';
import {ethers} from 'ethers'
import { useState } from 'react';
import TokenArtifact from "./artifacts/contracts/Token.sol/Token.json"
const tokenAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3"


function App() {
 const [tokenData, setTokenData] = useState({})
 const [amount, setAmount] = useState()
 const [userAccountId, setUserAccountId] = useState()

async function requestAccount() {
  await window.ethereum.request({ method: 'eth_requestAccounts' });
}

 const provider = new ethers.providers.Web3Provider(window.ethereum);
 const signer = provider.getSigner();
 async function _intializeContract(init) {
  // We first initialize ethers by creating a provider using window.ethereum
  // When, we initialize the contract using that provider and the token's
  // artifact. You can do this same thing with your contracts.
  const contract = new ethers.Contract(
    tokenAddress,
    TokenArtifact.abi,
    init
  );

  return contract
}
 async function _getTokenData() {
  const contract = await _intializeContract(signer)
  const name = await contract.name();
  const symbol = await contract.symbol();
  const tokenData = {name, symbol}
  setTokenData(tokenData);
}
async function sendMDToken() {
  if (typeof window.ethereum !== 'undefined') {
    await requestAccount()
    const contract = await _intializeContract(signer)
    const transaction = await contract.transfer(userAccountId, amount);
    await transaction.wait();
    console.log(`${amount} MDToken has been sent to ${userAccountId}`);
  }
}
async function getBalance() {
  if (typeof window.ethereum !== 'undefined') {
    const contract = await _intializeContract(signer)
    const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' })
    const balance = await contract.balanceOf(account);
    console.log("Account Balance: ", balance.toString());
  }
}
  return (
    <div className="App">
      <header className="App-header">
      <button onClick={_getTokenData}>get token data</button>
      <h1>{tokenData.name}</h1>
      <h1>{tokenData.symbol}</h1>
      <button onClick={getBalance}>Get Balance</button>
      <button onClick={sendMDToken}>Send MDToken</button>
      <input onChange={e => setUserAccountId(e.target.value)} placeholder="Account ID" />
        <input onChange={e => setAmount(e.target.value)} placeholder="Amount" />
      </header>
    </div>
  );
}
export default App;

The first two functions we will be writing will be the requestAccount function, which will allow us to connect to the MetaMask wallet of the user, and the _intializeContract function, which will initialize and make available our contact for us to use.

connecting the smart contract to our react frontend

Copy
async function requestAccount() {
  await window.ethereum.request({ method: 'eth_requestAccounts' });
}

 const provider = new ethers.providers.Web3Provider(window.ethereum);
 const signer = provider.getSigner();
 async function _intializeContract(init) {
  // We first initialize ethers by creating a provider using window.ethereum
  // When, we initialize the contract using that provider and the token's
  // artifact. You can do this same thing with your contracts.
  const contract = new ethers.Contract(
    tokenAddress,
    TokenArtifact.abi,
    init
  );

  return contract
}

The requestAccount function is an async function that calls the window.ethereum.request method and allows the user to connect to their MetaMask wallet. For us to make use of the _intializeContract function, we need to make use of the provider and the signer. The provider is obtained from our ethers library we imported, and it makes use of the Web3Provider.

In the _intializeContract function, we will create a contract variable that uses the Contract method from Ethers library, which accepts three parameters: the token address, the token ABI, and the signer or provider. Then we return the contract. We will see how the _intializeContract function would be used later.

These two functions are what will allow us to perform the primary task we want in our dApp, which are:

  1. Displaying our MDToken information.
  2. Get the balance of our MDtoken.
  3. Sending MDtoken to the user.

The three functions in our dApp for the above task are _getTokenData, getBalance and sendMDToken.

In our UI, we have three buttons that call the _getTokenData, getBalance, and sendMDToken functions. Then two inputs, where the user can supply the account address to send the tokens and the amount input for the amount to be sent.

connecting the smart contract to our react frontend

Copy
return (
    <div className="App">
      <header className="App-header">
      <button onClick={_getTokenData}>get token data</button>
      <h1>{tokenData.name}</h1>
      <h1>{tokenData.symbol}</h1>
      <button onClick={getBalance}>Get Balance</button>
      <button onClick={sendMDToken}>Send MDToken</button>
      <input onChange={e => setUserAccountId(e.target.value)} placeholder="Account ID" />
        <input onChange={e => setAmount(e.target.value)} placeholder="Amount" />
      </header>
    </div>
  );


Now that we have an overview of what our dApp will be doing let us go into details on how things are running.

When the user clicks on the get token data button, the _getTokenData function is called.
In our _getTokenData function, we run our _intializeContract function and pass in our signer. The function returns a contract, and we assign our contract to the contract variable we created. Now we can use the contract to get our MDToken name and the symbol which are public in our token contract. Then we call our setTokenData, which accepts the token data object with our name and symbol and updates the tokenData state.

connecting the smart contract to our react frontend

Copy
async function _getTokenData() {
  const contract = await _intializeContract(signer)
  const name = await contract.name();
  const symbol = await contract.symbol();
  const tokenData = {name, symbol}
  setTokenData(tokenData);
}

The getBalance function first of all checks if the window.ethereum object is present. If it is present, it means that we have MetaMask installed and we have a wallet. Then we initialize our contract. After initializing the contract we get the account connected to our MetaMask and we use our contract to get the balance of the account using the balanceOf method from our contract then we log the account balance.

connecting the smart contract to our react frontend

Copy
async function getBalance() {
  if (typeof window.ethereum !== 'undefined') {
    const contract = await _intializeContract(signer)
    const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' })
    const balance = await contract.balanceOf(account);
    console.log("Account Balance: ", balance.toString());
  }
}

For the last method which is the sendMDToken, we first check if window.ethereum object is present then we run the requestAccount function and initialize our contract. We use the contract transfer method which accepts userAccountId and amount as arguments, then we wait for the transaction to take place and log a success message.

connecting the smart contract to our react frontend

Copy
async function sendMDToken() {
  if (typeof window.ethereum !== 'undefined') {
    await requestAccount()
    const contract = await _intializeContract(signer)
    const transaction = await contract.transfer(userAccountId, amount);
    await transaction.wait();
    console.log(`${amount} MDToken has been sent to ${userAccountId}`);
  }
}

Note: For us to send MDTokens, we must first import the token from etamask. To do this, we click on metamask, click import token, select custom token, then paste the token address or contract address and you will see that our custom MDToken has been imported with the available coins.

Now we can get our token information, check our MDToken balance which is logged into the console, and transfer MDToken when we provide the account ID and amount we want to transfer in our input.

You should make sure you are not sending real funds to any of the Hardhat accounts provided. If you transfer any actual funds to any account used in this tutorial, your funds will be lost forever.

Conclusion

We have finally come to the end of this tutorial. In the tutorial, we looked at how to create, run, compile and deploy smart contracts created using the hardhat ethereum development environment. Hope this gets you started on your journey of building amazing stuff!

Source:

Subscribe to our newsletter for more articles and guides on Ethereum. If you have any feedback, feel free to reach out to us via Twitter. You can always chat with us on our Discord community server, featuring some of the coolest developers you’ll ever meet :)

Related articles 54

How to Send a Transaction On Solana Using JavaScript
Apr 13, 2022

Hello reader! Today is an exhilarating day because we are going on an expedition to the Solana Blockchain. Solana is an up-and-coming blockchain seeking to improve upon the current ecosystem's solutions to the complex problem of providing a secure, scalable, decentralized...

Continue reading
How to do a non-custodial transaction with QuickNode
Apr 12, 2022

Private keys are one of the most sensitive pieces of data when it comes to cryptography and the blockchain. However, there has always been debate/confusion about choosing between custodial wallets (where the wallet provider has custody of the user’s private key) and...

Continue reading
How to connect to Ethereum using .NET (Nethereum)
Apr 12, 2022

Dotnet or .NET is very popular for the development of desktop applications, most Windows desktop applications are built using .NET, and it also contributes largely to web application’s tech stack. In this guide, let’s see how we can connect to Ethereum using .NET and

Continue reading
How to Set Up a Near Project from Scratch
Jan 27, 2022

In this tutorial we will look at how we can setup a basic NEAR project from scratch, installing and configuring dependencies and customizing the project to work well with AssemblyScript.We will first start by initializing our project with a package.json file using...

Continue reading
Como crear y lanzar un ERC-721 (NFT)
Dec 29, 2021

Coleccionables digitales que son compatibles con ERC-721 se han vuelto muy populares desde el lanzamiento de Cryptokitties y han ganado adopción masiva en los últimos meses. Esta guía cubrirá la parte de creación y lanzamiento...

Continue reading
How to connect to Ethereum network using Java / Web3j
Apr 12, 2022

We can say that Java is one of the most versatile languages out there, and it continues to be relevant in today's time. Java is so popular because of its massive user base and use cases. In this guide/tutorial, we'll learn how to connect to the Ethereum Blockchain network...

Continue reading
How to integrate IPFS with Ethereum
Apr 12, 2022

It can be costly to store massive files on a blockchain mainnet, and this is where decentralized file storing systems like IPFS can come in handy. Sometimes, NFTs use IPFS as well. In this guide, we’ll cover how we can integrate IPFS with...

Continue reading
How to Connect to the Ethereum Network using Ruby
Jun 13, 2022

The Ruby programming language has a huge fanbase. Ruby was developed by its creator with an intention to invent a language developers can enjoy learning and using. Ruby has been largely accepted by developers all around the world since its launch, in fact, the biggest...

Continue reading
How to connect to Ethereum network with ethers.js
Apr 12, 2022

When someone thinks of developing a dApp the first tool that comes to their mind is web3.js which is pretty common because of its popularity in the community and wide use cases, dApp development has been consistently growing and there are a lot of developers who want to...

Continue reading
How to Mint an NFT on Solana
Apr 12, 2022

Updated at: April 10, 2022Welcome to another QuickNode guide on Solana - the up-and-coming blockchain that seeks to solve the scalability issues of Ethereum. We will be walking through step-by-step how to create an NFT on Solana. NFT, short for Non Fungible Token,...

Continue reading
The Web3 Developer Stack
Apr 12, 2022

A developer stack is a bag of technologies a developer possesses. For example, MEAN (MongoDB, Express.js, AngularJS/Angular, and Node.js) and MERN (MongoDB, Express.js, React, and Node.js) are common web developer stacks. Similarly, today we will learn more about the web3...

Continue reading
How to deploy a smart contract with Brownie
Apr 12, 2022

Python is one of the most versatile programming languages; from researchers running their test models to developers using it in heavy production environments, it has use cases in every possible technical field. In today's guide, we will learn about Brownie, a Python-based...

Continue reading
How to Get All Tokens Held by a Wallet in Solana
Jun 24, 2022

Hello readers! To kick off Solana Summer and the current whitelist meta, we thought it would be helpful to dig into all of the token accounts you and your users have using the getParsedProgramAccounts method. This tool is convenient for querying different...

Continue reading
Introduction to Scaffold-ETH 🏗
Dec 29, 2021

Developing applications involves juggling several moving pieces like front-ends, back-ends, and databases. But developing a decentralized application on a blockchain adds a few more elements like smart contracts and nodes that allow you to connect to the...

Continue reading
Como crear un NFT en SOLANA
Dec 29, 2021

¡Hola querido lector! Bienvenidos a una nueva guía de Solana.Solana es una blockchain que promete mucho a la hora de intentar resolver los problemas de escalabilidad que podemos apreciar en otras blockchains, como Ethereum por...

Continue reading
How to Get Transaction Logs on Solana
Jun 28, 2022

Ever need to pull all the transactions associated with a Wallet? Want to see all of the mint transactions associated with a Candy Machine? Or maybe see transaction history of an NFT? Solana's getSignaturesForAddress method is a versatile tool that makes...

Continue reading
How to Send an EIP-1559 Transaction
Apr 12, 2022

While Ethereum has been trying to scale, it has encountered some gas price issues. Many layer 2 solutions and sidechains sprang into existence to solve this problem, but Ethereum is the main chain, and at some point, it has to be improved. EIP-1559 was introduced to...

Continue reading
How to Create an Address in Solana using JavaScript
Apr 12, 2022

Hello reader! Welcome to QuickNode's first Solana guide. Solana is an up-and-coming blockchain that seeks to solve the scalability issues that Ethereum has been handling. You will walk through step-by-step how to create a Solana address using the @solana/web3.js...

Continue reading
How to create your own DAO with Aragon
Apr 12, 2022

Blockchain provides us with the power of decentralization. Decentralization means the transfer of power to users/members rather than having a single centralized authority governing everything; it enables various use cases in finance, governance, voting, fundraising, etc....

Continue reading
How to Connect to Terra with JavaScript using Terra.js
Apr 12, 2022

Stablecoins have been bridging the gap between traditional currencies and blockchains. Stablecoins offer stable price tokens pegged by a reserve asset which is often a fiat current like USD, EUR, GBP. The Terra protocol provides a framework to work with stablecoins. This...

Continue reading
How to connect to Ethereum network using Go
Apr 12, 2022

Go helps you make faster scalable backends and this guide will show you how to connect your backend to Ethereum (and make it even faster, more reliable, and globally accessible, all thanks to QuickNode’s global infrastructure). What is...

Continue reading
How To Fork Ethereum Mainnet with Hardhat
Apr 12, 2022

Forking the chain at an older block of the blockchain is helpful if you want to simulate the blockchain’s state at that block; Hardhat has this functionality built in. In this guide, let’s go through the process of forking the Ethereum Mainnet at an older...

Continue reading
How to connect to Ethereum using PHP
Apr 12, 2022

PHP is a very popular choice among developers and has a vast community due to its long presence in web development. In this guide, we’ll cover how to connect to Ethereum with PHP using the web3.php...

Continue reading
How to use Subspace with QuickNode
Apr 12, 2022

In this guide, we'll understand a bit about reactive development and how to use Subspace with QuickNode.JavaScript is the programming language behind most of the internet apps and websites. JavaScript today has become one of the most used programming languages,...

Continue reading
How to Connect Your Dapp With MetaMask Using Ethers.js
Dec 29, 2021

In our dApp, we will have a simple react user interface that has a material button asking the user to connect to MetaMask. And if they do not have an account, they can create one or log in to their account. They will then view their wallet balance and address displayed on...

Continue reading
How to generate a new Ethereum address in Go
Dec 29, 2021

Golang is very popular among backend developers for building infrastructures and microservices. Go is a procedural programming language. Developed in 2007 by Robert Griesemer, Rob Pike, and Ken Thompson at Google, then launched in 2009 as...

Continue reading
How to generate a new Ethereum address in Python
Dec 29, 2021

Python is one of the most versatile programming languages out there with an abundance of use cases; We can build many applications with Python from client-side to back end. In this guide, we will cover creating an Ethereum address in Python using the

Continue reading
How to Lazy Mint an NFT on Rarible with Rarepress
Apr 12, 2022

NFTs are great for creators to monetize their artwork and for people to get ownership of an item. But since gas prices are usually high given the highly in-demand space on Ethereum, minting an NFT or an NFT collection can become costly for a creator. Lazy minting solves...

Continue reading