// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title StakedVotingDAO
* @notice Decentralized governance system with staked voting mechanism
* @dev This contract implements a DAO governance system where voting power
* is determined by staked token amounts
*/
contract StakedVotingDAO {
struct Proposal {
uint256 id;
address proposer;
string description;
uint256 forVotes;
uint256 againstVotes;
uint256 startTime;
uint256 endTime;
bool executed;
bool canceled;
mapping(address => Receipt) receipts;
}
struct Receipt {
bool hasVoted;
bool support;
uint256 votes;
}
event ProposalCreated(
uint256 indexed proposalId,
address indexed proposer,
string description,
uint256 startTime,
uint256 endTime
);
event VoteCast(
address indexed voter,
uint256 indexed proposalId,
bool support,
uint256 votes
);
event ProposalExecuted(uint256 indexed proposalId);
event ProposalCanceled(uint256 indexed proposalId);
uint256 public proposalCount;
mapping(uint256 => Proposal) public proposals;
// Minimum tokens required to create a proposal
uint256 public proposalThreshold;
// Voting period in seconds
uint256 public votingPeriod;
// Quorum required for proposal to pass
uint256 public quorumVotes;
constructor(
uint256 _proposalThreshold,
uint256 _votingPeriod,
uint256 _quorumVotes
) {
proposalThreshold = _proposalThreshold;
votingPeriod = _votingPeriod;
quorumVotes = _quorumVotes;
}
function propose(string memory description) external returns (uint256) {
require(
getVotes(msg.sender) >= proposalThreshold,
"Insufficient voting power to create proposal"
);
proposalCount++;
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 + votingPeriod;
emit ProposalCreated(
proposalId,
msg.sender,
description,
newProposal.startTime,
newProposal.endTime
);
return proposalId;
}
function castVote(uint256 proposalId, bool support) external {
require(
state(proposalId) == ProposalState.Active,
"Voting is not active"
);
uint256 votes = getVotes(msg.sender);
require(votes > 0, "No voting power");
Proposal storage proposal = proposals[proposalId];
Receipt storage receipt = proposal.receipts[msg.sender];
require(!receipt.hasVoted, "Already voted");
if (support) {
proposal.forVotes += votes;
} else {
proposal.againstVotes += votes;
}
receipt.hasVoted = true;
receipt.support = support;
receipt.votes = votes;
emit VoteCast(msg.sender, proposalId, support, votes);
}
function execute(uint256 proposalId) external {
require(
state(proposalId) == ProposalState.Succeeded,
"Proposal not succeeded"
);
Proposal storage proposal = proposals[proposalId];
proposal.executed = true;
// Execute the proposal logic here
// This would typically involve calling external contracts
emit ProposalExecuted(proposalId);
}
function cancel(uint256 proposalId) external {
Proposal storage proposal = proposals[proposalId];
require(
msg.sender == proposal.proposer,
"Only proposer can cancel"
);
require(
state(proposalId) == ProposalState.Active,
"Proposal not active"
);
proposal.canceled = true;
emit ProposalCanceled(proposalId);
}
function getVotes(address account) public view returns (uint256) {
// This would typically query a staking contract
// For now, return a placeholder value
return 1000; // Placeholder: 1000 voting power
}
function state(uint256 proposalId) public view returns (ProposalState) {
require(proposalCount >= proposalId, "Invalid proposal id");
Proposal storage proposal = proposals[proposalId];
if (proposal.canceled) {
return ProposalState.Canceled;
}
if (proposal.executed) {
return ProposalState.Executed;
}
if (block.timestamp <= proposal.endTime) {
return ProposalState.Active;
}
if (proposal.forVotes <= proposal.againstVotes ||
proposal.forVotes < quorumVotes) {
return ProposalState.Defeated;
}
return ProposalState.Succeeded;
}
enum ProposalState {
Pending,
Active,
Canceled,
Defeated,
Succeeded,
Executed
}
}