DEV Community

Cover image for Building a Decentralized Todo List DApp in React and Solidity
Scofield Idehen
Scofield Idehen

Posted on • Originally published at

Building a Decentralized Todo List DApp in React and Solidity

Blockchain technology and Ethereum's innovation with Turing-complete smart contracts provide the ability to develop serverless applications with backend code running on a decentralized network.

This removes reliance on centralized servers and builds censorship resistance into the core of our apps.

Web3 Vs Web2: The Untold Lies of Blockchain Technology

Learn why we feel are sham find out in this guide: Web3 Vs Web2: The Untold Lies of Blockchain Technology

In this comprehensive guide, we will demonstrate these strengths by architecting the backend smart contract for a blockchain-powered Todo List DApp.

Our tech stack includes:

  • Solidity - For implementing self-executing logic on Ethereum
  • Hardhat - Local Ethereum development and testing framework
  • React - For building a sleek frontend UI

By the end, you will gain practical skills in crafting complex smart contracts ready for real-world usage.

Development Environment Setup

The Hardhat framework sets up an integrated local environment for rapidly building Ethereum-based decentralized applications. Hardhat includes utilities for compiling, deploying, testing, and debugging Solidity smart contracts - great for boosting developer productivity.

First install Hardhat:

npm install --save-dev hardhat 
Enter fullscreen mode Exit fullscreen mode

Then initialize a sample project with:

npx hardhat
Enter fullscreen mode Exit fullscreen mode

Choose the Create a sample project option when prompted to generate a starter kit including a configurable hardhat.config.js file and folders for contracts and tests.

Boost Your Solidity Development Game with VS Code The Ultimate Setup Guide

Boost Your Blockchain Development Game with VS Code The Ultimate Setup Guide

We need additional plugins for enhanced capabilities around Ethers wallet integration and advanced contract testing using the Waffle library:

npm install --save-dev @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai
Enter fullscreen mode Exit fullscreen mode

Ethers.js assists with wallet management used later for deploying contracts while Waffle handles spinning up a testing blockchain network that mimics the Ethereum mainnet.
Finally, connect your Alchemy or Infura API key in hardhat.config.js to deploy to public test networks:

module.exports = {
  solidity: "0.8.4",
  networks: {
    rinkeby: {
      url: `${process.env.ALCHEMY_API_URL}`,  
      accounts: [`0x${process.env.ACCOUNT_PRIVATE_KEY}`]
Enter fullscreen mode Exit fullscreen mode

Smart Contract Design

With our Hardhat-powered development environment set up, let's start coding the smart contract backend!

Inside contracts/TodoList.sol, first structure a Todo data model containing essential fields via a Solidity struct:

struct Todo {
  uint id;
  string text;
  bool completed; 
Enter fullscreen mode Exit fullscreen mode

Two critical aspects of any functional Todo list include:

  1. Persisting added Todo items
  2. Tracking completion status |

Hence our data model stores a unique id, a text description of the actual todo task, and a boolean flag completed denoting whether it's been marked done.

We manage storage via a list declared as:

Todo[] public todos;
Enter fullscreen mode Exit fullscreen mode

With the data foundation in place, we specify key application logic around managing todos in the form of Solidity functions:

createTodo - Insert new Todo into list by pushing to todos array
toggleCompleted - Flip the completed flag to true/false
getTodos - Fetch and return the todos array

Modular design best practices recommend emitting events in response to state changes. This allows detached frontends and analytics engines to conveniently listen for logs instead of directly observing contract storage which consumes higher gas fees.

event TodoCreated(uint indexed id, string text);  

function createTodo(string calldata _text) external {
  todos.push(Todo(_nextTodoId++, _text, false)); 
  emit TodoCreated(_nextTodoId, _text);
Enter fullscreen mode Exit fullscreen mode

Here when a new todo gets created, we emit a TodoCreated event containing the auto-incremented id and text details.

Thoroughly Testing the Contract

With core functionality coded, it is prudent software engineering practice to comprehensively test smart contracts before relying on their correctness.

Waffle provides a testing environment by running a temporary Ethereum node optimized for rapid iteration. Under test/TodoList.spec.js we leverage Waffle's integration with Mocha/Chai for familiar syntax:

describe('TodoList', () => {
  let todoList
  let owner

  beforeEach(async () => {
    // Deploy a new contract instance before each test

    owner = new ethers.Wallets.createRandom()  
    const TodoList = await ethers.getContractFactory('TodoList') 
    todoList = await TodoList.deploy() 

  it('creates new todo', async () => {

    await todoList.connect(owner).createTodo('Get milk')

    const todos = await todoList.getTodos()
    assert.equal(todos.length, 1)


Enter fullscreen mode Exit fullscreen mode

We test critical functionality like creating todos and retrieving them for validation against expected behavior per specifications. These end-to-end integration style tests capture real usage scenarios.

Combined with unit tests and code review, our comprehensive test suite guarantees the TodoList contract works correctly before launch.

Deploying to Public Testnet

Once satisfied with functionality on the local emulated blockchain, we script a pipeline for standing up a live version on the public Rinkeby test network.

Configure Alchemy/Infura endpoints under Hardhat to connect to Rinkeby. Then simply import helpers and deploy in scripts/deploy.js:

async function main() {

  const TodoList = await ethers.getContractFactory('TodoList')

  const todoList = await TodoList.deploy()

  await todoList.deployed()

  console.log('TodoList deployed to: ', todoList.address)


Enter fullscreen mode Exit fullscreen mode

Run via npx hardhat run scripts/deploy.js --network rinkeby.
The console prints the deployed contract address on success. Share this address with the DApp frontend to enable Web3 connectivity.

With an active instance now running on public infrastructure outside our proprietary control, the Todo list inherits blockchain's censorship resistance and persistent availability guarantees through decentralization.


In this guide, we built a full-fledged backend for a decentralized Todo list DApp showcasing concepts like:

  • Spinning up a Hardhat-powered dev environment
  • Structuring data models with Solidity
  • Developing core create, read, update contract functions
  • Writing comprehensive integration test suites with Waffle
  • Scripting deployment pipelines for publishing on Ethereum's public testnets

Composing the backend in this manner enables us to realize the Web3 vision of crafting resilient software immune from centralized failure points.

Equipped with these learnings, integrating any frontend such as React via web3 libraries like Ethers.js can now connect users to this serverless data layer living permanently on the blockchain.

This is the first section, in the next section we are going to dive into deployment and testing our contract.

If you like our work and want to help us continue dropping content like this, buy us a cup of coffee.

If you find this post exciting, find more exciting posts on Learnhub Blog; we write everything tech from Cloud computing to Frontend Dev, Cybersecurity, AI, and Blockchain.


Top comments (2)

9opsec profile image

Is there a Github repo that has the full code?

scofieldidehen profile image
Scofield Idehen

Let me add it.