// SPDX-License-Identifier: BSD-3-Clause-Clear pragma solidity ^0.8.24; {{IMPORTS}} /// @title {{CONTRACT_NAME}} - Encrypted Voting /// @notice Private voting system using FHE - votes are encrypted and tallied homomorphically /// @dev Generated with Lab-Z Composable Templates contract {{CONTRACT_NAME}} is {{INHERITS}} { // ============ Errors ============ /// @dev Proposal does not exist error ProposalNotFound(uint256 proposalId); /// @dev Voting period has ended error VotingEnded(uint256 proposalId); /// @dev Voting period has not ended yet error VotingNotEnded(uint256 proposalId); /// @dev User has already voted error AlreadyVoted(address voter, uint256 proposalId); /// @dev Invalid vote value error InvalidVote(); {{ERRORS}} // ============ Events ============ /// @notice Emitted when a new proposal is created event ProposalCreated(uint256 indexed proposalId, string description, uint256 endTime); /// @notice Emitted when a vote is cast event VoteCast(address indexed voter, uint256 indexed proposalId); {{EVENTS}} // ============ Structs ============ struct Proposal { string description; uint256 endTime; [[VOTE_TYPE]] yesVotes; [[VOTE_TYPE]] noVotes; bool exists; bool finalized; } // ============ State Variables ============ /// @dev Proposal storage mapping(uint256 => Proposal) private _proposals; /// @dev Track if user has voted on a proposal mapping(uint256 => mapping(address => bool)) private _hasVoted; /// @dev Next proposal ID uint256 private _nextProposalId; {{STATE_VARIABLES}} // ============ Modifiers ============ {{MODIFIERS}} // ============ Constructor ============ constructor({{CONSTRUCTOR_PARAMS}}) { _nextProposalId = 1; {{CONSTRUCTOR_BODY}} } // ============ External Functions ============ /// @notice Create a new proposal /// @param description The proposal description /// @param durationSeconds How long the voting period lasts /// @return proposalId The ID of the created proposal function createProposal( string calldata description, uint256 durationSeconds ) external {{VOTE_MODIFIERS}} returns (uint256 proposalId) { proposalId = _nextProposalId++; Proposal storage proposal = _proposals[proposalId]; proposal.description = description; proposal.endTime = block.timestamp + durationSeconds; proposal.yesVotes = FHE.asEuint32(0); proposal.noVotes = FHE.asEuint32(0); proposal.exists = true; // Set ACL for vote counts FHE.allowThis(proposal.yesVotes); FHE.allowThis(proposal.noVotes); emit ProposalCreated(proposalId, description, proposal.endTime); } /// @notice Cast an encrypted vote /// @param proposalId The proposal to vote on /// @param encryptedVote Encrypted vote (1 = yes, 0 = no) /// @param inputProof Zero-knowledge proof for the input function vote( uint256 proposalId, [[EXTERNAL_VOTE_TYPE]] encryptedVote, bytes calldata inputProof ) external {{VOTE_MODIFIERS}} { {{PRE_VOTE}} Proposal storage proposal = _proposals[proposalId]; if (!proposal.exists) { revert ProposalNotFound(proposalId); } if (block.timestamp > proposal.endTime) { revert VotingEnded(proposalId); } if (_hasVoted[proposalId][msg.sender]) { revert AlreadyVoted(msg.sender, proposalId); } // Convert external input to internal encrypted value [[VOTE_TYPE]] eVote = FHE.fromExternal(encryptedVote, inputProof); // Encrypted vote: if vote > 0, it's a yes vote ebool isYes = FHE.gt(eVote, FHE.asEuint32(0)); // Homomorphic conditional addition // yesVotes += isYes ? 1 : 0 // noVotes += isYes ? 0 : 1 [[VOTE_TYPE]] one = FHE.asEuint32(1); [[VOTE_TYPE]] zero = FHE.asEuint32(0); proposal.yesVotes = FHE.add(proposal.yesVotes, FHE.select(isYes, one, zero)); proposal.noVotes = FHE.add(proposal.noVotes, FHE.select(isYes, zero, one)); // Mark as voted _hasVoted[proposalId][msg.sender] = true; {{POST_VOTE}} // Set ACL permissions FHE.allowThis(proposal.yesVotes); FHE.allowThis(proposal.noVotes); {{ACL_VOTE}} emit VoteCast(msg.sender, proposalId); } {{EXTERNAL_FUNCTIONS}} // ============ View Functions ============ /// @notice Get proposal details (without encrypted vote counts) /// @param proposalId The proposal ID /// @return description The proposal description /// @return endTime When voting ends /// @return exists Whether the proposal exists /// @return finalized Whether the proposal has been finalized function getProposal(uint256 proposalId) external view returns ( string memory description, uint256 endTime, bool exists, bool finalized ) { Proposal storage proposal = _proposals[proposalId]; return (proposal.description, proposal.endTime, proposal.exists, proposal.finalized); } /// @notice Check if an address has voted on a proposal /// @param proposalId The proposal ID /// @param voter The voter address /// @return Whether the voter has voted function hasVoted(uint256 proposalId, address voter) external view returns (bool) { return _hasVoted[proposalId][voter]; } /// @notice Get the encrypted vote counts (requires ACL permission) /// @param proposalId The proposal ID /// @return yesVotes Encrypted yes vote count /// @return noVotes Encrypted no vote count function getVoteCounts(uint256 proposalId) external view returns ( [[VOTE_TYPE]] yesVotes, [[VOTE_TYPE]] noVotes ) { Proposal storage proposal = _proposals[proposalId]; if (!proposal.exists) { revert ProposalNotFound(proposalId); } return (proposal.yesVotes, proposal.noVotes); } /// @notice Get the next proposal ID /// @return The next proposal ID that will be assigned function nextProposalId() external view returns (uint256) { return _nextProposalId; } {{VIEW_FUNCTIONS}} // ============ Internal Functions ============ {{INTERNAL_FUNCTIONS}} }