Private voting use case
The private voting use case describes how Semaphore interacts with your users and Ethereum to allow users to cast private votes in your application. Learn how Semaphore enables applications to do the following:
- Register members as voters.
- Allow members to vote anonymously.
- Prove voter membership.
- Record and prove votes.
- Prevent double-voting.
Rolesβ
Developer or community adminβ
As a developer or community admin, you deploy the following:
- Smart contract on Ethereum: implements the Semaphore base contract to create a poll (Semaphore group that members join to vote), post transactions, and verify proofs on Ethereum.
- Decentralized application (dApp): your application that provides a user interface (UI) where members join a poll and vote on a proposal.
Community memberβ
Community members connect their wallets to the dApp to take the following actions:
- Verify ownership of the community token.
- Generate an anonymous ID.
- Cast a vote.
Relayβ
To preserve anonymity and avoid disclosing the member's wallet address, the dApp may use a relay to broadcast the vote. The relay calls the contract function that then posts the member's vote transaction to Ethereum.
Private votingβ
Consider a scenario where your community issues a token that users can mint. The token might be a Proof of Attendance (POAP), NFT, or social token that your users can mint to receive membership and vote in your community.
The voting scenario has the following steps:
- Create a poll: Coordinator creates a poll, or group, in which members can vote on a proposal.
- Register voters: Members join the poll to vote.
- Record votes: Once the poll opens, members may cast one vote, or signal, on the topic.
Create a pollβ
A community coordinator or dApp administrator uses the deployed smart contract to create an on-chain (Ethereum) poll, a Semaphore group that members can join and cast votes to.
In the following sample code, the voting contract declares a createPoll
function that uses the Semaphore base _createGroup
function:
function createPoll(
uint256 pollId,
address coordinator,
uint8 depth
) public override {
require(address(verifiers[depth]) != address(0), "SemaphoreVoting: depth value is not supported");
_createGroup(pollId, depth, 0);
Poll memory poll;
poll.coordinator = coordinator;
polls[pollId] = poll;
emit PollCreated(pollId, coordinator);
}
A poll is a Semaphore group that stores the following:
- A topic to vote on.
- The public ID of the poll creator.
- Semaphore IDs of members who joined the poll.
To create the poll, the administrator calls the smart contract function--for example:
SemaphoreVoting.createPoll(pollId, coordinator, depth)
Next, learn how to register voters for the poll.
Register votersβ
Before a user can register to vote, the dApp needs to verify membership by checking the user's wallet for the NFT.
To grant access to the wallet, the user clicks a Connect wallet
button in the dApp and allows the dApp to check for the NFT.
Once a member is verified, the dApp provides the following member interactions:
To learn how to connect to Ethereum wallets, visit the ethers.js Getting Started documentation.
Generate a private identityβ
To generate a private identity, the member completes a form in the dApp UI.
With the form values and the @semaphore-protocol/identity
library, the dApp prompts the member to sign a wallet message and then generates the signed private identity.
The private identity is known only to the member and can be used in future interactions with the dApp.
Next, learn how members join a poll.
Join a pollβ
Once the member has a private identity for the dApp, the member may select a poll to vote in. When the member selects a poll, the dApp does the following:
- Uses the
@semaphore-protocol/identity
library to generate an anonymous Semaphore ID, or identity commitment, from the private identity. - Calls a contract function that adds the new Semaphore ID to the on-chain poll.
With a member registered for a poll, learn how the dApp records votes.
Record votesβ
Once members have joined a poll, the coordinator starts the poll to allow voting. When a member votes (for example, by selecting a radio button), then the dApp takes the following actions:
- Uses the
@semaphore-protocol/proof
library to create a proof of the vote, the poll identifier, the Semaphore ID, and a nullifier that prevents double-voting. - Sends the vote proof to the relay.
Relatedβ
- To get started developing with Semaphore, see the Quick setup guide.
- For an example app that you can use to start your own project, see Semaphore boilerplate.