Blockchain 14 min read

Build and Deploy a Web3 Smart Contract Project with React, Vite, and Hardhat

This guide walks you through understanding blockchain contracts, setting up a React‑Vite‑Tailwind front‑end, installing ethers and Hardhat, writing and testing a Solidity contract, configuring Hardhat, deploying to Goerli via Alchemy, and integrating the contract with a React application.

ELab Team
ELab Team
ELab Team
Build and Deploy a Web3 Smart Contract Project with React, Vite, and Hardhat

What Is Web3

Web3.0 refers to a decentralized internet where blockchain smart contracts replace traditional legal agreements, enabling automatic execution of digital transactions.

What Is a Contract

A blockchain contract records terms in code and executes them automatically, similar to a digital agreement that ensures a transaction completes.

For example, buying vegetables at a market requires a platform, legal regulations, and transaction rules; a smart contract digitizes these factors.

Project Directory

Client Side

The front‑end is built with React, Vite, and TailwindCSS, following standard H5 development practices.

TailwindCSS – https://tailwindcss.com/brand

Vite – https://vitejs.cn/

Operation Demo

Connect wallet

Disconnect

Add account (simulate transaction)

Execute transaction

View transaction record – Etherscan link

Contract Part

Core dependencies: ethers and Hardhat.

Dependency Installation

ethers – a complete Ethereum wallet implementation ( npm )

hardhat – professional Ethereum development environment ( npm )

@nomiclabs/hardhat-ethers – Hardhat plugin integrating ethers

@nomiclabs/hardhat-waffle – Hardhat plugin for Waffle testing

chai – assertion library for JavaScript tests

ethereum-waffle – testing framework for smart contracts

Create Contract Project

npx hardhat // create contract project
npx hardhat test // run test script and verify the example contract

Add Plugins

Solidity enhances readability; it is a statically‑typed language that runs on the Ethereum Virtual Machine.

Write Contract

//Transactions.sol

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

contract Transactions {
  uint256 transactionCounter;
  event Transfer(address from, address receiver, uint amount, string message, uint256 timestamp, string keyword);
  struct TransferStruct {
    address sender;
    address receiver;
    uint amount;
    string message;
    uint256 timestamp;
    string keyword;
  }
  TransferStruct[] transactions;
  function addToBlockchain(address payable receiver, uint amount, string memory message, string memory keyword) public {
    transactionCounter += 1;
    transactions.push(TransferStruct(msg.sender, receiver, amount, message, block.timestamp, keyword));
    emit Transfer(msg.sender, receiver, amount, message, block.timestamp, keyword);
  }
  function getAllTransactions() public view returns (TransferStruct[] memory) { return transactions; }
  function getTransactionCount() public view returns (uint256) { return transactionCounter; }
}

Contract Keywords

Reference: https://blog.csdn.net/m0_52991090/article/details/125627003

Deploy Contract

//deploy.js

const main = async () => {
  const transactionsFactory = await hre.ethers.getContractFactory("Transactions");
  const transactionsContract = await transactionsFactory.deploy();
  await transactionsContract.deployed();
  console.log("Transactions address: ", transactionsContract.address);
};

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

runMain();

Deploying requires test Ether (gas) in your wallet.

Gas

Gas measures computational work in the EVM (e.g., adding two numbers costs 3 gas, sending a transaction costs 21000 gas).

Wallet Setup

Install MetaMask from the Chrome Web Store.

Register using a Google email; the highlighted address is your wallet address.

Switch to a test network (e.g., Goerli) in MetaMask.

Get Test Ether

Use Goerli faucet (https://goerli-faucet.pk910.de/) to request test ETH for your wallet address.

Login to Alchemy

Alchemy provides blockchain node services; create an account and an app to obtain an API URL.

Hardhat Configuration

//hardhat.config.js

require('@nomiclabs/hardhat-waffle');

module.exports = {
  solidity: '0.8.0',
  networks: {
    goerli: {
      url: 'https://eth-goerli.g.alchemy.com/v2/mTxELUuUL2VIbHiXuZXSGNUV8gLCoN3x',
      accounts: ['0dd713782ba9bcd8d1a3b0a8ae09611cb910599bb223e1c7e9a0ad44a2e3b58a'],
    },
  },
};

MetaMask Private Key

Retrieve the private key from MetaMask (screenshots omitted for brevity).

Deploy Command

npx hardhat run scripts/deploy.js --network goerli

After successful deployment you obtain the contract address and ABI.

ABI

The ABI (Application Binary Interface) is stored in

/smart_contact/artifacts/contracts/Transactions.sol/Transactions.json

and defines how external applications interact with the contract.

Interact with Contract in React

import React, { useEffect, useState } from "react";
import { ethers } from "ethers";
import { contractABI, contractAddress } from "../utils/constants";

export const TransactionContext = React.createContext();

const { ethereum } = window;

const createEthereumContract = () => {
  const provider = new ethers.providers.Web3Provider(ethereum);
  const signer = provider.getSigner();
  return new ethers.Contract(contractAddress, contractABI, signer);
};

export const TransactionsProvider = ({ children }) => {
  const [formData, setFormData] = useState({ addressTo: "", amount: "", keyword: "", message: "" });
  const [currentAccount, setCurrentAccount] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [transactionCount, setTransactionCount] = useState(localStorage.getItem("transactionCount"));
  const [transactions, setTransactions] = useState([]);

  const handleChange = (e, name) => {
    setFormData(prev => ({ ...prev, [name]: e.target.value }));
  };

  const getAllTransactions = async () => {
    if (!ethereum) return;
    const contract = createEthereumContract();
    const available = await contract.getAllTransactions();
    const structured = available.map(tx => ({
      addressTo: tx.receiver,
      addressFrom: tx.sender,
      timestamp: new Date(tx.timestamp.toNumber() * 1000).toLocaleString(),
      message: tx.message,
      keyword: tx.keyword,
      amount: parseInt(tx.amount._hex) / 10**18,
    }));
    setTransactions(structured);
  };

  const checkIfWalletIsConnect = async () => {
    if (!ethereum) return alert("Please install MetaMask.");
    const accounts = await ethereum.request({ method: "eth_accounts" });
    if (accounts.length) {
      setCurrentAccount(accounts[0]);
      getAllTransactions();
    }
  };

  const checkIfTransactionsExists = async () => {
    if (!ethereum) return;
    const contract = createEthereumContract();
    const count = await contract.getTransactionCount();
    window.localStorage.setItem("transactionCount", count);
  };

  const connectWallet = async () => {
    if (!ethereum) return alert("Please install MetaMask.");
    const accounts = await ethereum.request({ method: "eth_requestAccounts" });
    setCurrentAccount(accounts[0]);
    window.location.reload();
  };

  const sendTransaction = async () => {
    if (!ethereum) return;
    const { addressTo, amount, keyword, message } = formData;
    const contract = createEthereumContract();
    const parsedAmount = ethers.utils.parseEther(amount);
    await ethereum.request({
      method: "eth_sendTransaction",
      params: [{ from: currentAccount, to: addressTo, gas: "0x5208", value: parsedAmount._hex }],
    });
    const txHash = await contract.addToBlockchain(addressTo, parsedAmount, message, keyword);
    setIsLoading(true);
    await txHash.wait();
    setIsLoading(false);
    const txCount = await contract.getTransactionCount();
    setTransactionCount(txCount.toNumber());
    window.location.reload();
  };

  useEffect(() => {
    checkIfWalletIsConnect();
    checkIfTransactionsExists();
  }, [transactionCount]);

  return (
    <TransactionContext.Provider value={{ transactionCount, connectWallet, transactions, currentAccount, isLoading, sendTransaction, handleChange, formData }}>
      {children}
    </TransactionContext.Provider>
  );
};

Explanation

The transfer flow first sends funds to the contract address, then the contract forwards them to the target wallet; this pattern is typical for smart‑contract‑based business applications. On a test network, having the private key is sufficient to simulate transactions.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ReActBlockchainWeb3EthereumSmart ContractTailwindCSSHardhat
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.