Vladyslav Kovalchuk
← All cases

Building a decentralized chat to learn Web3 from the ground up

Building a decentralized chat to learn Web3 from the ground up screenshot

Problem

I wanted to build Web3 applications seriously, not just read about them. Most tutorials stop at connecting a wallet and calling a single contract method. I needed to go through the full cycle — writing and deploying a smart contract, handling transactions and confirmations on the frontend, managing wallet state, and structuring a codebase that could grow into a larger dapp. The goal was to pick a deliberately simple product idea (a chat room) so the focus stayed on the Web3 tooling and architecture rather than business logic.

Constraints

  • The product had to be simple enough to finish quickly, but the architecture had to be realistic — not a throwaway demo.
  • All messages had to live on-chain. No database, no centralized backend for chat data.
  • The app had to work for users with zero ETH in their wallet, so a faucet was necessary.
  • The frontend had to feel like a real product, not a blockchain experiment with raw transaction hashes.

Solution

I wrote a MessageBoard smart contract in Solidity that stores messages (sender address, text, timestamp) on-chain and emits events for real-time updates. I deployed it to Sepolia via Hardhat. On the frontend, I used Next.js 15 with wagmi and viem for all blockchain interactions — reading messages, writing transactions, and subscribing to NewMessage events. RainbowKit handled the wallet connection UX. I built a server-side faucet as a Next.js API route that uses a private key to send test ETH to users, keeping the key secure on the server. The UI was built with shadcn/ui components, Tailwind CSS, and full dark mode support.

Key technical decisions

Wagmi + viem over ethers.js
Wagmi provides React hooks designed specifically for wallet and contract interactions — useReadContract, useWriteContract, useWatchContractEvent. Combined with viem as the transport layer, this gave me type-safe contract calls and better bundle size than ethers.js. I still used ethers on the server for the faucet where React hooks are not available.
On-chain event subscriptions for real-time UX
Instead of polling the contract for new messages, I used useWatchContractEvent to listen to the NewMessage event. This gave near-real-time updates without unnecessary RPC calls and taught me how event-driven architecture works on Ethereum.
Atomic component structure
I organized components into atoms, forms, widgets, and providers. Even for a small app, this separation made the codebase navigable and established patterns I can reuse in larger dapps — especially the provider composition pattern for wagmi, RainbowKit, and React Query.
Server-side faucet
Exposing a private key on the client is not an option. The faucet runs as a Next.js API route that holds the key in an environment variable and sends small amounts of Sepolia ETH. This was a practical lesson in keeping secrets server-side in a Web3 context.

Outcome

The app is live on Vercel and functional on Sepolia. I came out of this project confident with the full Web3 frontend stack: wagmi for contract hooks, viem for low-level calls, RainbowKit for wallet UX, and Hardhat for contract development and deployment. The codebase is structured to scale — adding new contract interactions or screens is a matter of writing a new hook and composing existing providers. The project directly informed the architecture of the larger dapp I am building now.