On-chain proposal creation and voting on Ethereum Sepolia. Transparent, permanent, and tamper-proof.
Live Contract: 0xb626C2dFabB8312Dc3d284D8054a3a29Ba2258D3
Author: @0xrayn
The landing page shows live on-chain stats (total proposals, total votes, pass rate) alongside all active proposals. No wallet needed to view.
Each card shows real-time vote counts and percentages pulled directly from the blockchain. Three vote options:
FOR means you support the proposal and want it to pass. AGAINST means you oppose it. ABSTAIN means you acknowledge the proposal but choose not to influence the FOR/AGAINST outcome. It still counts toward quorum.
Every vote is an actual Ethereum transaction. Once confirmed on-chain it cannot be changed.
Full detail page with the complete description, creator address, time remaining, quorum progress bar, and a live vote breakdown chart. You can also cast your vote from here.
Any connected wallet can submit a new governance proposal. Requires a title (min 10 characters), description (min 20 characters), and a voting duration of 1, 3, 7, or 14 days. Proposals go live on-chain the moment the transaction confirms.
Search proposals by title, description, or ID. Filter by status: ALL, ACTIVE, PENDING, or ENDED. Useful once the number of proposals grows.
Real-time governance statistics from the blockchain. Shows total proposals over time, vote distribution across all proposals, top proposals by participation, and the overall pass rate.
In-app documentation covering how the smart contract works, the ABI and function signatures, and local setup instructions.
Connect your wallet to see your personal voting history: every proposal you voted on, which choice you made, and a direct Etherscan link for each transaction.
| Layer | Technology |
|---|---|
| Framework | Next.js 16.2 (App Router, TypeScript) |
| Smart Contract | Solidity ^0.8.24 via Hardhat |
| Blockchain Client | Ethers.js v6 |
| Styling | Tailwind CSS v4 + Shadcn/UI |
| 3D Background | Three.js |
| Animations | Framer Motion |
| Notifications | Sonner |
| Wallet Support | MetaMask, Bitget, Coinbase, Brave, Trust (EIP-6963) + WalletConnect v2 |
The contract is already deployed on Sepolia. Just run the frontend: no private key or deployment needed.
git clone https://github.com/0xrayn/chainvotes
cd chainvotes
npm install
npm run devOpen http://localhost:3000 and proposals load directly from the blockchain.
To vote or create a proposal you need a wallet with some Sepolia ETH.
1. Clone and install
git clone https://github.com/0xrayn/chainvotes
cd chainvotes
npm install2. Create .env.local
cp .env.example .env.localFill in your values:
DEPLOYER_PRIVATE_KEY=0xYOUR_PRIVATE_KEY
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY
ETHERSCAN_API_KEY=YOUR_ETHERSCAN_KEY
NEXT_PUBLIC_CONTRACT_ADDRESS=
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=YOUR_PROJECT_IDNever commit .env.local to GitHub. It is already in .gitignore.
3. Get Sepolia ETH (free)
sepoliafaucet.com or faucet.quicknode.com/ethereum/sepolia
4. Deploy
npm run deploy:sepolia5. Verify on Etherscan (optional)
npm run verify -- 0xYOUR_CONTRACT_ADDRESS6. Start the app
npm run devInstall one of these extensions and switch to Sepolia Testnet (Chain ID: 11155111):
- MetaMask — most popular
- Bitget Wallet — all-in-one Web3
- Coinbase Wallet
- Trust Wallet
- Brave Wallet — built into Brave browser
Any wallet implementing EIP-6963 is detected automatically.
Connect from your phone without installing any browser extension:
- Click Connect Wallet in the app
- Select WalletConnect
- A QR code will appear
- Open your mobile wallet → tap the scan/WalletConnect icon
- Scan the QR code and approve — done
Supported mobile wallets include Trust Wallet, MetaMask Mobile, Rainbow, Coinbase Wallet, Bitget Wallet, and 300+ others.
Setup required: WalletConnect needs a free Project ID from cloud.reown.com (formerly WalletConnect Cloud). Add it to
.env.localasNEXT_PUBLIC_WALLETCONNECT_PROJECT_ID.
WalletConnect was rebranded to Reown in late 2024. The cloud dashboard is now at cloud.reown.com — the underlying protocol and package names remain the same.
Steps to get a Project ID:
- Go to cloud.reown.com and sign up (free)
- Click Create Project → choose AppKit
- Give it any name (e.g. "ChainVotes")
- Copy the Project ID
- Add to
.env.local:
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=paste_your_project_id_here- Restart the dev server:
npm run dev
No credit card required. The free tier is sufficient for development and small projects.
Open app
Proposals load automatically from the blockchain (no wallet needed)
Connect wallet to vote FOR / AGAINST / ABSTAIN
Confirm the transaction in your wallet
Vote is stored permanently on-chain
Toast appears with an Etherscan link to verify your transaction
| Command | Description |
|---|---|
npm run dev |
Start development server |
npm run build |
Build for production |
npm run compile |
Compile the smart contract |
npm run deploy:sepolia |
Deploy contract to Sepolia |
npm run deploy:local |
Deploy to local Hardhat node |
npm run verify -- 0x... |
Verify contract on Etherscan |
npm run node |
Start local Hardhat node |
chainvotes/
├── app/
│ ├── api/proposals/route.ts server-side API with 10s cache
│ ├── analytics/page.tsx governance stats
│ ├── explore/page.tsx search and filter
│ ├── proposal/[id]/page.tsx proposal detail and voting
│ ├── docs/page.tsx in-app documentation
│ ├── profile/page.tsx personal voting history
│ ├── layout.tsx
│ ├── page.tsx main governance page
│ └── globals.css
├── context/
│ └── ProposalsContext.tsx single shared data source for all pages
├── components/
│ ├── Navbar.tsx
│ ├── Hero.tsx
│ ├── ProposalCard.tsx
│ ├── CreateProposal.tsx
│ ├── Results.tsx
│ ├── ConnectWalletModal.tsx EIP-6963 + WalletConnect
│ ├── ThreeBackground.tsx
│ └── ui/
├── contracts/
│ └── ChainVotes.sol
├── hooks/
│ ├── useWallet.ts EIP-6963 multi-wallet + WalletConnect v2
│ └── useProposals.ts re-exports from context
├── lib/
│ ├── contract.ts ABI and address
│ └── utils.ts
├── scripts/
│ └── deploy.ts
├── types/
├── .env.example
└── hardhat.config.ts
| Variable | Required | Description |
|---|---|---|
DEPLOYER_PRIVATE_KEY |
Option B | Deployer wallet key. Server-only, never commit. |
SEPOLIA_RPC_URL |
Option B | Alchemy or Infura RPC URL. Server-only. |
ETHERSCAN_API_KEY |
Optional | For contract verification on Etherscan. |
NEXT_PUBLIC_CONTRACT_ADDRESS |
Auto | Filled automatically by the deploy script. |
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID |
WalletConnect | Free Project ID from cloud.reown.com. Required for mobile wallet support via QR code. |
| Status | Aspect | Detail |
|---|---|---|
| SAFE | Double voting prevented | AlreadyVoted error plus votes[address][proposalId] mapping, enforced on-chain |
| SAFE | Input validation on-chain | Title min 10 chars, description min 20 chars, duration between 1 second and 30 days |
| SAFE | No dangerous owner functions | No selfdestruct, no withdraw, no manipulative onlyOwner |
| SAFE | Time-based status | Computed from block.timestamp, cannot be manipulated by anyone including the deployer |
| NOTE | Anyone can create proposals | No minimum stake. Fine for testnet, consider restricting for mainnet. |
| NOTE | Quorum not enforced on-chain | Quorum is a frontend display only; the contract does not block results if quorum is not met |
| Status | Aspect | Detail |
|---|---|---|
| FIXED | Rate limiting | 10-second in-memory cache plus Cache-Control: s-maxage=10 header |
| FIXED | Cache invalidation after vote | Client calls POST /api/proposals after tx.wait() so the UI updates immediately |
| FIXED | RPC key not exposed to client | Only SEPOLIA_RPC_URL (server-only env var) is used in the API route |
| FIXED | No redundant RPC calls | Proposals are fetched once via React Context and shared across all pages |
| SAFE | Votes validated on-chain | The contract is the source of truth, frontend manipulation is impossible |
Do I need a wallet to view proposals? No. Proposals load from the blockchain without any wallet connection. A wallet is only needed to vote or create proposals.
Can I change my vote after submitting? No. Once a transaction confirms on-chain, your vote is permanent and cannot be modified.
What wallets are supported? Browser extensions: MetaMask, Bitget Wallet, Coinbase Wallet, Trust Wallet, and Brave Wallet (plus any EIP-6963 compliant wallet). Mobile: any wallet supporting WalletConnect v2 — Trust Wallet, MetaMask Mobile, Rainbow, and 300+ others.
Do I need a WalletConnect Project ID? Only if you want to use the WalletConnect / mobile QR feature. Get one free at cloud.reown.com. Browser extension wallets work without it.
WalletConnect redirects to reown.com — is that correct?
Yes. WalletConnect rebranded to Reown in late 2024. The dashboard is now at cloud.reown.com but the underlying protocol and npm packages (@walletconnect/ethereum-provider) remain unchanged.
Where do I get Sepolia ETH? sepoliafaucet.com or faucet.quicknode.com. Free, arrives in a few minutes.
What does ABSTAIN mean? Abstaining means you participate in governance without influencing the FOR/AGAINST result. It counts toward quorum but not toward the pass or fail decision.
MIT © 0xrayn








