A confidential voting application built with Zama's FHEVM technology, demonstrating privacy-preserving voting using Fully Homomorphic Encryption (FHE).
- 🔐 End-to-End Encryption: All votes are encrypted using FHE before submission
- 👤 Individual Privacy: Nobody can see how you voted, not even the contract owner
- 📊 Aggregate Results: Only aggregate vote counts can be revealed
- 🚫 Double-Vote Prevention: Each address can vote only once
- ✅ Verifiable Security: Comprehensive test suite with 18 passing tests
- 🎨 Modern UI: Beautiful Zama-branded interface
- Node.js >= 20
- pnpm
- MetaMask browser extension
# Clone the repository
git clone <your-repo-url>
cd fhevm-private-poll
# Initialize submodules
git submodule update --init --recursive
# Install dependencies
pnpm installTerminal 1 - Start Hardhat:
pnpm chainTerminal 2 - Deploy Contracts:
cd packages/hardhat
pnpm deploy:localhostTerminal 3 - Start Frontend:
pnpm startOpen Browser:
http://localhost:3000
-
Add Hardhat Network:
- Network Name:
Hardhat Local - RPC URL:
http://localhost:8545 - Chain ID:
31337 - Currency:
ETH
- Network Name:
-
Import Test Account:
Private Key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
- ✅ Real wallet connection
- ✅ Full UI/UX flow
⚠️ FHE operations simulated (no Gateway needed)- 📍 Best for: Presentations, testing UI
Access: http://localhost:3000
- ✅ No wallet needed
- ✅ Complete flow visualization
⚠️ All operations simulated- 📍 Best for: Screenshots, documentation
Access: Open demo-full.html in browser
- ✅ Real FHE operations (mocked)
- ✅ Full contract testing
- ✅ 18 comprehensive tests
- 📍 Best for: Verifying contract logic
Run:
cd packages/hardhat
npx hardhat test test/PrivatePoll.test.ts- ✅ Real FHE encryption
- ✅ Real blockchain transactions
- ✅ Zama's Gateway service
- 📍 Best for: Production testing
Setup: See Deployment
fhevm-private-poll/
├── packages/
│ ├── hardhat/ # Smart contracts
│ │ ├── contracts/
│ │ │ └── PrivatePoll.sol # Main voting contract
│ │ ├── test/
│ │ │ └── PrivatePoll.test.ts # 18 test cases
│ │ └── deploy/
│ │ └── deploy.ts # Deployment script
│ │
│ └── nextjs/ # Frontend
│ ├── app/
│ │ └── _components/
│ │ ├── SimplePollDemo.tsx # Localhost demo
│ │ └── PrivatePollDemo.tsx # Production version
│ └── hooks/
│ └── privatepoll/
│ └── usePrivatePollWagmi.tsx
│
├── demo-full.html # Static demo
└── README.md
cd packages/hardhat
npx hardhat test| Category | Tests | Status |
|---|---|---|
| Deployment | 5 | ✅ |
| Voting | 5 | ✅ |
| Vote Counting | 3 | ✅ |
| Results | 2 | ✅ |
| Privacy | 2 | ✅ |
| Complete Flow | 1 | ✅ |
| Total | 18 | ✅ |
✔ should set the correct poll question
✔ should allow Alice to cast a vote for Option A
✔ should prevent double voting
✔ should decrypt vote counts correctly
✔ should keep individual votes encrypted
✔ should handle a complete poll lifecycle
18 passing (369ms)
-
Setup Environment:
cd packages/hardhat npx hardhat vars set INFURA_API_KEY npx hardhat vars set MNEMONIC
-
Deploy:
pnpm deploy:sepolia
-
Update Frontend:
- Switch from
SimplePollDemotoPrivatePollDemoinapp/page.tsx - Configure Sepolia network in MetaMask
- Switch from
- Deploy contract to Sepolia
- Verify contract on Etherscan
- Test with real Gateway/Relayer
- Update frontend configuration
- Test full voting flow
- Document contract addresses
- Implementation Plan - Technical design
- Test Results - Testing documentation
- Walkthrough - Complete guide
- Troubleshooting - Common issues
User selects option
↓
Vote encrypted with FHE (value = 1)
↓
ZK Proof generated
↓
Encrypted vote + proof submitted to blockchain
↓
Smart contract adds encrypted vote to aggregate count
↓
ACL permissions updated
-
What's Hidden:
- Individual vote choices
- Identity of voters (beyond address)
- Real-time vote distribution
-
What's Visible:
- Who voted (addresses)
- Aggregate totals (after decryption)
- Poll metadata
// Encrypt vote (client-side)
euint32 vote = FHE.asEuint32(1);
// Add to aggregate (on-chain, stays encrypted)
voteCounts[optionId] = FHE.add(voteCounts[optionId], vote);
// Decrypt aggregate (owner only)
uint32 result = decrypt(voteCounts[optionId]);- Smart Contracts: Solidity 0.8.24
- FHE Library: @fhevm/solidity
- Frontend: Next.js 15, React, TypeScript
- Wallet: Wagmi, RainbowKit
- Testing: Hardhat, Chai
- Styling: Tailwind CSS
- Zama Branding: Yellow (#FFD208) and dark (#2D2D2D) theme
- Responsive Design: Works on desktop and mobile
- Real-time Feedback: Progress indicators for all actions
- Accessibility: Clear labels and status messages
- Animations: Smooth transitions and progress bars
This implementation can be adapted for:
- Governance: DAO voting, proposals
- Elections: Student government, club elections
- Research: Anonymous surveys, data collection
- Business: Employee feedback, feature voting
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE file for details
This project is built upon the excellent work from:
- jobjab-dev/fhevm-react-template - The original React template for FHEVM that this project is based on. Thanks to jobjab-dev for creating a solid foundation for FHEVM development!
- Zama for FHEVM technology
- fhevm-hardhat-template
- Scaffold-ETH
- Documentation: Zama Docs
- Community: Zama Discord
Made with 🔐 using Zama's Fully Homomorphic Encryption