Building a Decentralized Todo List DApp in React and Solidity

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 Web3 is a sham. Find out in this guide: Web3 Vs Web2: The Untold Lies of Blockchain Technology.

This comprehensive guide 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 for real-world use.

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

Then initialize a sample project with:

npx hardhat

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

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}`]
    },
  }
}

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; 
}

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;

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);
}

Here, when a new todo gets created, we emit a TodoCreated an 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)

  })

})

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 locally 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)

}

main()

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.

Conclusion

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, and update contract functions

  • Writing comprehensive integration test suites with Waffle

  • Scripting deployment pipelines for publishing on Ethereum's public testnets

Composing the backend lets us 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.

Resource