README
🌐 Web3 Blog Platform
A fully decentralized blogging platform built on blockchain technology, where writers truly own their content and readers support creators directly. Articles are stored permanently on IPFS with metadata on-chain, ensuring censorship resistance and true content ownership.
🚀 Quick Start
# Clone and install
git clone https://github.com/yourusername/web3-blog.git
cd web3-blog
# Install dependencies
cd client && pnpm install
cd ../server && pnpm install
# Setup environment
cp client/.env.example client/.env.local
cp server/.env.example server/.env
# Start local blockchain
cd server && pnpm hardhat node &
# Deploy contract
pnpm deploy:local
# Start server
pnpm dev &
# Start client (new terminal)
cd client && pnpm dev
Visit http://localhost:3000 and connect your wallet!
📋 Table of Contents
- Features
- Tech Stack
- Installation
- Environment Setup
- Running Locally
- Testing
- Deployment
- Architecture
- Project Structure
- Contributing
✨ Features
🔐 True Ownership
- Writers own content permanently on blockchain
- Censorship-resistant with IPFS storage
- Immutable publishing
📝 Rich Content
- Novel editor with Markdown support
- Image uploads to IPFS
- Tag system (up to 10 tags)
- Auto-save drafts
- Reading time calculation
🔍 Discovery
- Real-time search with debouncing
- Multi-tag filtering
- Pagination
- Author profiles with stats
- Reading progress bar
💬 Social
- On-chain likes with animations
- Decentralized comments
- Share on social media
- Author statistics
🛠️ Developer Experience
- Multi-wallet support (RainbowKit)
- Multi-network (Polygon, Base, localhost)
- Full TypeScript
- Comprehensive testing (68 tests)
- Modular architecture
🏗️ Tech Stack
Frontend
- Next.js 16 - React framework with App Router
- TypeScript 5.7 - Type safety
- Tailwind CSS 4 - Styling
- shadcn/ui - Component library
- Novel - Rich text editor
- Jest + Playwright - Testing
Web3
- wagmi v2.19 - React hooks for Ethereum
- viem v2.43 - TypeScript Ethereum library
- RainbowKit 2.2 - Wallet connection
- ethers v6 - Blockchain interaction
Backend
- Express - API server
- Hardhat 2.20 - Contract development
- Solidity 0.8.28 - Smart contracts
- OpenZeppelin 5.4 - Secure libraries
- Pinata - IPFS pinning
- Jest + Supertest - API testing
📦 Installation
Prerequisites
- Node.js 18+
- pnpm (
npm install -g pnpm) - Git
- MetaMask or compatible wallet
Clone Repository
git clone https://github.com/yourusername/web3-blog.git
cd web3-blog
Install Dependencies
# Client
cd client
pnpm install
# Server
cd ../server
pnpm install
🔧 Environment Setup
📍 Getting Required Keys
1. WalletConnect Project ID (Required)
- Go to https://cloud.walletconnect.com
- Create account and new project
- Copy Project ID
2. Pinata JWT (Required for IPFS uploads)
- Go to https://app.pinata.cloud
- Create account
- Navigate to API Keys → New Key
- Enable "pinFileToIPFS" and "pinJSONToIPFS"
- Copy JWT token
3. Alchemy RPC (Optional, recommended for production)
- Go to https://dashboard.alchemy.com
- Create account and new app
- Select Polygon Amoy or Mainnet
- Copy HTTP URL
📝 Client Environment
Create client/.env.local:
# API & Site URLs
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_SITE_URL=http://localhost:3000
# WalletConnect (Required - get from cloud.walletconnect.com)
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id_here
# Contract Addresses (auto-filled for localhost)
NEXT_PUBLIC_CONTRACT_ADDRESS_LOCALHOST=0x5FbDB2315678afecb367f032d93F642f64180aa3
NEXT_PUBLIC_CONTRACT_ADDRESS_POLYGON_AMOY=0x0000000000000000000000000000000000000000
NEXT_PUBLIC_CONTRACT_ADDRESS_POLYGON=0x0000000000000000000000000000000000000000
NEXT_PUBLIC_CONTRACT_ADDRESS_BASE_SEPOLIA=0x0000000000000000000000000000000000000000
NEXT_PUBLIC_CONTRACT_ADDRESS_BASE=0x0000000000000000000000000000000000000000
Why client needs contract addresses?
- Client reads blockchain data directly (free, decentralized)
- Reduces server load and latency
- Works even if server is down (read-only mode)
- True Web3: trustless, censorship-resistant
📝 Server Environment
Create server/.env:
# Network Configuration
NETWORK=localhost
NODE_ENV=development
PORT=3001
# CORS Configuration
CORS_ORIGINS=http://localhost:3000,http://localhost:3002
PRODUCTION_DOMAIN=
# Blockchain Configuration
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
# Contract Addresses by Network
CONTRACT_ADDRESS_LOCALHOST=0x5FbDB2315678afecb367f032d93F642f64180aa3
CONTRACT_ADDRESS_AMOY=
CONTRACT_ADDRESS_POLYGON=
# RPC URLs by Network
RPC_URL_LOCALHOST=http://127.0.0.1:8545
RPC_URL_AMOY=https://rpc-amoy.polygon.technology
RPC_URL_POLYGON=https://polygon-rpc.com
# IPFS Configuration (Required for production)
IPFS_GATEWAY=https://gateway.pinata.cloud/ipfs/
PINATA_JWT=your_pinata_jwt_here
For localhost development: Use the Hardhat default private key shown above (has test ETH).
For production: Use your own wallet private key with actual funds.
🏃 Running Locally
Step 1: Start Local Blockchain
cd server
pnpm hardhat node
Keep this terminal running. You'll see 20 test accounts with 10,000 ETH each.
Step 2: Deploy Smart Contract
In a new terminal:
cd server
pnpm deploy:local
Copy the deployed contract address from output:
BlogPlatform deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Step 3: Update Contract Address
The address is already in .env.local, but verify it matches:
NEXT_PUBLIC_CONTRACT_ADDRESS_LOCALHOST=0x5FbDB2315678afecb367f032d93F642f64180aa3
Step 4: Start Server
cd server
pnpm dev
Server runs at http://localhost:3001
- API Info: http://localhost:3001
- Swagger Docs: http://localhost:3001/api-docs
Step 5: Start Client
cd client
pnpm dev
Client runs at http://localhost:3000
Step 6: Connect Wallet
- Open http://localhost:3000
- Click "Connect Wallet"
- If prompted, add Hardhat network:
- Network Name: Hardhat Local
- RPC URL: http://127.0.0.1:8545
- Chain ID: 31337
- Currency: ETH
- Import test account (use private key from Step 1)
✅ Ready to publish your first article!
🧪 Testing
Client Tests
cd client
# Unit tests (Jest)
pnpm test # Watch mode
pnpm test:ci # CI mode with coverage
# E2E tests (Playwright)
pnpm test:e2e # Headless
pnpm test:e2e:ui # UI mode
pnpm test:e2e:headed # Headed browser
Client Test Coverage:
- ✅ 6 unit tests (utils, IPFS helpers)
- ✅ 10 E2E tests (navigation, forms)
Server Tests
cd server
# API tests
pnpm test # All tests
pnpm test:api # API unit tests only
pnpm test:e2e # E2E workflow tests
pnpm test:coverage # With coverage report
Server Test Results:
Test Suites: 2 passed, 2 total
Tests: 28 passed, 28 total
Coverage: 70.58% statements, 84.21% functions
Smart Contract Tests
cd server
pnpm test:contracts
Contract Test Results:
40 passing
Coverage: 100% statements, 95% branches
All Tests
# Run everything
pnpm test:all
Total: 68 tests passing ✅
🚀 Deployment
Deploy to Polygon Amoy (Testnet)
1. Get Test MATIC
- Visit https://faucet.polygon.technology
- Select "Amoy Testnet"
- Paste your wallet address
- Request test MATIC
2. Deploy Contract
cd server
# Update .env with your wallet private key
PRIVATE_KEY=0xYourPrivateKeyHere
# Deploy
pnpm deploy:amoy
Save the deployed contract address!
3. Deploy Server (Vercel)
cd server
vercel --prod
Set these environment variables in Vercel dashboard:
NETWORK=amoy
CONTRACT_ADDRESS_AMOY=0xYourDeployedAddress
RPC_URL_AMOY=https://rpc-amoy.polygon.technology
PRIVATE_KEY=your_relayer_private_key
PINATA_JWT=your_pinata_jwt
CORS_ORIGINS=https://your-client-domain.vercel.app
PRODUCTION_DOMAIN=your-server-domain.vercel.app
IPFS_GATEWAY=https://gateway.pinata.cloud/ipfs/
4. Deploy Client (Vercel)
cd client
vercel --prod
Set these environment variables in Vercel dashboard:
NEXT_PUBLIC_API_URL=https://your-server-domain.vercel.app
NEXT_PUBLIC_SITE_URL=https://your-client-domain.vercel.app
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id
NEXT_PUBLIC_CONTRACT_ADDRESS_POLYGON_AMOY=0xYourDeployedAddress
5. Verify Deployment
- ✅ Visit server:
https://your-server.vercel.app/ - ✅ Check API docs:
https://your-server.vercel.app/api-docs - ✅ Visit client:
https://your-client.vercel.app/ - ✅ Connect wallet and publish test article
Deploy to Polygon Mainnet
Same steps as Amoy, but:
- Use
NETWORK=polygon - Use mainnet RPC URLs
- Ensure wallet has real MATIC for gas
- Deploy with
pnpm deploy:polygon
🏛️ Architecture
Hybrid Architecture
The platform uses a hybrid approach combining:
Client-Side (Decentralized)
- Direct blockchain reads (free, no gas)
- Wallet-signed transactions (user pays gas)
- IPFS content fetching
- Real-time updates
Benefits:
- ✅ Trustless - no server dependency
- ✅ Censorship-resistant
- ✅ Privacy - direct blockchain interaction
- ✅ Resilient - works if server is down
Server-Side (API)
- Meta-transactions (gasless for users)
- IPFS uploads (secure Pinata integration)
- Relayer pattern (server pays gas)
- REST API for convenience
Benefits:
- ✅ Gasless onboarding
- ✅ Better UX (no wallet confirmations for reads)
- ✅ Secure IPFS uploads
- ✅ Traditional API for integrations
Network Configuration
Environment-based network selection:
NETWORK=localhost → Uses RPC_URL_LOCALHOST, CONTRACT_ADDRESS_LOCALHOST
NETWORK=amoy → Uses RPC_URL_AMOY, CONTRACT_ADDRESS_AMOY
NETWORK=polygon → Uses RPC_URL_POLYGON, CONTRACT_ADDRESS_POLYGON
Smart Contract Architecture
BlogPlatform.sol
├── Articles (create, update, delete)
├── Likes (like/unlike)
├── Comments (add, retrieve)
├── Author Management
└── Tag-based Filtering
Security:
- ReentrancyGuard on state changes
- Owner-only modifications
- Input validation
- Event emissions
Server Architecture
server/src/
├── server.ts # Entry point (12 lines)
├── app.ts # Express app setup
├── config/
│ ├── network.ts # Blockchain configuration
│ ├── cors.ts # CORS logic
│ └── swagger.ts # API documentation
├── middleware/
│ ├── errorHandler.ts
│ └── signatureVerify.ts
├── controllers/ # Business logic
│ ├── articleController.ts
│ ├── commentController.ts
│ ├── likeController.ts
│ └── ipfsController.ts
└── routes/ # API routes
├── index.ts # Root + API info
├── articles.ts
├── comments.ts
├── likes.ts
└── ipfs.ts
📂 Project Structure
web3-blog/
├── client/ # Next.js frontend
│ ├── src/
│ │ ├── app/ # App router pages
│ │ ├── components/ # React components
│ │ ├── config/ # Configuration
│ │ ├── hooks/ # Custom hooks
│ │ ├── lib/ # Utilities
│ │ └── types/ # TypeScript types
│ ├── e2e/ # Playwright E2E tests
│ ├── __tests__/ # Jest unit tests
│ ├── playwright.config.ts
│ ├── jest.config.ts
│ └── package.json
│
├── server/ # Express + Hardhat backend
│ ├── src/
│ │ ├── config/ # Configuration modules
│ │ ├── controllers/ # Business logic
│ │ ├── middleware/ # Express middleware
│ │ └── routes/ # API routes
│ ├── contracts/ # Solidity contracts
│ ├── scripts/ # Deployment scripts
│ ├── test/ # Jest + Supertest tests
│ └── package.json
│
└── README.md # This file
🤝 Contributing
Contributions welcome! Please:
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing) - Open Pull Request
Guidelines:
- Follow TypeScript best practices
- Write tests for new features
- Update documentation
- Use conventional commits
- Ensure all tests pass
📄 License
MIT License - see LICENSE file for details.
🙏 Acknowledgments
- Next.js, wagmi, viem teams
- RainbowKit, shadcn/ui
- OpenZeppelin, Hardhat
- Pinata, Helia IPFS
Built with ❤️ by Md Arif Hossain
📧 hi@devarif.me | 🌐 devarif.me