Skip to ContentGo to accessibility pageKeyboard shortcuts menu
OpenStax Logo
Introduction to Computer Science

11.6 Sample Ethereum Blockchain Web 2.0/Web 3.0 Application

Introduction to Computer Science11.6 Sample Ethereum Blockchain Web 2.0/Web 3.0 Application

Learning Objectives

By the end of this section, you will be able to:

  • Build a hybrid Ethereum blockchain Web 3.0 application
  • Create the React app and install dependencies
  • Create the smart contract
  • Create the front-end React components
  • Add Web3 to the React app
  • Configure the React app to communicate with the smart contract

So far, this chapter has explored how to develop a Todo web application, as well as a Todo mobile application, using Bootstrap, Django, React, React Native, and Node. In this final section, you will learn how to create a simple Todo application using React with Web 3.0 powered by Ethereum smart contracts on the blockchain.

Creating a Todo Ethereum Blockchain Web 3.0 Application

As you have learned, Todo applications can be created using different tools. In this section, you will explore the Ethereum blockchain, which creates a secure peer-to-peer network through the use of a smart contract, which is a secure digital agreement that enables users to transact directly with each other via the blockchain. You will use React to create a Todo Ethereum blockchain Web 3.0 application.

Prerequisites

To build the Todo Ethereum blockchain Web 3.0 application, you should use React v17.0.2, Bootstrap v4.5.0, Node v14.17.5, Web 3.js v1.2.2, Truffle v5.0.2, and Solidity v0.8.11. In addition, Ganache is used as the personal blockchain for development. To begin, do the following:

  • Download and install Ganache. Launch Ganache and choose the quick start Ethereum option as seen in Figure 11.46.
    Screenshot of Ethereum option.
    Figure 11.46 This is the quick start Ethereum option that is available after installing Ganache. (credit: Ganache, under MIT license)
  • The next step is to install the MetaMask Chrome plug-in. Configure the MetaMask account and log in.

Creating the React App

Previously in this chapter, you created React apps. For this application, you need to create a new React app.

To create the new React app, run the following command:

$ npx create-react-app ethreact

Install Bootstrap, Web3, and Other Dependencies

To use Bootstrap and Web3 in a React app, corresponding packages must be installed. To do this, in the ethreact/ directory, run the following commands:

  $ npm install bootstrap@4.6.0 reactstrap@8.9.0 --legacy-peer-deps
  $ npm install react-bootstrap
  $ npm install web3@1.2.2
  

Next, import the Bootstrap CSS file into the React application. Open ethreact/src/index.js and add the following Bootstrap import.

import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Creating the Smart Contract

To develop the Ethereum smart contract, you will use the Truffle Framework, which is a popular suite of tools used to develop smart contracts. Implement the Truffle Framework using Solidity, which is a high-level, object-oriented language that is focused on the implementation of smart contracts. To use the Truffle Framework, in the ethreact/ directory, run the installation following command:

$ npm install -g truffle@5.0.2

To set up the React app to use Truffle, in the ethreact/ directory, run the following command, which may take several minutes.

$ truffle init

When the initialization is completed, the contracts/ directory and Migrations.sol file will be generated. In addition, the truffle-config.js file will be generated, as highlighted in Figure 11.47.

Screenshot of truffle-config.js file.
Figure 11.47 Here is the truffle-config.js file. (rendered with Truffle by Truffle Security Co., under MIT license; attribution: Copyright Rice University, OpenStax, under CC BY 4.0 license)

To create the smart contract, in the ethreact/contracts/ directory, create the file TodoList.sol and add the following code.

pragma solidity ^0.5.0;
contract TodoList {
  uint public taskCount = 0;

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

  mapping(uint => Task) public tasks;

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

  event TaskCompleted(
    uint id,
    bool completed
  );

  constructor () public {
    createTask("Check out dappuniversity.com");
  }

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

}

Next, compile the smart contract. In the ethreact/ directory, run the following command:

  $ truffle compile
  

This command should provide a status confirming that the smart contract has been successfully compiled as seen in the following code:

> Compiled successfully using:
  - solc: 0.5.16+commit.9c3226ce.Emscription.clang

Figure 11.48 shows how this process will generate a few files, including two JSON files in the build/contracts/ directory and a migration file in the migrations/ directory.

Screenshot of JSON and migration files.
Figure 11.48 These are the JSON and migration files generated when the smart contract is compiled. (rendered using JSON; attribution: Copyright Rice University, OpenStax, under CC BY 4.0 license)

The TodoList.json file in the build/contracts/ directory is the smart contract Abstract Binary Interface (ABI) file. This file contains the following:

  • compiled bytecode from the Solidity smart contract code that can run on the Ethereum Virtual Machine (EVM)
  • a JSON representation of the smart contract.

Next, configure the React app to connect to the Ganache blockchain network. To do this, open the ethreact/truffle-config.js file and uncomment the two sections shown. Under “networks,” enable the connection host and port to the Ganache blockchain network. Ensure that the host and port are in sync with the following settings in Ganache.

development: {
  host: "127.0.0.1",     // Localhost (default: none)
  port: 7545,            // Standard Ethereum port (default: none)
  network_id: "*",       // Any network (default: none)
},

Next, under “compilers,” enable the solc optimizer.

// Configure your compilers
compilers: {
  solc: {
    // version: "0.5.1",    // Fetch exact version from solc-bin (default: truffle's version)
    // docker: true,        // Use "0.5.1" you've installed locally with docker (default: false)
    // settings: {          // See the solidity docs for advice about optimization and evmVersion
     optimizer: {
       enabled: true,
       runs: 200
     },
    //  evmVersion: "byzantium"
    // }
  }
},

Next, create a migration script to deploy the smart contract to the Ganache blockchain network. In the ethreact/migrations/ directory, create the file 2_deploy_contracts.js and add the following code.

var TodoList = artifacts.require("./TodoList.sol");

module.exports = function(deployer) {
  deployer.deploy(TodoList);
};

The next step is to migrate the contract. In the ethreact/ directory, run this command.

$ truffle migrate

Figure 11.49 displays how the contract will be migrated and provides the transaction details. You should make a note of the contract address in the output because it will be added to the config.js file later.

Screenshot of transaction details for migrating contract.
Figure 11.49 Here are the transaction details for migrating the contract. (attribution: Copyright Rice University, OpenStax, under CC BY 4.0 license)

Creating the Front-End React Components

The TodoList component renders the user interface for the Todo app. In the ethreact/src/ directory, create the components/ directory. In the components/ directory, create the file TodoList.js and then create the TodoList component.

import React, { Component } from 'react'

class TodoList extends Component {

  render() {
    return (
      <div id="content">
        <form onSubmit={(event) => {
          event.preventDefault()
          this.props.createTask(this.task.value)
        }}>
          <input
            id="newTask"
            ref={(input) => {
              this.task = input
            }}
            type="text"
            className="form-control"
            placeholder="Add task..."
            required />
          <input type="submit" hidden={true} />
        </form>
        <ul id="taskList" className="list-unstyled">
          { this.props.tasks.map((task, key) => {
            return(
              <div className="taskTemplate" className="checkbox" key={key}>
                <label>
                  <input
                  type="checkbox"
                  name={task.id}
                  defaultChecked={task.completed}
                  ref={(input) => {
                    this.checkbox = input
                  }}
                  onClick={(event) => {
                    this.props.toggleCompleted(this.checkbox.name) }}/>
                  <span className="content">{task.content}</span>
                </label>
              </div>
            )
          })}
        </ul>
        <ul id="completedTaskList" className="list-unstyled">
        </ul>
      </div>
    );
  }
}
export default TodoList;

Update the ethreact/src/App.js to import and render the TodoList component.

render() {
  return (
    <div>
      <nav className="navbar navbar-dark fixed-top bg-dark flex-md-nowrap p-0 shadow">
        <a className="navbar-brand col-sm-3 col-md-2 mr-0" href="#">ToDo</a>
        <ul className="navbar-nav px-3">
          <li className="nav-item text-nowrap d-none d-sm-none d-sm-block">
            <small><a className="nav-link" href="#"><span id="account"></span></a></small>
          </li>
        </ul>
      </nav>
      <div className="container-fluid">
        <div className="row">
          <main role="main" className="col-lg-12 d-flex justify-content-center">
            { this.state.loading
              ? <div id="loader" className="text-center"><p className="text-center">Loading...</p></div>
              : <TodoList
                tasks={this.state.tasks}
                createTask={this.createTask}
                toggleCompleted={this.toggleCompleted} />
            }
          </main>
        </div>
      </div>
    </div>
  );
}

Adding Web3 to the React App

The next step is to add Web3 and configure it to connect to Ganache. To do this, open ethreact/src/App.js, import the Web3 package, and add the code shown here. In this code, a web3 connection is instantiated providing the Ganache URL (http://localhost:8545). This enables the React app to connect to the blockchain. The blockchain account information is then accessed and added to the React app’s state object. In the ethreact/src/ directory, create the file getWeb3.js and add the code shown. This code creates a Web3 object via different connection approaches depending on the approach used (e.g., type of DApp browser, using localhost).

import Web3 from "web3";

const getWeb3 = () =>
  new Promise((resolve, reject) => {
    // Wait for loading completion to avoid race conditions with web3 injection timing.
    window.addEventListener("load", async () => {
      // Modern dapp browsers...
      if (window.ethereum) {
        const web3 = new Web3(window.ethereum);
        try {
          // Request account access if needed
          await window.ethereum.enable();
          // Accounts now exposed
          resolve(web3);
        } catch (error) {
          reject(error);
        }
      }
      // Legacy dapp browsers...
      else if (window.web3) {
        // Use Mist/MetaMask's provider.
        //const web3 = window.web3; // ORIG
        const web3 = window.web3.currentProvider.enable()
        console.log("Injected web3 detected.");
        resolve(web3);
      }
      // Fallback to localhost; use dev console port by default...
      else {
        const provider = new Web3.providers.HttpProvider(
          "http://127.0.0.1:8545"
        );
        const web3 = new Web3(provider);
        console.log("No web3 instance injected, using Local web3.");
        resolve(web3);
      }
    });
  });

export default getWeb3;

Configuring the React App to Communicate with the Smart Contract

To make the tasks from the smart contract available to the React app, create a config file, along with the contract address and ABI. To do this, in the ethreact/src/ directory, create the file config.js. Then, create two variables: TODO_LIST_ADDRESS and TODO_LIST_ABI. Grab the contract address that was provided when the smart contract was deployed to the blockchain and assign it to TODO_LIST_ADDRESS. Open the ethreact/build/contracts/TodoList.json file and copy the ABI portion only and assign it to TODO_LIST_ABI.

export const TODO_LIST_ADDRESS = '0x9A93A9951c1cAcF0Cecc320Fa6aD4e05d55a2d02'

export const TODO_LIST_ABI = [
  {
    "inputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "id",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "completed",
        "type": "bool"
      }
    ],
    "name": "TaskCompleted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "id",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "string",
        "name": "content",
        "type": "string"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "completed",
        "type": "bool"
      }
    ],
    "name": "TaskCreated",
    "type": "event"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "taskCount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "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"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "string",
        "name": "_content",
        "type": "string"
      }
    ],
    "name": "createTask",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "internalType": "uint256",
        "name": "_id",
        "type": "uint256"
      }
    ],
    "name": "toggleCompleted",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  }
]

Next, update ethreact/src/App.js. Import getWeb3.js and add the following code to the componentDidMount() life cycle method. This will connect to the blockchain network and load the contract before the React components renders in the browser.

componentDidMount = async () => {
  try {
    // Get network provider and web3 instance.
    const web3 = await getWeb3();

    // Use web3 to get the user's accounts.
    const accounts = await web3.eth.getAccounts();

    // Get the contract instance.
    const networkId = await web3.eth.net.getId();
    let deployedNetwork = TodoListABI.networks[networkId];
    const todoListInstance = new web3.eth.Contract(
      TODO_LIST_ABI,
      deployedNetwork && deployedNetwork.address
    );

    // Set web3, accounts, and contract to the state, and then proceed with an
    // example of interacting with the contract's methods.
    this.setState({ web3, accounts, todoListContract: todoListInstance }, this.runExample);
  } catch (error) {
    // Catch any errors for any of the above operations.
    alert(
      'Failed to load web3, accounts, or contract. Check console for details.',
    );
    console.error(error);
  }
};

Launch the React app by running the following command:

$npm start

Launch a browser with the MetaMask plug-in installed and navigate to http://localhost:3000. Create a task.

Citation/Attribution

This book may not be used in the training of large language models or otherwise be ingested into large language models or generative AI offerings without OpenStax's permission.

Want to cite, share, or modify this book? This book uses the Creative Commons Attribution License and you must attribute OpenStax.

Attribution information
  • If you are redistributing all or part of this book in a print format, then you must include on every physical page the following attribution:
    Access for free at https://openstax.org/books/introduction-computer-science/pages/1-introduction
  • If you are redistributing all or part of this book in a digital format, then you must include on every digital page view the following attribution:
    Access for free at https://openstax.org/books/introduction-computer-science/pages/1-introduction
Citation information

© Oct 29, 2024 OpenStax. Textbook content produced by OpenStax is licensed under a Creative Commons Attribution License . The OpenStax name, OpenStax logo, OpenStax book covers, OpenStax CNX name, and OpenStax CNX logo are not subject to the Creative Commons license and may not be reproduced without the prior and express written consent of Rice University.