The Technology
Behind SIGIL
Three cryptographic pillars — privacy, fairness, and anti-collusion — work together. Votes are encrypted, quadratically weighted, and protected by MACI research.
ZK Private Voting
[ How It Works ]
Votes are encrypted client-side and stored on-chain as ciphertext. The message queue is hashed with Poseidon, and the tally is verified by Groth16 proofs — without any reveal phase. Individual votes remain private.
- check_circleOnly ciphertext is stored on-chain (and hashed into AccQueue leaves)
- check_circleOn-chain registration and voice credit proxy enforce eligibility and voting power
- check_circleNo reveal phase exists — individual votes are permanently sealed, even after voting ends
AccQueue Leaf (On-Chain) (On-Chain)
0x4a2e8c1f9b3d7e5a6012c8b7f3d9e0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7Credit System: Cost = Vote²
Quadratic Voting
Traditional "1 token = 1 vote" can over‑weight large holders. Quadratic voting makes extra influence cost more, so conviction matters without easy domination.
How It Works:
Each voter receives voice credits. Casting more votes on the same proposal costs quadratically more: cost = votes². One vote costs 1 credit, but 10 votes costs 100 credits. This forces voters to budget their influence across proposals, naturally preventing plutocratic domination while letting conviction shine through.
MACI Anti-Collusion — Ethereum PSE Research
Forced Vote (Forced Vote (Coerced))
A briber forces Alice to vote "For" and watches her submit it on-chain. She complies to appear cooperative.
Key Change (EdDSA)
Alice secretly changes her EdDSA key. The briber cannot detect this — it looks like any other encrypted message.
Real Vote (Final)
Alice re-votes with her true choice using the new key. During reverse-order processing, only this vote counts.
Built on Ethereum Privacy & Scaling Explorations (PSE) research
Stronger Together
SIGIL combines ZK privacy, quadratic fairness, and MACI anti-collusion in a single system.
What SIGIL adds beyond PSE MACI
Quadratic Voting — D2 integration with cost = votes² credit system
In-Circuit Decryption — DuplexSponge runs inside the ZK circuit, reducing trust in coordinator decryption
No Reveal Phase — Individual votes are not published; only totals are revealed
ERC20 Voice Credits — Use ERC20-based voting credits via proxy
Automated Coordinator — GitHub Actions cron can replace a dedicated server
Technical Specifications — Concrete numbers behind the cryptography
Varies
Groth16 proof generation time varies by circuit size, hardware, and network conditions.
Varies
On-chain verification cost depends on batch size and network conditions.
Poseidon
t=4, rate=3 — optimized for ZK circuits.
Baby Jubjub
EdDSA-compatible elliptic curve embedded in BN254 scalar field
DuplexSponge
Poseidon-based sponge cipher + ECDH key exchange for vote encryption
Depth 4
Quinary tree — supports up to 624 voters per poll (production configuration)
Varies
Constraint count depends on circuit build.
Varies
Constraint count depends on circuit build.
pot20
Powers of Tau ceremony supporting up to 2²⁰ (1M) constraints. Groth16 phase 2 per circuit.
System Architecture
How the four core contracts work together
MACI
Entry point. Handles voter registration (signUp) and deploys new Poll instances.
Poll
Stores encrypted vote messages in an immutable AccQueue. Manages voting period.
MessageProcessor
Decrypts messages in reverse order. Applies key changes. Generates state transition proof.
Tally
Computes final vote counts. Generates Groth16 tally proof. Publishes results on-chain.
MACI → POLL → MESSAGE PROCESSOR → TALLY
Built for Developers
codeBuilt for
Developers
Everything you need to add private voting to a Web3 project.
TypeScript SDK
Type-safe SDK. Create polls, cast votes, and read results from the client and on-chain.
const sigil = new SigilClient({ maciAddress })
const results = await sigil.getResults(pollId)Embeddable Widget
Mount a vanilla JS widget into any page. Handles key generation, encryption, and transaction submission.
mountSigilWidget({ target: '#vote', pollId: 0 })Crypto Primitives
Use low-level crypto utilities for flexibility: command packing, message encryption, and key derivation.
import { packCommand, buildEncryptedVoteMessage } from "sigil-sdk"
const msg = await buildEncryptedVoteMessage(params)Built for Token Communities
Security & Trust
Open Source — audit and fork freely
PSE MACI Protocol — Built on Ethereum Foundation Privacy & Scaling Explorations research
On-chain Groth16 — Every tally verified by smart contract, not a server
Zero Knowledge — Coordinator tallies votes but individual choices are never published
Protocol Stack
POSEIDON HASH | EDDSA KEYS
GROTH16 PROOFS | BABY JUBJUB
DUPLEXSPONGE CIPHER | ECDH
Tallying Pipeline
When voting ends, the coordinator automatically begins tallying. Each step requires blockchain transactions, and total time depends on network conditions.
Data Merge
Voter list and encrypted messages are merged into Merkle trees. Requires multiple blockchain transactions.
Message Processing
The coordinator decrypts all messages in reverse order, applies key changes, and identifies valid votes. A ZK proof is generated and submitted on-chain for each batch.
Tally Proof
Valid votes are tallied and a Groth16 proof is generated. This proof mathematically guarantees the tally is correct.
Publish Results
Aggregate results (total For/Against) are published on-chain. Individual votes are never revealed — only the final totals.
Total time: varies by network
Proof generation and confirmations vary by circuit size, hardware, and chain conditions. L2 networks often reduce latency compared to L1.
Infrastructure & Operations
cloud_doneSIGIL minimizes backend infrastructure. Most work happens in the browser and on-chain. The coordinator can run on GitHub Actions or your own server.
Minimal Backend
The frontend is a Next.js app deployable on Vercel/Netlify. Crypto runs in the browser; data lives on-chain. No app backend, only a coordinator job.
ERC20 Voice Credits
ERC20-based voting credits are supported via proxy (1 token = 1 credit, floor division). Configure the token address at deployment.
L2 / Multi-Chain Deployment
All contracts are standard Solidity 0.8.24 with no chain-specific dependencies. Deploy on any EVM-compatible chain. On L2 networks (Base, Arbitrum, Polygon), costs and latency are typically lower than L1, depending on network conditions.
Token-Gated Proposals
When proposal gates are configured, token holders can create proposals directly. Owners can reconfigure or clear gates.
GitHub Actions Coordinator
The coordinator can run as a GitHub Actions cron job (every 5 minutes). It merges queues, generates Groth16 proofs, and publishes results on-chain.
Custom Server Option
For faster processing, run the coordinator on your own server (npx tsx src/run.ts). It checks every ~10 seconds instead of 5 minutes. Requires a funded wallet and the coordinator key.
Delegation Registry
Delegation is supported. Full voting-power delegation requires DelegatingVoiceCreditProxy and depends on deployment config.
Timelocked Execution
Optional on-chain execution when the executor is configured and execution is registered.
On-Chain Deployments
Contracts are deployed on Ethereum Sepolia. Links are provided per contract.
Security Guarantees — Seven cryptographic properties guaranteed by the MACI protocol
Collusion Resistant
Only Coordinator decrypts; bribers cannot verify compliance
Receipt-Free
Key Change invalidates any "proof" shown to a briber
Vote Privacy
No reveal phase — individual votes are permanently encrypted
Uncensorable
Immutable AccQueue — omitting a vote causes proof failure
Unforgeable
EdDSA signature required with voter's current key
Non-Repudiable
New votes override, never delete — all messages are permanent
Correct Execution
Groth16 on-chain verification proves tally correctness