diff --git a/contracts/GovernanceContract.sol b/contracts/GovernanceContract.sol new file mode 100644 index 0000000..a0acfa7 --- /dev/null +++ b/contracts/GovernanceContract.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "./interfaces/IGovernance.sol"; +import "./VotingContract.sol"; + +contract GovernanceContract is IGovernance { + struct Proposal { + address proposer; + string description; + bytes32 proposalHash; + uint256 votingStartTime; + uint256 votingEndTime; + ProposalStatus status; + uint256 totalVotesFor; + uint256 totalVotesAgainst; + mapping(address => bool) hasVoted; + } + + enum ProposalStatus { + Pending, + Active, + Passed, + Rejected, + Executed + } + + VotingContract public votingContract; + mapping(uint256 => Proposal) public proposals; + uint256 public proposalCount; + uint256 public constant VOTING_PERIOD = 7 days; + uint256 public constant QUORUM_PERCENTAGE = 30; + uint256 public constant PASS_THRESHOLD_PERCENTAGE = 50; + + event ProposalCreated( + uint256 indexed proposalId, + address indexed proposer, + string description + ); + + event ProposalVoted( + uint256 indexed proposalId, + address indexed voter, + bool support + ); + + event ProposalStatusChanged( + uint256 indexed proposalId, + ProposalStatus newStatus + ); + + constructor(address _votingContractAddress) { + require(_votingContractAddress != address(0), "Invalid voting contract address"); + votingContract = VotingContract(_votingContractAddress); + } + + function createProposal(string memory _description) external returns (uint256) { + require(bytes(_description).length > 0, "Description cannot be empty"); + + uint256 proposalId = proposalCount++; + Proposal storage newProposal = proposals[proposalId]; + + newProposal.proposer = msg.sender; + newProposal.description = _description; + newProposal.proposalHash = keccak256(abi.encodePacked(_description, block.timestamp, msg.sender)); + newProposal.votingStartTime = block.timestamp; + newProposal.votingEndTime = block.timestamp + VOTING_PERIOD; + newProposal.status = ProposalStatus.Active; + + emit ProposalCreated(proposalId, msg.sender, _description); + return proposalId; + } + + function vote(uint256 _proposalId, bool _support) external { + Proposal storage proposal = proposals[_proposalId]; + + require(proposal.status == ProposalStatus.Active, "Proposal not active"); + require(block.timestamp <= proposal.votingEndTime, "Voting period has ended"); + require(!proposal.hasVoted[msg.sender], "Already voted"); + + proposal.hasVoted[msg.sender] = true; + + if (_support) { + proposal.totalVotesFor += votingContract.getVotingPower(msg.sender); + } else { + proposal.totalVotesAgainst += votingContract.getVotingPower(msg.sender); + } + + emit ProposalVoted(_proposalId, msg.sender, _support); + } + + function finalizeProposal(uint256 _proposalId) external { + Proposal storage proposal = proposals[_proposalId]; + + require(block.timestamp > proposal.votingEndTime, "Voting period not ended"); + require(proposal.status == ProposalStatus.Active, "Proposal already finalized"); + + uint256 totalVotes = proposal.totalVotesFor + proposal.totalVotesAgainst; + uint256 quorumVotes = (votingContract.getTotalVotingPower() * QUORUM_PERCENTAGE) / 100; + + if (totalVotes >= quorumVotes) { + uint256 supportPercentage = (proposal.totalVotesFor * 100) / totalVotes; + + if (supportPercentage >= PASS_THRESHOLD_PERCENTAGE) { + proposal.status = ProposalStatus.Passed; + } else { + proposal.status = ProposalStatus.Rejected; + } + } else { + proposal.status = ProposalStatus.Rejected; + } + + emit ProposalStatusChanged(_proposalId, proposal.status); + } + + function getProposalDetails(uint256 _proposalId) external view returns ( + address proposer, + string memory description, + uint256 votingStartTime, + uint256 votingEndTime, + ProposalStatus status, + uint256 totalVotesFor, + uint256 totalVotesAgainst + ) { + Proposal storage proposal = proposals[_proposalId]; + return ( + proposal.proposer, + proposal.description, + proposal.votingStartTime, + proposal.votingEndTime, + proposal.status, + proposal.totalVotesFor, + proposal.totalVotesAgainst + ); + } +} \ No newline at end of file