// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/access/Ownable.sol"; /** * @title Voting Contract * @notice On-chain voting mechanism for governance proposals * @dev Simple voting implementation with yes/no votes */ contract Voting is Ownable { struct Proposal { string description; uint256 yesVotes; uint256 noVotes; uint256 startTime; uint256 endTime; bool executed; mapping(address => bool) hasVoted; } Proposal[] public proposals; mapping(address => bool) public voters; uint256 public votingPeriod = 7 days; uint256 public quorum = 50; // 50% of voters event ProposalCreated(uint256 indexed proposalId, string description); event VoteCast(uint256 indexed proposalId, address indexed voter, bool support); event ProposalExecuted(uint256 indexed proposalId); modifier onlyVoter() { require(voters[msg.sender], "Voting: not a voter"); _; } constructor() Ownable(msg.sender) {} /** * @notice Add a voter */ function addVoter(address voter) external onlyOwner { voters[voter] = true; } /** * @notice Remove a voter */ function removeVoter(address voter) external onlyOwner { voters[voter] = false; } /** * @notice Create a new proposal */ function createProposal(string memory description) external onlyVoter returns (uint256) { uint256 proposalId = proposals.length; Proposal storage proposal = proposals.push(); proposal.description = description; proposal.startTime = block.timestamp; proposal.endTime = block.timestamp + votingPeriod; emit ProposalCreated(proposalId, description); return proposalId; } /** * @notice Vote on a proposal */ function vote(uint256 proposalId, bool support) external onlyVoter { Proposal storage proposal = proposals[proposalId]; require(block.timestamp >= proposal.startTime, "Voting: not started"); require(block.timestamp <= proposal.endTime, "Voting: ended"); require(!proposal.hasVoted[msg.sender], "Voting: already voted"); proposal.hasVoted[msg.sender] = true; if (support) { proposal.yesVotes++; } else { proposal.noVotes++; } emit VoteCast(proposalId, msg.sender, support); } /** * @notice Execute a proposal if it passes */ function executeProposal(uint256 proposalId) external { Proposal storage proposal = proposals[proposalId]; require(block.timestamp > proposal.endTime, "Voting: not ended"); require(!proposal.executed, "Voting: already executed"); uint256 totalVotes = proposal.yesVotes + proposal.noVotes; require(totalVotes > 0, "Voting: no votes"); // Check quorum uint256 voterCount = _getVoterCount(); require((totalVotes * 100) / voterCount >= quorum, "Voting: quorum not met"); // Check if proposal passed require(proposal.yesVotes > proposal.noVotes, "Voting: proposal failed"); proposal.executed = true; emit ProposalExecuted(proposalId); } /** * @notice Get proposal details */ function getProposal(uint256 proposalId) external view returns ( string memory description, uint256 yesVotes, uint256 noVotes, uint256 startTime, uint256 endTime, bool executed ) { Proposal storage proposal = proposals[proposalId]; return ( proposal.description, proposal.yesVotes, proposal.noVotes, proposal.startTime, proposal.endTime, proposal.executed ); } /** * @notice Get voter count */ function _getVoterCount() internal view returns (uint256) { // Simplified - in production, maintain a count return 10; // Placeholder } /** * @notice Update voting period */ function setVotingPeriod(uint256 newPeriod) external onlyOwner { votingPeriod = newPeriod; } /** * @notice Update quorum */ function setQuorum(uint256 newQuorum) external onlyOwner { require(newQuorum <= 100, "Voting: invalid quorum"); quorum = newQuorum; } }