aere-contracts/contracts/AereGovernance.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

224 lines
7.3 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./AereToken.sol";
/**
* @title AERE Governance Contract
* @dev Manages decentralized governance and proposals
*/
contract AereGovernance is Ownable, ReentrancyGuard {
AereToken public aereToken;
enum ProposalStatus { Active, Passed, Failed, Executed, Cancelled }
enum VoteType { Yes, No, Abstain }
struct Proposal {
uint256 id;
string title;
string description;
address proposer;
uint256 startTime;
uint256 endTime;
uint256 yesVotes;
uint256 noVotes;
uint256 abstainVotes;
ProposalStatus status;
uint256 executionTime;
bytes executionData;
}
struct Vote {
VoteType voteType;
uint256 votingPower;
bool hasVoted;
}
// State variables
mapping(uint256 => Proposal) public proposals;
mapping(uint256 => mapping(address => Vote)) public votes;
mapping(address => uint256) public votingPower;
uint256 public proposalCount = 0;
uint256 public constant VOTING_PERIOD = 7 days;
uint256 public constant EXECUTION_DELAY = 2 days;
uint256 public constant PROPOSAL_THRESHOLD = 100000 * 10**18; // 100K AERE
uint256 public constant QUORUM_PERCENTAGE = 20; // 20%
// Events
event ProposalCreated(uint256 indexed proposalId, address indexed proposer, string title);
event VoteCast(uint256 indexed proposalId, address indexed voter, VoteType voteType, uint256 votingPower);
event ProposalExecuted(uint256 indexed proposalId);
event ProposalCancelled(uint256 indexed proposalId);
constructor(address _aereToken) {
aereToken = AereToken(_aereToken);
}
/**
* @dev Create new governance proposal
*/
function createProposal(
string memory title,
string memory description,
bytes memory executionData
) external returns (uint256) {
require(aereToken.balanceOf(msg.sender) >= PROPOSAL_THRESHOLD, "Insufficient tokens to propose");
proposalCount++;
uint256 proposalId = proposalCount;
proposals[proposalId] = Proposal({
id: proposalId,
title: title,
description: description,
proposer: msg.sender,
startTime: block.timestamp,
endTime: block.timestamp + VOTING_PERIOD,
yesVotes: 0,
noVotes: 0,
abstainVotes: 0,
status: ProposalStatus.Active,
executionTime: 0,
executionData: executionData
});
emit ProposalCreated(proposalId, msg.sender, title);
return proposalId;
}
/**
* @dev Cast vote on proposal
*/
function castVote(uint256 proposalId, VoteType voteType) external {
Proposal storage proposal = proposals[proposalId];
require(proposal.status == ProposalStatus.Active, "Proposal not active");
require(block.timestamp <= proposal.endTime, "Voting period ended");
require(!votes[proposalId][msg.sender].hasVoted, "Already voted");
uint256 voterPower = aereToken.balanceOf(msg.sender);
require(voterPower > 0, "No voting power");
votes[proposalId][msg.sender] = Vote({
voteType: voteType,
votingPower: voterPower,
hasVoted: true
});
if (voteType == VoteType.Yes) {
proposal.yesVotes += voterPower;
} else if (voteType == VoteType.No) {
proposal.noVotes += voterPower;
} else {
proposal.abstainVotes += voterPower;
}
emit VoteCast(proposalId, msg.sender, voteType, voterPower);
}
/**
* @dev Finalize proposal after voting period
*/
function finalizeProposal(uint256 proposalId) external {
Proposal storage proposal = proposals[proposalId];
require(proposal.status == ProposalStatus.Active, "Proposal not active");
require(block.timestamp > proposal.endTime, "Voting still active");
uint256 totalVotes = proposal.yesVotes + proposal.noVotes + proposal.abstainVotes;
uint256 quorumRequired = (aereToken.getCirculatingSupply() * QUORUM_PERCENTAGE) / 100;
if (totalVotes >= quorumRequired && proposal.yesVotes > proposal.noVotes) {
proposal.status = ProposalStatus.Passed;
proposal.executionTime = block.timestamp + EXECUTION_DELAY;
} else {
proposal.status = ProposalStatus.Failed;
}
}
/**
* @dev Execute passed proposal
*/
function executeProposal(uint256 proposalId) external {
Proposal storage proposal = proposals[proposalId];
require(proposal.status == ProposalStatus.Passed, "Proposal not passed");
require(block.timestamp >= proposal.executionTime, "Execution delay not met");
proposal.status = ProposalStatus.Executed;
// Execute proposal logic here
if (proposal.executionData.length > 0) {
(bool success,) = address(this).call(proposal.executionData);
require(success, "Execution failed");
}
emit ProposalExecuted(proposalId);
}
/**
* @dev Cancel proposal (only proposer or owner)
*/
function cancelProposal(uint256 proposalId) external {
Proposal storage proposal = proposals[proposalId];
require(
msg.sender == proposal.proposer || msg.sender == owner(),
"Not authorized to cancel"
);
require(proposal.status == ProposalStatus.Active, "Proposal not active");
proposal.status = ProposalStatus.Cancelled;
emit ProposalCancelled(proposalId);
}
/**
* @dev Get proposal details
*/
function getProposal(uint256 proposalId) external view returns (Proposal memory) {
return proposals[proposalId];
}
/**
* @dev Get vote details
*/
function getVote(uint256 proposalId, address voter) external view returns (Vote memory) {
return votes[proposalId][voter];
}
/**
* @dev Check if proposal has quorum
*/
function hasQuorum(uint256 proposalId) external view returns (bool) {
Proposal memory proposal = proposals[proposalId];
uint256 totalVotes = proposal.yesVotes + proposal.noVotes + proposal.abstainVotes;
uint256 quorumRequired = (aereToken.getCirculatingSupply() * QUORUM_PERCENTAGE) / 100;
return totalVotes >= quorumRequired;
}
/**
* @dev Get governance statistics
*/
function getGovernanceStats() external view returns (
uint256 _totalProposals,
uint256 _activeProposals,
uint256 _totalVoters,
uint256 _quorumPercentage
) {
uint256 activeCount = 0;
for (uint256 i = 1; i <= proposalCount; i++) {
if (proposals[i].status == ProposalStatus.Active) {
activeCount++;
}
}
return (
proposalCount,
activeCount,
aereToken.getCirculatingSupply() / (10**18), // Convert to readable number
QUORUM_PERCENTAGE
);
}
}