Build a basic app on Ethereum using modern tools like Hardhat and Ethers.js. To-Do List dApp

Cuong Le
Grid Solutions
Published in
6 min readDec 1, 2022

--

This blog will show you how to build your first dApp powered by hardhat, Ethersjs, and Gatsby (a ReactJS framework).

Introduction: Some concepts

  1. How does a dApp work?
  • Traditional App: FrontEnd — BackEnd — Database
  • Decentralize App: FrontEnd — SmartContract(BackEnd + Database) on a Blockchain network

2. What is Blockchain?

A blockchain is a public database that is updated and shared across many computers in a network

  • Block refers to data and state being stored in the consecutive group known as “blocks”
  • Chain refers to the fact that each lock cryptographically references its parent.

3. What is Ethereum?

  • It is a blockchain with a computer embedded in it.
  • There is a single computer whose state everyone on the Ethereum network agrees on:
    - everyone who participates in the Ethereum network keeps a copy of the state of this computer
    - any participant can broadcast a request for this computer to perform arbitrary computation
    - when a request is a broadcast, other participants on the network verify, validate, and execute the computation.
    - the execution causes a state change in the Ethereum Virtual Machine (EVM)
  • Requests for computation are called transaction requests (TX)
    - The record of all transactions and the EVM’s present state gets stored on the blockchain
    - Cryptographic mechanisms ensure that once transactions are verified as valid and added to the blockchain, they can’t be tampered with later.

4. What are smart contracts?

  • It is the programs uploaded to and executed by the network. In other word, it is a reusable program that a developer publishes into the EVM state.
  • Smart contracts don’t run on your computer or any server, they live on the Ethereum network.

5. Hardhat Framework

Hardhat is a development environment for Ethereum software. It consists of different components for editing, compiling, debugging, and deploying your smart contracts and dApps, all of which work together to create a complete development environment.

6. Ethers.js

The ethers.js library aims to be a complete and compact library for interacting with the Ethereum Blockchain and its ecosystem. It was originally designed for use with ethers.io and has since expanded into a more general-purpose library.

7. Openzeppelin

OpenZeppelin provides security products to build, automate, and operate decentralized applications. We also protect leading organizations by performing security audits on their systems and products.

8. Gatsby

Gatsby enables developers to build fast, secure, and powerful websites using a React-based framework and innovative data layer that makes integrating different content, APIs, and services into one web experience incredibly simple.

Step by Step to building your first dApp — To-do List

  • To view the final source code for this project, visit this repo
  • To view the deployed of To-do List dApp, please check out this app

1. Prerequisite:

  • Node v16.x.x or greater

2. Configure the project for development on your local

  • Please follow the link to create a new project using TypeScript
  • In the new project created above, please run these commands below:
npm install --save-dev hardhat
npx hardhat
  • Please install the dependencies below, providing almost all the features that you need to build the To-Do List dApp
npm install --save-dev @nomicfoundation/hardhat-toolbox 
@nomicfoundation/hardhat-chai-matchers chai @nomiclabs/hardhat-ethers
@nomiclabs/hardhat-etherscan @nomicfoundation/hardhat-network-helpers
@typechain/ethers-v5 @typechain/hardhat hardhat-gas-reporter
solidity-coverage ethers typechain @types/mocha
  • After configuring the project, what you’ve done is shown in the image below:
    - contracts directory: this is the location of all smart contacts.
    - scripts directory: This is where you store all scripts, including deployment and customized tasks.
    - node_modules directory: This is the home of all our Node dependencies.
    - src directory: this is where we’ll develop our client-side application.
    - test directory: this is where we’ll write our tests for our smart contracts.
    - hardhat.config.ts file: this is the main configuration file for our Hardhat project

3. To-do List smart contract

Some methods provided for the smart contract include:

  • Create a new task
  • Get all tasks of a current request sender
  • Check to see if the task has been completed.

The full smart contract goes as follows:

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

import "hardhat/console.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";

contract TodoList is ERC721Enumerable {
using Counters for Counters.Counter;

Counters.Counter private _tokenIds;

struct Task {
uint id;
address assignee;
string content;
bool isCompleted;
}

mapping(uint => Task) public _tasks;

event TaskCreated(uint256 tokenId, address assignee, string content, bool isCompleted);

event TaskCompleted(uint256 tokenId, address assignee, bool isCompleted);

event TaskDeleted(uint256 tokenId, address assignee);

constructor() ERC721("TodoList", "TDL"){
createTask("Nice to meet you");
}

function createTask(string memory content) public {
// 1st tokenId is 1
_tokenIds.increment();
uint256 newTaskId = _tokenIds.current();

_safeMint(msg.sender, newTaskId);
_tasks[newTaskId] = Task(newTaskId, msg.sender, content, false);

emit TaskCreated(newTaskId, msg.sender, content, false);
}

function getByTaskId(uint256 taskId) public view returns (Task memory) {
return _tasks[taskId];
}

// This function should be consider in 2 cases after burning a task (latest task, a middle task)
function getAllOfSender() public view returns (Task[] memory) {
if (balanceOf(msg.sender) > 0) {
// total supply task
uint256 numberOfTasks = balanceOf(msg.sender);
// each element in the currentTasks will be initialized value is zero
Task[] memory currentTasks = new Task[](numberOfTasks);

for (uint256 i = 0; i < numberOfTasks; i++) {
uint256 tokenId = tokenOfOwnerByIndex(msg.sender, i);
currentTasks[i] = _tasks[tokenId];
}

return currentTasks;
} else {
Task[] memory currentTasks = new Task[](0);
return currentTasks;
}
}

// Who can invoke the method, owner of the smart contract or owner of the token?
function burn(uint256 taskId) public {
require(msg.sender == ownerOf(taskId), "The NFT does not belong to the sender");

if(_exists(taskId)) {
delete _tasks[taskId];
_burn(taskId);

emit TaskDeleted(taskId, msg.sender);
}
}

// edit data of a minted taskId
function toggleCompleted(uint256 taskId) public {
Task memory task = _tasks[taskId];
console.log("task.isCompleted: %s", task.isCompleted);
task.isCompleted = !task.isCompleted;
console.log("task.isCompleted: %s", task.isCompleted);
_tasks[taskId] = task;

emit TaskCompleted(taskId, msg.sender, task.isCompleted);
}
}

4. Deploy on the Goerli testnet

  • Please install the Metamask extension on your web browser, get a private key for your account in Metamask, and create an environment variable for it in the .env file.
  • Please sign up for an account at Alchemy.
  • Then, please create a new app with the following settings:
    - chain is Ethereum
    - network is Goerli (an Ethereum testnet)
  • After that, you can get an API key for deploying the To-do List smart contract. Please set up the API key.
  • Finally, please run the command below to deploy the To-do List smart contract on the Goerli testnet:
npx hardhat run scripts/deploy.ts --network goerli

5. Deploy the front end of the To-Do List dApp on the Netlify

  • Please create a new account at Netlify.
  • After logging into Netlify, click on Add a new siteImport an existing project ⇒ connect to GitHub (you can choose any Git provider you want) ⇒ choose a GitHub repository for your To-Do List dApp.
  • If you can not find your To-Do List dApp repository on GitHub from the UI of Netlify, please check the Integrations/Applications tab on your GitHub account setting and select a repository that Netlify can access.
  • Please set up the TodoListAddress environment variable, and Publish directory to public/
  • You cannot successfully build the Gatsby project using the compiled artifacts output in our Hardhat project because it is a JSON file. It would help if you considered using a file with a human-readable ABI (Contract Application Binary Interface).
    - A Human-Readable ABI is simply an array of strings, where each string is the Solidity signature.
    - You can get the ABI value in the compiled artifact and paste it into a new .tsx file, it will be the ABI for a smart contract.

6. Final Result:

  • You can access and check the project’s functions at the To-Do List

Conclusion

  • Thanks to many free blockchain platforms supported by the community, you can easy to develop and publish some awesome projects without any fee to learn and deep into this new technology.
  • Please feel free to get it and add what you want, or you can share the community and your idea by filing a new issue in the repository.
  • This is just my pet project while learning to develop a dApp, so it has remained so buggy. I will improve it when I have free time.
  • I have developed this one by referring to many things somewhere on the internet that I do not remember. Please forgive me if I cannot share references with you.

--

--