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.
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 contractAdd 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 goerliAfter 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.jsonand 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
