Building Your First Web3 DApp: Guide to Ethereum Development
Ever wondered how to build applications that interact with blockchain? In this guide, I will create a simple decentralized application (DApp) that lets users connect their wallet and interact with a smart contract on the Ethereum network. Don’t worry if you’re new to Web3 — I will break everything down into simple steps!
What We’ll Build
We’re going to create a simple “Message Board” DApp where users can:
- Connect their MetaMask wallet
- Post messages to the blockchain
- Read messages from other users
Prerequisites
Before we start, make sure you have:
- Node.js installed on your computer
- MetaMask browser extension installed
- Basic knowledge of JavaScript
- Some test ETH on the Sepolia network (we’ll show you how to get this)
Step 1: Setting Up Your Development Environment
First, let’s create a new project and install the necessary dependencies:
mkdir message-board-dapp
cd message-board-dapp
npm init -y
npm install ethers hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers
npx hardhat init
Step 2: Creating the Smart Contract
Create a new file called MessageBoard.sol
in the contracts
folder:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MessageBoard {
struct Message {
address sender;
string content;
uint256 timestamp;
}
Message[] public messages;
event NewMessage(address sender, string content, uint256 timestamp);
function postMessage(string memory _content) public {
messages.push(Message(msg.sender, _content, block.timestamp));
emit NewMessage(msg.sender, _content, block.timestamp);
}
function getMessages() public view returns (Message[] memory) {
return messages;
}
}
Step 3: Setting Up the Frontend
Create a new index.html
file in your project root:
<!DOCTYPE html>
<html>
<head>
<title>Message Board DApp</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.message {
border: 1px solid #ddd;
padding: 10px;
margin: 10px 0;
border-radius: 5px;
}
</style>
</head>
<body>
<h1>Blockchain Message Board</h1>
<button id="connectWallet">Connect Wallet</button>
<div id="messageForm" style="display: none;">
<h2>Post a Message</h2>
<input type="text" id="messageInput" placeholder="Enter your message">
<button id="postMessage">Post Message</button>
</div>
<div id="messageBoard">
<h2>Messages</h2>
<div id="messages"></div>
</div>
<script src="app.js"></script>
</body>
</html>
Now create app.js
for our frontend logic:
import { ethers } from "https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js";
class MessageBoardApp {
constructor() {
this.contract = null;
this.provider = null;
this.signer = null;
this.contractAddress = "YOUR_CONTRACT_ADDRESS";
this.init();
}
async init() {
// Setup event listeners
document.getElementById('connectWallet').addEventListener('click', () => this.connectWallet());
document.getElementById('postMessage').addEventListener('click', () => this.postMessage());
// Check if MetaMask is installed
if (typeof window.ethereum !== 'undefined') {
this.provider = new ethers.BrowserProvider(window.ethereum);
await this.loadMessages();
} else {
alert('Please install MetaMask to use this DApp!');
}
}
async connectWallet() {
try {
// Request account access
await window.ethereum.request({ method: 'eth_requestAccounts' });
this.signer = await this.provider.getSigner();
// Show message form once connected
document.getElementById('messageForm').style.display = 'block';
document.getElementById('connectWallet').textContent = 'Connected!';
} catch (error) {
console.error('Error connecting wallet:', error);
}
}
async postMessage() {
if (!this.signer) {
alert('Please connect your wallet first!');
return;
}
const message = document.getElementById('messageInput').value;
if (!message) return;
try {
const contract = new ethers.Contract(this.contractAddress, contractABI, this.signer);
const tx = await contract.postMessage(message);
await tx.wait();
// Clear input and reload messages
document.getElementById('messageInput').value = '';
await this.loadMessages();
} catch (error) {
console.error('Error posting message:', error);
}
}
async loadMessages() {
const contract = new ethers.Contract(this.contractAddress, contractABI, this.provider);
const messages = await contract.getMessages();
const messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML = '';
messages.forEach(msg => {
const messageElement = document.createElement('div');
messageElement.className = 'message';
messageElement.innerHTML = `
<p><strong>From:</strong> ${msg.sender}</p>
<p>${msg.content}</p>
<small>Posted on: ${new Date(msg.timestamp * 1000).toLocaleString()}</small>
`;
messagesDiv.appendChild(messageElement);
});
}
}
// Initialize the app
new MessageBoardApp();
Step 4: Deploying the Smart Contract
- Create a deployment script in
scripts/deploy.js
:
async function main() {
const MessageBoard = await ethers.getContractFactory("MessageBoard");
const messageBoard = await MessageBoard.deploy();
await messageBoard.deployed();
console.log("MessageBoard deployed to:", messageBoard.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
2. Configure Hardhat for Sepolia network in hardhat.config.js
:
require("@nomiclabs/hardhat-waffle");
module.exports = {
solidity: "0.8.0",
networks: {
sepolia: {
url: "YOUR_SEPOLIA_RPC_URL",
accounts: ["YOUR_PRIVATE_KEY"]
}
}
};
3. Deploy the contract:
npx hardhat run scripts/deploy.js --network sepolia
Step 5: Testing Your DApp
- Get some test ETH from a Sepolia faucet
- Update the
contractAddress
inapp.js
with your deployed contract address - Serve your frontend files using a local server:
npx http-server
Common Issues and Solutions
- MetaMask not connecting
- Make sure you’re on the Sepolia network in MetaMask
- Check if your browser allows pop-ups
2. Transaction failing
- Ensure you have enough test ETH for gas fees
- Check the console for specific error messages
3. Messages not loading
- Verify your contract address is correct
- Ensure you’re connected to the right network
Next Steps
Now that you have a basic DApp working, here are some ways to expand it:
- Add the ability to like or reply to messages
- Store message metadata on IPFS
- Add user profiles
- Implement message categories or tags
Conclusion
Congratulations! You’ve built your first Web3 DApp. While this is a simple example, it demonstrates the core concepts of Web3 development:
- Smart contract development
- Wallet integration
- User interaction with the blockchain
- Frontend integration with Web3 technologies
Remember, the blockchain is immutable, so always test thoroughly on testnets before deploying to mainnet.