RK
Reetesh Kumar@iMBitcoinB

Building DApps with React and Solidity on Ethereum

Jan 13, 2024

0

9 min read

A decentralized application (DApp) is a type of software application that operates on a decentralized network rather than being controlled by a single entity or authority. Unlike traditional applications that rely on central servers, DApps leverage blockchain technology to ensure decentralization, transparency, and immutability.

DApps are built on top of blockchain platforms like Ethereum, Solana, and Avalanche. Ethereum is the most popular blockchain platform for building DApps.

In this tutorial, we will learn how to build a DApp using React and Solidity on Ethereum. We will also learn how to deploy the DApp on the Ethereum blockchain and interact with it using Metamask.

Setting up the Remix IDE#

Remix IDE is an online IDE that allows you to write, compile, and deploy smart contracts on the Ethereum blockchain. It is a great tool for beginners to get started with smart contract development. Since it has everything you need to get started with smart contract development, you don't need to install anything on your computer.

To get started, head over to the Remix IDE and create a new project. You will see a screen like this :

Remix IDE

Delete the default contract files and create a new contract called Storage.sol. We will use this contract to store and retrieve number from the Ethereum blockchain.

Writing the Smart Contract#

Let's write a simple smart contract that will allow us to store and retrieve number from the Ethereum blockchain.

solidity
 
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
 
contract Storage {
    uint public data;
 
    constructor(){
        data = 0;
    }
 
    function setData(uint _data) public {
        data = data + _data;
    }
 
    function getData() public view returns (uint) {
        return data;
    }
}

The Storage contract has two functions: setData and getData. The setData function allows us to store a number on the blockchain, and the getData function allows us to retrieve the number from the blockchain.

Deploying the Smart Contract#

Now that we have written the smart contract, let's deploy it on the Ethereum blockchain. To deploy the smart contract, click on the Deploy & run transactions icon on the left sidebar.

  1. On the Top you can see Environment dropdown, select Injected provider - metamask from the dropdown. This will allow you to interact with the smart contract using Metamask.

  2. Make sure you have Metamask installed on your browser and you are connected to the Sepolia Test Network.

  3. You can get free test ETH from here.

Now that everything is set up, click on the Deploy button to deploy the smart contract.

Once the smart contract is deployed, you will get the address of the smart contract. Copy the address and save it somewhere as we will need it later.

We need to copy the ABI (Application Binary Interface) of the smart contract as well. To copy the ABI, click on solidity compiler icon on the left sidebar and click on the ABI and copy and store it.

Building the Frontend#

We will use Create React App to set up the React app. To get started, run the following command :

bash
npx create-react-app dapp

Once the React app is created, navigate to the project directory and install the following dependencies:

bash
cd dapp
npm install web3 @metamask/sdk-react react-hot-toast

Setting up Metamask SDK#

Create a utils folder in the src directory and create a file called MetamaskProvider.jsx inside the utils folder. We will use this file to initialize the Metamask SDK.

javascript
import React from 'react';
import { MetaMaskProvider } from '@metamask/sdk-react';
import icon from '../assets/logo192.png';
 
const MetamaskProvider = ({ children }) => {
  return (
    <>
      <MetaMaskProvider
        debug={true}
        sdkOptions={{
          dappMetadata: {
            name: 'Storage App',
            url: window.location.host,
            iconUrl: icon, // optional
          },
        }}
      >
        {children};
      </MetaMaskProvider>
    </>
  );
};
 
export default MetamaskProvider;

Now that we have initialized the Metamask SDK, let's wrap the Index component with the MetamaskProvider component. Index component is the entry point of the React app.

javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import MetamaskProvider from './utils/MetamaskProvider';
 
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <MetamaskProvider>
      <App />
    </MetamaskProvider>
  </React.StrictMode>
);

Setting up Contract Instance#

Within the utils folder, create a file called constant.js. We will use this file to initialize the contract instance.

We will add our contract address and ABI in this file.

javascript
export const address = '0x47472ae8413b98F645742884341CD88e9D10D6B6';
 
export const abi = [
  {
    inputs: [
      {
        internalType: 'uint256',
        name: '_data',
        type: 'uint256',
      },
    ],
    name: 'setData',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [],
    stateMutability: 'nonpayable',
    type: 'constructor',
  },
  {
    inputs: [],
    name: 'data',
    outputs: [
      {
        internalType: 'uint256',
        name: '',
        type: 'uint256',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'getData',
    outputs: [
      {
        internalType: 'uint256',
        name: '',
        type: 'uint256',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
];

To initialize the contract instance, we will setup a Context with following code :

javascript
import { createContext, useState, useContext, useEffect } from 'react';
import { useSDK } from '@metamask/sdk-react';
import Web3 from 'web3';
import { abi, address } from '../utils/constant';
 
const contextState = {
  account: '',
  connectToMetaMask: () => {},
  connected: false,
  connecting: false,
  provider: {},
  sdk: {},
  web3: {},
};
 
const AppContext = createContext(contextState);
 
export const AppContextProvider = ({ children }) => {
  const [account, setAccount] = useState('');
  const { sdk, connected, connecting, provider } = useSDK();
 
  const connectToMetaMask = async () => {
    try {
      const accounts = await sdk?.connect();
      setAccount(accounts?.[0]);
    } catch (err) {
      console.warn(`failed to connect..`, err);
    }
  };
 
  const app = new Web3(provider);
  const web3 = new app.eth.Contract(abi, address);
 
  useEffect(() => {
    if (!account) {
      connectToMetaMask();
    }
  }, [connected]);
 
  return (
    <AppContext.Provider
      value={{
        account,
        connectToMetaMask,
        connected,
        connecting,
        provider,
        sdk,
        web3,
      }}
    >
      {children}
    </AppContext.Provider>
  );
};
 
export const useAppContext = () => useContext(AppContext);
export default AppContext;

Explanation:

  • We are using the useSDK hook from the @metamask/sdk-react package to initialize the Metamask SDK. Which will allow us to interact with the Metamask wallet.
  • We are using the Web3 package to initialize the contract instance. We are passing the provider to the Web3 instance. The provider is provided by the Metamask SDK. The provider allows us to interact with the Ethereum blockchain.
  • We are passing our ABI and contract address to the Web3 instance to initialize the contract instance.

Now that we have initialized the contract instance, let's wrap the App component with the AppContextProvider component. as we wrapped the Index component with the MetamaskProvider component.

Building the UI#

Now that we have set up the React app, let's build the UI. We will create a components folder in the src directory and create a file called Home.jsx inside the components folder.

javascript
import React, { useEffect, useRef, useState } from 'react';
import styles from './home.module.css';
import { useAppContext } from '../contexts/AppContext';
import toast from 'react-hot-toast';
 
const Home = () => {
  const { web3, account, connectToMetaMask, connected, connecting } =
    useAppContext();
  const inputRef = useRef(null);
 
  const [isLoading, setIsLoading] = useState('idle');
  const [number, setNumber] = useState('');
 
  const getNumber = async (e) => {
    try {
      setIsLoading('fetching');
      const number = await web3.methods.getData().call();
      setIsLoading('idle');
      setNumber(number);
    } catch (error) {
      setIsLoading('idle');
      toast.error('Error in fetching fleet');
    }
  };
 
  const handleAddNumber = async (e) => {
    e.preventDefault();
    if (inputRef.current.value === '') {
      return;
    }
 
    try {
      setIsLoading('adding');
      if (!account) {
        toast.error('Please connect to your wallet');
        setIsLoading('idle');
        return;
      }
 
      await web3.methods
        .setData(inputRef.current.value)
        .send({
          from: account,
          gas: 3000000,
        })
        .on('receipt', () => {
          inputRef.current.value = '';
          getNumber();
          toast.success('Number added successfully');
          setIsLoading('idle');
        })
        .on('error', () => {
          throw new Error('Error in adding number');
        });
    } catch (error) {
      toast.error('Error in adding number');
      setIsLoading('idle');
    }
  };
 
  useEffect(() => {
    if (connected) {
      getNumber();
    }
  }, [connected]);
 
  return (
    <section className={styles.home}>
      <div className={styles.wallet}>
        {!connected && (
          <button onClick={connectToMetaMask}>
            {connecting ? 'Connecting...' : 'Connect to MetaMask'}
          </button>
        )}
      </div>
      <div className={styles.number}>
        {isLoading === 'fetching' ? (
          <p>Fetching number...</p>
        ) : (
          <p>
            Number: <span>{number.toString()}</span>
          </p>
        )}
      </div>
      <div className={styles.form}>
        <form onSubmit={handleAddNumber}>
          <input
            type="number"
            placeholder="Enter number"
            ref={inputRef}
            disabled={!connected}
          />
          <button type="submit" disabled={!connected || isLoading === 'adding'}>
            {isLoading === 'adding' ? 'Adding...' : 'Add Number'}
          </button>
        </form>
      </div>
    </section>
  );
};
 
export default Home;

As you can see, we have created a simple UI that allows us to add and retrieve the number from the blockchain.

I have created two functions: getNumber and handleAddNumber. The getNumber function allows us to retrieve the number from the blockchain, and the handleAddNumber function allows us to add the number to the blockchain.

To Learn more about Web3, check out the official documentation.

Now that we have created the UI, let's add the Home component to the App component.

javascript
import React from 'react';
import { Toaster } from 'react-hot-toast';
import Home from './components/Home';
 
const App = () => {
  return (
    <main className="main">
      <Home />
      <Toaster />
    </main>
  );
};
 
export default App;

Now that we have added the Home component to the App component, let's run the app and see if everything is working fine.

To run the app, run the following command:

bash
npm start

You will be able to Add and Retrieve the number from the blockchain.

Make sure you are connected to the Sepolia Test Network on Metamask since we are using seploia for this tutorial.

You can find the complete code here

Conclusion#

Building DApps is fun and exciting. In this article, we learned how to build a DApp using React and Solidity on Ethereum. We also learned how to deploy the DApp on the Ethereum blockchain and interact with it using Metamask.

There are so many things that we can do with DApps. I hope you enjoyed this tutorial. If you have any questions, feel free to comment below. I will try to answer them as soon as possible.

Comments (3)

Markdown is supported.*
c1
very helpful

1

0

Feb 19, 2024

fc
Nice 💯

0

0

Jan 13, 2024

fc
Good

0

0

Jan 13, 2024

Related Posts