Skip to content

Building with Hardhat on Moonbeam

Hardhat Create Project

Introduction

Hardhat is an Ethereum development environment that helps developers manage and automate the recurring tasks inherent to building smart contracts and DApps. Hardhat can directly interact with Moonbeam's Ethereum API so it can also be used to deploy smart contracts into Moonbeam.

This guide will cover how to use Hardhat to compile, deploy, and debug Ethereum smart contracts on the Moonbase Alpha TestNet.

Checking Prerequisites

We need to install Node.js (we'll use v15.x) and the npm package manager. You can download directly from Node.js or in your terminal:

curl -sL https://deb.nodesource.com/setup_15.x | sudo -E bash -

sudo apt install -y nodejs
# You can use homebrew (https://docs.brew.sh/Installation)
brew install node

# Or you can use nvm (https://github.com/nvm-sh/nvm)
nvm install node

We can verify that everything is installed correctly by querying the version for each package:

node -v
npm -v

As of writing of this guide, the versions used were 15.7.0 and 7.4.3, respectively.

Also, you will need the following:

Once all requirements have been met, you are ready to build with Hardhat.

Starting a Hardhat Project

To start a new project, create a directory for it:

mkdir hardhat && cd hardhat

Then, initialize the project by running:

npm init -y

You will notice a newly created package.json, which will continue to grow as you install project dependencies.

To get started with Hardhat, we will install it in our newly created project directory:

npm install hardhat

Once installed, run:

npx hardhat

This will create a Hardhat config file (hardhat.config.js) in our project directory.

Note

npx is used to run executables installed locally in your project. Although Hardhat can be installed globally, we recommend installing locally in each project so that you can control the version on a project by project basis.

After running the command, choose Create an empty hardhat.config.js:

Hardhat Create Project

The Contract File

We are going to store our contract in the contracts directory. Create it:

mkdir contracts && cd contracts

The smart contract that we'll deploy as an example will be called Box: it will let people store a value that can be later retrieved.

We will save this file as contracts/Box.sol:

// contracts/Box.sol
pragma solidity ^0.8.1;

contract Box {
    uint256 private value;

    // Emitted when the stored value changes
    event ValueChanged(uint256 newValue);

    // Stores a new value in the contract
    function store(uint256 newValue) public {
        value = newValue;
        emit ValueChanged(newValue);
    }

    // Reads the last stored value
    function retrieve() public view returns (uint256) {
        return value;
    }
}

Hardhat Configuration File

Let's modify our Hardhat configuration file so we can compile and deploy this contract to Moonbase Alpha.

If you have not yet done so, create a MetaMask Account, connect to Moonbase Alpha, and fund it through Mission Control. We will use the private key of the account created to deploy the contract.

We start by requiring the ethers plugin, which brings the [ethers.js][/integrations/ethers/] library that allows you to interact with the blockchain in a simple way. We can install ethers plugin by running:

npm install @nomiclabs/hardhat-ethers ethers

Next, we import the private key that we've retrieved from MetaMask and store it in a .json file.

Note

Please always manage your private keys with a designated secret manager or similar service. Never save or commit your private keys inside your repositories.

Inside the module.exports, we need to provide the Solidity version (0.8.1 according to our contract file), and the network details:

  • Network name: moonbase
  • URL: https://rpc.testnet.moonbeam.network
  • ChainID: 1287

If you want to deploy to a local Moonbeam development node, you can use the following network details:

  • Network name: dev
  • URL: http://127.0.0.1:9933
  • ChainID: 1281

The Hardhat configuration file should look like this:

// ethers plugin required to interact with the contract
require('@nomiclabs/hardhat-ethers');

// private key from the pre-funded Moonbase Alpha testing account
const { privateKey } = require('./secrets.json');

module.exports = {
  // latest Solidity version
  solidity: "0.8.1",

  networks: {
    // Moonbase Alpha network specification
    moonbase: {
      url: `https://rpc.testnet.moonbeam.network`,
      chainId: 1287,
      accounts: [privateKey]
    }
  }
};

Next, let's create a secrets.json, where the private key mentioned before is stored. Make sure to add the file to your project's .gitignore, and to never reveal your private key. The secrets.json file must contain a privateKey entry, for example:

{
    "privateKey": "YOUR-PRIVATE-KEY-HERE"
}

Congratulations! We are ready for deployment!

Compiling Solidity

Our contract, Box.sol, uses Solidity 0.8.1. Make sure the Hardhat configuration file is correctly set up with this solidity version. If so, we can compile the contract by running:

npx hardhat compile

Hardhat Contract Compile

After compilation, an artifacts directory is created: it holds the bytecode and metadata of the contract, which are .json files. It’s a good idea to add this directory to your .gitignore.

Deploying the Contract

In order to deploy the Box smart contract, we will need to write a simple deployment script. First, let's create a new directory (scripts). Inside the newly created directory, add a new file deploy.js.

mkdir scripts && cd scripts
touch deploy.js

Next, we need to write our deployment script using ethers. Because we'll be running it with Hardhat, we don't need to import any libraries. The script is a simplified version of that used in this tutorial.

We start by creating a local instance of the contract with the getContractFactory() method. Next, let's use the deploy() method that exists within this instance to initiate the smart contract. Lastly, we wait for its deployment by using deployed(). Once deployed, we can fetch the address of the contract inside the box instantiation.

// scripts/deploy.js
async function main() {
   // We get the contract to deploy
   const Box = await ethers.getContractFactory('Box');
   console.log('Deploying Box...');

   // Instantiating a new Box smart contract
   const box = await Box.deploy();

   // Waiting for the deployment to resolve
   await box.deployed();
   console.log('Box deployed to:', box.address);
}

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

Using the run command, we can now deploy the Box contract to Moonbase Alpha:

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

Note

To deploy to a Moonbeam development node, replace moonbase for dev in the run command.

After a few seconds, the contract is deployed, and you should see the address in the terminal.

Hardhat Contract Deploy

Congratulations, your contract is live! Save the address, as we will use it to interact with this contract instance in the next step.

Interacting with the Contract

Let's use Hardhat to interact with our newly deployed contract in Moonbase Alpha. To do so, launch hardhat console by running:

npx hardhat console --network moonbase

Note

To deploy to a Moonbeam development node, replace moonbase for dev in the console command.

Then, add the following lines of code one line at a time. First, we create a local instance of the Box.solcontract once again. Don't worry about the undefined output you will get after each line is executed:

const Box = await ethers.getContractFactory('Box');

Next, let's connect this instance to an existing one by passing in the address we obtained when deploying the contract:

const box = await Box.attach('0x425668350bD782D80D457d5F9bc7782A24B8c2ef');

After attaching to the contract, we are ready to interact with it. While the console is still in session, let's call the store method and store a simple value:

await box.store(5)

The transaction will be signed by your Moonbase account and broadcast to the network. The output should look similar to:

Transaction output

Notice your address labeled from, the address of the contract, and the data that is being passed. Now, let's retrieve the value by running:

(await box.retrieve()).toNumber()

We should see 5 or the value you have stored initially.

Congratulations, you have completed the Hardhat tutorial! 🤯 🎉

For more information on Hardhat, hardhat plugins, and other exciting functionality, please visit hardhat.org.