Building a Web3 To-Do List App: A Step-by-Step Guide

laurentiu.raducu

Building a Web3 To-Do List App: A Step-by-Step Guide

Web3 technology is revolutionizing the way we interact with decentralized applications (dApps) on the blockchain. Today I’ll guide you through the process of developing and publishing a simple yet functional Web3 to-do list app using Solidity, the popular programming language for smart contracts on the Ethereum blockchain.

Prerequisites

Before we dive into the code, make sure you have the following tools installed:

  1. Node.js (v14.x or later)
  2. Truffle (v5.x or later)
  3. Ganache (for a local Ethereum blockchain)
  4. MetaMask browser extension (for interacting with the dApp)

Setting up the Project

Create a new directory for your project and navigate to it using the command line. Then, initialize a new Truffle project by running:

mkdir web3-todo-list
cd web3-todo-list
truffle init

Creating the Smart Contract

In the “contracts” directory, create a new file named “ToDoList.sol” and add the following code:

pragma solidity ^0.8.0;

contract ToDoList {
    uint256 public taskCount = 0;

    struct Task {
        uint256 id;
        string content;
        bool completed;
    }

    mapping(uint256 => Task) public tasks;

    event TaskCreated(uint256 id, string content, bool completed);

    function createTask(string memory _content) public {
        taskCount++;
        tasks[taskCount] = Task(taskCount, _content, false);
        emit TaskCreated(taskCount, _content, false);
    }
    event TaskToggled(uint256 id, bool completed);

    function toggleTask(uint256 _id) public {
        Task memory _task = tasks[_id];
        _task.completed = !_task.completed;
        tasks[_id] = _task;
        emit TaskToggled(_id, _task.completed);
    }
}

This smart contract defines a simple to-do list with a `Task` struct and a mapping to store tasks. We have also implemented two functions: `createTask` to add new tasks and `toggleTask` to toggle their completion status. Additionally, we emit events when tasks are created and toggled.

Compiling the Smart Contract

In the “migrations” directory, create a new file named “2_deploy_contracts.js” and add the following code:

const ToDoList = artifacts.require("ToDoList"); 
module.exports = function (deployer) { deployer.deploy(ToDoList); };

This migration file ensures that our smart contract is deployed to the local blockchain. To compile the smart contract, run the following command:

truffle compile

Deploying the Smart Contract

First, start Ganache to launch your local blockchain. Then, in your project directory, run:

truffle migrate

This command will deploy the smart contract to your local Ethereum blockchain.

Building the Frontend

Create a new “src” directory in your project folder and add an “index.html” file with the following content:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web3 To-Do List</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">

</head>
<body>
    <div class="container">
        <h1 class="text-center">Web3 To-Do List</h1>
        <form id="createTaskForm">
            <div class="form-group">
                <label for="taskContent">Task</label>
                <input type="text" class="form-control" id="taskContent" placeholder="Enter new task">
            </div>
            <button type="submit" class="btn btn-primary">Add Task</button>
        </form>
        <ul id="taskList" class="list-group mt-4"></ul>
    </div>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@truffle/contract@4.3.25/dist/truffle-contract.min.js"></script>
    <script src="js/app.js"></script>
</body>
</html>

Next, create a “js” directory inside the “src” folder and add an “app.js” file with the following content:

// Import the contract ABI (Application Binary Interface)
import contractArtifact from '../../build/contracts/ToDoList.json';

document.addEventListener('DOMContentLoaded', async () => {
const web3 = new Web3(Web3.givenProvider);
const networkId = await web3.eth.net.getId();
const contractAddress = contractArtifact.networks[networkId].address;
const toDoListContract = new web3.eth.Contract(contractArtifact.abi, contractAddress);
const accounts = await web3.eth.getAccounts();

const createTaskForm = document.getElementById('createTaskForm');
const taskList = document.getElementById('taskList');

createTaskForm.addEventListener('submit', async (e) => {
    e.preventDefault();
    const taskContent = document.getElementById('taskContent').value;
    await toDoListContract.methods.createTask(taskContent).send({ from: accounts[0] });
    updateTaskList();
});

const updateTaskList = async () => {
    taskList.innerHTML = '';
    const taskCount = await toDoListContract.methods.taskCount().call();
    for (let i = 1; i <= taskCount; i++) {
        const task = await toDoListContract.methods.tasks(i).call();
        const taskElement = document.createElement('li');
        taskElement.className = 'list-group-item';
        taskElement.innerHTML = `<input type="checkbox" class="mr-2" ${task.completed ? 'checked' : ''}><span>${task.content}</span>`;
        taskElement.addEventListener('click', async () => {
            await toDoListContract.methods.toggleTask(i).send({ from: accounts[0]});
updateTaskList();
});
taskList.appendChild(taskElement);
}
};

// Initialize task list
updateTaskList();

// Connect to MetaMask
if (window.ethereum) {
    window.ethereum.on('accountsChanged', async () => {
        accounts = await web3.eth.getAccounts();
        updateTaskList();
    });

    window.ethereum.on('chainChanged', async () => {
        networkId = await web3.eth.net.getId();
        updateTaskList();
    });
}});

This JavaScript file handles the interaction between the front-end and the smart contract using Web3.js. It sets up event listeners for creating tasks and toggling their completion status. Moreover, it listens for account and network changes in MetaMask.

Serving the dApp

To serve the dApp, you can use any local development server, such as http-server. If you don’t have it installed, you can install it globally using npm:

npm install -g http-server

Then, in your project directory, run the following command:

http-server ./src

Open your browser and navigate to “http://localhost:8080” (or the appropriate port). Connect MetaMask to your local Ethereum network (Ganache) and start adding tasks to your Web3 to-do list!


In this tutorial, we built a simple Web3 to-do list app using Solidity, Truffle, Ganache, and MetaMask. This basic dApp can serve as a starting point for developing more complex, decentralized applications on the Ethereum blockchain. Happy coding!