{"id":20844320,"url":"https://github.com/bootnodedev/op-anon-voting-monorepo","last_synced_at":"2026-04-19T21:31:56.839Z","repository":{"id":255230918,"uuid":"805524542","full_name":"BootNodeDev/op-anon-voting-monorepo","owner":"BootNodeDev","description":null,"archived":false,"fork":false,"pushed_at":"2024-08-29T20:26:26.000Z","size":1214,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-01-19T03:47:56.495Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://op-anon-voting-monorepo.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BootNodeDev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-05-24T19:12:23.000Z","updated_at":"2024-08-29T20:26:30.000Z","dependencies_parsed_at":"2024-08-29T22:24:19.010Z","dependency_job_id":null,"html_url":"https://github.com/BootNodeDev/op-anon-voting-monorepo","commit_stats":null,"previous_names":["bootnodedev/op-anon-voting-monorepo"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BootNodeDev%2Fop-anon-voting-monorepo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BootNodeDev%2Fop-anon-voting-monorepo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BootNodeDev%2Fop-anon-voting-monorepo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BootNodeDev%2Fop-anon-voting-monorepo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BootNodeDev","download_url":"https://codeload.github.com/BootNodeDev/op-anon-voting-monorepo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243205245,"owners_count":20253425,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-18T02:09:18.214Z","updated_at":"2025-12-25T22:03:44.598Z","avatar_url":"https://github.com/BootNodeDev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://bootnode.dev\"\u003e\n      \u003cimg src=\"https://www.bootnode.dev/external/github-headers/avoting.png\" alt=\"bootnode anon voting banner\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\u003cbr /\u003e\n\n\u003cdiv align=\"center\"\u003e\u003cstrong\u003eRevolutionary anonymous voting system\u003c/strong\u003e\u003c/div\u003e\n\u003cdiv align=\"center\"\u003ePreserves users’ identity privacy\u003c/div\u003e\n\u003cbr /\u003e\n\n# Anonimous Voting\n\n![CI](https://github.com/BootNodeDev/op-anon-voting-monorepo/actions/workflows/ci.yml/badge.svg)\n![TS](https://img.shields.io/badge/TypeScript-^5.4-blue)\n![SOL](https://img.shields.io/badge/Solidity-0.8.4-blue)\n[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](http://perso.crans.org/besson/LICENSE.html)\n\nIn today's governance landscape ensuring the integrity and privacy of voting processes is paramount. This project outlines the development of a privacy-preserving voting governance application, leveraging cutting-edge technologies such as ZK proofs, Ethereum Attestation Services, and Semaphore.\nKey Components\n- **ZK Proofs:** Zero-Knowledge (ZK) proofs play a critical role in maintaining voter anonymity and data privacy. By enabling verifiable computation without revealing sensitive information, ZK ensures that the integrity of the voting process is upheld without compromising vote confidentiality.\n- **Ethereum Attestation Services:** Ethereum Attestation Services are utilized to create a gated voting system. These services provide robust mechanisms for verifying voter eligibility while maintaining a decentralized and secure environment. Attestations serve as cryptographic proof of identity or qualification, ensuring that only authorized participants can vote.\n- **Semaphore:** Semaphore is employed to allow individuals to cast verifiable votes without revealing their identities. This is crucial for preventing vote manipulation and ensuring fair outcomes.\n\n\nThe proof of concept we developed aimed to demonstrate that by using technologies that we mentioned, it is possible for users to vote on different proposals anonymously, without revealing the identity of the voters.\n\n## Solution\n### Technical Actors\n\n##### Frontend\n- Creates the identity\n- Generates proof of user vote\n##### Backend\n- Verifies that the addresses are attested as badge holders or any other attestation group\n\n##### Semaphore contract\n- [Reference contract]('https://github.com/semaphore-protocol/semaphore/blob/v3.15.2/packages/contracts/contracts/extensions/SemaphoreVoting.sol')\n- Adds identities to a group\n- Checks the validity of the voting option\n- Adds vote to a dynamic array for the poll id\n- Emits an event with the vote and poll id\n- Adds voter checking EAS Attestation UID\n\n##### Relayer/Coordinator\n- Relays transactions on behalf of the users\n- Is added as the coordinator for new polls\n- Could be an EOA, a Smart Contract, or a Smart Account.\n\n\n### Identity Generation\nSemaphore enables the creation of an identity on the client side using JavaScript, allowing for deterministic generation. The identity consists of three values: two secrets and one public. The public value is the identity commitment, while the two secrets are the nullifier and the trapdoor.\nWhen a user wants to participate in the voting process, the backend receives their signature and identity commitment. The backend then validates whether the public key from the signature corresponds to a badge holder. Once validated, the identity commitment is added to a group.\n\n\n### Relayer\nThe relayer begins by creating a proposal and assigning a Poll ID. It then adds voters to each proposal and starts the poll. When a user casts a vote, the relayer creates an object that includes the vote signal, the nullifier hash, the Poll ID, and the proof. This object is relayed to the contract. Once the voting period concludes, the relayer closes the voting.\n### Tallying\nCould be done either by the front or the backend\nReads the events emitted by Semaphore Contract filtered by Poll ID\nPublicly verifiable since the events are published on-chain\n\n\n### Flows\n#### Enrollment Flow\nUser creates an Identity Commitment on the client-side, fetches their Badgeholder Attestation UID from the EAS subgraph, and submits both to the Semaphore contract (which will verify the user is in fact a Badgeholder) and if so add them to the group, which will then allow them to generate a proof for their vote.\n\n\n#### Voting Flow\nOnce a user's Identity Commitment is added to the group and the poll has started, the user queries the contract for the group information related to the proposal they are part of. Using the group information or merkleTreeRoot, the user can generate a proof. This proof, along with the vote, is then sent to the backend to be broadcast to the Semaphore contract.\n\n\n#### Vote Count Flow\nOnce the vote is cast, it is securely stored in an array in the Semaphore contract. The tally can be handled either by the frontend or the backend of the application. The process ensures public verifiability, as all voting events are published on-chain. This transparent method allows anyone to audit the votes and verify the integrity of the election process without compromising voters and their relations to a vote.\n\n\n## Pre-requisites to Test Anon Voting\n\n1. Have an EOA with OP ETH for gas.\n2. Have a valid attestation. Addresses with valid attestations will be enabled to vote on polls where said attestation is required.\n\n### How to issue an attestation to an address:\n\n1. Enter [https://optimism.easscan.org/attestations](https://optimism.easscan.org/attestations)\n2. Connect the wallet from which the Attestations are going to be granted (this address will have to be set as an environment variable `TRUSTED_ATTESTER` when deploying the smart contracts)\n3. Click on \"Make Attestation\"\n4. Input `78` in the Schema input field and select `RETROPGF BADGEHOLDER`\n5. Click on \"Use this Schema\"\n6. Fill the fields with the following:\n    - **Recipient:** The address that will receive the attestation\n    - **RPGF ROUND :** `1`\n    - **REFERRED BY :** Leave as is. (`0x0000000000000000000000000000000000000000`)\n    - **REFERRED METHOD:** `referredMethod`\n7. Select \"On Chain\"\n8. Click on \"Make attestation\"\n9. Confirm the transaction\n\n**[Make Batch Attestations using AttestFest!](https://scribehow.com/shared/Make_Batch_attestations_using_Attest_Fest__LZn6__gDQa2fGgsahEU85A)**\n\n## How to use Anon Voting\n\n### Demo\n[![Welcome to anon voting](https://i.vimeocdn.com/video/1914972606-71a1332ab582caddfcc8bb0c3ab9f7e9dc03e5aa08ed672f2043b24adcb0fa2e-d)](https://vimeo.com/998842028)\n\n\n### Create\n\n1. Provide a unique Poll ID\n2. Provide a title for the poll\n3. Set a coordinator to govern the lifecycle of the poll\n4. Set the required RetroPGF Round\n5. Click on Create Poll\n6. Click on Generate Identity\n7. :bulb: Now the Poll is open to enrollment. Users have to enroll at this stage to be able to vote.\n\n\u003cimg width=\"1512\" alt=\"2 Create Poll\" src=\"https://github.com/BootNodeDev/op-anon-voting-monorepo/assets/88392991/58b2800c-ad6b-4a4e-a6f0-1f4bd170ac8b\"\u003e\n\n\n### Enroll to vote\n\n1. Search for a Poll using its unique ID\n2. Click on Generate Identity\n3. If your address is attested under the correct Schema, you can enroll to vote.\n4. Click on Enroll to vote\n\n\u003cimg width=\"1512\" alt=\"3 Enroll to vote\" src=\"https://github.com/BootNodeDev/op-anon-voting-monorepo/assets/88392991/543cef48-cf03-41f6-809e-68995e3294b1\"\u003e\n\n\n### Start the poll\n\n1. Coordinator can click on Start Poll to enable voting for the enrolled users\n\n### Vote\n\n1. Search for a Poll using its unique ID\n2. Enrolled users can only vote after the poll started\n3. Generate identity\n4. Select your vote and click on Cast Vote\n\n\u003cimg width=\"1512\" alt=\"4 Vote\" src=\"https://github.com/BootNodeDev/op-anon-voting-monorepo/assets/88392991/68b01a3a-41d4-4983-a23b-7773b8e40da0\"\u003e\n\n### End the poll\n\n1. Once the voting is completed, the coordinator can End the Poll\n\n\u003cimg width=\"1512\" alt=\"5 Poll Results\" src=\"https://github.com/BootNodeDev/op-anon-voting-monorepo/assets/88392991/d1bf9a11-640f-411c-aab8-f48e46e151d2\"\u003e\n\n\n## For a further stage, once the POC is done and tech-related constraints and risks are mitigated\n\n* a. Enabling private/public feedback upon proposal generation: by leveraging the same gate-keeping mechanism (EAS Attestations and Semaphore Groups), we can create a feedback system that can guarantee the person providing feedback is, in fact, allowed to do so. This can be done publicly by having the poster broadcast their own transaction including an EAS Attestation UID that can be used to check whether they are allowed to provide feedback. This can be taken a step further to only allow feedback from those who provide a zk proof of having previously enrolled to vote for the proposal.\nWith the use of a relayer we can make the feedback be completely private in the same way we do for casting a vote.\n```mermaid\nsequenceDiagram\nactor Alice\nparticipant MS as Message Storage\nparticipant R as Relayer\nparticipant SC as Smart Contract\nparticipant FE as Front-end\nAlice -\u003e\u003e MS: Create message\nMS -\u003e\u003e Alice: messageId\nalt Public feedback\nAlice -\u003e\u003e SC: Broadcast tx (messageId, Attestation UID[, ZK proof])\nelse Private Feedback\nAlice -\u003e\u003e R: Publish message (messageId, signature, Attestation UID[, ZK proof])\nR -\u003e\u003e SC: Broadcast tx (messageId[, ZK proof])\nend\nFE -\u003e\u003e SC: Get message IDs\nSC -\u003e\u003e FE: Message IDs\nFE -\u003e\u003e MS: Get messages\nMS -\u003e\u003e FE: Messages\nFE -\u003e\u003e FE: Display all public/private feedback\n```\n\n* b. Ability to whistleblow: by leveraging the same stack used for anonymous voting (EAS Attestations + Semaphore Groups + relayer), we can add the ability to whistleblow without revealing the identity of the whistleblower. For this the whistleblower would provide the following to the relayer: a message signed with their private key that proves they are in control of a specific address; an EAS Attestation UID issued by a trusted attester to the same address; a zk proof of having previously enrolled to vote for the proposal; the leak they wish to be made public without revealing their identity.\n```mermaid\nsequenceDiagram\nactor Bob\nparticipant MS as Message Storage\nparticipant R as Relayer\nparticipant SC as Smart Contract\nparticipant FE as Front-end\nBob -\u003e\u003e MS: Create leak\nMS -\u003e\u003e Bob: leakId\nBob -\u003e\u003e R: Publish leak (leakId, signature, Attestation UID[, ZK proof])\nR -\u003e\u003e SC: Broadcast tx (leakId[, ZK proof])\nFE -\u003e\u003e SC: Get leak IDs\nSC -\u003e\u003e FE: Leak IDs\nFE -\u003e\u003e MS: Get leaks\nMS -\u003e\u003e FE: Leaks\nFE -\u003e\u003e FE: Display all leaks\n```\n\n\n## Development\n\n### Smart Contracts\n\nTo deploy the contracts:\n\n\nSet the following environment variables:\n\n- `RPC_URL`: The RPC endpoint URL\n- `PRIVATE_KEY`: The account private key used to deploy contracts\n- `TRUSTED_ATTESTER`: The address that we consider as trusted for issuing attestations\n\n```\ncd contracts/\nyarn\nyarn deployOp\n```\n\n...and set the resulting address to:\n\n- `CURRENT_ADDRESS`: The address obtained from the deployment\n\nNext, verify the contract:\n\n```\nforge verify-contract --chain optimism --rpc-url $RPC_URL -e $ETHERSCAN_API_KEY $CURRENT_ADDRESS AnonVoting --watch\n```\n\n### UI\n\nTo run the dapp:\n\n```\ncd frontend/\nyarn\nyarn dev\n```\n\nThe postinstall script automatically generates types for the subgraph and hooks for contract interaction.\n\n### Environment variables\n\n#### NEXT_PUBLIC_SUBGRAPH_URI\n\nEAS subgraph URL. (e.g., `https://optimism.easscan.org/graphql`)\n\n#### NEXT_PUBLIC_ANON_VOTING_ADDRESS\n\nAddress of the latest contract deployment (e.g., `0x235511e3c7b0b055fa1431576ebac4438a600bb5`)\n\n#### NEXT_PUBLIC_EAS_SCHEMA\n\nAddress of the schema to query the `uid` from the EAS subgraph\n\n#### NEXT_PUBLIC_EAS_ATTESTER\n\nAddress of attester to query the `uid` from the EAS subgraph\n\n#### ETHERSCAN_API_KEY\n\nAPI key for verifying new deployments and generating wagmi hooks for `NEXT_PUBLIC_ANON_VOTING_ADDRESS`\n\n#### PK_RELAYER\n\nPrivate key for the relayer account (used in serverless Next.js functions). Ensure this account has sufficient ETH for\nvote transactions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbootnodedev%2Fop-anon-voting-monorepo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbootnodedev%2Fop-anon-voting-monorepo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbootnodedev%2Fop-anon-voting-monorepo/lists"}