Files
aitbc/contracts/contracts/DAOGovernanceEnhanced.sol
AITBC System b033923756 chore: normalize file permissions across repository
- Remove executable permissions from configuration files (.editorconfig, .env.example, .gitignore)
- Remove executable permissions from documentation files (README.md, LICENSE, SECURITY.md)
- Remove executable permissions from web assets (HTML, CSS, JS files)
- Remove executable permissions from data files (JSON, SQL, YAML, requirements.txt)
- Remove executable permissions from source code files across all apps
- Add executable permissions to Python
2026-03-08 11:26:18 +01:00

515 lines
18 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/IModularContracts.sol";
import "./ContractRegistry.sol";
/**
* @title DAOGovernanceEnhanced
* @dev Enhanced multi-jurisdictional DAO framework with modular integrations
* @notice Integrates with TreasuryManager, CrossChainGovernance, and PerformanceAggregator
*/
contract DAOGovernanceEnhanced is IModularContract, Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
// State variables
uint256 public version = 2; // Enhanced version
IERC20 public governanceToken;
ContractRegistry public registry;
ITreasuryManager public treasuryManager;
ICrossChainGovernance public crossChainGovernance;
IPerformanceAggregator public performanceAggregator;
// Staking Parameters
uint256 public minStakeAmount;
uint256 public unbondingPeriod = 7 days;
// Enhanced Staker struct
struct Staker {
uint256 amount;
uint256 unbondingAmount;
uint256 unbondingCompleteTime;
uint256 lastStakeTime;
uint256 reputationScore;
uint256 votingPower;
bool isActive;
}
// Enhanced Proposal struct
enum ProposalState { Pending, Active, Canceled, Defeated, Succeeded, Queued, Expired, Executed }
enum ProposalType { TREASURY_ALLOCATION, PARAMETER_CHANGE, CROSS_CHAIN, REWARD_DISTRIBUTION }
struct Proposal {
uint256 id;
address proposer;
string region; // "" for global
string descriptionHash;
uint256 forVotes;
uint256 againstVotes;
uint256 abstainVotes;
uint256 startTime;
uint256 endTime;
bool executed;
bool canceled;
ProposalState state;
ProposalType proposalType;
address targetContract;
bytes callData;
uint256 value;
mapping(address => bool) hasVoted;
mapping(address => uint8) voteType; // 0=against, 1=for, 2=abstain
}
// Cross-chain proposal
struct CrossChainProposal {
uint256 sourceChainId;
bytes32 proposalHash;
uint256 localProposalId;
bool isValidated;
uint256 validationTime;
address validator;
bytes32 validationProof;
}
// Mappings
mapping(address => Staker) public stakers;
mapping(uint256 => Proposal) public proposals;
mapping(string => mapping(address => bool)) public isRegionalCouncilMember;
mapping(string => address[]) public regionalCouncilMembers;
mapping(uint256 => CrossChainProposal) public crossChainProposals;
mapping(uint256 => uint256) public proposalToCrossChain;
// Counters
uint256 public proposalCount;
uint256 public totalStaked;
uint256[] public activeProposalIds;
// Events
event Staked(address indexed user, uint256 amount);
event Unstaked(address indexed user, uint256 amount);
event ProposalCreated(uint256 indexed id, address proposer, string region, ProposalType proposalType);
event VoteCast(address indexed voter, uint256 indexed proposalId, uint8 voteType, uint256 weight);
event ProposalExecuted(uint256 indexed id);
event CrossChainProposalSubmitted(uint256 indexed localId, uint256 sourceChainId, bytes32 proposalHash);
event ReputationUpdated(address indexed staker, uint256 newReputation);
event VotingPowerUpdated(address indexed staker, uint256 newVotingPower);
// Errors
error InvalidAmount(uint256 amount);
error InsufficientStake(uint256 required, uint256 available);
error ProposalNotFound(uint256 proposalId);
error ProposalNotActive(uint256 proposalId);
error AlreadyVoted(uint256 proposalId, address voter);
error InvalidVoteType(uint8 voteType);
error NotCouncilMember(string region, address member);
error RegistryNotSet();
error CrossChainValidationFailed(uint256 proposalId);
modifier validAmount(uint256 amount) {
if (amount == 0) revert InvalidAmount(amount);
_;
}
modifier validProposal(uint256 proposalId) {
if (proposals[proposalId].id == 0) revert ProposalNotFound(proposalId);
_;
}
modifier onlyActiveStaker() {
if (stakers[msg.sender].amount < minStakeAmount) revert InsufficientStake(minStakeAmount, stakers[msg.sender].amount);
_;
}
modifier onlyCouncilMember(string memory region) {
if (bytes(region).length > 0 && !isRegionalCouncilMember[region][msg.sender]) {
revert NotCouncilMember(region, msg.sender);
}
_;
}
modifier registrySet() {
if (address(registry) == address(0)) revert RegistryNotSet();
_;
}
constructor(address _governanceToken, uint256 _minStakeAmount) {
governanceToken = IERC20(_governanceToken);
minStakeAmount = _minStakeAmount;
}
/**
* @dev Initialize the enhanced DAO governance (implements IModularContract)
*/
function initialize(address _registry) external override {
require(address(registry) == address(0), "Already initialized");
registry = ContractRegistry(_registry);
// Register this contract
bytes32 contractId = keccak256(abi.encodePacked("DAOGovernanceEnhanced"));
registry.registerContract(contractId, address(this));
// Get integration addresses from registry
treasuryManager = ITreasuryManager(registry.getContract(keccak256(abi.encodePacked("TreasuryManager"))));
crossChainGovernance = ICrossChainGovernance(registry.getContract(keccak256(abi.encodePacked("CrossChainGovernance"))));
performanceAggregator = IPerformanceAggregator(registry.getContract(keccak256(abi.encodePacked("PerformanceAggregator"))));
}
/**
* @dev Upgrade the contract
*/
function upgrade(address newImplementation) external override onlyOwner {
version++;
// Implementation upgrade logic would go here
}
/**
* @dev Pause the contract
*/
function pause() external override onlyOwner {
// Implementation would use Pausable mixin
}
/**
* @dev Unpause the contract
*/
function unpause() external override onlyOwner {
// Implementation would use Pausable mixin
}
/**
* @dev Get current version
*/
function getVersion() external view override returns (uint256) {
return version;
}
// --- Enhanced Staking ---
function stake(uint256 _amount) external nonReentrant validAmount(_amount) {
governanceToken.safeTransferFrom(msg.sender, address(this), _amount);
Staker storage staker = stakers[msg.sender];
staker.amount += _amount;
staker.lastStakeTime = block.timestamp;
staker.isActive = true;
totalStaked += _amount;
// Update voting power based on reputation
_updateVotingPower(msg.sender);
require(staker.amount >= minStakeAmount, "Below min stake");
emit Staked(msg.sender, _amount);
}
function initiateUnstake(uint256 _amount) external nonReentrant {
Staker storage staker = stakers[msg.sender];
require(_amount > 0 && staker.amount >= _amount, "Invalid amount");
require(staker.unbondingAmount == 0, "Unbonding already in progress");
staker.amount -= _amount;
staker.unbondingAmount = _amount;
staker.unbondingCompleteTime = block.timestamp + unbondingPeriod;
totalStaked -= _amount;
// Update voting power
_updateVotingPower(msg.sender);
}
function completeUnstake() external nonReentrant {
Staker storage staker = stakers[msg.sender];
require(staker.unbondingAmount > 0, "Nothing to unstake");
require(block.timestamp >= staker.unbondingCompleteTime, "Unbonding not complete");
uint256 amount = staker.unbondingAmount;
staker.unbondingAmount = 0;
governanceToken.safeTransfer(msg.sender, amount);
emit Unstaked(msg.sender, amount);
}
// --- Enhanced Proposals & Voting ---
function createProposal(
string calldata _region,
string calldata _descriptionHash,
uint256 _votingPeriod,
ProposalType _proposalType,
address _targetContract,
bytes calldata _callData,
uint256 _value
) external onlyActiveStaker onlyCouncilMember(_region) nonReentrant returns (uint256) {
proposalCount++;
Proposal storage p = proposals[proposalCount];
p.id = proposalCount;
p.proposer = msg.sender;
p.region = _region;
p.descriptionHash = _descriptionHash;
p.startTime = block.timestamp;
p.endTime = block.timestamp + _votingPeriod;
p.state = ProposalState.Active;
p.proposalType = _proposalType;
p.targetContract = _targetContract;
p.callData = _callData;
p.value = _value;
activeProposalIds.push(proposalCount);
emit ProposalCreated(p.id, msg.sender, _region, _proposalType);
return p.id;
}
function castVote(uint256 _proposalId, uint8 _voteType) external validProposal(_proposalId) nonReentrant {
Proposal storage p = proposals[_proposalId];
require(block.timestamp >= p.startTime && block.timestamp <= p.endTime, "Voting closed");
require(!p.hasVoted[msg.sender], "Already voted");
require(_voteType <= 2, "Invalid vote type");
uint256 weight = _calculateVotingWeight(msg.sender, p.region);
require(weight > 0, "No voting weight");
p.hasVoted[msg.sender] = true;
p.voteType[msg.sender] = _voteType;
if (_voteType == 0) { // Against
p.againstVotes += weight;
} else if (_voteType == 1) { // For
p.forVotes += weight;
} else { // Abstain
p.abstainVotes += weight;
}
emit VoteCast(msg.sender, _proposalId, _voteType, weight);
}
function executeProposal(uint256 _proposalId) external validProposal(_proposalId) nonReentrant {
Proposal storage p = proposals[_proposalId];
require(block.timestamp > p.endTime, "Voting not ended");
require(p.state == ProposalState.Active, "Invalid proposal state");
// Check if proposal passed
uint256 totalVotes = p.forVotes + p.againstVotes + p.abstainVotes;
bool passed = p.forVotes > p.againstVotes && totalVotes > 0;
if (passed) {
p.state = ProposalState.Succeeded;
_executeProposalAction(_proposalId);
p.state = ProposalState.Executed;
} else {
p.state = ProposalState.Defeated;
}
// Remove from active proposals
_removeFromActiveProposals(_proposalId);
emit ProposalExecuted(_proposalId);
}
// --- Cross-Chain Integration ---
function submitCrossChainProposal(
uint256 _sourceChainId,
bytes32 _proposalHash,
string calldata _descriptionHash
) external onlyActiveStaker nonReentrant returns (uint256) {
// Create local proposal for cross-chain validation
bytes memory callData = abi.encodeWithSignature("validateCrossChainProposal(uint256,bytes32)", _sourceChainId, _proposalHash);
uint256 localProposalId = _createCrossChainProposal(
_descriptionHash,
7 days,
ProposalType.CROSS_CHAIN,
address(crossChainGovernance),
callData,
0
);
// Store cross-chain reference
crossChainProposals[localProposalId] = CrossChainProposal({
sourceChainId: _sourceChainId,
proposalHash: _proposalHash,
localProposalId: localProposalId,
isValidated: false,
validationTime: 0,
validator: address(0),
validationProof: bytes32(0)
});
proposalToCrossChain[localProposalId] = _sourceChainId;
emit CrossChainProposalSubmitted(localProposalId, _sourceChainId, _proposalHash);
return localProposalId;
}
function _createCrossChainProposal(
string calldata _descriptionHash,
uint256 _votingPeriod,
ProposalType _proposalType,
address _targetContract,
bytes memory _callData,
uint256 _value
) internal onlyActiveStaker nonReentrant returns (uint256) {
proposalCount++;
Proposal storage p = proposals[proposalCount];
p.id = proposalCount;
p.proposer = msg.sender;
p.region = "";
p.descriptionHash = _descriptionHash;
p.startTime = block.timestamp;
p.endTime = block.timestamp + _votingPeriod;
p.state = ProposalState.Active;
p.proposalType = _proposalType;
p.targetContract = _targetContract;
p.callData = _callData;
p.value = _value;
activeProposalIds.push(proposalCount);
emit ProposalCreated(p.id, msg.sender, "", _proposalType);
return p.id;
}
function validateCrossChainVote(uint256 _proposalId, bytes32 _voteProof) external {
if (address(crossChainGovernance) != address(0)) {
crossChainGovernance.validateCrossChainVote(_proposalId, _voteProof);
}
}
// --- Internal Functions ---
function _calculateVotingWeight(address _voter, string memory _region) internal view returns (uint256) {
Staker memory staker = stakers[_voter];
// Regional council members have equal voting power
if (bytes(_region).length > 0 && isRegionalCouncilMember[_region][_voter]) {
return 1;
}
// Global voting based on stake and reputation
return staker.votingPower;
}
function _executeProposalAction(uint256 _proposalId) internal {
Proposal storage p = proposals[_proposalId];
if (p.targetContract != address(0) && p.callData.length > 0) {
// Execute the proposal action
(bool success, ) = p.targetContract.call{value: p.value}(p.callData);
require(success, "Execution failed");
}
}
function _updateVotingPower(address _staker) internal {
Staker storage staker = stakers[_staker];
// Base voting power is stake amount
uint256 basePower = staker.amount;
// Apply reputation multiplier
uint256 reputationMultiplier = 10000; // 1x default
if (address(performanceAggregator) != address(0)) {
uint256 reputation = performanceAggregator.getReputationScore(_staker);
reputationMultiplier = performanceAggregator.calculateAPYMultiplier(reputation);
}
staker.votingPower = (basePower * reputationMultiplier) / 10000;
staker.reputationScore = performanceAggregator.getReputationScore(_staker);
emit VotingPowerUpdated(_staker, staker.votingPower);
emit ReputationUpdated(_staker, staker.reputationScore);
}
function _removeFromActiveProposals(uint256 _proposalId) internal {
for (uint256 i = 0; i < activeProposalIds.length; i++) {
if (activeProposalIds[i] == _proposalId) {
activeProposalIds[i] = activeProposalIds[activeProposalIds.length - 1];
activeProposalIds.pop();
break;
}
}
}
// --- View Functions ---
function getStakerInfo(address _staker) external view returns (
uint256 amount,
uint256 votingPower,
uint256 reputationScore,
bool isActive
) {
Staker memory staker = stakers[_staker];
return (
staker.amount,
staker.votingPower,
staker.reputationScore,
staker.isActive
);
}
function getProposalInfo(uint256 _proposalId) external view returns (
address proposer,
string memory region,
ProposalState state,
ProposalType proposalType,
uint256 forVotes,
uint256 againstVotes,
uint256 abstainVotes,
uint256 startTime,
uint256 endTime
) {
Proposal storage p = proposals[_proposalId];
return (
p.proposer,
p.region,
p.state,
p.proposalType,
p.forVotes,
p.againstVotes,
p.abstainVotes,
p.startTime,
p.endTime
);
}
function getActiveProposals() external view returns (uint256[] memory) {
return activeProposalIds;
}
function getRegionalCouncilMembers(string memory _region) external view returns (address[] memory) {
return regionalCouncilMembers[_region];
}
// --- Admin Functions ---
function setRegionalCouncilMember(string calldata _region, address _member, bool _status) external onlyOwner {
isRegionalCouncilMember[_region][_member] = _status;
if (_status) {
regionalCouncilMembers[_region].push(_member);
}
}
function setMinStakeAmount(uint256 _minStakeAmount) external onlyOwner {
minStakeAmount = _minStakeAmount;
}
function setUnbondingPeriod(uint256 _unbondingPeriod) external onlyOwner {
unbondingPeriod = _unbondingPeriod;
}
function emergencyPause() external onlyOwner {
// Emergency pause functionality
}
function emergencyUnpause() external onlyOwner {
// Emergency unpause functionality
}
}