How to Create a Todo List App for Web3

Laurentiu Raducu
5 min readMar 4, 2023

--

Photo by Shubham Dhage on Unsplash

Todo list apps are a popular way to organize tasks and manage productivity. With the rise of web3 technology, it’s possible to create a decentralized todo list app that utilizes the power of blockchain to enhance security and privacy. In this article, I’ll guide you through the process of creating a todo list app for web3. Sit back, open your favourite IDE and let’s get started!

Step 1: Choose a Blockchain Platform

The first step in creating a web3 app is to choose a blockchain platform. Ethereum is a popular platform for web3 development due to its flexibility and robust ecosystem. However, other platforms like Binance Smart Chain or Polkadot can also be used. Choose the platform that best fits your needs. Keep in mind that in Ethereum, the language of choice is Solidity, whereas for Polkadot, it’s Rust. For this demo, we are going to use Solidity with the Ethereum ecosystem. To learn more about Solidity, check out my article about creating your own cryptocurrency.

Step 2: Develop a Smart Contract

A smart contract is a self-executing contract with the terms of the agreement between buyer and seller being directly written into lines of code. In the context of a todo list app, the smart contract will handle task creation, deletion, and completion. Use Solidity or another programming language to develop your smart contract.

Here’s an example smart contract for a todo list app:

pragma solidity ^0.8.0;

contract TodoList {
struct Task {
uint id;
string content;
bool completed;
}

mapping(uint => Task) public tasks;
uint public taskCount;

event TaskCreated(uint id, string content, bool completed);
event TaskCompleted(uint id, bool completed);
event TaskDeleted(uint id);

function createTask(string memory _content) public {
taskCount ++;
tasks[taskCount] = Task(taskCount, _content, false);
emit TaskCreated(taskCount, _content, false);
}

function toggleCompleted(uint _id) public {
Task memory _task = tasks[_id];
_task.completed = !_task.completed;
tasks[_id] = _task;
emit TaskCompleted(_id, _task.completed);
}

function deleteTask(uint _id) public {
delete tasks[_id];
emit TaskDeleted(_id);
}
}

This smart contract defines a Task struct and uses a mapping to store tasks. The createTask() function creates a new task and emits a TaskCreated event. The toggleCompleted() function toggles the completed status of a task and emits a TaskCompleted event. The deleteTask() function deletes a task and emits a TaskDeleted event.

Step 3: Build the Front-End

The front-end of your todo list app is what users will interact with. Use web development languages like HTML, CSS, and JavaScript to build the front-end of your app. Additionally, use web3.js or another web3 library to interact with the smart contract from the front-end.

Here’s an example front-end for a todo list app:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Todo List</title>
<script src="https://cdn.jsdelivr.net/npm/ethers@5.0.umd.min.js"></script>
<script src="./js/app.js"></script>
</head>
<body>
<h1>Todo List</h1>
<form onsubmit="createTask(); return false;">
<input type="text" id="taskContent" placeholder="Enter task content...">
<button type="submit">Add Task</button>
</form>
<ul id="taskList"></ul>
</body>
</html>

This front-end defines a form to create tasks and an unordered list to display tasks. The createTask() function calls the createTask() function on the smart contract from the front-end using web3.js. Here’s an example app.js file that interacts with the smart contract:

const provider = new ethers.providers.Web3Provider(web3.currentProvider);
const signer = provider.getSigner();
const contractAddress = '0x123456...';

const abi = [
{
"inputs": [],
"name": "taskCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "_content",
"type": "string"
}
],
"name": "createTask",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_id",
"type": "uint256"
}
],
"name": "deleteTask",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_id",
"type": "uint256"
}
],
"name": "toggleCompleted",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "tasks",
"outputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "string",
"name": "content",
"type": "string"
},
{
"internalType": "bool",
"name": "completed",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_id",
"type": "uint256"
}
],
"name": "getTask",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "string",
"name": "",
"type": "string"
},
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_id",
"type": "uint256"
},
{
"internalType": "string",
"name": "_content",
"type": "string"
}
],
"name": "updateTaskContent",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
];

const contract = new ethers.Contract(contractAddress, abi, signer);

function createTask() {
const taskContent = document.getElementById('taskContent').value;
contract.createTask(taskContent).(function() {
const taskList = document.getElementById('taskList');
const taskTemplate = document.getElementById('taskTemplate');

contract.on('TaskCreated', (id, content, completed) => {
const taskNode = taskTemplate.cloneNode(true);
taskNode.getElementsByClassName('content')[0].innerText = content;
taskNode.getElementsByClassName('toggle')[0].setAttribute('data-id', id);
taskNode.getElementsByClassName('delete')[0].setAttribute('data-id', id);
taskList.appendChild(taskNode);
});

contract.on('TaskCompleted', (id, completed) => {
const taskNode = document.querySelector(`[data-id="${id}"]`);
taskNode.classList.toggle('completed', completed);
});

contract.on('TaskDeleted', (id) => {
const taskNode = document.querySelector(`[data-id="${id}"]`);
taskNode.remove();
});

taskList.addEventListener('click', async (event) => {
const target = event.target;

if (target.classList.contains('toggle')) {
const id = target.getAttribute('data-id');
await contract.toggleCompleted(id);
}

if (target.classList.contains('delete')) {
const id = target.getAttribute('data-id');
await contract.deleteTask(id);
}
});

async function renderTasks() {
const taskCount = await contract.taskCount();

for (let i = 1; i <= taskCount; i++) {
const [id, content, completed] = await contract.getTask(i);

const taskNode = taskTemplate.cloneNode(true);
taskNode.getElementsByClassName('content')[0].innerText = content;
taskNode.getElementsByClassName('toggle')[0].setAttribute('data-id', id);
taskNode.getElementsByClassName('delete')[0].setAttribute('data-id', id);
taskNode.classList.toggle('completed', completed);
taskList.appendChild(taskNode);
}
}

renderTasks();
})
();

This JavaScript code sets up event listeners for creating, completing, and deleting tasks, and uses the web3.js library to interact with the smart contract. It also renders the existing tasks from the smart contract when the page loads.

That’s it! You now have a todo list app that uses a smart contract on the Ethereum blockchain to store and manage tasks. Users can create new tasks, mark them as completed, and delete them. The app is fully decentralized and secure, and users can be confident that their data is stored on a tamper-proof blockchain. Basically, this is just a simple example of what’s possible with web3 development. There are many other features and technologies that you can use to build more complex and sophisticated decentralized apps. But hopefully this tutorial has given you a good introduction to the basics of web3 development and inspired you to explore this exciting new field further.

--

--

No responses yet