Skip to content

Building a DApp with Ethereum — Full Tutorial

DodaTech 2 min read

In this tutorial, you'll learn about Building a DApp with Ethereum. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

What You'll Learn

Build a complete decentralized application from scratch: a Solidity smart contract, a React frontend, and wallet integration with ethers.js.

Why It Matters

Understanding the full DApp stack makes you a capable Web3 developer. The patterns here apply to any Ethereum-compatible Blockchain.

Prerequisites

  • Basic JavaScript and React knowledge
  • MetaMask browser extension installed
  • Node.js installed

Step 1: The Smart Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract TodoList {
    struct Todo {
        uint256 id;
        string content;
        bool completed;
    }

    mapping(address => Todo[]) private todos;
    mapping(address => uint256) private todoCount;

    event TodoAdded(address indexed user, uint256 id, string content);
    event TodoToggled(address indexed user, uint256 id, bool completed);

    function addTodo(string memory content) public {
        todoCount[msg.sender]++;
        todos[msg.sender].push(Todo(
            todoCount[msg.sender],
            content,
            false
        ));
        emit TodoAdded(msg.sender, todoCount[msg.sender], content);
    }

    function toggleTodo(uint256 id) public {
        Todo storage todo = todos[msg.sender][id - 1];
        todo.completed = !todo.completed;
        emit TodoToggled(msg.sender, id, todo.completed);
    }

    function getTodos() public view returns (Todo[] memory) {
        return todos[msg.sender];
    }
}

Deploy this to Sepolia testnet using Remix (see the Solidity tutorial).

Step 2: Create the React App

npx create-react-app ethereum-todo-dapp
cd ethereum-todo-dapp
npm install ethers

Step 3: Connect to Wallet

// src/web3.js
import { ethers } from "ethers";

export async function getProvider() {
  if (!window.ethereum) {
    throw new Error("MetaMask not installed");
  }
  return new ethers.BrowserProvider(window.ethereum);
}

export async function getSigner() {
  const provider = await getProvider();
  return provider.getSigner();
}

export async function connectWallet() {
  const provider = await getProvider();
  await provider.send("eth_requestAccounts", []);
  const signer = provider.getSigner();
  const address = await signer.getAddress();
  return address;
}

Step 4: Create the Contract Interface

// src/contract.js
import { ethers } from "ethers";
import { getSigner } from "./web3";

const CONTRACT_ADDRESS = "0xYOUR_DEPLOYED_ADDRESS";
const CONTRACT_ABI = [
  // Copy ABI from Remix compilation
  "function addTodo(string memory content) public",
  "function toggleTodo(uint256 id) public",
  "function getTodos() public view returns ((uint256,string,bool)[])"
];

export async function getContract() {
  const signer = await getSigner();
  return new ethers.Contract(CONTRACT_ADDRESS, CONTRACT_ABI, signer);
}

export async function addTodo(content) {
  const contract = await getContract();
  const tx = await contract.addTodo(content);
  await tx.wait(); // Wait for confirmation
}

export async function getTodos() {
  const contract = await getContract();
  return contract.getTodos();
}

Step 5: Build the UI

// src/App.js
import { useState, useEffect } from "react";
import { connectWallet } from "./web3";
import { addTodo, getTodos } from "./contract";

function App() {
  const [address, setAddress] = useState(null);
  const [todos, setTodos] = useState([]);
  const [content, setContent] = useState("");

  const handleConnect = async () => {
    const addr = await connectWallet();
    setAddress(addr);
    loadTodos();
  };

  const loadTodos = async () => {
    const result = await getTodos();
    setTodos(result);
  };

  const handleAdd = async () => {
    await addTodo(content);
    setContent("");
    loadTodos();
  };

  return (
    <div>
      {!address ? (
        <button onClick={handleConnect}>Connect Wallet</button>
      ) : (
        <div>
          <p>Connected: {address}</p>
          <input value={content}
            onChange={e => setContent(e.target.value)} />
          <button onClick={handleAdd}>Add Todo</button>
          <ul>
            {todos.map(t => (
              <li key={t.id}>{t.content}</li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

Complete Flow

  1. User connects MetaMask
  2. App reads their todos from the smart contract
  3. User adds a todo → Transaction sent to Blockchain
  4. After confirmation (10-20 seconds), the new todo appears
  5. Every interaction costs a small gas fee

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro