Hardhat 101: From Create to Deploy

Introduction

Hardhat is a popular development environment for Ethereum that enables developers to compile, deploy, test, and debug smart contracts. It provides a rich set of features out of the box and is highly extensible through plugins.

In this guide, we‘ll walk through the process of creating and deploying a smart contract using Hardhat. We‘ll cover setting up a project, writing and testing a smart contract, deploying to a live network, and leveraging various Hardhat plugins to enhance the development workflow. By the end, you‘ll have a solid foundation for building smart contract projects with Hardhat.

Setting Up a Hardhat Project

Before diving into Hardhat, make sure you have Node.js and npm installed. You can download them from the official website if you don‘t already have them set up.

With Node.js ready, create a new directory for your project and initialize an npm project:

mkdir my-project
cd my-project 
npm init -y

This will create a package.json file to manage your project‘s dependencies.

Next, install Hardhat in your project:

npm install --save-dev hardhat

Once installed, create a new Hardhat project:

npx hardhat

Select "Create a basic sample project" and follow the prompts to set up your project. Hardhat will generate a bunch of files and folders:

  • contracts/: Directory for Solidity smart contracts
  • scripts/: Directory for deployment and interaction scripts
  • test/: Directory for smart contract tests
  • hardhat.config.js: Hardhat configuration file

Writing and Compiling Smart Contracts

With our project ready, let‘s write a simple smart contract. Create a new file contracts/Token.sol and add the following code:

pragma solidity ^0.8.0;

contract Token {
    string public name = "My Token";
    string public symbol = "MTK";
    uint256 public totalSupply = 1000000;
    address public owner;

    mapping(address => uint256) balances;

    constructor() {
        balances[msg.sender] = totalSupply;
        owner = msg.sender;
    }

    function transfer(address to, uint256 amount) external {
        require(balances[msg.sender] >= amount, "Not enough tokens");
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }

    function balanceOf(address account) external view returns (uint256) {
        return balances[account];
    }
}

This is a basic ERC20-style token contract that allows users to transfer tokens and check balances.

To compile the contract, run:

npx hardhat compile

Hardhat will output the compiled artifacts in the artifacts/ directory. It uses the Solidity compiler version defined in hardhat.config.js, defaulting to the latest version.

Testing Smart Contracts

Hardhat makes it easy to write smart contract tests using JavaScript or TypeScript. It integrates with testing frameworks like Mocha and Waffle out of the box.

Create a new test file test/Token.js and add the following code:

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

describe("Token contract", function () {
  let Token;
  let token;
  let owner;
  let addr1;
  let addr2;

  beforeEach(async function () {
    Token = await ethers.getContractFactory("Token");
    token = await Token.deploy();
    [owner, addr1, addr2] = await ethers.getSigners();
  });

  it("Should set the right owner", async function () {
    expect(await token.owner()).to.equal(owner.address);
  });

  it("Should assign the total supply of tokens to the owner", async function () {
    const ownerBalance = await token.balanceOf(owner.address);
    expect(await token.totalSupply()).to.equal(ownerBalance);
  });

  it("Should transfer tokens between accounts", async function () {
    await token.transfer(addr1.address, 50);
    const addr1Balance = await token.balanceOf(addr1.address);
    expect(addr1Balance).to.equal(50);

    await token.connect(addr1).transfer(addr2.address, 50);
    const addr2Balance = await token.balanceOf(addr2.address);
    expect(addr2Balance).to.equal(50);
  });

  it("Should fail if sender doesn‘t have enough tokens", async function () {
    const initialOwnerBalance = await token.balanceOf(owner.address);

    await expect(
      token.connect(addr1).transfer(owner.address, 1)
    ).to.be.revertedWith("Not enough tokens");

    expect(await token.balanceOf(owner.address)).to.equal(
      initialOwnerBalance
    );
  });
});

This test suite checks that the contract sets the right owner, assigns the token supply, allows transferring tokens between accounts, and prevents sending more tokens than an account has.

To run the tests:

npx hardhat test

Hardhat will compile the contracts, spin up a local Ethereum network, run the tests, and output the results. If a test fails, Hardhat provides helpful error messages pointing to the failing line.

Deploying Smart Contracts

Hardhat makes it simple to deploy contracts to Ethereum networks, both local and live. Let‘s start by deploying to Hardhat‘s built-in local network.

Create a new deployment script scripts/deploy.js:

async function main() {
  const [deployer] = await ethers.getSigners();
  console.log("Deploying contract with account:", deployer.address);

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

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

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

To deploy:

npx hardhat run scripts/deploy.js

The contract will be deployed to the local Hardhat network, and the token‘s address will be logged.

To deploy to a live testnet like Goerli, you‘ll need:

  1. A Goerli RPC URL from a service like Alchemy or Infura
  2. A funded Goerli test account

Install the dotenv package to load sensitive info from a .env file:

npm install dotenv

Create a .env file with your Goerli URL and private key:

GOERLI_URL=<your-goerli-url>
PRIVATE_KEY=<your-private-key>

Update hardhat.config.js:

require("@nomiclabs/hardhat-waffle");
require("dotenv").config();

module.exports = {
  solidity: "0.8.0",
  networks: {
    goerli: {
      url: process.env.GOERLI_URL,
      accounts: [process.env.PRIVATE_KEY]
    }
  }
};

Finally, deploy to Goerli:

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

Your contract will be live on the Goerli testnet! To verify it on Etherscan, see the hardhat-etherscan plugin.

Using Hardhat Plugins

Hardhat has a robust plugin system to extend its functionality. Some popular plugins:

  • hardhat-ethers: Injects ethers.js into the Hardhat runtime
  • hardhat-truffle5: Enables Truffle 5 support
  • hardhat-gas-reporter: Reports gas usage per unit test
  • hardhat-contract-sizer: Reports contract sizes
  • hardhat-abi-exporter: Exports smart contract ABIs

To install a plugin:

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

And add it to hardhat.config.js:

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

Consult each plugin‘s documentation for configuration and usage details.

Hardhat Deploy

While Hardhat‘s built-in deployment support is sufficient for simple contracts, hardhat-deploy takes things to the next level.

It offers advanced deployment features like multi-stage deployments, scriptable deployments, tracking deployments per network, and replicating live environments locally.

Install the plugin:

npm install --save-dev hardhat-deploy

And add it to hardhat.config.js:

require("hardhat-deploy");

Instead of a single deploy.js script, hardhat-deploy uses deployment scripts under the deploy/ directory. Each script represents a deployment stage.

A basic deployment script looks like:

module.exports = async ({ getNamedAccounts, deployments, getChainId }) => {
  const { deploy } = deployments;
  const { deployer } = await getNamedAccounts();
  const chainId = await getChainId();

  await deploy("Token", {
    from: deployer,
    log: true,
  });
};

module.exports.tags = ["Token"];

Run the deployment with:

npx hardhat deploy

Hardhat Deploy will execute the scripts in the proper order and track deployments per network. It also offers helpful functions to get the latest contract deployments and interact with contracts in tests and scripts.

Additional Hardhat Tips

Here are a few more things to note about Hardhat:

  • Use console.log in contracts for easy smart contract logging
  • Impersonate any account using hardhat_impersonateAccount for testing
  • Fork mainnet with hardhat_reset for integration tests against live contracts
  • Define custom hardhat tasks in hardhat.config.js to automate routine operations
  • Extend Hardhat with TypeScript or customize its config to suit your needs

Conclusion

Hardhat is a powerful tool for Ethereum developers looking to streamline their smart contract workflow. Its built-in features and extensive plugins make it suitable for projects of all sizes.

In this guide, we covered setting up a Hardhat project, writing and testing a token contract, deploying it locally and to Goerli, and leveraging various plugins to enhance functionality. You‘re now equipped to build your own Hardhat projects!

Hardhat has excellent documentation and an active community, so you‘ll have plenty of support as you continue your smart contract journey. Feel free to also explore other development tools like Truffle and Brownie to find your best fit.

Now it‘s your turn – get out there and start creating with Hardhat! The Ethereum ecosystem awaits your next world-changing dApp. Happy building!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Similar Posts