A decentralized voting application built on Midnight Network using real Zero-Knowledge proofs for private, verifiable voting. Built with Compact smart contracts and deployed on Midnight testnet.
- π True Privacy: Individual votes are private using ZK-SNARKs (not mocked!)
- π³οΈ One Wallet, One Vote: ZK proofs ensure each wallet can only vote once per proposal
- β° Time-Limited Voting: Proposals have configurable voting periods
- π Public Tallies: Vote counts are public, but individual votes remain private
- π Testnet Deployed: Running on real Midnight Network infrastructure
- π Real-time Updates: Live vote count updates
- Public State: Proposals, vote tallies, deadlines
- Private State: Individual voting records (who voted on what)
- ZK Circuits: Proves voter eligibility without revealing identity
- Location:
src/contract/voting.compact
- Lace Wallet integration
- ZK proof generation in browser
- Real-time proposal updates
- Responsive UI
- Docker - For running the Midnight proof server
- Node.js 18+ and npm
- Lace Wallet Extension - Install from Chrome Web Store
- Midnight Testnet Tokens - Get from Midnight Faucet
cd OpenVote
npm installThe proof server is required for generating ZK proofs:
docker-compose up -dVerify it's running:
curl http://localhost:6300/healthcp .env.example .envEdit .env and add your deployer private key from Lace wallet.
# Compile the Compact contract
npm run build:contract
# Generate TypeScript bindings
npm run build:bindings
# Deploy to Midnight testnet
npm run deploySave the contract address output and add it to .env:
VITE_CONTRACT_ADDRESS=<your_contract_address_here>
npm run devOpen http://localhost:3000 in your browser.
- Connect your Lace wallet
- Fill in the proposal form:
- Title: Short, descriptive title
- Description: What's being voted on
- Duration: Voting period in blocks (~20 seconds per block)
- Click "Create Proposal"
- Confirm transaction in Lace wallet
- Find a proposal with "Open" status
- Click "Vote Yay" or "Vote Nay"
- ZK Proof Generation: Your browser generates a proof that:
- You haven't voted on this proposal yet
- You're eligible to vote
- WITHOUT revealing your wallet address or vote choice
- Confirm the transaction
- Your vote is added to the public tally, but remains private!
- During Voting: Live tallies update as votes come in
- After Deadline: Final results are displayed
- Privacy: You can see vote counts but never individual votes
OpenVote/
βββ src/
β βββ contract/
β β βββ voting.compact # Smart contract
β βββ components/
β β βββ CreateProposal.tsx # Proposal creation form
β β βββ ProposalList.tsx # List all proposals
β β βββ ProposalCard.tsx # Single proposal display
β βββ services/
β β βββ VotingService.ts # Contract interaction layer
β βββ App.tsx # Main app component
β βββ main.tsx # Entry point
βββ docker-compose.yml # Proof server config
βββ package.json
βββ vite.config.ts
- Compact: Domain-specific language for ZK circuits
- Midnight.js: SDK for interacting with Midnight Network
- React + TypeScript: Modern frontend
- Vite: Fast build tool
- Docker: Containerized proof server
# Compile Compact contract
npm run build:contract
# Generate TypeScript bindings
npm run build:bindings
# Build entire project
npm run build
# Deploy to testnet
npm run deploy
# Start dev server
npm run dev- Votes recorded on-chain are public
- Anyone can see who voted for what
- Vulnerable to coercion and manipulation
- Private Voting Record: The contract maintains a private map of
[wallet, proposalId] => hasVoted - ZK Proof Generation: When voting, your browser generates a proof that:
"I know a secret (my private key) that controls wallet X, AND wallet X has NOT voted on proposal Y yet, BUT I won't reveal which wallet or my vote" - On-Chain Verification: The contract verifies the proof without learning:
- Your wallet address
- Your vote choice
- Any identifying information
- Public Tally Update: Only the vote count increases
- β Real proof server (Docker container)
- β Actual ZK-SNARK circuit compilation
- β Genuine cryptographic proofs generated client-side
- β On-chain verification on Midnight testnet
- β Private state managed by Compact runtime
- RPC:
wss://rpc.testnet-02.midnight.network - Proof Server:
http://localhost:6300(local Docker) - Explorer: Midnight Explorer
- Install Lace wallet
- Create/import a wallet
- Visit Midnight Faucet
- Enter your wallet address
- Receive test tokens
- Private voting prevents whale manipulation
- Transparent tallies ensure fair outcomes
- Time-limited proposals create urgency
- Anonymous feedback collection
- Honest opinions without fear of backlash
- Verifiable results
- Private ballot casting
- Public vote counting
- Tamper-proof record keeping
# Check if Docker is running
docker ps
# Restart proof server
docker-compose down && docker-compose up -d
# Check logs
docker-compose logs -f- Install Lace wallet extension
- Refresh browser after installation
- Ensure extension is enabled
- Check you have testnet tokens
- Verify contract address is set correctly
- Ensure proof server is running
- Each wallet can only vote once per proposal
- This is enforced via ZK proofs
- Try voting on a different proposal
This project demonstrates:
- Real-world ZK-SNARK implementation (not mocked!)
- Smart contract development in Compact
- Private state management
- Client-side proof generation
- Decentralized application architecture
- Wallet integration
- Testnet deployment
MIT License - feel free to use this for learning, hackathons, or production!
Built for the Midnight Network hackathon, showcasing real privacy-preserving voting without simulation or mocking.
Ready to vote privately? Get started now! π
Questions? Open an issue or check the Midnight docs.