A decentralized tipping board on Ethereum Sepolia.
Support community heroes like Ifeoma β who maintains your city's bus timetables for free β with instant, fee-free ETH tips. Every tip is recorded permanently on-chain.
- π¦ MetaMask / WalletConnect integration via RainbowKit
- β‘ Instant ETH tips with short appreciation notes
- π Fully on-chain supporter wall β zero databases, zero middlemen
- π Real-time updates via contract events
- π Owner withdrawal for Ifeoma to claim her tips
- π ENS-ready address display (shortened + Etherscan links)
- π± Responsive design β works on mobile and desktop
- π¨ Premium dark-mode UI with glassmorphism and animations
blockchain_proj/
βββ contracts/
β βββ PraiseBoard.sol # Ownable Solidity contract
βββ scripts/
β βββ deploy.ts # Hardhat deploy script
βββ test/
β βββ PraiseBoard.test.ts # Full test suite (13 tests)
βββ frontend/
β βββ src/
β β βββ abis/PraiseBoard.ts # Contract ABI (typed)
β β βββ config/wagmi.ts # Wagmi + RainbowKit config
β β βββ hooks/
β β β βββ usePraiseBoard.ts # Contract interactions
β β β βββ useWalletState.ts # Wallet / network state
β β βββ components/
β β β βββ ConnectButton.tsx # Wallet connect
β β β βββ NetworkGuard.tsx # Wrong network banner
β β β βββ TipForm.tsx # Send tip UI
β β β βββ SupporterWall.tsx # Live on-chain feed
β β β βββ WithdrawButton.tsx # Owner withdraw panel
β β βββ App.tsx
β β βββ main.tsx
β βββ index.html
βββ hardhat.config.ts
βββ .env.example
βββ README.md
| Layer | Technology |
|---|---|
| Smart Contract | Solidity 0.8.24, OpenZeppelin 5.x |
| Dev Framework | Hardhat 2.x + TypeScript |
| Blockchain | Ethereum Sepolia |
| RPC | QuickNode |
| Frontend | React 18, Vite 5, TypeScript |
| Web3 Client | wagmi 2.x + viem 2.x |
| Wallet UI | RainbowKit 2.x |
| State | TanStack React Query 5 |
| Styling | Vanilla CSS (dark glassmorphism) |
| Property | Value |
|---|---|
| Contract Name | PraiseBoard |
| Network | Ethereum Sepolia (chainId: 11155111) |
| Contract Address | 0x___FILL_AFTER_DEPLOYMENT___ |
| Deploy Tx Hash | 0x___FILL_AFTER_DEPLOYMENT___ |
| Solidity | ^0.8.24 |
| License | MIT |
- Node.js 18+
- A funded Sepolia wallet (get test ETH at sepoliafaucet.com)
- QuickNode Sepolia RPC URL
git clone https://github.com/Harshad710/praise-board.git
cd blockchain_proj
npm installcp .env.example .envEdit .env:
QUICKNODE_URL=https://your-endpoint.quiknode.pro/your-key/
PRIVATE_KEY=your_deployer_private_keynpx hardhat compilenpx hardhat testnpx hardhat run scripts/deploy.ts --network sepoliaCopy the printed contract address.
npx hardhat verify --network sepolia <CONTRACT_ADDRESS>cd frontend
npm installcp .env.example .envEdit frontend/.env:
VITE_CONTRACT_ADDRESS=0xYourDeployedContractAddress
VITE_QUICKNODE_URL=https://your-endpoint.quiknode.pro/your-key/
VITE_WALLETCONNECT_PROJECT_ID=your_walletconnect_project_idNote: Get a free WalletConnect project ID at cloud.walletconnect.com
npm run dev| Variable | Required | Description |
|---|---|---|
QUICKNODE_URL |
Yes | QuickNode Sepolia RPC endpoint URL |
PRIVATE_KEY |
Yes | Deployer wallet private key |
| Variable | Required | Description |
|---|---|---|
VITE_CONTRACT_ADDRESS |
Yes | Deployed PraiseBoard contract address |
VITE_QUICKNODE_URL |
Yes | QuickNode Sepolia RPC for frontend |
VITE_WALLETCONNECT_PROJECT_ID |
Yes | WalletConnect project ID |
[Add screenshots after running the frontend]
- Home page with connect prompt
- Connected wallet + tip form
- Pending transaction state
- Success state with Etherscan link
- Supporter wall with tips
- Connect MetaMask / WalletConnect
- Send ETH tip with short note
- Note capped at 200 bytes on-chain + client-side validation
- Tip recorded on-chain (
Tip[]struct array) -
NewTipevent emitted on each tip - Supporter wall reads ONLY from blockchain (no database)
- Live wall refresh on new tip (event listener + refetch)
- Supporter wall sorted newest-first
- Owner withdraw function
- Wrong network detection + one-click switch to Sepolia
- Wallet not installed error handling
- User rejected transaction error handling
- Insufficient funds error handling
- Zero ETH tip rejected (
ZeroTipNotAllowedcustom error) - Long note rejected (
NoteTooLongcustom error) - Pending transaction state (spinner + Etherscan link)
- Success state with confirmation
- Hardhat tests: 13 passing tests
- TypeScript throughout
- ESLint + Prettier configured
- Checks-Effects-Interactions pattern in contract
- OpenZeppelin
Ownableinherited - Custom errors (no
requirestrings) -
getAllTips()view function - Responsive design (mobile + desktop)
PraiseBoard
Deployment
β should set the deployer as the owner
β should start with zero tips
receiveTip
β should accept a valid tip and emit NewTip event
β should store the sender address correctly
β should store the amount correctly
β should store the note correctly
β should store a non-zero timestamp
β should reject a zero-ETH tip with ZeroTipNotAllowed
β should reject a note longer than 200 bytes with NoteTooLong
β should accept a note of exactly 200 bytes
β should accumulate multiple tips from different senders
getAllTips
β should return an empty array when no tips have been sent
β should return all tips in correct order
withdraw
β should allow the owner to withdraw all funds
β should reject withdrawal from a non-owner account
β should reject withdrawal when balance is zero
16 passing
- Custom errors used throughout (no
revertstrings) β gas efficient - Checks-Effects-Interactions pattern in
receiveTipandwithdraw onlyOwnermodifier from OpenZeppelin guards withdrawal- Note length capped at 200 bytes on-chain
- Zero-value tips rejected with custom error
- Pull-over-push withdrawal pattern β no automatic transfers to external addresses
MIT