Introduction
Building a DApp (Decentralized Application) requires understanding both smart contracts and frontend integration. This guide covers the complete journey from concept to production deployment on Ethereum.
Core Concepts
DApp: Application combining smart contracts with a user interface.
Web3.js: JavaScript library for interacting with Ethereum nodes.
MetaMask: Browser extension wallet for managing Ethereum accounts.
ABI (Application Binary Interface): Interface specification for smart contracts.
RPC (Remote Procedure Call): Protocol for communicating with Ethereum nodes.
Testnet: Ethereum test network (Goerli, Sepolia) for development.
Gas: Computational cost of transactions on Ethereum.
Mainnet: Production Ethereum network.
DApp Architecture
DApp Architecture Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ User Interface (React/Vue) โ
โ โโ Connect Wallet (MetaMask) โ
โ โโ Display Data โ
โ โโ Send Transactions โ
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโโโโโโโโโ
โ Web3.js Library โ
โ (Ethereum Interaction) โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโโโโโโโโโ
โ Ethereum Node (RPC) โ
โ (Infura/Alchemy) โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโโโโโโโโโ
โ Smart Contracts โ
โ (Deployed on Chain) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโ
Setting Up Development Environment
1. Install Dependencies
# Install Node.js and npm
node --version
npm --version
# Create project directory
mkdir my-dapp
cd my-dapp
# Initialize npm project
npm init -y
# Install Web3.js
npm install web3
# Install Hardhat for smart contract development
npm install --save-dev hardhat
npx hardhat
2. Connect to Ethereum Network
const Web3 = require('web3');
// Connect to Ethereum node
const web3 = new Web3('https://goerli.infura.io/v3/YOUR_INFURA_KEY');
// Check connection
web3.eth.net.isListening()
.then(() => console.log('Connected to Ethereum'))
.catch(err => console.error('Connection failed:', err));
// Get account balance
web3.eth.getBalance('0x1234567890123456789012345678901234567890')
.then(balance => console.log('Balance:', web3.utils.fromWei(balance, 'ether'), 'ETH'));
3. MetaMask Integration
// Check if MetaMask is installed
if (typeof window.ethereum !== 'undefined') {
console.log('MetaMask is installed');
} else {
console.log('MetaMask not found');
}
// Request account access
async function connectWallet() {
try {
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
console.log('Connected account:', accounts[0]);
return accounts[0];
} catch (error) {
console.error('User denied account access:', error);
}
}
// Listen for account changes
window.ethereum.on('accountsChanged', (accounts) => {
console.log('Account changed:', accounts[0]);
});
// Listen for network changes
window.ethereum.on('chainChanged', (chainId) => {
console.log('Network changed:', chainId);
});
Smart Contract Interaction
1. Contract Deployment
const Web3 = require('web3');
const web3 = new Web3('https://goerli.infura.io/v3/YOUR_INFURA_KEY');
// Contract ABI and bytecode
const contractABI = [
{
"inputs": [],
"name": "getValue",
"outputs": [{"type": "uint256"}],
"stateMutability": "view",
"type": "function"
}
];
const contractBytecode = '0x...'; // Compiled contract bytecode
// Deploy contract
async function deployContract() {
const contract = new web3.eth.Contract(contractABI);
const deployment = contract.deploy({
data: contractBytecode,
arguments: []
});
const gas = await deployment.estimateGas();
const tx = await deployment.send({
from: '0x...', // Your address
gas: gas,
gasPrice: await web3.eth.getGasPrice()
});
console.log('Contract deployed at:', tx.options.address);
return tx;
}
2. Reading Contract Data
// Read contract state (no gas cost)
async function readContractData(contractAddress, contractABI) {
const contract = new web3.eth.Contract(contractABI, contractAddress);
// Call view function
const value = await contract.methods.getValue().call();
console.log('Contract value:', value);
return value;
}
3. Writing to Contract
// Write to contract (costs gas)
async function writeToContract(contractAddress, contractABI, userAddress) {
const contract = new web3.eth.Contract(contractABI, contractAddress);
// Estimate gas
const gas = await contract.methods.setValue(100).estimateGas({
from: userAddress
});
// Send transaction
const tx = await contract.methods.setValue(100).send({
from: userAddress,
gas: gas,
gasPrice: await web3.eth.getGasPrice()
});
console.log('Transaction hash:', tx.transactionHash);
return tx;
}
Frontend Integration (React Example)
import React, { useState, useEffect } from 'react';
import Web3 from 'web3';
function DApp() {
const [account, setAccount] = useState(null);
const [web3, setWeb3] = useState(null);
const [contract, setContract] = useState(null);
const [value, setValue] = useState(0);
// Initialize Web3 and connect wallet
useEffect(() => {
const initWeb3 = async () => {
if (window.ethereum) {
const web3Instance = new Web3(window.ethereum);
setWeb3(web3Instance);
try {
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
setAccount(accounts[0]);
} catch (error) {
console.error('User denied account access');
}
}
};
initWeb3();
}, []);
// Read contract data
useEffect(() => {
const readData = async () => {
if (contract) {
const val = await contract.methods.getValue().call();
setValue(val);
}
};
readData();
}, [contract]);
// Write to contract
const handleSetValue = async (newValue) => {
if (contract && account) {
try {
await contract.methods.setValue(newValue).send({
from: account
});
const val = await contract.methods.getValue().call();
setValue(val);
} catch (error) {
console.error('Transaction failed:', error);
}
}
};
return (
<div>
<h1>My DApp</h1>
<p>Connected Account: {account}</p>
<p>Contract Value: {value}</p>
<button onClick={() => handleSetValue(100)}>
Set Value to 100
</button>
</div>
);
}
export default DApp;
Deployment Strategies
1. Testnet Deployment
# Deploy to Goerli testnet
npx hardhat run scripts/deploy.js --network goerli
# Get testnet ETH from faucet
# https://goerlifaucet.com
2. Mainnet Deployment
# Deploy to Ethereum mainnet
npx hardhat run scripts/deploy.js --network mainnet
# Verify contract on Etherscan
npx hardhat verify --network mainnet CONTRACT_ADDRESS "constructor args"
3. Hosting Frontend
# Build React app
npm run build
# Deploy to Vercel
npm install -g vercel
vercel
# Or deploy to IPFS
npm install -g ipfs
ipfs add -r build/
Best Practices
1. Security
- Always get smart contracts audited
- Use OpenZeppelin libraries
- Test thoroughly on testnet
- Implement access controls
2. Performance
- Cache contract data
- Use event listeners instead of polling
- Optimize gas usage
- Implement pagination for large datasets
3. User Experience
- Show transaction status
- Handle errors gracefully
- Provide clear feedback
- Support multiple wallets
Common Pitfalls
Pitfall 1: Hardcoded Private Keys
Problem: Exposing private keys in code.
Solution: Use environment variables and .env files.
Pitfall 2: Not Handling Network Changes
Problem: App breaks when user switches networks.
Solution: Listen for chainChanged events and update accordingly.
Pitfall 3: Insufficient Gas Estimation
Problem: Transactions fail due to low gas.
Solution: Always estimate gas and add buffer.
Pros and Cons vs Alternatives
| Aspect | Ethereum | Polygon | Solana |
|---|---|---|---|
| Decentralization | โ Highest | โ ๏ธ Medium | โ ๏ธ Medium |
| Security | โ Proven | โ Good | โ ๏ธ Newer |
| Speed | โ ๏ธ Slow | โ Fast | โ Very Fast |
| Cost | โ Expensive | โ Cheap | โ Very Cheap |
| Ecosystem | โ Largest | โ Growing | โ Growing |
Resources
Documentation
Tools
Learning
Conclusion
Building DApps on Ethereum requires understanding both smart contracts and Web3 integration. Start with testnet, thoroughly test, and gradually move to mainnet. Always prioritize security and user experience.
Next Steps:
- Set up development environment
- Write and test smart contract
- Deploy to testnet
- Build frontend interface
- Get security audit
- Deploy to mainnet
Comments