A decentralized Twitter-like application with:
- Solidity + Hardhat for the smart contract
- React + Web3.js + Tailwind CSS for the frontend
- Node.js + Express + MongoDB for backend caching and indexing
- IPFS via Pinata for tweet content storage
The blockchain remains the source of truth. MongoDB acts only as a cache and indexing layer so every client sees globally synchronized tweets and likes faster.
The contract lives in contracts/Twitter.sol and supports:
createTweet(string ipfsHash)likeTweet(uint tweetId)getAllTweets()TweetCreatedandTweetLikedevents- double-like prevention through
hasLiked
The backend lives in backend/:
backend/
├── config/
│ └── db.js
├── models/
│ └── Tweet.js
├── routes/
│ └── tweets.js
├── services/
│ └── blockchainListener.js
├── .env.example
├── package.json
└── server.js
What it does:
- connects to MongoDB
- replays tweet + like state from the contract
- listens for
TweetCreatedandTweetLikedevents - updates MongoDB automatically
- exposes cached tweet APIs for the frontend
Available APIs:
GET /healthGET /tweetsGET /tweets/:idGET /tweets/user/:address
The frontend now:
- reads the feed from the backend cache
- still creates tweets on-chain through MetaMask
- still likes tweets on-chain through MetaMask
- waits for backend cache sync after each transaction
- polls the backend every 4 seconds by default for near real-time updates
Key frontend files:
client/src/App.jsxclient/src/utils/web3.jsclient/src/utils/api.jsclient/src/utils/ipfs.js
Root .env for Hardhat:
cp .env.example .envExpected values:
SEPOLIA_RPC_URL=
PRIVATE_KEY=
ETHERSCAN_API_KEY=Backend .env:
cp backend/.env.example backend/.envExpected values:
PORT=5001
RPC_URL=
PRIVATE_KEY=
MONGO_URI=
CONTRACT_ADDRESS=
CHAIN_START_BLOCK=0Notes:
PRIVATE_KEYis kept for environment consistency, but the current backend is read-only and never sends likes or tweets itself.- Set
CHAIN_START_BLOCKto your deployment block on Sepolia for faster historical reindexing.
Client .env:
cp client/.env.example client/.envExpected values:
VITE_SEPOLIA_CHAIN_ID=11155111
VITE_EXPECTED_NETWORK_NAME=Sepolia
VITE_API_BASE_URL=http://localhost:5001
VITE_TWEETS_POLL_INTERVAL_MS=4000
VITE_IPFS_GATEWAY_BASE=https://gateway.pinata.cloud/ipfs
VITE_PINATA_JWT=Install root, backend, and client dependencies:
npm install
npm install --prefix backend
npm install --prefix clientnpm run nodenpm run deploy:localThis updates:
client/src/contracts/Twitter.jsonclient/src/contracts/contract-address.json
Run your local MongoDB server however you normally do. Example:
mongod --dbpath /path/to/your/dbnpm run backend:devnpm run client:dev- Put your Sepolia RPC URL and deployer private key in root
.env. - Run:
npm run deploy:sepolia- Copy the deployed contract address into
backend/.envasCONTRACT_ADDRESS. - Set
RPC_URLinbackend/.envto the same Sepolia RPC. - Start the backend and frontend.
Verified locally:
npm run compilenpm run testnpm run build --prefix client- backend files parse with
node --check - local backend indexed a real
TweetCreatedevent into MongoDB - local backend indexed a real
TweetLikedevent into MongoDB GET /tweets,GET /tweets/:id, andGET /tweets/user/:addressreturned the expected cached data
- The smart contract is the source of truth.
- The backend never creates tweets or likes on behalf of users.
- Likes must always go through the smart contract.
- MongoDB is only a cache and indexing layer.
