From 3250fae87669e3fcf8a2aba32ca30bda2af90865 Mon Sep 17 00:00:00 2001 From: sourcebridge_42 Date: Sun, 19 Apr 2026 09:08:00 +0000 Subject: [PATCH] citizen: implement Implement: Handles voting logic for the governance contract --- contracts/VotingContract.sol | 130 +++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 contracts/VotingContract.sol diff --git a/contracts/VotingContract.sol b/contracts/VotingContract.sol new file mode 100644 index 0000000..1848f3b --- /dev/null +++ b/contracts/VotingContract.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "./interfaces/IGovernance.sol"; + +contract VotingContract { + struct Proposal { + uint256 id; + address proposer; + string description; + uint256 startTime; + uint256 endTime; + uint256 votesFor; + uint256 votesAgainst; + mapping(address => bool) hasVoted; + ProposalStatus status; + } + + enum ProposalStatus { + Pending, + Active, + Passed, + Rejected, + Executed + } + + uint256 public constant VOTING_PERIOD = 7 days; + uint256 public constant QUORUM_PERCENTAGE = 20; + uint256 public constant MIN_VOTING_THRESHOLD = 100 ether; + + mapping(uint256 => Proposal) public proposals; + uint256 public proposalCount; + + event ProposalCreated( + uint256 indexed proposalId, + address indexed proposer, + string description + ); + + event VoteCast( + uint256 indexed proposalId, + address indexed voter, + bool support + ); + + event ProposalStatusChanged( + uint256 indexed proposalId, + ProposalStatus newStatus + ); + + modifier validProposal(uint256 proposalId) { + require(proposalId < proposalCount, "Invalid proposal ID"); + _; + } + + function createProposal(string memory description) external payable returns (uint256) { + require(msg.value >= MIN_VOTING_THRESHOLD, "Insufficient proposal stake"); + + uint256 proposalId = proposalCount++; + Proposal storage newProposal = proposals[proposalId]; + + newProposal.id = proposalId; + newProposal.proposer = msg.sender; + newProposal.description = description; + newProposal.startTime = block.timestamp; + newProposal.endTime = block.timestamp + VOTING_PERIOD; + newProposal.status = ProposalStatus.Active; + + emit ProposalCreated(proposalId, msg.sender, description); + return proposalId; + } + + function castVote(uint256 proposalId, bool support) external validProposal(proposalId) { + Proposal storage proposal = proposals[proposalId]; + + require(block.timestamp >= proposal.startTime && block.timestamp <= proposal.endTime, + "Voting is not active for this proposal"); + require(!proposal.hasVoted[msg.sender], "Already voted"); + + proposal.hasVoted[msg.sender] = true; + + if (support) { + proposal.votesFor++; + } else { + proposal.votesAgainst++; + } + + emit VoteCast(proposalId, msg.sender, support); + } + + function finalizeProposal(uint256 proposalId) external validProposal(proposalId) { + Proposal storage proposal = proposals[proposalId]; + + require(block.timestamp > proposal.endTime, "Voting period not ended"); + require(proposal.status == ProposalStatus.Active, "Proposal already finalized"); + + uint256 totalVotes = proposal.votesFor + proposal.votesAgainst; + uint256 quorumVotes = (totalVotes * 100) / QUORUM_PERCENTAGE; + + if (proposal.votesFor > proposal.votesAgainst && totalVotes >= quorumVotes) { + proposal.status = ProposalStatus.Passed; + } else { + proposal.status = ProposalStatus.Rejected; + } + + emit ProposalStatusChanged(proposalId, proposal.status); + } + + function getProposalDetails(uint256 proposalId) + external + view + validProposal(proposalId) + returns ( + address proposer, + string memory description, + uint256 votesFor, + uint256 votesAgainst, + ProposalStatus status + ) + { + Proposal storage proposal = proposals[proposalId]; + return ( + proposal.proposer, + proposal.description, + proposal.votesFor, + proposal.votesAgainst, + proposal.status + ); + } +} \ No newline at end of file