Getting Started With Hardhat Smart Contract Framework

Rahul Ravindran

Hardhat is a new smart contract framework that is gaining popularity in the past year. It makes development and debugging easier with minimual setup or configuration. In this post we will review how to get started with Hardhat framework

You may also checkout the video tutorial from our YouTube Channel:

Overview

Hardhat was created by Nomic Labs, helps developers to manage and automate the common tasks of the process of building smart contracts and Dapps, as easily introducing more functionality around this workflow. That means you can compile, run and test smart contracts at the very core. Hardhat is gaining populartiy according to the last solidity developer survey 2020.

Hardhat comes built-in with Hardhat Network, a local Ethereum network designed for development. Its functionality focuses around Solidity debugging, featuring stack traces, console.log() and explicit error messages when transactions fail.

Hardhat Features

  • Easily Run Solidity Locally: Easily deploy your contracts, run tests and debug Solidity code without dealing with live environments. Hardhat Network is a local Ethereum network designed for development.
  • Built-in debugging flow: Hardhat is the best choice for Solidity debugging. You get Solidity stack traces, console.log and explicit error messages when transactions fail.
  • Large Plugin Ecosystem: Extend Hardhat with a composable ecosystem of plugins that add functionality and integrate your existing tools into a smooth workflow.
  • Built-in typescript support and test suite: Catch mistakes before you even run your code by switching to a typed language. Hardhat provides full native support for TypeScript.

Installing Hardhat

To get started create a new folder and run: npm init -y

Once your project is ready, you should run npm install --save-dev hardhat

to use your local installation of Hardhat, you need to use npx to run it (i.e. npx hardhat). Lets create a Hardhat project, run npx hardhat in your project folder.

Once the project is setup, your folder structure should look something like this:

These are the default paths for a Hardhat project: – contracts/ is where the source files for your contracts should be. – test/ is where your tests should go. – scripts/ is where simple automation scripts go.

If you need to change these paths, take a look at the paths configuration section.

Hardhat Network is initialized by default in this state:

  • A brand new blockchain, just with the genesis block.
  • 20 accounts with 10000 ETH each, generated with the mnemonic "test test test test test test test test test test test junk". Their addresses are:
    • 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
    • 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
    • 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
    • 0x90F79bf6EB2c4f870365E785982E1f101E93b906
    • 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65
    • 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc
    • 0x976EA74026E726554dB657fA54763abd0C3a0aa9
    • 0x14dC79964da2C08b23698B3D3cc7Ca32193d9955
    • 0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f
    • 0xa0Ee7A142d267C1f36714E4a8F75612F20a79720
    • 0xBcd4042DE499D14e55001CcbB24a551F3b954096
    • 0x71bE63f3384f5fb98995898A86B02Fb2426c5788
    • 0xFABB0ac9d68B0B445fB7357272Ff202C5651694a
    • 0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec
    • 0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097
    • 0xcd3B766CCDd6AE721141F452C550Ca635964ce71
    • 0x2546BcD3c84621e976D8185a91A922aE77ECEc30
    • 0xbDA5747bFD65F08deb54cb465eB87D40e51B197E
    • 0xdD2FD4581271e230360230F9337D5c0430Bf44C0
    • 0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199

To customise it, take a look at the configuration section.

A Simple Solidity Smart Contract

The default project ships with a simple smart contract:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "hardhat/console.sol";

contract Greeter {
    string private greeting;

    constructor(string memory _greeting) {
        console.log("Deploying a Greeter with greeting:", _greeting);
        greeting = _greeting;
    }

    function greet() public view returns (string memory) {
        return greeting;
    }

    function setGreeting(string memory _greeting) public {
        console.log("Changing greeting from '%s' to '%s'", greeting, _greeting);
        greeting = _greeting;
    }
}

The contract essentially sets a private variable when you call the setGreeting function.

Compiling Your Contract

Run the following command to compile your contract:

npx hardhat compile

Interacting and Testing your smart contracts

One the easiest way to interact and check your smart contract functionality is to write unit tests for it and see if the contract is behaving as expected. Writing smart contract tests in Hardhat is done using JavaScript or TypeScript.

Tests using Waffle are written with Mocha (opens new window)alongside Chai (opens new window). If you haven’t heard of them, they are super popular JavaScript testing utilities.

Inside the test folder you’ll find sample-test.js. Let’s take a look at it, and we’ll explain it next:

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Greeter", function () {
  it("Should return the new greeting once it's changed", async function () {
    const Greeter = await ethers.getContractFactory("Greeter");
    const greeter = await Greeter.deploy("Hello, world!");
    await greeter.deployed();

    expect(await greeter.greet()).to.equal("Hello, world!");

    const setGreetingTx = await greeter.setGreeting("Hola, mundo!");

    // wait until the transaction is mined
    await setGreetingTx.wait();

    expect(await greeter.greet()).to.equal("Hola, mundo!");
  });
});

In your terminal, run npx hardhat test. You should see the following output:

You can also use web3.js instead of ehter.js your tests, an instance of it is available in the global scope. You can see this in the describe() test in Greeter.js:

const Greeter = artifacts.require("Greeter");

// Traditional Truffle test
contract("Greeter", accounts => {
  it("Should return the new greeting once it's changed", async function() {
    const greeter = await Greeter.new("Hello, world!");
    assert.equal(await greeter.greet(), "Hello, world!");

    await greeter.setGreeting("Hola, mundo!");

    assert.equal(await greeter.greet(), "Hola, mundo!");
  });
});

// Vanilla Mocha test. Increased compatibility with tools that integrate Mocha.
describe("Greeter contract", function() {
  let accounts;

  before(async function() {
    accounts = await web3.eth.getAccounts();
  });

  describe("Deployment", function() {
    it("Should deploy with the right greeting", async function() {
      const greeter = await Greeter.new("Hello, world!");
      assert.equal(await greeter.greet(), "Hello, world!");

      const greeter2 = await Greeter.new("Hola, mundo!");
      assert.equal(await greeter2.greet(), "Hola, mundo!");
    });
  });
});

Checkout the plugin’s [README file ] (https://github.com/nomiclabs/hardhat/tree/master/packages/hardhat-truffle5)for more information about it.

Working with local hardhat node

Start a hardhat node

bash
npx hardhat node

Connect hardhat node to Metamask

Open Metamask > Select the network dropdown from the top left > Select Custom RPC and enter the following details:

  • Network Name: <Enter a name for the network>
  • New RPC URL: http://127.0.0.1:8545
  • Chain ID: 31337

    Click save. You can use this network to connect to the local hardhat node.

Connect your local hardhat account to Metamask for making transactions

  • After running npx hardhat node you will see a list of 20 addresses logged in the terminal
  • To configure an account copy its private key from the terminal (i.e the text after Private Key:)
  • Open Metamask > Click the account icon on top right > Import Account > Paste the private key you just copied > click Import
  • You should now have the account connected with 10000 ETH

Deploying your contracts

When it comes to deploying, there are no official plugins that implement a deployment system for Hardhat yet, but there’s an open issuewith some ideas and we’d value your opinion on how to best design it.

In the meantime, we recommend deploying your smart contracts using scripts, or using the hardhat-deploy community plugin. You can deploy the Greeter contract from the sample project with a deploy script scripts/deploy.js like this:

async function main() {
  // We get the contract to deploy
  const Greeter = await ethers.getContractFactory("Greeter");
  const greeter = await Greeter.deploy("Hello, Hardhat!");

  console.log("Greeter deployed to:", greeter.address);
}

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

You can deploy in the localhost network following these steps:

  1. Start a local node
    npx hardhat node
  2. Open a new terminal and deploy the smart contract in the localhost network
    npx hardhat run --network localhost scripts/deploy.js

As general rule, you can target any network configured in the hardhat.config.js

npx hardhat run --network <your-network> scripts/deploy.js

Using Ganache instead of the Hardhat Network

You don’t need to do anything special to use Ganache if you don’t want to.

Just start Ganache and then run Hardhat with

npx hardhat --network localhost test

Using the hardhat-ganache plugin

If you don’t want to manually start and stop Ganache every time, you can use the hardhat-ganache plugin.

This plugin creates a network called ganache, and automatically starts and stops Ganache before and after running your tests and scripts.

To use it, you have to install it with npm

npm install --save-dev @nomiclabs/hardhat-ganache

and add this line at the beginning of your hardhat.config.js

require("@nomiclabs/hardhat-ganache");

Finally, you can run your tests with

npx hardhat --network ganache test

Writing your custom scripts

Inside scripts/ you will find sample-script.js. Read through its comments to have a better idea of what it does.

// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
const hre = require("hardhat");

async function main() {
  // Hardhat always runs the compile task when running scripts with its command
  // line interface.
  //
  // If this script is run directly using `node` you may want to call compile
  // manually to make sure everything is compiled
  // await hre.run('compile');

  // We get the contract to deploy
  const Greeter = await hre.ethers.getContractFactory("Greeter");
  const greeter = await Greeter.deploy("Hello, Hardhat!");

  await greeter.deployed();

  console.log("Greeter deployed to:", greeter.address);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

Before running the script with node you need to declare ethers. This is needed because Hardhat won’t be injecting it on the global scope as it does when calling the run task.

const hre = require("hardhat");
const ethers = hre.ethers;

async function main() {
  //...
}

To run the script, execute

node scripts/sample-script.js

By accessing the Hardhat Runtime Environment at the top, you are allowed to run the script in a standalone fashion. Hardhat always runs the compile task when it’s invoked via npx hardhat run, but in a standalone fashion you may want to call compile manually to make sure everything is compiled.

Conclusion

Hardhat is a powerful framework to compile, deploy, test, and debug your Ethereum software. It helps developers manage and automate the recurring tasks that are inherent to the process of building smart contracts and dApps, as well as easily introducing more functionality around this workflow. This means compiling, running and testing smart contracts at the very core. It is gaining popularity in the recent months and its definitely a tool that you should get familiar with to ease local development.

0 Comments

Leave a Reply

More great articles

Intro to Binance Smart Chain

Introduction Binance Chain was launched by Binance in April 2019. Its primary focus is to facilitate fast, decentralized (or non-custodial)…

Read Story

How to store your NFT metadata

I will be addressing these issues in this article. More specifically, we will show you how to: Upload Images to…

Read Story

An Introduction to Celo Blockchain

Introducing Celo’s Technology Celo is a blockchain ecosystem focused on increasing cryptocurrency adoption among smartphone users. By using phone numbers…

Read Story

Never miss a minute

Get great content to your inbox every week. No spam.
[contact-form-7 id="6" title="Footer CTA Subscribe Form"]
Arrow-up