ghost-dao-contracts/src/governance/GhostGovernor.sol
Uncle Fatso 8ac8b67767
get rid of annoying foundry warnings
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2026-03-09 00:37:41 +03:00

190 lines
7.0 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Governor} from "@openzeppelin-contracts/governance/Governor.sol";
import {GovernorPreventLateQuorum} from "@openzeppelin-contracts/governance/extensions/GovernorPreventLateQuorum.sol";
import {GovernorSettings} from "@openzeppelin-contracts/governance/extensions/GovernorSettings.sol";
import {GovernorStorage} from "@openzeppelin-contracts/governance/extensions/GovernorStorage.sol";
import {GovernorVotes} from "@openzeppelin-contracts/governance/extensions/GovernorVotes.sol";
import {GovernorVotesQuorumFraction} from "@openzeppelin-contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import {IVotes} from "@openzeppelin-contracts/governance/utils/IVotes.sol";
import {ReentrancyGuard} from "@openzeppelin-contracts/utils/ReentrancyGuard.sol";
import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
import {GovernorGhostCounting} from "./GovernorGhostCounting.sol";
import {Babylonian} from "../libraries/FixedPoint.sol";
contract GhostGovernor is
GovernorGhostCounting,
GovernorPreventLateQuorum,
GovernorSettings,
GovernorStorage,
GovernorVotes,
GovernorVotesQuorumFraction,
ReentrancyGuard
{
using SafeERC20 for IERC20;
uint256 public activeProposedLock;
uint256 private _lastProposalId;
mapping(uint256 => uint256) public lockedAmounts;
error ProposerNotEnoughVotes(uint256 proposalVotes, uint256 accumulatedVotes);
error InsufficientFundsToExtend(uint256 votes, uint256 neededVotes);
constructor(
IVotes _ghst,
uint48 _initialVoteExtension,
uint48 _initialVotingDelay,
uint32 _initialVotingPeriod,
uint256 _initialProposalThreshold,
uint256 _quorumFraction
)
Governor("GhostGovernorV1")
GovernorVotes(_ghst)
GovernorPreventLateQuorum(_initialVoteExtension)
GovernorSettings(_initialVotingDelay, _initialVotingPeriod, _initialProposalThreshold)
GovernorVotesQuorumFraction(_quorumFraction)
{}
function proposalThreshold()
public
view
virtual
override(Governor, GovernorSettings)
returns (uint256)
{
return super.proposalThreshold();
}
function proposalDeadline(uint256 proposalId)
public
view
virtual
override(Governor, GovernorPreventLateQuorum)
returns (uint256)
{
return super.proposalDeadline(proposalId);
}
function releaseLocked(uint256 proposalId) public nonReentrant returns (uint256) {
uint256 releaseAmount = lockedAmounts[proposalId];
address proposer = proposalProposer(proposalId);
bytes32 currentProposalState = _encodeStateBitmap(state(proposalId));
bytes32 activePendingMask =
_encodeStateBitmap(ProposalState.Pending) |
_encodeStateBitmap(ProposalState.Active);
if (releaseAmount > 0 && (currentProposalState & activePendingMask == 0)) {
lockedAmounts[proposalId] = 0;
IERC20(address(token())).safeTransfer(proposer, releaseAmount);
return releaseAmount;
}
return 0;
}
function state(uint256 proposalId) public view virtual override returns (ProposalState) {
ProposalState result = super.state(proposalId);
if (result == ProposalState.Active) {
(uint256 againstVotes, uint256 forVotes,) = proposalVotes(proposalId);
uint256 halfTotalVotes = token().getPastTotalSupply(proposalSnapshot(proposalId)) / 2;
if (forVotes > halfTotalVotes) return ProposalState.Succeeded;
if (againstVotes > halfTotalVotes) return ProposalState.Defeated;
}
return result;
}
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override returns (uint256) {
address proposer = _msgSender();
// check description restriction
if (!_isValidDescriptionForProposer(proposer, description)) {
revert GovernorRestrictedProposer(proposer);
}
// check proposal threshold
uint256 proposerVotes = getVotes(proposer, clock() - 1);
uint256 votesThreshold = proposalThreshold();
if (proposerVotes < votesThreshold) {
revert GovernorInsufficientProposerVotes(proposer, proposerVotes, votesThreshold);
}
uint256 proposalId = _lastProposalId;
if (proposalId != 0) {
bytes32 currentProposalState = _encodeStateBitmap(state(proposalId));
bytes32 activePendingMask =
_encodeStateBitmap(ProposalState.Pending) |
_encodeStateBitmap(ProposalState.Active);
if (currentProposalState & activePendingMask != 0) {
if (proposerVotes <= activeProposedLock) {
revert ProposerNotEnoughVotes(proposerVotes, activeProposedLock);
}
(
address[] memory currentTargets,
uint256[] memory currentValues,
bytes[] memory currentCalldatas,
bytes32 descriptionHash
) = proposalDetails(proposalId);
_cancel(currentTargets, currentValues, currentCalldatas, descriptionHash);
}
}
proposalId = _propose(targets, values, calldatas, description, proposer);
_countVote(proposalId, proposer, 1, proposerVotes, "");
lockedAmounts[proposalId] += proposerVotes;
activeProposedLock = proposerVotes;
_lastProposalId = proposalId;
IERC20(address(token())).safeTransferFrom(proposer, address(this), proposerVotes);
return proposalId;
}
function _propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description,
address proposer
) internal virtual override(Governor, GovernorStorage) returns (uint256) {
return super._propose(targets, values, calldatas, description, proposer);
}
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason,
bytes memory params
) internal virtual override(Governor, GovernorPreventLateQuorum) returns (uint256) {
return super._castVote(proposalId, account, support, reason, params);
}
function _voteSucceeded(uint256 proposalId)
internal
view
virtual
override(Governor, GovernorGhostCounting)
returns (bool)
{
uint256 totalVotes = token().getPastTotalSupply(proposalSnapshot(proposalId));
(uint256 againstVotes, uint256 forVotes,) = proposalVotes(proposalId);
uint256 rhs = 3 * (5 * againstVotes + forVotes) * Babylonian.sqrt(totalVotes);
uint256 lhs = (forVotes + againstVotes);
lhs = lhs * Babylonian.sqrt(80 * lhs + totalVotes);
return lhs > rhs;
}
}