Marketplace has launched, further enabling blockchain developers! Learn more

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

September 09, 2022

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 35

How to do a non-custodial transaction with QuickNode
Originally Published On: Jun 19, 2021
Updated On: Sep 9, 2022

Private keys are one of the most... A non-custodial wallet is a... Custodian of Private... At We could use pretty much any... We could use any of the many... Let us create a token transfer... Congratulations on making a...

Continue reading
How to Set Up a Near Project from Scratch
Originally Published On: Jan 27, 2022
Updated On: Sep 9, 2022

In this tutorial we will look at... In this section, we will... In this section, we will look at... In this section, we will look at... When writing smart contracts on... In the last section, we looked... In the last section, we looked... In the last section, we looked... We have finally come to the end...

Continue reading
Estimating gas price using pending transactions in Python
Originally Published On: Feb 20, 2021
Updated On: Sep 9, 2022

To send a transaction on the... Gas is the unit to measure the... Why do we want to estimate gas?... Our first step here would be to... For our purposes today, we could... The first thing we’re going to... Now you know how to estimate gas...

Continue reading
Como crear y lanzar un ERC-721 (NFT)
Originally Published On: Dec 27, 2021
Updated On: Sep 9, 2022

Coleccionables digitales que son... Fungible significa que es lo... ERC es la abreviación para... Ahora que conocemos qué es y... Antes de escribir nuestro... Felicidades por crear tu propio...

Continue reading
How to Access Bitcoin Mempool
Originally Published On: Aug 16, 2021
Updated On: Sep 21, 2022

Bitcoin is the father of... The transactions which are sent... For our purpose today, we need a... We will use Bitcoin RPC methods... Congratulations on mastering the...

Continue reading
How to Mint an NFT on Polygon with Ethers.js
Originally Published On: Mar 28, 2022
Updated On: Sep 9, 2022

This guide demonstrates how to... Ethers.js is a complete Ethereum... As a reminder, the act of... We will deploy our contract on... Time to write the code! Start by... Note, you will need some... That’s it! You have minted an...

Continue reading
How to access Ethereum Mempool
Originally Published On: Oct 15, 2020
Updated On: Sep 15, 2022

On Ethereum, when a transaction... In blockchain terminology, a... For the purpose of this guide,... First, query the Let’s examine how to subscribe... This short guide showed you how...

Continue reading
The Web3 Developer Stack
Originally Published On: Jul 3, 2021
Updated On: Sep 15, 2022

A developer stack is a bag of... If we can imagine web3 as a car... The Chainlink price feed... Head over to the We’ll need a Kovan node to... We’ll now write a short... Congratulations :D You’ve taken...

Continue reading
How to Mint NFTs on the Stacks Blockchain
Originally Published On: Aug 19, 2022
Updated On: Sep 9, 2022

Stacks is an emerging blockchain... The Stacks ecosystem has adopted... Open up a terminal window and... Implementing the SIP-009... This section will demonstrate... The moment you've been waiting... Awesome Work! You’ve learned how...

Continue reading
Introduction to Scaffold-ETH 🏗
Originally Published On: Oct 7, 2021
Updated On: Sep 9, 2022

Developing applications involves... First of all, before getting... Scaffold-ETH comes with a demo... We will set up a QuickNode... Congratulations, and thank you...

Continue reading
How to connect to Ethereum using PHP
Originally Published On: Jun 11, 2021
Updated On: Sep 9, 2022

PHP is a very popular choice... Before installing the web3.php,... We could use pretty much any... Now, make a PHP script file... Congratulations on connecting to...

Continue reading
How to use Subspace with QuickNode
Originally Published On: Sep 8, 2020
Updated On: Sep 9, 2022

In this guide, we'll understand... To install Subspace, we'll need... For our purposes today, we could... Now let's create a short script,... Run the file using the node...

Continue reading
How to Connect Your Dapp With MetaMask Using Ethers.js
Originally Published On: Dec 13, 2021
Updated On: Sep 9, 2022

In our dApp, we will have a... A dApp is an application that... We will need to install the... Ethers.js is a lightweight... With this application, we will... Next, we will create a function... After connecting to MetaMask, we... To handle the wallet address of... This section will show us how to... In this tutorial, we...

Continue reading
How to Lazy Mint an NFT on Rarible with Rarepress
Originally Published On: Feb 23, 2022
Updated On: Sep 9, 2022

NFTs are great for creators to... To mint an NFT, you write data... Lazy Minting is a process in... We will write some JavaScript... Rarepress interacts with the... Now that we have everything in... Congratulations on listing your...

Continue reading
How to get the balance of an ERC-20 token
Originally Published On: Jun 14, 2021
Updated On: Sep 21, 2022

When a new token is made on the... You could use any Ethereum... Now that you've done the legwork... Getting the ERC20-Token Balance... In the top of your... ABI is short for You will most likely interact... You can use a similar process... We now have a connection to an... Having made it to the end, you...

Continue reading