Building a dApp with NextJS + Solidity + Web3.js

Building a dApp with NextJS + Solidity + Web3.js

In this tutorial, we'll create a decentralized to-do list application (dApp) using NextJS for the front-end, Solidity for smart contracts, Web3.js for interacting with the blockchain, and Alchemy for connecting to Ethereum. We will also use Sepolia, an Ethereum testnet, to deploy and test our application without using real ether (ETH).

Definitions

  1. dApp (Decentralized Application): A dApp runs on a decentralized network, like a blockchain. Unlike traditional applications, dApps do not have a central point of control and use smart contracts to manage application logic securely and transparently.
  2. Front-end: The front-end is the visible part of the application that users interact with directly. It includes user interfaces (UIs) and is usually built with HTML, CSS, and JavaScript. In this project, we use Next.js for the front-end.
  3. Backend: The backend is the part of the application that handles logic, calculations, and interactions with the database. In a dApp, part of the backend logic is handled by smart contracts on the blockchain. We will use Hardhat to develop and test our backend in Solidity.
  4. JavaScript: JavaScript is a programming language used to develop both the front-end and parts of the back-end of web applications. In this project, we'll use JavaScript to create and manage our front-end with Next.js and interact with the blockchain via web3.js.
  5. Web3.js: Web3.js is a JavaScript library that interacts with the Ethereum blockchain. It allows web applications to send transactions, read data, and interact with smart contracts.
  6. Hardhat: Hardhat is an Ethereum development environment for compiling, deploying, testing, and debugging smart contracts. It provides powerful tools to simplify the process.
  7. Alchemy: Alchemy is a blockchain development platform that provides tools and infrastructure for interacting with the Ethereum blockchain. It offers enhanced APIs and a superior node infrastructure to make it easier to develop and deploy dApps.
  8. Sepolia: Sepolia is an Ethereum testnet used to deploy and test smart contracts without using real ether (ETH). Testnets like Sepolia allow developers to test their applications in a simulated environment.

Overview of the steps

  1. Smart Contract Development:
    • Define a smart contract to manage to-do list items.
    • Compile and deploy the contract.
  2. Front-end Next.js :
    • Set up a Next.js project.
    • Connect the front-end to the Ethereum network using Web3.js.
    • Implement the functionality to create, read, and manage to-do list items.
  3. Deploying the Front-end locally:
    • Run the application locally to test integration with the smart contract.

1. Smart Contract Development

Backend Project Configuration

To get started, we need to create a Hardhat project. Open your terminal, create or navigate to a new, empty directory, and run the following command:

npm install ethers hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers @openzeppelin/contracts dotenv
Explanation: This command installs several essential packages for developing and testing smart contracts with Hardhat, including interacting with Ethereum, for local development, and for handling environment variables.ethers hardhat dotenv

Next, initialize a new development environment with the Hardhat command:

npx hardhat
Explanation: This command initializes a new Hardhat project in the current directory. Follow the instructions to create a basic project.

After running this command, select the "Create a basic sample project" option and for the other options, press "yes".

You should see the following files and folders created in your root directory:

  • hardhat.config.js: Contains the Hardhat configuration.
  • scripts: Contains a script named sample-script.js that will deploy your smart contract when executed.
  • test: Contains a sample test script.
  • contracts: Contains an example of a Solidity smart contract.

Get an Ethereum API Key with Alchemy

To create an API, follow these steps:

Image description
  1. Sign up for Alchemy.
  2. Go to the Dashboard.
  3. Select the "Sepolia" network.
  4. Copy the HTTP key after creating the application on Alchemy.

Create a file in the root of your project and store this HTTP key as follows:.env

ALCHEMY_SEPOLIA_URL="YOUR_KEY_HTTP_ALCHEMY"
Explanation: The file stores environment variables in a secure way, such as the URL of your Alchemy API, so that they are not exposed directly in your source code.env

Getting your account private key from Metamask

Metamask is a cryptocurrency wallet that allows you to interact with the Ethereum blockchain. This private key is required for our smart contract deployment script to run it and take gas fees to ether from our wallet.

  1. Click on the profile icon.
  2. Select the account you want to export.
  3. Click on "Account Details".
  4. Click on "Export Private Key" and enter your password.
  5. Copy and paste this private key into your .env
Image description

ACCOUNT_PRIVATE_KEY="YOUR_PRIVATE_KEY"

.env

Explanation: The private key of your Metamask account is required to sign and send transactions on the Ethereum network. Never share this key and keep it secure.

Obtaining test tokens for Sepolia

To test your smart contracts on Sepolia, you will need test tokens. You can get test tokens (ETH) from testnet faucets.

Image description
Explanation: Testnet faucets provide you with free ETH that you can use to pay gas fees when deploying and testing your smart contracts on testnets like Sepolia.

Update hardhat.config.js

Update the configuration in hardhat.config.js as follows:

require("@nomiclabs/hardhat-waffle");
require('dotenv').config();

module.exports = {
  solidity: "0.8.4",
  networks: {
    sepolia: {
      url: process.env.ALCHEMY_SEPOLIA_URL,
      accounts: [process.env.ACCOUNT_PRIVATE_KEY]
    }
  }
};

hardhat.config.js

Explanation: This configuration allows Hardhat to connect to the Sepolia network via Alchemy using your API URL and Metamask account private key to deploy and test smart contracts.

Creating Smart Contract Logic

Create a new file in the contracts directory named. Add the following code:TaskContract.sol

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.4;

contract TaskContract {

    event AddTask(address recipient, uint taskId);
    event DeleteTask(uint taskId, bool isDeleted);

    struct Task {
        uint id;
        address username;
        string taskText;
        bool isDeleted;
    }

    Task[] private tasks;
    mapping(uint256 => address) taskToOwner;

    function addTask(string memory taskText, bool isDeleted) external {
        uint taskId = tasks.length;
        tasks.push(Task(taskId, msg.sender, taskText, isDeleted));
        taskToOwner[taskId] = msg.sender;
        emit AddTask(msg.sender, taskId);
    }

    function getMyTasks() external view returns (Task[] memory) {
        Task[] memory temporary = new Task[](tasks.length);
        uint counter = 0;
        for (uint i = 0; i < tasks.length; i++) {
            if (taskToOwner[i] == msg.sender && tasks[i].isDeleted == false) {
                temporary[counter] = tasks[i];
                counter++;
            }
        }

        Task[] memory result = new Task[](counter);
        for (uint i = 0; i < counter; i++) {
            result[i] = temporary[i];
        }
        return result;
    }

    function deleteTask(uint taskId, bool isDeleted) external {
        if (taskToOwner[taskId] == msg.sender) {
            tasks[taskId].isDeleted = isDeleted;
            emit DeleteTask(taskId

, isDeleted);
        }
    }
}

TaskContract.sol


Explanation: This smart contract manages a list of tasks. It allows you to add, retrieve, and delete tasks. Tasks are associated with the address of the user who creates them.

Testing Smart Contracts

To test the smart contract, create an open and update it with the following code:test/TaskContractTest.js

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Task Contract", function () {
  let TaskContract;
  let taskContract;
  let owner;

  const NUM_TOTAL_TASKS = 5;
  let totalTasks;

  beforeEach(async function () {
    TaskContract = await ethers.getContractFactory("TaskContract");
    [owner] = await ethers.getSigners();
    taskContract = await TaskContract.deploy();

    totalTasks = [];
    for (let i = 0; i < NUM_TOTAL_TASKS; i++) {
      let task = {
        'taskText': 'Task number: ' + i,
        'isDeleted': false
      };
      await taskContract.addTask(task.taskText, task.isDeleted);
      totalTasks.push(task);
    }
  });

  describe("Add Task", function () {
    it("should emit AddTask event", async function () {
      let task = {
        'taskText': 'New Task',
        'isDeleted': false
      };
      await expect(await taskContract.addTask(task.taskText, task.isDeleted))
        .to.emit(taskContract, 'AddTask')
        .withArgs(owner.address, NUM_TOTAL_TASKS);
    });
  });

  describe("Get All Tasks", function () {
    it("should return the correct number of total tasks", async function () {
      const tasksFromChain = await taskContract.getMyTasks();
      expect(tasksFromChain.length).to.equal(NUM_TOTAL_TASKS);
    });
  });

  describe("Delete Task", function () {
    it("should emit delete task event", async function () {
      const TASK_ID = 0;
      const TASK_DELETED = true;

      await expect(taskContract.deleteTask(TASK_ID, TASK_DELETED))
        .to.emit(taskContract, 'DeleteTask')
        .withArgs(TASK_ID, TASK_DELETED);
    });
  });
});

Explanation: These tests verify that the smart contract is working properly by adding, retrieval, and removing tasks. Tests use for assertions and to interact with the contractchai ethers

Run the unit tests with the following command:

npx hardhat test
Explanation: This command runs the tests defined in the directory to verify that the smart contract is working as expected test

Deploying the Smart Contract on the SEPOLIA network

To deploy the contract, create a file in the folder and add the following contents:deploy.js in folder scripts

const main = async () => {
  const contractFactory = await ethers.getContractFactory('TaskContract');
  const contract = await contractFactory.deploy();
  await contract.deployed();

  console.log("Contract deployed to:", contract.address);
};

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.log(error);
    process.exit(1);
  }
};

runMain();

Explanation: This script deploys the smart contract on the Sepolia network using Hardhat. It displays the address of the deployed contract after the deployment is complete.

Run the script with the following command:

npx hardhat run scripts/deploy.js --network sepolia
Explanation: This command runs the deployment script on the Sepolia network, using the configuration information provided in hardhat.config.js

2. Front-end Next.js

Initialize the Next.js project

npx create-next-app todo-dapp-frontend
cd todo-dapp-frontend
Explanation: This command creates a new Next.js project and navigates to the project directory.

Install the necessary dependencies

Install Web3.js and other required dependencies for the frontend:

npm install web3 axios @emotion/react @emotion/styled @mui/icons-material @mui/material react-toastify
Explanation: This command installs the libraries needed to build the user interface, manage notifications, and interact with the blockchain.

Configuration du Frontend

Creating the Application Structure
  1. SRC folder and pages: In the folder, create a folder with a .src pages.js

Here is the content of the file:pages.js

"use client";
import { React, useState, useEffect } from 'react';
import { Container, Box, Typography, TextField, Button, AppBar, Toolbar, IconButton } from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import TaskTable from '../components/Task';
import Web3 from 'web3';
import { TaskContractAddress } from '../config';
import TaskAbi from '../utils/TaskContract.json';

export default function Home() {
  const [tasks, setTasks] = useState([]);
  const [input, setInput] = useState('');
  const [currentAccount, setCurrentAccount] = useState('');
  const [correctNetwork, setCorrectNetwork] = useState(false);

  // Fonction pour récupérer toutes les tâches
  const getAllTasks = async () => {
    try {
      const { ethereum } = window;

      if (ethereum) {
        const web3 = new Web3(ethereum);
        const TaskContract = new web3.eth.Contract(TaskAbi.abi, TaskContractAddress);

        let allTasks = await TaskContract.methods.getMyTasks().call();
        allTasks = allTasks.map(task => ({
          id: task.id.toString(),
          taskText: task.taskText,
          wallet: task.wallet,
          taskDate: new Date(task.taskDate * 1000).toLocaleDateString(),
          taskTime: new Date(task.taskDate * 1000).toLocaleTimeString(),
          isDeleted: task.isDeleted
        }));
        setTasks(allTasks);
      } else {
        console.log("Ethereum object doesn't exist");
      }
    } catch (error) {
      console.log(error);
    }
  };

  useEffect(() => {
    getAllTasks();
  }, []);

  // Fonction pour connecter le portefeuille Metamask
  const connectWallet = async () => {
    try {
      const { ethereum } = window;

      if (!ethereum) {
        toast.error('Metamask not detected');
        return;
      }
      let chainId = await ethereum.request({ method: 'eth_chainId' });
      console.log('Connected to chain:' + chainId);

      const sepoliaChainId = '0xaa36a7';

      if (chainId !== sepoliaChainId) {
        alert('You are not connected to the Sepolia Testnet!');
        return;
      } else {
        setCorrectNetwork(true);
      }

      const accounts = await ethereum.request({ method: 'eth_requestAccounts' });

      console.log('Found account', accounts[0]);
      setCurrentAccount(accounts[0]);
      toast.success('Wallet connected');
    } catch (error) {
      console.log('Error connecting to metamask', error);
    }
  };

  // Fonction pour ajouter une tâche
  const addTask = async (e) => {
    e.preventDefault();

    const task = {
      'id': tasks.length + 1,
      'taskText': input,
      'isDeleted': false
    };

    try {
      const { ethereum } = window;

      if (ethereum) {
        const web3 = new Web3(ethereum);
        const TaskContract = new web3.eth.Contract(TaskAbi.abi, TaskContractAddress);

        await TaskContract.methods.addTask(task.taskText, task.isDeleted).send({ from: currentAccount });
        setTasks([...tasks, task]);
        setInput('');
        toast.success('Task added');
      } else {
        console.log("Ethereum object doesn't exist!");
      }
    } catch (error) {
      console.log("Error submitting new Task", error);
      toast.error('Error adding task');
    }
  };

  // Fonction pour supprimer une tâche
  const deleteTask = async (taskId) => {
    try {
      const { ethereum } = window;

      if (ethereum) {
        const web3 = new Web3(ethereum);
        const TaskContract = new web3.eth.Contract(TaskAbi.abi, TaskContractAddress);

        await TaskContract.methods.deleteTask(taskId, true).send({ from: currentAccount });

        const updatedTasks = tasks.map(task => 
          task.id === taskId.toString() ? { ...task, isDeleted: true } : task
        );

        setTasks(updatedTasks);
        toast.success('Task deleted');
      } else {
        console.log("Ethereum object doesn't exist");
      }
    } catch (error) {
      console.log(error);
      toast.error('Error deleting task');
    }
  };


  return (
    <div>
      <ToastContainer />
      <AppBar position="static">
        <Toolbar>
          <IconButton edge="start" color="inherit" aria-label="

menu">
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" style={{ flexGrow: 1 }}>
            TodoList DApp
          </Typography>
          {currentAccount === '' ? (
            <Button color="inherit" onClick={connectWallet}>Connect Wallet</Button>
          ) : (
            <Typography variant="h6">{currentAccount}</Typography>
          )}
        </Toolbar>
      </AppBar>
      <Container>
        {currentAccount === '' ? (
          <Box display="flex" justifyContent="center" alignItems="center" height="100vh">
            <Button variant="contained" color="primary" size="large" onClick={connectWallet}>
              Connect Wallet
            </Button>
          </Box>
        ) : correctNetwork ? (
          <Box mt={4}>
            <Typography variant="h4" align="center" gutterBottom> TodoList DApp </Typography>
            <Box component="form" onSubmit={addTask} display="flex" justifyContent="center" mb={2}>
              <TextField 
                id="outlined-basic" 
                label="New Task" 
                variant="outlined" 
                value={input}
                onChange={e => setInput(e.target.value)}
                style={{ marginRight: 8 }}
              />
              <Button variant="contained" color="primary" type="submit">Add Task</Button>
            </Box>
            <TaskTable tasks={tasks} onDelete={deleteTask} />
          </Box>
        ) : (
          <Box display="flex" flexDirection="column" alignItems="center" mt={4}>
            <Typography variant="h6" color="error">Please connect to the Sepolia Testnet</Typography>
            <Typography variant="subtitle1">and reload the page</Typography>
          </Box>
        )}
      </Container>
    </div>
  );
}
Explanation: This React component represents the main page of our dApp. It allows you to connect the Metamask wallet, retrieve and display tasks from the blockchain, and manage the addition and deletion of tasks. is used to retrieve tasks when the page loadsuseEffect
  1. Components folder: Create a folder and add the src/components/Task.js

Here is the content of the file:Task.js

import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper } from '@mui/material';
import DeleteIcon from '@mui/icons-material/Delete';
import './Task.css';

const TaskTable = ({ tasks, onDelete }) => {
    return (
        <TableContainer component={Paper} className="task-table">
            <Table>
                <TableHead>
                    <TableRow>
                        <TableCell>ID</TableCell>
                        <TableCell>Task</TableCell>
                        <TableCell>Action</TableCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                    {tasks.map((task) => (
                        <TableRow key={task.id}>
                            <TableCell>{task.id.toString()}</TableCell>
                            <TableCell>{task.taskText}</TableCell>
                            <TableCell>
                                <DeleteIcon fontSize="large" style={{ opacity: 0.7, cursor: 'pointer' }} onClick={() => onDelete(task.id)} />
                            </TableCell>
                        </TableRow>
                    ))}
                </TableBody>
            </Table>
        </TableContainer>
    );
};

export default TaskTable;
Explanation: This component represents the task board. It displays each task in a table row and includes a delete button for each task. The button calls the passed function as a prop when clicked onDelete
  1. CSS file: Add a file in .Task.csscomponents

Here is the content of the file:Task.css

/* Task.css */

.task-table {
    margin-top: 25px;
}

.task-table th {
    background-color: #f5f5f5;
    font-weight: bold;
}

.task-table td, .task-table th {
    padding: 12px 15px;
}

.todo__list {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 20px;
    border-bottom: 1px solid #ddd;
    background-color: #ffffff;
    transition: background-color 0.3s ease;
    border-radius: 4px;
    margin-bottom: 10px;
}

.todo__list:last-child {
    border-bottom: none;
}

.todo__list:hover {
    background-color: #f1f1f1;
}

.MuiSvgIcon-root {
    cursor: pointer;
    transition: opacity 0.3s, transform 0.3s;
    opacity: 0.7;
}

.MuiSvgIcon-root:hover {
    opacity: 1;
    transform: scale(1.1);
    color: #ee0808;
}
Explanation: This CSS file styles the task board and delete button. It sets margins, background colors, and transitions to enhance the visual appearance of the to-do list.
  1. Utils folder: Create a folder in and add the recovered file at the backend in the folder.utilssrcTaskContract.jsonartifacts/contracts/TaskContract

Here is the content of the file:TaskContract.json

{
    "_format": "hh-sol-artifact-1",
    "contractName": "TaskContract",
    "sourceName": "contracts/TaskContract.sol",
    "abi": [
      {
        "anonymous": false,
        "inputs": [
          {
            "indexed": false,
            "internalType": "address",
            "name": "recipient",
            "type": "address"
          },
          {
            "indexed": false,
            "internalType": "uint256",
            "name": "taskId",
            "type": "uint256"
          }
        ],
        "name": "AddTask",
        "type": "event"
      },
      {
        "anonymous": false,
        "inputs": [
          {
            "indexed": false,
            "internalType": "uint256",
            "name": "taskId",
            "type": "uint256"
          },
          {
            "indexed": false,
            "internalType": "bool",
            "name": "isDeleted",
            "type": "bool"
          }
        ],
        "name": "DeleteTask",
        "type": "event"
      },
      {
        "inputs": [
          {
            "internalType": "string",
            "name": "taskText",
            "type": "string"
          },
          {
            "internalType": "bool",
            "name": "isDeleted",
            "type": "bool"
          }
        ],
        "name": "addTask",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "inputs": [
          {
            "internalType": "uint256",
            "name": "taskId",
            "type": "uint256"
          },
          {
            "internalType": "bool",
            "name": "isDeleted",
            "type": "bool"
          }
        ],
        "name": "deleteTask",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "inputs": [],
        "name": "getMyTasks",
        "outputs": [
          {
            "components": [
              {
                "internalType": "uint256",
                "name": "id",
                "type": "uint256"
              },
              {
                "internalType": "address",
                "name": "username",
                "type": "address"
              },
              {
                "internalType": "string",
                "name": "taskText",
                "type": "string"
              },
              {
                "internalType": "bool",
                "name": "isDeleted",
                "type": "bool"
              }
            ],
            "internalType": "struct TaskContract.Task[]",
            "name": "",
            "type": "tuple[]"
          }
        ],
        "stateMutability": "view",
        "type": "function"
      }
    ],
    "bytecode": "0x608060405234801561001057600080fd5b50610e62806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806320df4581146100465780636e13f81814610062578063aaba290714610080575b600080fd5b610060600480360381019061005b91906108fc565b61009c565b005b61006a610229565b6040516100779190610b34565b60405180910390f35b61009a60048036038101906100959190610950565b61067f565b005b600080805490509050600060405180608001604052808381526020013373ffffffffffffffffffffffffffffffffffffffff168152602001858152602001841515815250908060018154018082558091505060019003906000526020600020906004020160009091909190915060008201518160000155602082

01518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040820151816002019080519060200190610176929190610787565b5060608201518160030160006101000a81548160ff0219169083151502179055505050336001600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f1f54e1ba1832d428fbd7e7792beaf62b1fc5a382c207ffd614209c1413e94fda338260405161021c929190610b0b565b60405180910390a1505050565b60606000808054905067ffffffffffffffff811115610271577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280602002602001820160405280156102aa57816020015b61029761080d565b81526020019060019003908161028f5790505b5090506000805b600080549050811015610553573373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614801561038857506000151560008281548110610365577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b906000526020600020906004020160030160009054906101000a900460ff161515145b1561054057600081815481106103c7577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000209060040201604051806080016040529081600082015481526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160028201805461045090610cb4565b80601f016020809104026020016040519081016040528092919081815260200182805461047c90610cb4565b80156104c95780601f1061049e576101008083540402835291602001916104c9565b820191906000526020600020905b8154815290600101906020018083116104ac57829003601f168201915b505050505081526020016003820160009054906101000a900460ff161515151581525050838381518110610526577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020026020010181905250818061053c90610d17565b9250505b808061054b90610d17565b9150506102b1565b5060008167ffffffffffffffff811115610596577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280602002602001820160405280156105cf57816020015b6105bc61080d565b8152602001906001900390816105b45790505b50905060005b8281101561067557838181518110610616577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020026020010151828281518110610657577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020026020010181905250808061066d90610d17565b9150506105d5565b5080935050505090565b3373ffffffffffffffffffffffffffffffffffffffff166001600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610783578060008381548110610721577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b906000526020600020906004020160030160006101000a81548160ff0219169083151502179055507ff88a35c3d2016c409a46570b994a17b408dbc83c14a03f521512d50b85386d06828260405161077a929190610b56565b60405180910390a15b5050565b82805461079390610cb4565b90600052602060002090601f0160209004810192826107b557600085556107fc565b82601f106107ce57805160ff19168380011785556107fc565b828001600101855582156107fc579182015b828111156107fb5782518255916020019190600101906107e0565b5b509050610809919061084d565b5090565b604051806080016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001606081526020016000151581525090565b5b8082111561086657600081600090555060010161084e565b5090565b600061087d61087884610ba4565b610b7f565b90508281526020810184848401111561089557600080fd5b6108a0848285610c72565b509392505050565b6000813590506108b781610dfe565b92915050565b600082601f8301126108ce57600080fd5b81356108de84826020860161086a565b91505092915050565b6000813590506108f681610e15565b92915050565b6000806040838503121561090f57600080fd5b600083013567ffffffffffffffff81111561092957600080fd5b610935858286016108bd565b9250506020610946858286016108a8565b9150509250929050565b6000806040838503121561096357600080fd5b6000610971858286016108e7565b9250506020610982858286016108a8565b9150509250929050565b60006109988383610a8a565b905092915050565b6109a981610c2a565b82525050565b6109b881610c2a565b82525050565b60006109c982610be5565b6109d38185610c08565b9350836020820285016109e585610bd5565b8060005b85811015610a215784840389528151610a02858261098c565b9450610a0d83610bfb565b925060208a019950506001810190506109e9565b50829750879550505050505092915050565b610a3c81610c3c565b82525050565b610a4b81610c3c565b82525050565b6000610a5c82610bf0565b610a668185610c19565b9350610a76818560208601610c81565b610a7f81610ded565b840191505092915050565b6000608083016000830151610aa26000860182610aed565b506020830151610ab560208601826109a0565b5060408301518482036040860152610acd8282610a51565b9150506060830151610ae26060860182610a33565b508091505092915050565b610af681610c68565b82525050565b610b0581610c68565b82525050565b6000604082019050610b2060008301856109af565b610b2d6020830184610afc565b9392505050565b60006020820190508181036000830152610b4e81846109be565b905092915050565b6000604082019050610b6b6000830185610afc565b610b786020830184610a42565b9392505050565b6000610b89610b9a565b9050610b958282610ce6565b919050565b6000604051905090565b600067ffffffffffffffff821115610bbf57610bbe610dbe565b5b610bc882610ded565b9050602081019050919050565b600081905060208201905

0919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b600082825260208201905092915050565b600082825260208201905092915050565b6000610c3582610c48565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b83811015610c9f578082015181840152602081019050610c84565b83811115610cae576000848401525b50505050565b60006002820490506001821680610ccc57607f821691505b60208210811415610ce057610cdf610d8f565b5b50919050565b610cef82610ded565b810181811067ffffffffffffffff82111715610d0e57610d0d610dbe565b5b80604052505050565b6000610d2282610c68565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610d5557610d54610d60565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b610e0781610c3c565b8114610e1257600080fd5b50565b610e1e81610c68565b8114610e2957600080fd5b5056fea26469706673582212209cf2120d1c2331a546dc67e1b144e55ded28e1e2a9bbcd5277ea9cf6eaec679464736f6c63430008040033",
    "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c806320df4581146100465780636e13f81814610062578063aaba290714610080575b600080fd5b610060600480360381019061005b91906108fc565b61009c565b005b61006a610229565b6040516100779190610b34565b60405180910390f35b61009a60048036038101906100959190610950565b61067f565b005b600080805490509050600060405180608001604052808381526020013373ffffffffffffffffffffffffffffffffffffffff16815260200185815260200184151581525090806001815401808255809150506001900390600052602060002090600402016000909190919091506000820151816000015560208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040820151816002019080519060200190610176929190610787565b5060608201518160030160006101000a81548160ff0219169083151502179055505050336001600083815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f1f54e1ba1832d428fbd7e7792beaf62b1fc5a382c207ffd614209c1413e94fda338260405161021c929190610b0b565b60405180910390a1505050565b60606000808054905067ffffffffffffffff811115610271577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280602002602001820160405280156102aa57816020015b61029761080d565b81526020019060019003908161028f5790505b5090506000805b600080549050811015610553573373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614801561038857506000151560008281548110610365577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b906000526020600020906004020160030160009054906101000a900460ff161515145b1561054057600081815481106103c7577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000209060040201604051806080016040529081600082015481526020016001820160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160028201805461045090610cb4565b80601f016020809104026020016040519081016040528092919081815260200182805461047c90610cb4565b80156104c95780601f1061049e576101008083540402835291602001916104c9565b820191906000526020600020905b8154815290600101906020018083116104ac57829003601f168201915b505050505081526020016003820160009054906101000a900460ff161515151581525050838381518110610526577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020026020010181905250818061053c90610d17565b9250505b808061054b90610d17565b9150506102b1565b5060008167ffffffffffffffff811115610596577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040519080825280602002602001820160405280156105cf57816020015b6105bc61080d565b8152602001906001900390816105b45790505b50905060005b8281101561067557838181518110610616577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020026020010151828281518110610657577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020026020010181905250808061066d90610d17565b9150506105d5565b5080935050505090565b3373ffffffffffffffffffffffffffffffffffffffff166001600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610783578060008381548110610721577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b906000526020600020906004020160030160006101000a81548160ff0219169083151502179055507ff88a35c3d2016c409a46570b994a17b408dbc83c14a03f521512d50b85386d06828260405161077a929190610b56565b60405180910390a15b5050565b82805461079390610cb4565b90600052602060002090601f0160209004810192826107b557600085556107fc565b82601f106107ce57805160ff19168380011785556107fc565b828001600101855582156107fc579182015b828111156107fb5782518255916020019190600101906107e0565b5b509050610809919061084d565b5090565b604051806080016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001606081526020016000151581525090565b5b8082111561086657600081600090555060010161084e565b5090565b600061087d61087884610ba4565b610b7f565b905082815260208101

84848401111561089557600080fd5b6108a0848285610c72565b509392505050565b6000813590506108b781610dfe565b92915050565b600082601f8301126108ce57600080fd5b81356108de84826020860161086a565b91505092915050565b6000813590506108f681610e15565b92915050565b6000806040838503121561090f57600080fd5b600083013567ffffffffffffffff81111561092957600080fd5b610935858286016108bd565b9250506020610946858286016108a8565b9150509250929050565b6000806040838503121561096357600080fd5b6000610971858286016108e7565b9250506020610982858286016108a8565b9150509250929050565b60006109988383610a8a565b905092915050565b6109a981610c2a565b82525050565b6109b881610c2a565b82525050565b60006109c982610be5565b6109d38185610c08565b9350836020820285016109e585610bd5565b8060005b85811015610a215784840389528151610a02858261098c565b9450610a0d83610bfb565b925060208a019950506001810190506109e9565b50829750879550505050505092915050565b610a3c81610c3c565b82525050565b610a4b81610c3c565b82525050565b6000610a5c82610bf0565b610a668185610c19565b9350610a76818560208601610c81565b610a7f81610ded565b840191505092915050565b6000608083016000830151610aa26000860182610aed565b506020830151610ab560208601826109a0565b5060408301518482036040860152610acd8282610a51565b9150506060830151610ae26060860182610a33565b508091505092915050565b610af681610c68565b82525050565b610b0581610c68565b82525050565b6000604082019050610b2060008301856109af565b610b2d6020830184610afc565b9392505050565b60006020820190508181036000830152610b4e81846109be565b905092915050565b6000604082019050610b6b6000830185610afc565b610b786020830184610a42565b9392505050565b6000610b89610b9a565b9050610b958282610ce6565b919050565b6000604051905090565b600067ffffffffffffffff821115610bbf57610bbe610dbe565b5b610bc882610ded565b9050602081019050919050565b6000819050602082019050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b600082825260208201905092915050565b600082825260208201905092915050565b6000610c3582610c48565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b83811015610c9f578082015181840152602081019050610c84565b83811115610cae576000848401525b50505050565b60006002820490506001821680610ccc57607f821691505b60208210811415610ce057610cdf610d8f565b5b50919050565b610cef82610ded565b810181811067ffffffffffffffff82111715610d0e57610d0d610dbe565b5b80604052505050565b6000610d2282610c68565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610d5557610d54610d60565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b610e0781610c3c565b8114610e1257600080fd5b50565b610e1e81610c68565b8114610e2957600080fd5b5056fea26469706673582212209cf2120d1c2331a546dc67e1b144e55ded28e1e2a9bbcd5277ea9cf6eaec679464736f6c63430008040033",
    "linkReferences": {},
    "deployedLinkReferences": {}
  }

TaskContract.json

Explanation : The JSON file contains the ABI (Application Binary Interface) and the compiled bytecode of the smart contract. The ABI is used by Web3.js to interact with the deployed smart contract.

Then, create a file at the root of the folder and paste the address of the deployed contract: config.js client

export const TaskContractAddress = "DEPLOYED_CONTRACT_ADDRESS";

config.js

Explanation: This JavaScript file exports the address of the deployed smart contract. This address will be used by the front-end to interact with the smart contract on the blockchain.

Deploy the front-end locally

To run the project locally, follow these steps:

  1. Open your terminal and navigate to the root directory of your front-end project.
  2. Make sure all dependencies are installed by running :npm install
  3. Launch the app with :npm run dev

Go to your browser and open to see your app in action:http://localhost:3000

Image description

Conclusion

This tutorial has guided you through the process of creating a to-do list dApp with Next.js, Solidity, and Web3.js. By following these steps, you can now develop and deploy your own decentralized applications. For more tutorials and resources, stay tuned and continue exploring the endless possibilities offered by blockchain.

View the project on GitHub

To-do List Blockchain dApp with NextJS + Solidity + Web3.js - Step-by-Step Tutorial


Learn more about web3.js

If you want to explore web3.js further, all functions with code examples can be found in the web3.js docs:

👉 Visit web3.js docs

👉 Follow @web3_js on Twitter for updates!

Still have questions? You can connect with other developers and get web3.js support in the ChainSafe Discord channel.

Just go to #web3js-general for all your web3.js questions. We hope to see you there soon! 😀 

👉 Join us on Discord!

About ChainSafe

ChainSafe is a leading blockchain research and development firm specializing in protocol engineering, cross-chain interoperability, and web3 gaming. Alongside its contributions to major ecosystems such as Ethereum, Polkadot, and Filecoin, ChainSafe creates solutions for developers across the web3 space utilizing expertise in gaminginteroperability, and decentralized storage. As part of its mission to build innovative products for users and improved tooling for developers, ChainSafe embodies an open-source and community-oriented ethos to advance the future of the internet.

Website | Twitter | Linkedin | GitHub | Discord | YouTube | Newsletter