December 21, 2024

Your first Solidity smart contract with Hardhat

It couldn’t be easier to write a smart contract and test it in NodeJS. Hardhat is a library which facilitates creating, publishing and testing a smart contract on Ethereum blockchain.

Let’s see how we can use this awesome plugin to write one simple smart contract. I will provide useful information and guidance in this article so stick around πŸ˜‰

What we plan to achieve

Practically, at the end of this article, we will have a working NodeJS project with a sample smart contract written in Solidity. We will make sure that:

  1. The project compiles correctly (both Javascript and Solidity code)
  2. The smart contract can be deployed to a test blockchain
  3. We can call the smart contract via Javascript calls in order to ensure operability

I’m also taking the opportunity to share with you some links about the new AFIEther project that I started around blockchain topics:

Initial setup

Let’s assume you have an NPM project initialized. In order to use Hardhat, we need to install it as a dev dependency and then call the following CLI command to setup our project:

npx hardhat init

In this case, I chose the Javascript type of project since I don’t want to clutter the structure so much. Typescript provides better editor completion support, so feel free to use it if you want to.

The initial Hardhat setup also creates a sample contract file called Lock with unit tests as well. I’m going to leverage code from this one in order to create our let’s call it “Computations” contract.

The Solidity source file

We will create this source file in the contracts/Computations.sol and will have the following structure:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Computations {
    constructor() {

    }

    function sum(int num1, int num2) public pure returns (int) {
        return num1 + num2;
    }
} 

In simple terms, we defined an empty constructor and a function which calculates the sum of 2 numbers. This function is marked pure since it’s not modifying the runtime data of the contract.

Define a unit test for our contract

Time to define some unit test file for the contract based on what Hardhat already provided as a sample. I’ll dump it here:

const {
    time,
    loadFixture,
  } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
  const { anyValue } = require("@nomicfoundation/hardhat-chai-matchers/withArgs");
  const { expect } = require("chai");

  describe("Computations", function () {
    async function deployComputations() {
      // Contracts are deployed using the first signer/account by default
      const [owner, otherAccount] = await ethers.getSigners();

      const Computations = await ethers.getContractFactory("Computations");
      const computations = await Computations.deploy();

      return { computations, owner, otherAccount };
    }

    describe("Sums", function () {
      it("should return the sum of 2 numbers", async function () {
        const { computations } = await loadFixture(deployComputations);

        const sum1 = await computations.sum(1, 2);
        const sum2 = await computations.sum(-1, 2);
  
        expect(sum1).to.equal(3);
        expect(sum2).to.equal(1);
      });

      it("should return the sum of 2 negative numbers", async function () {
        const { computations } = await loadFixture(deployComputations);

        const sum = await computations.sum(-1, -2);
  
        expect(sum).to.equal(-3);
      });
    });
  });

Hardhat uses Chai as testing library. We are keeping the imports exactly how Hardhat provided them. Then we have the deployComputations function which takes care of deploying our contract to the runtime test blochchain. Finally we define the scenarios where we test our sum method.

By running the npx hardhat test command we can check that the tests pass:

So far so good, but

The Hardhat provided code confines us to writing and testing the contract. We want to be able to deploy our contract to a test blockchain and also wire our contract to be called from there. For now, we will stick to localhost blockchain which needs to be started with the command:

npx hardhat node

You will see an output very similar to the one above. Now we have a localhost blockchain into which we can deploy the contract:

npx hardhat ignition deploy ./ignition/modules/Computations.js  --network localhost

You should see some activity in the console window and a contract address.

We’re going to use this address in the next code block (app.js). This is where we actually call the contract:

const computationsAbi = require('./artifacts/contracts/Computations.sol/Computations.json');

async function execute() {
  // Connect to the network
  const url = 'http://localhost:8545';
  const customProvider = new ethers.JsonRpcProvider(url);

  let contractAddress = "0x5FbDB2315678afecb367f032d93F642f64180aa3";

  const computationsContract = new ethers.Contract(contractAddress, computationsAbi.abi, customProvider);

  const num1 = 90;
  const num2 = 32;

  console.log(`The sum of ${num1} and ${num2} is: ${await computationsContract.sum(90, 32)}`);
}

execute().then();

In order for the contract call to succeed, we need to perform these steps:

  1. Get the contract ABI from the generated JSON
  2. Create a custom RPC provider which points to localhost
  3. Instantiate the contract with the right address, ABI and provider
  4. Call the sum method and print its output

Let’s call now the app.js file with the following command:

npx hardhat run app.js

πŸ‘ It’s important to call the Javascript file with the Hardhat command, otherwise it’ll not work!

Please check the full code for this demo on Github.

Conclusion

As we can notice, Hardhat made our lives easy by providing such a magnificent way of interacting, testing and deploying our smart contract. There are just so many things to explore in this world and I’m excited to bring more articles on this topic! All the best!

Thanks for reading, I hope you found this article useful and interesting. If you have any suggestions don’t hesitate toΒ contact me. If you found my content useful please consider a small donation. Any support is greatly appreciated! CheersΒ Β πŸ˜‰

afivan

Enthusiast adventurer, software developer with a high sense of creativity, discipline and achievement. I like to travel, I like music and outdoor sports. Because I have a broken ligament, I prefer safer activities like running or biking. In a couple of years, my ambition is to become a good technical lead with entrepreneurial mindset. From a personal point of view, I’d like to establish my own family, so I’ll have lots of things to do, there’s never time to get bored πŸ˜‚

View all posts by afivan →