CompoundGovernor

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/CompoundGovernor.sol": {
      "content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.26;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IGovernor} from "contracts/extensions/IGovernor.sol";
import {GovernorUpgradeable} from "contracts/extensions/GovernorUpgradeable.sol";
import {GovernorSequentialProposalIdUpgradeable} from "contracts/extensions/GovernorSequentialProposalIdUpgradeable.sol";
import {GovernorVotesCompUpgradeable} from "contracts/extensions/GovernorVotesCompUpgradeable.sol";
import {GovernorSettableFixedQuorumUpgradeable} from "contracts/extensions/GovernorSettableFixedQuorumUpgradeable.sol";
import {GovernorCountingFractionalUpgradeable} from "contracts/extensions/GovernorCountingFractionalUpgradeable.sol";
import {GovernorTimelockCompoundUpgradeable} from "contracts/extensions/GovernorTimelockCompoundUpgradeable.sol";
import {ICompoundTimelock} from "@openzeppelin/contracts/vendor/compound/ICompoundTimelock.sol";
import {GovernorSettingsUpgradeable} from "contracts/extensions/GovernorSettingsUpgradeable.sol";
import {GovernorPreventLateQuorumUpgradeable} from "contracts/extensions/GovernorPreventLateQuorumUpgradeable.sol";
import {IComp} from "contracts/interfaces/IComp.sol";
import {GovernorAlphaInterface} from "contracts/GovernorBravoInterfaces.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

/// @title CompoundGovernor
/// @author WOOF! Software
/// @notice A governance contract for the Compound DAO.
/// @custom:security-contact dmitriy@woof.software
contract CompoundGovernor is
    Initializable,
    GovernorVotesCompUpgradeable,
    GovernorTimelockCompoundUpgradeable,
    GovernorSettingsUpgradeable,
    GovernorCountingFractionalUpgradeable,
    GovernorPreventLateQuorumUpgradeable,
    GovernorSettableFixedQuorumUpgradeable,
    GovernorSequentialProposalIdUpgradeable
{
    /// @dev To use the `EnumerableSet` library for managing a set of addresses.
    using EnumerableSet for EnumerableSet.AddressSet;

    /**
     * @notice Emitted when the expiration of a whitelisted account is set or updated.
     * @param setter The address of the account that set the expiration.
     * @param account The address of the account being whitelisted.
     * @param expiration The timestamp until which the account is whitelisted.
     */
    event WhitelistAccountExpirationSet(address setter, address account, uint256 expiration);

    /// @notice Emitted when the whitelistGuardian is set or changed.
    /// @param oldGuardian The address of the previous whitelistGuardian.
    /// @param newGuardian The address of the new whitelistGuardian.
    event WhitelistGuardianSet(address oldGuardian, address newGuardian);

    /// @notice Emitted when the proposal guardian is set or updated.
    /// @param oldProposalGuardian The address of the previous proposal guardian.
    /// @param oldProposalGuardianExpiry The expiration timestamp of the previous proposal guardian's role.
    /// @param newProposalGuardian The address of the new proposal guardian.
    /// @param newProposalGuardianExpiry The expiration timestamp of the new proposal guardian's role.
    event ProposalGuardianSet(
        address oldProposalGuardian,
        uint96 oldProposalGuardianExpiry,
        address newProposalGuardian,
        uint96 newProposalGuardianExpiry
    );

    /// @notice Emitted when a new proposer is added to the allowed proposers list.
    /// @param proposer The address of the proposer that was added.
    event ProposerAdded(address indexed proposer);

    /// @notice Emitted when a proposer is removed from the allowed proposers list.
    /// @param proposer The address of the proposer that was removed.
    event ProposerRemoved(address indexed proposer);

    /// @notice Error thrown when an unauthorized address attempts to perform a restricted action.
    /// @param reason A brief description of why the caller is unauthorized.
    /// @param caller The address that attempted the unauthorized action.
    error Unauthorized(bytes32 reason, address caller);

    /// @notice Error thrown when a proposer attempts to create a new proposal while they have an active proposal.
    /// @param proposer The address of the proposer.
    /// @param proposalId The ID of the active proposal.
    /// @param state The state of the active proposal.
    error ProposerActiveProposal(address proposer, uint256 proposalId, ProposalState state);

    /// @notice Error thrown when a zero address is used.
    error ZeroAddress();

    /// @notice Error thrown when a zero address is used at a specific index.
    /// @param index The index where the zero address was used.
    error ZeroAddressAtIndex(uint256 index);

    /// @notice Error thrown when a caller is not an allowed proposer.
    error OnlyAllowedProposers();

    /// @notice Error thrown when an account is already set.
    /// @param account The address of the account that is already set.
    error AlreadySet(address account);

    /// @notice Error thrown when an account is the proposal guardian.
    /// @param account The address of the account that is the proposal guardian.
    error IsProposalGuardian(address account);

    /// @notice Error thrown when an account's lifetime exceeds the maximum lifetime.
    /// @param account The address of the account that exceeds the maximum lifetime.
    error ExceedsMaxLifetime(address account);

    /// @notice Error thrown when an account is not in the allowed proposers list.
    /// @param account The address of the account that is not in the allowed proposers list.
    error NotInAllowedProposers(address account);

    /// @notice Error thrown when amount of allowed proposers is below the minimum proposers.
    error BelowMinimumProposers();

    /// @notice Error thrown during batchWhitelist when the first address is not the proposal guardian.
    /// @param account The address of the account that is not the proposal guardian.
    error FirstMustBeProposalGuardian(address account);

    /// @notice Error thrown during batchWhitelist when an account is duplicated.
    /// @param account The address of the account that is already set.
    error DuplicateAddress(address account);

    /// @notice Error thrown when the minimum number of proposers is reached.
    /// @dev This error is thrown when the proposal guardian tries to add an allowed proposer when the minimum number of
    /// proposers is reached.
    error MinProposersReached();

    /// @notice Error thrown when a proposal is invalid because the proposal guardian has expired and only
    /// setProposalGuardian proposals are allowed.
    error InvalidProposalWhenGuardianExpired();

    /// @notice Error thrown when the caller is not the proxy admin.
    error OnlyProxyAdmin();

    /// @notice Error thrown when the caller is not the proposer or the proposal guardian.
    error OnlyProposerOrProposalGuardian();

    /// @notice The address and expiration of the proposal guardian.
    struct ProposalGuardian {
        // Address of the `ProposalGuardian`
        address account;
        // Timestamp at which the guardian loses the ability to cancel proposals
        uint96 expiration;
    }

    GovernorAlphaInterface private constant compoundGovernorBravo =
        GovernorAlphaInterface(0xc0Da02939E1441F497fd74F78cE7Decb17B66529);

    /// @notice Address which manages whitelisted proposals and whitelist accounts.
    /// @dev This address has the ability to set account whitelist expirations and can be changed through the governance
    /// process.
    address public whitelistGuardian;

    /// @notice Account which has the ability to cancel proposals. This privilege expires at the given expiration
    /// timestamp.
    ProposalGuardian public proposalGuardian;

    /// @notice Stores the expiration of account whitelist status as a timestamp.
    mapping(address account => uint256 timestamp) public whitelistAccountExpirations;

    /// @notice Stores the latest proposal ID for each proposer.
    mapping(address proposer => uint256 latestProposalId) public latestProposalIds;

    /*//////////////////////////////////////////////////////////////
                              NEW STORAGE
    //////////////////////////////////////////////////////////////*/

    /// @notice The address of the proxy admin.
    /// @dev This is the address of the proxy admin that will be used to upgrade the proxy and call batchWhitelist.
    address public constant PROXY_ADMIN = 0x725ED7F44F0888aeC1b7630AB1ACdced91E0591A;

    /// @notice Minimum number of proposers that must remain in the allowed proposers list.
    uint8 public constant MIN_PROPOSERS = 5;

    /// @notice Maximum lifetime for a temporary proposer.
    uint32 public constant MAX_TEMPORARY_PROPOSER_LIFETIME = 365 days;

    /// @notice Function selector for setProposalGuardian(ProposalGuardian).
    bytes4 public constant SET_PROPOSAL_GUARDIAN_SELECTOR = 0xb80d105a;

    /// @notice A set of addresses that are allowed to make proposals.
    /// @dev Using EnumerableSet for managing the allow list.
    EnumerableSet.AddressSet private allowedProposers;

    /// @notice Disables the initialize function.
    constructor() {
        _disableInitializers();
    }

    /// @notice Initialize Governor.
    /// @param _initialVotingDelay The initial voting delay.
    /// @param _initialVotingPeriod The initial voting period.
    /// @param _initialProposalThreshold The initial proposal threshold.
    /// @param _compAddress The address of the Comp token.
    /// @param _quorumVotes The quorum votes.
    /// @param _timelockAddress The address of the Timelock.
    /// @param _initialVoteExtension The initial vote extension.
    /// @param _whitelistGuardian The address of the whitelist guardian.
    /// @param _proposalGuardian The proposal guardian.
    function initialize(
        uint48 _initialVotingDelay,
        uint32 _initialVotingPeriod,
        uint256 _initialProposalThreshold,
        IComp _compAddress,
        uint256 _quorumVotes,
        ICompoundTimelock _timelockAddress,
        uint48 _initialVoteExtension,
        address _whitelistGuardian,
        ProposalGuardian calldata _proposalGuardian
    ) public initializer {
        __Governor_init("Compound Governor");
        __GovernorSettings_init(_initialVotingDelay, _initialVotingPeriod, _initialProposalThreshold);
        __GovernorVotesComp_init(_compAddress);
        __GovernorTimelockCompound_init(_timelockAddress);
        __GovernorPreventLateQuorum_init(_initialVoteExtension);
        __GovernorSettableFixedQuorum_init(_quorumVotes);
        __GovernorSequentialProposalId_init();
        __GovernorCountingFractional_init();
        _setWhitelistGuardian(_whitelistGuardian);
        _setProposalGuardian(_proposalGuardian);
    }

    /**
     * @notice Batch initializes the allowed proposers list during upgrade.
     * @dev This function can only be called once during the upgrade process.
     * @param _initProposers Array of addresses to add to the allowed proposers list.
     */
    function batchWhitelist(address[] calldata _initProposers) external reinitializer(2) {
        if (_msgSender() != PROXY_ADMIN) {
            revert OnlyProxyAdmin();
        }

        if (_initProposers.length < MIN_PROPOSERS) {
            revert BelowMinimumProposers();
        }

        // First address must be proposalGuardian
        if (_initProposers[0] != proposalGuardian.account) {
            revert FirstMustBeProposalGuardian(_initProposers[0]);
        }

        // Check for zero addresses and duplicates
        address proposer;
        for (uint256 i; i < _initProposers.length; ++i) {
            proposer = _initProposers[i];

            if (proposer == address(0)) {
                revert ZeroAddressAtIndex(i);
            }
            if (!allowedProposers.add(proposer)) {
                revert DuplicateAddress(proposer);
            }

            emit ProposerAdded(proposer);
        }
    }

    /// @notice Sets the next proposal ID. Designed to be callable once by the executor (timelock) on upgrade from
    /// Compound GovernorBravo.
    function setNextProposalId() external {
        if (_executor() != _msgSender()) {
            revert GovernorOnlyExecutor(_msgSender());
        }

        // In GovernorBravo, proposal IDs start at 1, so its proposalCount() function is the most recent
        // proposal ID created. This function sets the first proposal ID for the CompoundGovernor to 1 beyond that,
        // so the first proposal ID of compoundGovernor is one more that the last proposal ID of GovernorBravo.
        _setNextProposalId(compoundGovernorBravo.proposalCount() + 1);
    }

    /// @inheritdoc GovernorSequentialProposalIdUpgradeable
    /// @dev Since GovernorBravo indexed from 1, we decrement the proposal count by 1.
    function proposalCount() public view override returns (uint256) {
        return super.proposalCount() - 1;
    }

    /// @notice A modified `hashProposal` that supports sequential proposal IDs.
    function hashProposal(
        address[] memory _targets,
        uint256[] memory _values,
        bytes[] memory _calldatas,
        bytes32 _descriptionHash
    ) public virtual override(GovernorUpgradeable, GovernorSequentialProposalIdUpgradeable) returns (uint256) {
        return GovernorSequentialProposalIdUpgradeable.hashProposal(_targets, _values, _calldatas, _descriptionHash);
    }

    /// @notice Creates a new proposal. Skips proposal threshold check for whitelisted accounts.
    /// @param _targets An array of addresses that will be called if the proposal is executed.
    /// @param _values An array of ETH values to be sent to each address when the proposal is executed.
    /// @param _calldatas An array of calldata to be sent to each address when the proposal is executed.
    /// @param _description A human-readable description of the proposal.
    /// @return uint256 The ID of the newly created proposal.
    function propose(
        address[] memory _targets,
        uint256[] memory _values,
        bytes[] memory _calldatas,
        string memory _description
    ) public override(GovernorUpgradeable) returns (uint256) {
        address _proposer = _msgSender();

        // check description restriction
        if (!_isValidDescriptionForProposer(_proposer, _description)) {
            revert GovernorRestrictedProposer(_proposer);
        }

        // Check if proposer is in allowed proposers list or has temporary whitelist
        if (!isAllowedProposer(_proposer) && !isWhitelisted(_proposer)) {
            revert GovernorNotWhitelisted(_proposer);
        }

        // If proposal guardian has expired, only allow setProposalGuardian proposals
        if (isProposalGuardianExpired()) {
            if (!_isValidProposalWhenGuardianExpired(_targets, _calldatas)) {
                revert InvalidProposalWhenGuardianExpired();
            }
        }

        return _propose(_targets, _values, _calldatas, _description, _proposer);
    }

    /// @notice Internal function used to create a new proposal.
    /// @dev This is an override that supports sequential proposal IDs. Called by the public `propose` function.
    /// @param _targets An array of addresses that will be called if the proposal is executed.
    /// @param _values An array of ETH values to be sent to each address when the proposal is executed.
    /// @param _calldatas An array of calldata to be sent to each address when the proposal is executed.
    /// @param _description A human-readable description of the proposal.
    /// @param _proposer The address of the account creating the proposal.
    /// @return uint256 The ID of the newly created proposal.
    function _propose(
        address[] memory _targets,
        uint256[] memory _values,
        bytes[] memory _calldatas,
        string memory _description,
        address _proposer
    ) internal override(GovernorUpgradeable, GovernorSequentialProposalIdUpgradeable) returns (uint256) {
        uint256 _latestProposalId = latestProposalIds[_proposer];
        if (_latestProposalId != 0) {
            ProposalState _lastProposalState = state(_latestProposalId);
            if (_lastProposalState == ProposalState.Active || _lastProposalState == ProposalState.Pending) {
                revert ProposerActiveProposal(_proposer, _latestProposalId, _lastProposalState);
            }
        }
        uint256 _proposalId =
            GovernorSequentialProposalIdUpgradeable._propose(_targets, _values, _calldatas, _description, _proposer);
        latestProposalIds[_proposer] = _proposalId;
        return _proposalId;
    }

    /**
     * @notice Cancels an active proposal.
     * @dev This function can be called by the proposer or proposal guardian.
     * @param _targets An array of addresses that will be called if the proposal is executed.
     * @param _values An array of ETH values to be sent to each address when the proposal is executed.
     * @param _calldatas An array of calldata to be sent to each address when the proposal is executed.
     * @param _descriptionHash The hash of the proposal's description string.
     * @return uint256 The ID of the canceled proposal.
     */
    function cancel(
        address[] memory _targets,
        uint256[] memory _values,
        bytes[] memory _calldatas,
        bytes32 _descriptionHash
    ) public override returns (uint256) {
        uint256 _proposalId = hashProposal(_targets, _values, _calldatas, _descriptionHash);
        address _proposer = proposalProposer(_proposalId);

        // Only proposer and proposal guardian can cancel.
        if (
            _msgSender() != _proposer
                && (_msgSender() != proposalGuardian.account || block.timestamp > proposalGuardian.expiration)
        ) {
            revert OnlyProposerOrProposalGuardian();
        }

        return _cancel(_targets, _values, _calldatas, _descriptionHash);
    }

    /**
     * @notice Cancels a proposal given its ID.
     * @dev This function can be called by the proposer or proposal guardian.
     *      It retrieves proposal details and calls the main cancel function with those details.
     * @param _proposalId The ID of the proposal to cancel.
     */
    function cancel(uint256 _proposalId) public override {
        (address[] memory _targets, uint256[] memory _values, bytes[] memory _calldatas, bytes32 _descriptionHash) =
            proposalDetails(_proposalId);
        cancel(_targets, _values, _calldatas, _descriptionHash);
    }

    /**
     * @notice Sets or updates the whitelist expiration for a specific account.
     * A whitelisted account can create proposals without needing to be an allowed proposer.
     * A whitelisted account's proposals cannot be canceled by anyone except the `whitelistGuardian`.
     * A whitelisted account and `proposalGuardian` can still cancel its proposals.
     * @dev Only an allowed proposer can call this function to whitelist other accounts.
     * @param _account The address of the account to be whitelisted.
     * @param _expiration The timestamp until which the account will be whitelisted.
     */
    function setWhitelistAccountExpiration(address _account, uint256 _expiration) external {
        address _sender = _msgSender();
        // Check that msg.sender is in allowedProposers
        if (!allowedProposers.contains(_sender)) {
            revert OnlyAllowedProposers();
        }

        // Check that _account is not zero address
        if (_account == address(0)) {
            revert ZeroAddress();
        }

        // Check that _account is not equal to proposalGuardian
        if (_account == proposalGuardian.account) {
            revert IsProposalGuardian(_account);
        }

        // Check that _account is not in allowedProposers
        if (allowedProposers.contains(_account)) {
            revert AlreadySet(_account);
        }

        // If expiration is not less than now, check the lifetime constraint
        if (_expiration > block.timestamp && _expiration - block.timestamp > MAX_TEMPORARY_PROPOSER_LIFETIME) {
            revert ExceedsMaxLifetime(_account);
        }

        whitelistAccountExpirations[_account] = _expiration;
        emit WhitelistAccountExpirationSet(_sender, _account, _expiration);
    }

    /**
     * @notice Adds a new address to the allowed proposers list.
     * @dev Only the executor (timelock) or proposal guardian (when below minimum proposers) can call this function.
     * @param _newProposer The address to add to the allowed proposers list.
     */
    function addProposer(address _newProposer) external {
        address _sender = _msgSender();
        address _proposalGuardian = proposalGuardian.account;

        /**
         * Note Proposers can always be added by proposals
         * Note Proposal guardian can add proposers when below minimum proposers even if he is expired
         * This was done to prevent a case after upgrade where whitelist proposers expired and non of allowed
         * proposers were whitelisted
         * Note Proposal guardian can only add proposers when below minimum
         */
        if (_sender != _executor()) {
            if (_sender != proposalGuardian.account) {
                revert GovernorOnlyExecutor(_sender);
            }
            if (allowedProposers.length() >= MIN_PROPOSERS) {
                revert MinProposersReached();
            }
        }

        if (_newProposer == address(0)) {
            revert ZeroAddress();
        }

        if (_newProposer == _proposalGuardian) {
            revert IsProposalGuardian(_newProposer);
        }

        if (isAllowedProposer(_newProposer)) {
            revert AlreadySet(_newProposer);
        }

        allowedProposers.add(_newProposer);
        emit ProposerAdded(_newProposer);
    }

    /**
     * @notice Removes an address from the allowed proposers list.
     * @dev Only the executor (timelock) can call this function.
     * @param _proposer The address to remove from the allowed proposers list.
     */
    function removeProposer(address _proposer) external {
        if (_executor() != _msgSender()) {
            revert GovernorOnlyExecutor(_msgSender());
        }

        if (_proposer == address(0)) {
            revert ZeroAddress();
        }

        if (_proposer == proposalGuardian.account) {
            revert IsProposalGuardian(_proposer);
        }

        if (!isAllowedProposer(_proposer)) {
            revert NotInAllowedProposers(_proposer);
        }

        if (allowedProposers.length() <= MIN_PROPOSERS) {
            revert BelowMinimumProposers();
        }

        allowedProposers.remove(_proposer);
        emit ProposerRemoved(_proposer);
    }

    /// @notice Checks if an account is currently whitelisted.
    /// @param _account The address of the account to check.
    /// @return bool Returns true if the account is whitelisted (expiration is in the future), false otherwise.
    function isWhitelisted(address _account) public view returns (bool) {
        return (whitelistAccountExpirations[_account] > block.timestamp);
    }

    /// @notice Returns the list of allowed proposers.
    /// @return address[] An array of addresses that are allowed to make proposals.
    function getAllowedProposers() public view returns (address[] memory) {
        return allowedProposers.values();
    }

    /// @notice Checks if an address is in the allowed proposers list.
    /// @param _account The address to check.
    /// @return bool True if the address is an allowed proposer.
    function isAllowedProposer(address _account) public view returns (bool) {
        return allowedProposers.contains(_account);
    }

    /// @notice Checks if the proposal guardian has expired.
    /// @return bool True if the proposal guardian has expired, false otherwise.
    function isProposalGuardianExpired() public view returns (bool) {
        return block.timestamp > proposalGuardian.expiration;
    }

    /// @notice Validates that a proposal contains exactly one setProposalGuardian call.
    /// @param _targets An array of addresses that will be called if the proposal is executed.
    /// @param _calldatas An array of calldata to be sent to each address when the proposal is executed.
    /// @return bool True if the proposal contains exactly one setProposalGuardian call, false otherwise.
    function _isValidProposalWhenGuardianExpired(address[] memory _targets, bytes[] memory _calldatas)
        internal
        view
        returns (bool)
    {
        // Must have exactly one call when guardian has expired
        if (_targets.length != 1 || _calldatas.length != 1) {
            return false;
        }

        // Check that the single call is to this contract and uses setProposalGuardian selector
        if (_targets[0] != address(this)) {
            return false;
        }

        // Check that the calldata is the setProposalGuardian selector
        bytes4 selector = bytes4(_calldatas[0]);
        if (selector != SET_PROPOSAL_GUARDIAN_SELECTOR) {
            return false;
        }

        return true;
    }

    /// @notice Sets a new `whitelistGuardian`.
    /// @notice a `whitelistGuardian` can whitelist accounts and can cancel whitelisted accounts' proposals when they
    /// fall.
    /// below `proposalThreshold.
    /// @dev Only the executor (timelock) can call this function.
    /// @param _newWhitelistGuardian The address of the new `whitelistGuardian`.
    function setWhitelistGuardian(address _newWhitelistGuardian) external {
        _checkGovernance();
        _setWhitelistGuardian(_newWhitelistGuardian);
    }

    /// @notice Sets a new proposal guardian.
    /// @dev This function can only be called by the executor (timelock).
    /// @param _newProposalGuardian The new proposal guardian to be set, including their address and expiration.
    function setProposalGuardian(ProposalGuardian memory _newProposalGuardian) external {
        _checkGovernance();
        _setProposalGuardian(_newProposalGuardian);
    }

    /// @notice Admin function for setting the whitelistGuardian. WhitelistGuardian can cancel proposals from
    /// whitelisted addresses.
    /// @param _newWhitelistGuardian Account to set whitelistGuardian to (0x0 to remove whitelistGuardian).
    function _setWhitelistGuardian(address _newWhitelistGuardian) internal {
        emit WhitelistGuardianSet(whitelistGuardian, _newWhitelistGuardian);
        whitelistGuardian = _newWhitelistGuardian;
    }

    /**
     * @notice Internal function to set a new proposal guardian.
     * @dev Updates the proposal guardian and emits a {ProposalGuardianSet} event.
     *      If the new proposal guardian is different from the current one, the current proposal guardian
     *      is removed from the allowed proposers and the new proposal guardian is added.
     * @param _newProposalGuardian The new proposal guardian to be set, including their address and expiration.
     */
    function _setProposalGuardian(ProposalGuardian memory _newProposalGuardian) internal {
        address currentProposalGuardian = proposalGuardian.account;
        address newProposalGuardian = _newProposalGuardian.account;
        emit ProposalGuardianSet(
            currentProposalGuardian, proposalGuardian.expiration, newProposalGuardian, _newProposalGuardian.expiration
        );

        /// Note If batchWhitelist was not called during upgrade, we check that the current proposal guardian is in the
        /// allowed proposers
        if (currentProposalGuardian != newProposalGuardian) {
            allowedProposers.remove(currentProposalGuardian);
            allowedProposers.add(newProposalGuardian);
        }

        proposalGuardian = _newProposalGuardian;
    }

    /// @inheritdoc GovernorTimelockCompoundUpgradeable
    /// @dev We override this function to resolve ambiguity between inherited contracts.
    function _cancel(
        address[] memory _targets,
        uint256[] memory _values,
        bytes[] memory _calldatas,
        bytes32 _descriptionHash
    ) internal override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable) returns (uint256) {
        return GovernorTimelockCompoundUpgradeable._cancel(_targets, _values, _calldatas, _descriptionHash);
    }

    /// @inheritdoc GovernorPreventLateQuorumUpgradeable
    /// @dev We override this function to resolve ambiguity between inherited contracts.
    function _castVote(
        uint256 _proposalId,
        address _account,
        uint8 _support,
        string memory _reason,
        bytes memory _params
    ) internal override(GovernorUpgradeable, GovernorPreventLateQuorumUpgradeable) returns (uint256) {
        return GovernorPreventLateQuorumUpgradeable._castVote(_proposalId, _account, _support, _reason, _params);
    }

    /// @inheritdoc GovernorTimelockCompoundUpgradeable
    /// @dev We override this function to resolve ambiguity between inherited contracts.
    function _executeOperations(
        uint256 _proposalId,
        address[] memory _targets,
        uint256[] memory _values,
        bytes[] memory _calldatas,
        bytes32 _descriptionHash
    ) internal override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable) {
        return GovernorTimelockCompoundUpgradeable._executeOperations(
            _proposalId, _targets, _values, _calldatas, _descriptionHash
        );
    }

    /// @inheritdoc GovernorTimelockCompoundUpgradeable
    function _executor()
        internal
        view
        override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable)
        returns (address)
    {
        return GovernorTimelockCompoundUpgradeable._executor();
    }

    /// @inheritdoc GovernorTimelockCompoundUpgradeable
    /// @dev We override this function to resolve ambiguity between inherited contracts.
    function _queueOperations(
        uint256 _proposalId,
        address[] memory _targets,
        uint256[] memory _values,
        bytes[] memory _calldatas,
        bytes32 _descriptionHash
    ) internal override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable) returns (uint48) {
        return GovernorTimelockCompoundUpgradeable._queueOperations(
            _proposalId, _targets, _values, _calldatas, _descriptionHash
        );
    }

    /// @inheritdoc GovernorPreventLateQuorumUpgradeable
    /// @dev We override this function to resolve ambiguity between inherited contracts.
    function proposalDeadline(uint256 _proposalId)
        public
        view
        override(GovernorPreventLateQuorumUpgradeable, GovernorUpgradeable)
        returns (uint256)
    {
        return GovernorPreventLateQuorumUpgradeable.proposalDeadline(_proposalId);
    }

    /// @inheritdoc GovernorTimelockCompoundUpgradeable
    /// @dev We override this function to resolve ambiguity between inherited contracts.
    function proposalNeedsQueuing(uint256 _proposalId)
        public
        view
        override(GovernorTimelockCompoundUpgradeable, GovernorUpgradeable)
        returns (bool)
    {
        return GovernorTimelockCompoundUpgradeable.proposalNeedsQueuing(_proposalId);
    }

    /// @inheritdoc GovernorSettingsUpgradeable
    /// @dev We override this function to resolve ambiguity between inherited contracts.
    function proposalThreshold()
        public
        view
        override(GovernorSettingsUpgradeable, GovernorUpgradeable)
        returns (uint256)
    {
        return GovernorSettingsUpgradeable.proposalThreshold();
    }

    /// @inheritdoc GovernorTimelockCompoundUpgradeable
    /// @dev We override this function to resolve ambiguity between inherited contracts.
    function state(uint256 _proposalId)
        public
        view
        override(GovernorUpgradeable, GovernorTimelockCompoundUpgradeable)
        returns (ProposalState)
    {
        return GovernorTimelockCompoundUpgradeable.state(_proposalId);
    }

    /// @inheritdoc GovernorCountingFractionalUpgradeable
    // solhint-disable-next-line func-name-mixedcase
    function COUNTING_MODE()
        public
        pure
        override(IGovernor, GovernorCountingFractionalUpgradeable)
        returns (string memory)
    {
        return "support=bravo,fractional&quorum=for&params=fractional";
    }

    /// @notice Internal function that returns true if the amount of 'for' votes already cast meets the quorum limit,
    /// false otherwise.
    /// @dev We override this function to implement quorum functionality that only includes votes in favor.
    function _quorumReached(uint256 proposalId)
        internal
        view
        override(GovernorUpgradeable, GovernorCountingFractionalUpgradeable)
        returns (bool)
    {
        (, uint256 _forVotes,) = GovernorCountingFractionalUpgradeable.proposalVotes(proposalId);
        return quorum(proposalSnapshot(proposalId)) <= _forVotes;
    }
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
"
    },
    "contracts/extensions/IGovernor.sol": {
      "content": "// SPDX-License-Identifier: MIT
// Sourced from contract below, with change to hashProposal (removed pure) for sequential proposal ID support.
// OpenZeppelin Contracts (last updated v5.1.0) (governance/IGovernor.sol)

pragma solidity ^0.8.20;

import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {IERC6372} from "@openzeppelin/contracts/interfaces/IERC6372.sol";

/**
 * @dev Interface of the {Governor} core.
 *
 * NOTE: Event parameters lack the `indexed` keyword for compatibility with GovernorBravo events.
 * Making event parameters `indexed` affects how events are decoded, potentially breaking existing indexers.
 */
interface IGovernor is IERC165, IERC6372 {
    enum ProposalState {
        Pending,
        Active,
        Canceled,
        Defeated,
        Succeeded,
        Queued,
        Expired,
        Executed
    }

    /**
     * @dev Empty proposal or a mismatch between the parameters length for a proposal call.
     */
    error GovernorInvalidProposalLength(uint256 targets, uint256 calldatas, uint256 values);

    /**
     * @dev The vote was already cast.
     */
    error GovernorAlreadyCastVote(address voter);

    /**
     * @dev Token deposits are disabled in this contract.
     */
    error GovernorDisabledDeposit();

    /**
     * @dev The `account` is not a proposer.
     */
    error GovernorOnlyProposer(address account);

    /**
     * @dev The `account` is not the governance executor.
     */
    error GovernorOnlyExecutor(address account);

    /**
     * @dev The `proposalId` doesn't exist.
     */
    error GovernorNonexistentProposal(uint256 proposalId);

    /**
     * @dev The current state of a proposal is not the required for performing an operation.
     * The `expectedStates` is a bitmap with the bits enabled for each ProposalState enum position
     * counting from right to left.
     *
     * NOTE: If `expectedState` is `bytes32(0)`, the proposal is expected to not be in any state (i.e. not exist).
     * This is the case when a proposal that is expected to be unset is already initiated (the proposal is duplicated).
     *
     * See {Governor-_encodeStateBitmap}.
     */
    error GovernorUnexpectedProposalState(uint256 proposalId, ProposalState current, bytes32 expectedStates);

    /**
     * @dev The voting period set is not a valid period.
     */
    error GovernorInvalidVotingPeriod(uint256 votingPeriod);

    /**
     * @dev The `proposer` does not have the required votes to create a proposal.
     */
    error GovernorInsufficientProposerVotes(address proposer, uint256 votes, uint256 threshold);

    /**
     * @dev The `proposer` is not allowed to create a proposal.
     */
    error GovernorRestrictedProposer(address proposer);

    /**
     * @dev The vote type used is not valid for the corresponding counting module.
     */
    error GovernorInvalidVoteType();

    /**
     * @dev The provided params buffer is not supported by the counting module.
     */
    error GovernorInvalidVoteParams();

    /**
     * @dev Queue operation is not implemented for this governor. Execute should be called directly.
     */
    error GovernorQueueNotImplemented();

    /**
     * @dev The proposal hasn't been queued yet.
     */
    error GovernorNotQueuedProposal(uint256 proposalId);

    /**
     * @dev The proposal has already been queued.
     */
    error GovernorAlreadyQueuedProposal(uint256 proposalId);

    /**
     * @dev The provided signature is not valid for the expected `voter`.
     * If the `voter` is a contract, the signature is not valid using {IERC1271-isValidSignature}.
     */
    error GovernorInvalidSignature(address voter);

    /**
     * @dev The `account` is not whitelisted or not whitelisted permanently to propose.
     */
    error GovernorNotWhitelisted(address account);

    /**
     * @dev Emitted when a proposal is created.
     */
    event ProposalCreated(
        uint256 proposalId,
        address proposer,
        address[] targets,
        uint256[] values,
        string[] signatures,
        bytes[] calldatas,
        uint256 voteStart,
        uint256 voteEnd,
        string description
    );

    /**
     * @dev Emitted when a proposal is queued.
     */
    event ProposalQueued(uint256 proposalId, uint256 etaSeconds);

    /**
     * @dev Emitted when a proposal is executed.
     */
    event ProposalExecuted(uint256 proposalId);

    /**
     * @dev Emitted when a proposal is canceled.
     */
    event ProposalCanceled(uint256 proposalId);

    /**
     * @dev Emitted when a vote is cast without params.
     *
     * Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
     */
    event VoteCast(address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason);

    /**
     * @dev Emitted when a vote is cast with params.
     *
     * Note: `support` values should be seen as buckets. Their interpretation depends on the voting module used.
     * `params` are additional encoded parameters. Their interpretation  also depends on the voting module used.
     */
    event VoteCastWithParams(
        address indexed voter, uint256 proposalId, uint8 support, uint256 weight, string reason, bytes params
    );

    /**
     * @notice module:core
     * @dev Name of the governor instance (used in building the EIP-712 domain separator).
     */
    function name() external view returns (string memory);

    /**
     * @notice module:core
     * @dev Version of the governor instance (used in building the EIP-712 domain separator). Default: "1"
     */
    function version() external view returns (string memory);

    /**
     * @notice module:voting
     * @dev A description of the possible `support` values for {castVote} and the way these votes are counted, meant to
     * be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence
     * of
     * key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`.
     *
     * There are 2 standard keys: `support` and `quorum`.
     *
     * - `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`.
     * - `quorum=bravo` means that only For votes are counted towards quorum.
     * - `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum.
     *
     * If a counting module makes use of encoded `params`, it should  include this under a `params` key with a unique
     * name that describes the behavior. For example:
     *
     * - `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain.
     * - `params=erc721` might refer to a scheme where specific NFTs are delegated to vote.
     *
     * NOTE: The string can be decoded by the standard
     * https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams[`URLSearchParams`]
     * JavaScript class.
     */
    // solhint-disable-next-line func-name-mixedcase
    function COUNTING_MODE() external view returns (string memory);

    /**
     * @notice module:core
     * @dev Hashing function used to (re)build the proposal id from the proposal details..
     */
    function hashProposal(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory calldatas,
        bytes32 descriptionHash
    ) external returns (uint256);

    /**
     * @notice module:core
     * @dev Current state of a proposal, following Compound's convention
     */
    function state(uint256 proposalId) external view returns (ProposalState);

    /**
     * @notice module:core
     * @dev The number of votes required in order for a voter to become a proposer.
     */
    function proposalThreshold() external view returns (uint256);

    /**
     * @notice module:core
     * @dev Timepoint used to retrieve user's votes and quorum. If using block number (as per Compound's Comp), the
     * snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the
     * following block.
     */
    function proposalSnapshot(uint256 proposalId) external view returns (uint256);

    /**
     * @notice module:core
     * @dev Timepoint at which votes close. If using block number, votes close at the end of this block, so it is
     * possible to cast a vote during this block.
     */
    function proposalDeadline(uint256 proposalId) external view returns (uint256);

    /**
     * @notice module:core
     * @dev The account that created a proposal.
     */
    function proposalProposer(uint256 proposalId) external view returns (address);

    /**
     * @notice module:core
     * @dev The time when a queued proposal becomes executable ("ETA"). Unlike {proposalSnapshot} and
     * {proposalDeadline}, this doesn't use the governor clock, and instead relies on the executor's clock which may be
     * different. In most cases this will be a timestamp.
     */
    function proposalEta(uint256 proposalId) external view returns (uint256);

    /**
     * @notice module:core
     * @dev Whether a proposal needs to be queued before execution.
     */
    function proposalNeedsQueuing(uint256 proposalId) external view returns (bool);

    /**
     * @notice module:user-config
     * @dev Delay, between the proposal is created and the vote starts. The unit this duration is expressed in depends
     * on the clock (see ERC-6372) this contract uses.
     *
     * This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a
     * proposal starts.
     *
     * NOTE: While this interface returns a uint256, timepoints are stored as uint48 following the ERC-6372 clock type.
     * Consequently this value must fit in a uint48 (when added to the current clock). See {IERC6372-clock}.
     */
    function votingDelay() external view returns (uint256);

    /**
     * @notice module:user-config
     * @dev Delay between the vote start and vote end. The unit this duration is expressed in depends on the clock
     * (see ERC-6372) this contract uses.
     *
     * NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting
     * duration compared to the voting delay.
     *
     * NOTE: This value is stored when the proposal is submitted so that possible changes to the value do not affect
     * proposals that have already been submitted. The type used to save it is a uint32. Consequently, while this
     * interface returns a uint256, the value it returns should fit in a uint32.
     */
    function votingPeriod() external view returns (uint256);

    /**
     * @notice module:user-config
     * @dev Minimum number of cast voted required for a proposal to be successful.
     *
     * NOTE: The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows to scale the
     * quorum depending on values such as the totalSupply of a token at this timepoint (see {ERC20Votes}).
     */
    function quorum(uint256 timepoint) external view returns (uint256);

    /**
     * @notice module:reputation
     * @dev Voting power of an `account` at a specific `timepoint`.
     *
     * Note: this can be implemented in a number of ways, for example by reading the delegated balance from one (or
     * multiple), {ERC20Votes} tokens.
     */
    function getVotes(address account, uint256 timepoint) external view returns (uint256);

    /**
     * @notice module:reputation
     * @dev Voting power of an `account` at a specific `timepoint` given additional encoded parameters.
     */
    function getVotesWithParams(address account, uint256 timepoint, bytes memory params)
        external
        view
        returns (uint256);

    /**
     * @notice module:voting
     * @dev Returns whether `account` has cast a vote on `proposalId`.
     */
    function hasVoted(uint256 proposalId, address account) external view returns (bool);

    /**
     * @dev Create a new proposal. Vote start after a delay specified by {IGovernor-votingDelay} and lasts for a
     * duration specified by {IGovernor-votingPeriod}.
     *
     * Emits a {ProposalCreated} event.
     *
     * NOTE: The state of the Governor and `targets` may change between the proposal creation and its execution.
     * This may be the result of third party actions on the targeted contracts, or other governor proposals.
     * For example, the balance of this contract could be updated or its access control permissions may be modified,
     * possibly compromising the proposal's ability to execute successfully (e.g. the governor doesn't have enough
     * value to cover a proposal with multiple transfers).
     */
    function propose(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory calldatas,
        string memory description
    ) external returns (uint256 proposalId);

    /**
     * @dev Queue a proposal. Some governors require this step to be performed before execution can happen. If queuing
     * is not necessary, this function may revert.
     * Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached.
     *
     * Emits a {ProposalQueued} event.
     */
    function queue(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
        external
        returns (uint256 proposalId);

    /**
     * @dev Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the
     * deadline to be reached. Depending on the governor it might also be required that the proposal was queued and
     * that some delay passed.
     *
     * Emits a {ProposalExecuted} event.
     *
     * NOTE: Some modules can modify the requirements for execution, for example by adding an additional timelock.
     */
    function execute(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory calldatas,
        bytes32 descriptionHash
    ) external payable returns (uint256 proposalId);

    /**
     * @dev Cancel a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e.
     * before the vote starts.
     *
     * Emits a {ProposalCanceled} event.
     */
    function cancel(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory calldatas,
        bytes32 descriptionHash
    ) external returns (uint256 proposalId);

    /**
     * @dev Cast a vote
     *
     * Emits a {VoteCast} event.
     */
    function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance);

    /**
     * @dev Cast a vote with a reason
     *
     * Emits a {VoteCast} event.
     */
    function castVoteWithReason(uint256 proposalId, uint8 support, string calldata reason)
        external
        returns (uint256 balance);

    /**
     * @dev Cast a vote with a reason and additional encoded parameters
     *
     * Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
     */
    function castVoteWithReasonAndParams(uint256 proposalId, uint8 support, string calldata reason, bytes memory params)
        external
        returns (uint256 balance);

    /**
     * @dev Cast a vote using the voter's signature, including ERC-1271 signature support.
     *
     * Emits a {VoteCast} event.
     */
    function castVoteBySig(uint256 proposalId, uint8 support, address voter, bytes memory signature)
        external
        returns (uint256 balance);

    /**
     * @dev Cast a vote with a reason and additional encoded parameters using the voter's signature,
     * including ERC-1271 signature support.
     *
     * Emits a {VoteCast} or {VoteCastWithParams} event depending on the length of params.
     */
    function castVoteWithReasonAndParamsBySig(
        uint256 proposalId,
        uint8 support,
        address voter,
        string calldata reason,
        bytes memory params,
        bytes memory signature
    ) external returns (uint256 balance);
}
"
    },
    "contracts/extensions/GovernorUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// Sourced from contract below, with change to hashProposal (removed pure) for sequential proposal ID support.
// OpenZeppelin Contracts (last updated v5.1.0) (governance/extensions/GovernorUpgradeable.sol)

pragma solidity ^0.8.20;

import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {DoubleEndedQueue} from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import {NoncesUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol";
import {IGovernor} from "contracts/extensions/IGovernor.sol";
import {IERC6372} from "@openzeppelin/contracts/interfaces/IERC6372.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/**
 * @dev Core of the governance system, designed to be extended through various modules.
 *
 * This contract is abstract and requires several functions to be implemented in various modules:
 *
 * - A counting module must implement {quorum}, {_quorumReached}, {_voteSucceeded} and {_countVote}
 * - A voting module must implement {_getVotes}
 * - Additionally, {votingPeriod} must also be implemented
 */
abstract contract GovernorUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, EIP712Upgradeable, NoncesUpgradeable, IGovernor, IERC721Receiver, IERC1155Receiver {
    using DoubleEndedQueue for DoubleEndedQueue.Bytes32Deque;

    bytes32 public constant BALLOT_TYPEHASH =
        keccak256("Ballot(uint256 proposalId,uint8 support,address voter,uint256 nonce)");
    bytes32 public constant EXTENDED_BALLOT_TYPEHASH =
        keccak256(
            "ExtendedBallot(uint256 proposalId,uint8 support,address voter,uint256 nonce,string reason,bytes params)"
        );

    struct ProposalCore {
        address proposer;
        uint48 voteStart;
        uint32 voteDuration;
        bool executed;
        bool canceled;
        uint48 etaSeconds;
    }

    bytes32 private constant ALL_PROPOSAL_STATES_BITMAP = bytes32((2 ** (uint8(type(ProposalState).max) + 1)) - 1);
    /// @custom:storage-location erc7201:openzeppelin.storage.Governor
    struct GovernorStorage {
        string _name;

        mapping(uint256 proposalId => ProposalCore) _proposals;

        // This queue keeps track of the governor operating on itself. Calls to functions protected by the {onlyGovernance}
        // modifier needs to be whitelisted in this queue. Whitelisting is set in {execute}, consumed by the
        // {onlyGovernance} modifier and eventually reset after {_executeOperations} completes. This ensures that the
        // execution of {onlyGovernance} protected calls can only be achieved through successful proposals.
        DoubleEndedQueue.Bytes32Deque _governanceCall;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Governor")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant GovernorStorageLocation = 0x7c712897014dbe49c045ef1299aa2d5f9e67e48eea4403efa21f1e0f3ac0cb00;

    function _getGovernorStorage() private pure returns (GovernorStorage storage $) {
        assembly {
            $.slot := GovernorStorageLocation
        }
    }

    /**
     * @dev Restricts a function so it can only be executed through governance proposals. For example, governance
     * parameter setters in {GovernorSettings} are protected using this modifier.
     *
     * The governance executing address may be different from the Governor's own address, for example it could be a
     * timelock. This can be customized by modules by overriding {_executor}. The executor is only able to invoke these
     * functions during the execution of the governor's {execute} function, and not under any other circumstances. Thus,
     * for example, additional timelock proposers are not able to change governance parameters without going through the
     * governance protocol (since v4.6).
     */
    modifier onlyGovernance() {
        _checkGovernance();
        _;
    }

    /**
     * @dev Sets the value for {name} and {version}
     */
    function __Governor_init(string memory name_) internal onlyInitializing {
        __EIP712_init_unchained(name_, version());
        __Governor_init_unchained(name_);
    }

    function __Governor_init_unchained(string memory name_) internal onlyInitializing {
        GovernorStorage storage $ = _getGovernorStorage();
        $._name = name_;
    }

    /**
     * @dev Function to receive ETH that will be handled by the governor (disabled if executor is a third party contract)
     */
    receive() external payable virtual {
        if (_executor() != address(this)) {
            rever

Tags:
ERC20, ERC165, Multisig, Swap, Voting, Timelock, Upgradeable, Multi-Signature, Factory|addr:0x943c2789960141fe8f27f50e1ceb9fd8ac63fc4a|verified:true|block:23625728|tx:0xbe85c2a138976782bce9d596e31d85818256391d8d8e43591bd46d6dabf39d7c|first_check:1761048540

Submitted on: 2025-10-21 14:09:02

Comments

Log in to comment.

No comments yet.