Skip to content

Hardhat Setup for NFT Token

Now that we have implemented our token in Truffle, surely, you're eager to know how Hardhat can be of service.

It's framework or toolkit #2 that we're trying. Since we already have some experience with Truffle, we can also make some comparison here.

Let's start with the setup.

Install Hardhat

Hardhat is, like Truffle, JavaScript (node) based. That means, you install it via the node package manager.

But, unlike Truffle, it is project dependent and installed as a dev-dependency.

Create a new folder in our nft-project folder called hardhat.

mkdir -p nft-project/hardhat
code nft-project/hardhat

Now we initialize an empty npm project:

npm init -y

and then install Hardhat as a dev-dependency:

npm install --save-dev hardhat

Setup Hardhat

Now hardhat is installed as a dev-dependency. To setup a new hardhat project, you can run

npx hardhat

which will run some sort of wizard.

Cool thing about hardhat, you can also integrate it into a Typescript Project. That might be a big plus on one hand, but at the end, you just need the ABI and the address.

For this one, we will still start with a general JavaScript project. So, from the selection, select "Create a JavaScript project"

Select the right folder, and for .gitignore and hardhat-toolbox say "y".

What you'll end up with is a sample project with a contract, a deploy script in scripts and a test in the test folder.

You could now run

npx hardhat test

to run the tests, and it looks very much like in truffle. The reason is, hardhat also uses Mocha as a test-runner. The difference is, there isn't so much magic happening behind the scenes.

Compilation and the ABI Array

Similar to Truffle, when you run

npx hardhat compile

Hardhat will see which compiler it needs to use from the hardhat.config.js file in the root folder of the project, then compile the contracts and put the artifacts in the artifacts/contracts folder. There are two files actually, an artifact with ABI and bytecode. And a debug file, that either contains the source, compiled source and more info, or a pointer to a debug file.

However, it does not contain an address or any more info. So, how is deployment working with Hardhat now?!

Deployment with Hardhat

In hardhat you have, at least by default, no Address management like in Truffle. It's a lot more transparent, especially for newcomers, for the better or for worse. Hardhat puts a lot more responsibility into the developers hand and doesn't give you a clear structure. With one exception: Scripts are in the scripts folder.

Hardhat also comes with a development node, which looks and feels very much like Ganache.

Start it in one terminal with

npx hardhat node

Then run in the other terminal

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

If you inspect the deploy.js script, you will see, Hardhat uses by default ethers.js. We haven't really used Ethers.js yet, so, its a good time to start using it by example.

Add our NFT

Let's add our NFT and remove the existing Lock contract.

rm contracts/Lock.sol
rm scripts/deploy.js
rm test/Lock.js
rm -rf artifacts
rm -rf cache

Let's start with a blank Project.

Add a Spacebear.sol file in the contracts folder with the same content as the one from Truffle, except the console.log - more on that in a second:

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

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract Spacebear is ERC721, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    constructor() ERC721("Spacebear", "SBR") {}

    function _baseURI() internal pure override returns (string memory) {
        return "";

    function safeMint(address to) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _safeMint(to, tokenId);

    function buyToken() public payable {
        uint256 tokenId = _tokenIdCounter.current();
        require(msg.value == tokenId * 0.1 ether, "Not enough funds sent");

        _safeMint(msg.sender, tokenId);

    // The following functions are overrides required by Solidity.

    function _burn(uint256 tokenId) internal override(ERC721) {

    function tokenURI(uint256 tokenId)
        returns (string memory)
        return string(abi.encodePacked(_baseURI(),"_",tokenId+1,".json"));

If you run

npx hardhat compile

you'll see that openzeppelin is missing the contracts, so let's install them too:

npm install @openzeppelin/contracts

and then again

npx hardhat compile

One thing you might have witnessed: compilation is about a bazillion times faster than in Truffle. And you are right. Hardhat is blazing fast. Consistently. Something a lot of people like over Truffle.

Let's see in the next lab how we can deploy our smart contract.

Last update: November 13, 2022