130 lines
3.8 KiB
Solidity
130 lines
3.8 KiB
Solidity
// 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
|
|
);
|
|
}
|
|
} |