Building a real dApp with React, Solidity and Web3.js

web 3.0 (You can find a translation of this article here). It seems that people are tired of centralized systems where their digital privacy is violated on a daily basis by huge organizations. I also want to find a solution to this problem, and web 3.0 seems to be the answer for now.
This blog post is not intended to cover the know-how of blockchain and decentralized systems. Instead, it is intended for those who would like to create online solutions for users, customers and buyers to guarantee them the best privacy and security of their data.
With that cleared up, in this article, I’m going to show you the ABCs of how to build a decentralized application from scratch and set up a development environment. Listed below are some of the topics we will cover.

  1. Instruments

  2. Tool setup

  3. Code writing

Instruments

a. Truffle Framework

b. Ganache

c. Solidity 0.8.10

d. metamask

e. React 17.0.2

f. Web3.js 1.6.1

To get down to business, I would like to briefly talk about the above tools.
The first one, Truffle Framework, offers a set of tools for developing Ethereum smart contracts. It offers tools such as smart contract management, deployment and migration, network management, development console, etc.
Ganache is a personal blockchain, which is a local development blockchain that can be used to mimic the behavior of a public blockchain.
Solidity is a high-level object-oriented language for implementing smart contracts. Learn more about Solidity here.
Most browsers currently do not allow us to connect to the blockchain network, so I would use the Metamask chrome extension which will allow us to connect our chrome browser to the blockchain network.
For UI or front-end development, we will use the React library, which is one of the most widely used JavaScript libraries among the front-end communities.
Web3.js is a JavaScript library that allows us to communicate with the Ethereum blockchain. It turns our React app into a blockchain-enabled app.

Tool setup

Now that I’ve briefly covered the set of tools that will be used in this blog post, it’s time to understand and set up the tools.
First of all, I would like you to download Truffle Framework and installed it with the following command:

npm install -g truffle

Then download and install Ganache. When you do this and open it, you will see the following window:

Next, we need Metamask.
Add an extension metamask to your google chrome and navigate to a screen that will show something like the following. To set up Metamask, please see this article.

Code writing

Now that the tools are set up, let’s move on to the next step – writing smart contracts. To do this, open a terminal and create a folder in the projects folder with the following command:

mkdir blockchain

Now create a folder inside the blockchain folder with the following commands:

cd blockchain

mkdir contracts

cd contracts

Now run the command below to create a truffle project that will allow us to develop smart contracts:

truffle init

By executing the above command, you should get the result as shown below:

Now open your truffle project in your favorite text editor. I am going to use Sublime Text. You should see the following folder and file structure:

In folder contracts we will write our smart contracts.
In folder migrations we will be migrating our newly created smart contracts.
In folder test we usually write tests to validate our smart contract, however this is out of the scope of this article so we won’t go into that topic. I would highly recommend writing tests for your smart contracts before deploying them to public blockchain nodes.
File truffle-config.js contains the entire configuration of the truffle project.
Now let’s write our smart contract. Create a new file and name it contacts.sol in the contracts folder. Now open this file and write the following code in it:

pragma solidity >=0.4.22 <0.9.0;

This should always be the first line in the smart contract file. With this we indicate the version of Solidity.
Now let’s create our first smart contract:

pragma solidity >=0.4.22 <0.9.0;

contract Contacts {
  
}

We can write a smart contract using the keyword contractfollowed by the name of the contract, in this case Contacts.
Now, to keep track of the number of contacts inside our smart contract, we will create a state variable.

pragma solidity >=0.4.22 <0.9.0;

contract Contacts {
  uint public count = 0; // state variable
}

This is a special variable and any data we write to this variable will be stored in the blockchain storage. We use a special modifier keyword, i.e. publicto access this variable outside of the smart contract. We then assign the value 0 to this variable.
Now we have a state variable and it has a value. Let’s move to the front end and try to access this public state variable first. With this approach, we will establish a connection between the React app and our smart contract.
Run the following commands in the folder blockchainto create a React app.

npx create-react-app contacts

cd contacts

if you use yarn

yarn add web3

if you are using npm

npm install web3

As you can see we have also added web3.js as a dependency for this project so that our react app can interact with our smart contract.
Now open the contacts folder in your favorite editor. I am using Sublime Text.
We won’t be setting up a folder structure and implementing complex architecture because that’s beyond the scope of this blog post.
We will write all our code in a file App.js. So open this file in an editor.
Write the below code in a file App.js:

import { useEffect, useState } from 'react';
import Web3 from 'web3';

function App() {
  return (
    <div>
    </div>
  );
}

export default App;

We import a couple of hooks from React and Web3 from Web3.js. Now let’s create a state variable as shown below:

import { useEffect, useState } from 'react';
import Web3 from 'web3';

function App() {
  const [account, setAccount] = useState(); // state variable to set account.
  return (
    <div>
    </div>
  );
}

export default App;

Now inside the hook useEffect we will create our smart contract as shown below:

import { useEffect, useState } from 'react';
import Web3 from 'web3';

function App() {
  const [account, setAccount] = useState(); // state variable to set account.
  
  useEffect(() => {
    async function load() {
      const web3 = new Web3(Web3.givenProvider || 'http://localhost:7545');
      const accounts = await web3.eth.requestAccounts();
      
      setAccount(accounts[0]);
    }
    
    load();
   }, []);
  
   return (
     <div>
       Your account is: {account}
     </div>
   );
}

export default App;

Now, in order to successfully launch this client side, we need to take a few steps in the backend. Let’s go back to the folder contracts. First, open the file truffle-config.js and add the following properties:

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 7545,
      network_id: "*"
    }
  },
  compilers: {
    solc: {
      optimizer: {
        enabled: true,
        runs: 200
      }
    }
  }
}

Now inside the folder migrations create a new file and name it 2_deploy_contracts.js and paste the code below:


const Contacts = artifacts.require("./Contacts.sol");

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

Now in the terminal enter the below code to migrate your contract:

truffle migrate

You should see output similar to the one below:

Now go back to the front end folder (folder contracts) and run the command below to run the application:

yarn start

or

npm start

This will open your React app in a browser and launch Metamask to interact with your blockchain network. You should see a screen like below:

In the window that opens, click the Next button, and the following window will open in front of you.

Now click the Connect button. After that, a message should appear in your browser with your account number, as shown below:

Now that we have successfully connected to our smart contract and got an account ID. Let’s start creating a new function inside our smart contract to get a list of contacts and pass it to the front end to display them on the screen.
Let’s go back to the folder contractsopen the file Contracts.sol and add the following function.

pragma solidity >=0.4.22 <0.9.0;

contract Contacts {
  uint public count = 0; // state variable
  
  struct Contact {
    uint id;
    string name;
    string phone;
  }
  
  constructor() public {
    createContact('Zafar Saleem', '123123123');
  }
  
  mapping(uint => Contact) public contacts;
  
  function createContact(string memory _name, string memory _phone) public {
    count++;
    contacts[count] = Contact(count, _name, _phone);
  }
}

Now migrate this contract again as we have made changes to it since smart contracts are immutable.

truffle migrate

Now let’s get back to the front end, i.e. the folder contacts. Create a new file config.js inside a folder src/ and paste the code below into it.

export const CONTACT_ADDRESS = '0xfAd567EBdCb36f49F3a509FEDF9e72E3ad75ca59';

export const CONTACT_ABI = [
  {
    "constant": true,
    "inputs": [],
    "name": "count",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function",
    "signature": "0x06661abd"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "contacts",
    "outputs": [
      {
        "name": "id",
        "type": "uint256"
      },
      {
        "name": "name",
        "type": "string"
      },
      {
        "name": "phone",
        "type": "string"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function",
    "signature": "0xe0f478cb"
  },
  {
    "inputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "constructor",
    "signature": "constructor"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "_name",
        "type": "string"
      },
      {
        "name": "_phone",
        "type": "string"
      }
    ],
    "name": "createContact",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function",
    "signature": "0x3dce4920"
  }
];

Now import the smart contract address and ABI into the file App.jsas shown below, and also update the load function with the following code:

import { useEffect, useState } from 'react';
import Web3 from 'web3';
import { CONTACT_ABI, CONTACT_ADDRESS } from './config';

function App() {
  const [account, setAccount] = useState();
  const [contactList, setContactList] = useState();
  const [contacts, setContacts] = useState([]);

  useEffect(() => {
    async function load() {
      const web3 = new Web3(Web3.givenProvider || 'http://localhost:7545');
      const accounts = await web3.eth.requestAccounts();
      setAccount(accounts[0]);
      // Instantiate smart contract using ABI and address.
      const contactList = new web3.eth.Contract(CONTACT_ABI, CONTACT_ADDRESS);
      // set contact list to state variable.
      setContactList(contactList);
      // Then we get total number of contacts for iteration
      const counter = await contactList.methods.count().call();
      // iterate through the amount of time of counter
      for (var i = 1; i <= counter; i++) {
        // call the contacts method to get that particular contact from smart contract
        const contact = await contactList.methods.contacts(i).call();
        // add recently fetched contact to state variable.
        setContacts((contacts) => [...contacts, contact]);
      }
    }
    
    load();
  }, []);
  
  return (
    <div>
      Your account is: {account}
    </div>
  );
}

export default App;

The code above will get all the contacts from our smart contract and set them to the contacts state variable. The code is very well commented and explained.
Now let’s display all contacts via ul tag as shown below.

import { useEffect, useState } from 'react';
import Web3 from 'web3';
import { CONTACT_ABI, CONTACT_ADDRESS } from './config';

function App() {
  const [account, setAccount] = useState();
  const [contactList, setContactList] = useState();
  const [contacts, setContacts] = useState([]);
  
  useEffect(() => {
    async function load() {
      const web3 = new Web3(Web3.givenProvider || 'http://localhost:7545');
      const accounts = await web3.eth.requestAccounts();
      setAccount(accounts[0]);
      // Instantiate smart contract using ABI and address.
      const contactList = new web3.eth.Contract(CONTACT_ABI, CONTACT_ADDRESS);
      // set contact list to state variable.
      setContactList(contactList);
      // Then we get total number of contacts for iteration
      const counter = await contactList.methods.count().call();
      // iterate through the amount of time of counter
      for (var i = 1; i <= counter; i++) {
        // call the contacts method to get that particular contact from smart contract
        const contact = await contactList.methods.contacts(i).call();
        // add recently fetched contact to state variable.
        setContacts((contacts) => [...contacts, contact]);
      }
    }
    
    load();
    
  }, []);
  
  return (
    <div>
      Your account is: {account}
      <h1>Contacts</h1>
      <ul>
      {
        Object.keys(contacts).map((contact, index) => (
          <li key={`${contacts[index].name}-${index}`}>
            <h4>{contacts[index].name}</h4>
            <span><b>Phone: </b>{contacts[index].phone}</span>
          </li>
        ))
      }
      </ul>
    </div>
  );
}

export default App;

As you can see the updated code inside return – displays information about the contact. You should see something like the following:

If this is what you see, then congratulations, you have just created your first dApp with a smart contract and a web application that interacts with your smart contract on the blockchain.
Now that you know how to get information from a smart contract, keep improving it by adding new contacts, updating them, and deleting them with full CRUD operations.
That’s all in this article. The entire project can be found at repositories.

Below are the social media profiles if you would like to contact the author of the article.

LinkedIn | Github | gitlab | Website

Similar Posts

Leave a Reply