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.
Link to Learning
Ganache is a personal Ethereum blockchain environment that provides developers with the means to use a private blockchain for development and testing. Ganache can emulate the Ethereum blockchain, and because it is a private blockchain, it allows developers control over development and testing processes.
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.
- 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();
Link to Learning
Truffle provides a suite of tools that can be used to develop smart contracts. Truffle offers end-to-end development that includes the ability to develop, test, and implement smart contracts, while using Truffle to manage the workflow.
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.
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.
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.
Link to Learning
Solidity is a high-level, object-oriented language used to implement smart contracts. Influenced by JavaScript, C++, and Python, Solidity is focused on the Ethereum Virtual Machine. Smart contracts implemented by Solidity are used for purposes like blind auctions, voting, and crowdfunding. Visit this page on Solidity to learn more.
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.