web3 tutorial [02/10] - hardhat: ethereum development environment

by parttimelarry

Web2 vs. Web3 development environment

Cloud services can be expensive. Because of this, we don’t want to pay for production infrastructure until our application is tested and ready to go live. So we first set up a local development environment. This environment often consists of a web server (eg. Apache, nginx), a database server (eg. MySQL, PostgreSQL, MongoDB), and a web framework (eg. Ruby on Rails, Flask, Laravel). We write code locally, iterate, and test repeatedly until our application is ready for prime time. Only then do we deploy our app to production.

When building a web3 application, our end goal is to deploy our smart contract to Ethereum mainnet where it can be used by the world. But smart contracts are immutable and deploying to mainnet is expensive. We don’t want to deploy to mainnet until our code is tested and our data structures are finalized. While our dapp is in development, we want to be able to experiment, make mistakes, evolve our data structures, and simulate transactions using test ether and test accounts.

What is hardhat?

Hardhat is an Ethereum development environment that allows us to test, compile, and run our smart contract code. It provides a local blockchain where we can deploy our smart contracts and test them with generated accounts and test ether. Once our application is working locally, we can then deploy it to a testnet and ultimately to mainnet.

Node.js and npm

Hardhat is distributed as an npm (Node Package Manager) package and uses the Node.js ecosystem. So we must first make sure we have a recent version of node and npm. Let’s open a terminal and enter some commands to check:

node -v
v16.10.0
npm -v
8.3.1

I have node v16.10.0 and npm 8.3.1, but any version of node 16+ should be fine. If you don’t already have the node command, download the nodejs LTS version for your operating system here. The npm command comes with nodejs.

Setting up your project

Let’s create a new directory for our project:

mkdir calend3

Then inside of the calend3 directory, initialize the project:

cd calend3
npm init -y

This will create a new file called package.json inside of your directory. The -y flag after npm init skips the project creation prompts and uses default values for the project name and version. You can always edit these values later.

Wrote to /Users/larry/Projects/web3-tutorial/calend3/package.json:

{
  "name": "calend3",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Installing hardhat

Now we have an empty node project. But we haven’t installed any packages or dependencies. Let’s change that by installing hardhat.

npm install --save-dev hardhat

You will see hardhat install along with its dependencies.

Why did we use –save-dev here? This flag specifies that hardhat is a development dependency. When our application goes to production, we don’t need to bundle hardhat – it is only used in development.

Once the package is installed, your project folder will contain a node_modules directory and a package-lock.json. You will also see that hardhat has been added to your package.json under devDependencies:

cat package.json
{
  "name": "calend3",
  "version": "1.0.0",
  "description": "web3 appointment scheduler",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "parttimelarry",
  "license": "ISC",
  "devDependencies": {
    "hardhat": "^2.8.3"
  }
}

Hardhat CLI

Cool, hardhat is installed. How do we use it? Let’s start by typing npx hardhat:

npx hardhat

This will prompt you to either create a new sample hardhat project or just create an empty hardhat config. Choose the “basic sample project”.

888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

๐Ÿ‘ท Welcome to Hardhat v2.8.3 ๐Ÿ‘ทโ€

? What do you want to do? โ€ฆ
โฏ Create a basic sample project
  Create an advanced sample project
  Create an advanced sample project that uses TypeScript
  Create an empty hardhat.config.js
  Quit

When you choose the basic project, you will be prompted for some information. Just press enter at all of the prompts to use the defaults:

โœ” What do you want to do? ยท Create a basic sample project
โœ” Hardhat project root: ยท /Users/larry/Projects/web3-tutorial/calend3
โœ” Do you want to add a .gitignore? (Y/n) ยท y
โœ” Do you want to install this sample project's dependencies with npm (@nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers)? (Y/n) ยท y


npm install --save-dev @nomiclabs/hardhat-waffle@^2.0.0 ethereum-waffle@^3.0.0 chai@^4.2.0 @nomiclabs/hardhat-ethers@^2.0.0 ethers@^5.0.0

This will install a few more dependencies for development and testing, including chai, waffle, and ethers. We will talk more about how to use these in a moment. You should now see a few more files and folders in your project:

 tree -L 1
.
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ contracts
โ”œโ”€โ”€ hardhat.config.js
โ”œโ”€โ”€ node_modules
โ”œโ”€โ”€ package-lock.json
โ”œโ”€โ”€ package.json
โ”œโ”€โ”€ scripts
โ””โ”€โ”€ test

4 directories, 4 files

Tasks

The hardhat CLI command is used to run tasks. A hardhat task is just a JavaScript function that has access to the hardhat runtime environment (HRE). Now that your project is set up, typing npx hardhat will display a list of available tasks:

npx hardhat
Hardhat version 2.8.3

Usage: hardhat [GLOBAL OPTIONS] <TASK> [TASK OPTIONS]

GLOBAL OPTIONS:

  --config           	A Hardhat config file.
  --emoji            	Use emoji in messages.
  --help             	Shows this message, or a task's help if its name is provided
  --max-memory       	The maximum amount of memory that Hardhat can use.
  --network          	The network to connect to.
  --show-stack-traces	Show stack traces.
  --tsconfig         	A TypeScript config file.
  --verbose          	Enables Hardhat verbose logging
  --version          	Shows hardhat's version.


AVAILABLE TASKS:

  accounts	Prints the list of accounts
  check   	Check whatever you need
  clean   	Clears the cache and deletes all artifacts
  compile 	Compiles the entire project, building all artifacts
  console 	Opens a hardhat console
  flatten 	Flattens and prints contracts and their dependencies
  help    	Prints this message
  node    	Starts a JSON-RPC server on top of Hardhat Network
  run     	Runs a user-defined script after compiling the project
  test    	Runs mocha tests

To get help for a specific task run: npx hardhat help [task]

One of these tasks is accounts and it does exactly what the description says: it prints a list of acounts. Let’s try this out:

npx hardhat accounts

This outputs a list of account addresses:

0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
0x70997970C51812dc3A010C7d01b50e0d17dc79C8
0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
0x90F79bf6EB2c4f870365E785982E1f101E93b906
0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65
0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc
0x976EA74026E726554dB657fA54763abd0C3a0aa9
0x14dC79964da2C08b23698B3D3cc7Ca32193d9955
0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f
0xa0Ee7A142d267C1f36714E4a8F75612F20a79720
0xBcd4042DE499D14e55001CcbB24a551F3b954096
0x71bE63f3384f5fb98995898A86B02Fb2426c5788
0xFABB0ac9d68B0B445fB7357272Ff202C5651694a
0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec
0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097
0xcd3B766CCDd6AE721141F452C550Ca635964ce71
0x2546BcD3c84621e976D8185a91A922aE77ECEc30
0xbDA5747bFD65F08deb54cb465eB87D40e51B197E
0xdD2FD4581271e230360230F9337D5c0430Bf44C0
0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199

These are test accounts provided by hardhat. They each have test ether. Rather than creating many wallets ourselves and sending them all Ether to test, we have some test accounts already that are on the local hardhat network. We can use these to test our application.

Where is this task defined? Open the hardhat.config.js in your text editor and you can see the code. Notice the task has a name and description, followed by an async JavaScript function. This function retrieves the test accounts from the hardhat environment, then loops through and prints their addresses to the console.

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

Hardhat has other built-in tasks like compile, console, test, and node. We will use all of these commands in the next section to compile and test our Solidity code.

Hardhat Hackathon Boilerplate

The hardhat team provides a boilerplate hackathon project on Github at https://github.com/nomiclabs/hardhat-hackathon-boilerplate.

Take a quick look at the structure of this application to get a feel for how a dapp is structured. It has a contracts directory which contains smart contracts written in Solidity. It also has a frontend directory with a React web application. We will also be implementing a React UI for our application. But instead of using React class components, we will use hooks, which is a bit more modern.

Changing Some Defaults

Let’s make a few modifications to our own hardhat project before moving on to the next section:

  1. Delete the sample contract contracts/Greeter.sol. We will start from scratch.
  2. Rename scripts/sample-script.js to deploy.js
  3. Rename test/sample-test.js to test.js

In the next section, we will begin writing some Solidity code.