Building Web3 dApps with Solidity
A comprehensive guide to developing decentralized applications on Ethereum using Solidity and Hardhat.
Introduction
Decentralized Applications (dApps) are changing the way we interact with digital services. By leveraging blockchain technology, dApps offer transparency, security, and censorship resistance. At the heart of most dApps are Smart Contracts, self-executing contracts with the terms of the agreement directly written into code.
In this tutorial, we will walk through the process of building a full-stack dApp, from writing the smart contract in Solidity to connecting it with a frontend.
Prerequisites
- Node.js installed
- Metamask wallet extension
- Basic understanding of JavaScript/TypeScript
Setting Up the Project
We will use Hardhat, a popular development environment for Ethereum software.
mkdir my-dapp
cd my-dapp
npm init -y
npm install --save-dev hardhat
npx hardhat init
Choose "Create a TypeScript project" when prompted.
Writing the Smart Contract
Let's create a simple "Greeter" contract that allows users to store and retrieve a greeting message on the blockchain.
Create a file contracts/Greeter.sol:
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract Greeter {
string private greeting;
constructor(string memory _greeting) {
console.log("Deploying a Greeter with greeting:", _greeting);
greeting = _greeting;
}
function greet() public view returns (string memory) {
return greeting;
}
function setGreeting(string memory _greeting) public {
console.log("Changing greeting from '%s' to '%s'", greeting, _greeting);
greeting = _greeting;
}
}
Compiling and Deploying
Compile the contract:
npx hardhat compile
Create a deployment script scripts/deploy.ts:
import { ethers } from "hardhat";
async function main() {
const Greeter = await ethers.getContractFactory("Greeter");
const greeter = await Greeter.deploy("Hello, Web3!");
await greeter.deployed();
console.log("Greeter deployed to:", greeter.address);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Run a local blockchain node and deploy:
npx hardhat node
npx hardhat run scripts/deploy.ts --network localhost
Connecting the Frontend
Now that our contract is on the blockchain (local network), we can interact with it using a library like ethers.js or viem.
In your frontend application (e.g., Next.js or Vue), you would:
- Connect to the user's wallet (Metamask).
- Instantiate the contract using its address and ABI (Application Binary Interface).
- Call the
greet()andsetGreeting()functions.
import { ethers } from 'ethers';
import GreeterArtifact from './artifacts/contracts/Greeter.sol/Greeter.json';
const contractAddress = "YOUR_DEPLOYED_ADDRESS";
async function requestAccount() {
await window.ethereum.request({ method: 'eth_requestAccounts' });
}
async function fetchGreeting() {
if (typeof window.ethereum !== 'undefined') {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const contract = new ethers.Contract(contractAddress, GreeterArtifact.abi, provider);
try {
const data = await contract.greet();
console.log('data: ', data);
} catch (err) {
console.log("Error: ", err);
}
}
}
Conclusion
You've just built the foundation of a Web3 dApp! From here, you can explore more complex logic, token standards like ERC-20 and ERC-721 (NFTs), and deploy your contracts to testnets like Sepolia.