ProposalManager

Overview

ProposalManager is a governance contract that leverages OpenZeppelin’s Governor framework to manage proposals in the EYWA ecosystem. It integrates quorum calculations, time-based voting, timelock-controlled execution, and protections for certain function selectors.

Key Roles and Features:

  • Governance and Voting: Manages the proposal lifecycle (creation, voting, queueing, execution) using a standard Governor flow.

  • Timelock Control: Ensures that successfully passed proposals are executed after a governance-defined delay.

  • Protected Function Selectors: Prevents unauthorized proposals from calling certain critical functions unless the proposer is the contract owner.

  • Multi-Chain Awareness: Maintains a list of supported chain IDs and protected function selectors for different target contracts on those chains.

  • Adjustable Quorum: Allows the owner to update the quorum fraction within pre-defined bounds.

This contract integrates with the IEscrowManager (indirectly, through IVotes) for vote calculations, uses a timelock controller for delayed execution, and relies on IProposalManager for its extended interface.


Inherited Contracts and Interfaces

  • Governor (OpenZeppelin): Base contract for managing proposals and voting.

  • GovernorCountingSimple: Provides a simple majority counting mechanism for votes (For, Against, Abstain).

  • GovernorVotes: Integrates an IVotes contract (such as IEscrowManager) to determine voting power.

  • GovernorVotesQuorumFraction: Computes quorum as a fraction of the total supply of votes at the proposal’s snapshot.

  • GovernorTimelockControl: Integrates a TimelockController to delay execution of proposals after they pass.

  • Ownable (OpenZeppelin): Allows ownership management to restrict certain actions (like setting bridge or modifying chain IDs) to the owner.

  • IProposalManager: Defines additional interface elements, error codes, and events specific to EYWA’s proposal management logic.


Constants

  • MINIMUM_QUORUM_NUMERATOR = 25: Minimum quorum fraction (25%).

  • MAXIMUM_QUORUM_NUMERATOR = 80: Maximum quorum fraction (80%).


State Variables

  • s_bridge (address): The address of the bridge contract.

  • _chainIds (EnumerableSet.UintSet): Set of allowed chain IDs.

  • s_protectedSelectorsByChainIdAndTarget (mapping(uint256 => mapping(address => bytes4[]))): Protected selectors for specific targets on specific chains.

  • s_isProtectedSelector (mapping(uint256 => mapping(address => mapping(bytes4 => bool)))): Quick lookup to check if a particular selector is protected on a given chain and target.


Constructor

Signature:

constructor(
    IVotes escrowManager_,
    TimelockController timelock_,
    address owner_,
    address bridge_
)
    Governor("EYWA DAO")
    GovernorVotes(escrowManager_)
    GovernorVotesQuorumFraction(50) // initial quorum fraction: 50%
    GovernorTimelockControl(timelock_)
    Ownable(owner_)

Description: Initializes the ProposalManager with references to:

  • A voting power source (escrowManager_ implementing IVotes),

  • A TimelockController for queued proposal execution,

  • An initial owner,

  • A bridge_ address.

Sets the governance name to β€œEYWA DAO” and initial quorum fraction to 50%.


External Functions (IProposalManager and Overrides)

setBridge(address bridge_)

Signature:

function setBridge(address bridge_) external onlyOwner

Description: Updates the bridge contract address. Only callable by the contract owner.

Events:

  • Emits BridgeUpdated(oldBridge, newBridge).


addChainId(uint256 chainId_)

Signature:

function addChainId(uint256 chainId_) external onlyOwner

Description: Adds a chain ID to the set of recognized chain IDs. Reverts if the chain ID already exists.

Errors:

  • ChainIdAlreadyExists(chainId_) if the chain ID is already present.


removeChainId(uint256 chainId_)

Signature:

function removeChainId(uint256 chainId_) external onlyOwner

Description: Removes a chain ID from the recognized set. Reverts if the chain ID does not exist.

Errors:

  • ChainIdDoesNotExist(chainId_) if the chain ID is not found.


getChainIdsLength()

Signature:

function getChainIdsLength() external view returns (uint256)

Description: Returns the number of chain IDs currently recognized.

Return:

  • uint256: The length of the _chainIds set.


getChainIdAtIndex(uint256 index_)

Signature:

function getChainIdAtIndex(uint256 index_) external view returns (uint256)

Description: Retrieves a chain ID by index in the _chainIds set. Uses EnumerableSet indexing.

Return:

  • uint256: The chain ID at the given index.


addProtectedSelector(uint256 chainId_, address target_, bytes4 selector_)

Signature:

function addProtectedSelector(uint256 chainId_, address target_, bytes4 selector_) external onlyOwner

Description: Adds a protected function selector for a specific target contract on a given chain ID. Only the owner can add.

Checks:

  • The specified chainId_ must be already recognized.

  • The selector_ must not already exist for that target and chain.

Errors:

  • InvalidChainId(chainId_) if chain ID is not recognized.

  • SelectorAlreadyExists(selector_) if the selector already exists.


removeProtectedSelector(uint256 chainId_, address target_, bytes4 selector_)

Signature:

function removeProtectedSelector(uint256 chainId_, address target_, bytes4 selector_) external onlyOwner

Description: Removes a previously protected function selector. Only owner can remove.

Checks:

  • The specified chainId_ must be recognized.

  • The selector_ must exist.

Errors:

  • InvalidChainId(chainId_) if chain ID is not recognized.

  • SelectorDoesNotExist(selector_) if the selector does not exist.


updateQuorumNumerator(uint256 quorumNumerator_)

Signature:

function updateQuorumNumerator(uint256 quorumNumerator_) external override onlyOwner

Description: Adjusts the quorum fraction within allowed bounds (25% to 80%).

Errors:

  • GovernorInvalidQuorumFraction(quorumNumerator_, quorumDenominator()) if outside the allowed range.


updateTimelock(TimelockController)

Signature:

function updateTimelock(TimelockController) external override

Description: This function is required by GovernorTimelockControl but left empty as no dynamic timelock updates are performed.


propose(...)

Signature:

function propose(
    address[] memory targets_,
    uint256[] memory values_,
    bytes[] memory calldatas_,
    string memory description_
) 
    public 
    override (Governor, IGovernor)
    returns (uint256)

Description: Creates a new proposal. Before proposing, it checks selectors to ensure that any protected function calls are made only by the owner.

Calls:

  • _checkSelectors(msg.sender, targets_, calldatas_) to validate access.

Return:

  • uint256: The proposal ID.


clock()

Signature:

function clock() public view override (Governor, GovernorVotes, IERC6372) returns (uint48)

Description: Returns the current time as a uint48, overriding multiple inherited interfaces.


state(uint256 proposalId_)

Signature:

function state(uint256 proposalId_) public view override(Governor, GovernorTimelockControl, IGovernor) returns (ProposalState)

Description: Returns the current state of a proposal, integrating both Governor and Timelock logic.


quorum(uint256 timepoint_)

Signature:

function quorum(uint256 timepoint_)
    public
    view
    override (Governor, GovernorVotesQuorumFraction, IGovernor)
    returns (uint256)

Description: Calculates quorum based on timepoint_, the total supply at that time, and the current quorum fraction.


CLOCK_MODE()

Signature:

function CLOCK_MODE() public pure override (Governor, GovernorVotes, IERC6372) returns (string memory)

Description: Returns the β€œmode=timestamp” indicating that block timestamps are used for time measurement.


proposalThreshold()

Signature:

function proposalThreshold() public pure override (Governor, IGovernor) returns (uint256)

Description: Sets a static proposal threshold of 2,500e18 votes required to create a proposal.


votingDelay()

Signature:

function votingDelay() public pure override (Governor, IGovernor) returns (uint256)

Description: Returns a 2-day voting delay after a proposal is created before voting starts.


votingPeriod()

Signature:

function votingPeriod() public pure override (Governor, IGovernor) returns (uint256)

Description: Returns a 5-day voting period for each proposal.


proposalNeedsQueuing(uint256)

Signature:

function proposalNeedsQueuing(uint256) public pure override (Governor, GovernorTimelockControl, IGovernor) returns (bool)

Description: Indicates that proposals must be queued in the timelock after passing and before execution.


Internal Functions (Overrides from Governor/Timelock)

_queueOperations(...)

Signature:

function _queueOperations(
    uint256 proposalId_,
    address[] memory targets_,
    uint256[] memory values_,
    bytes[] memory calldatas_,
    bytes32 descriptionHash_
) 
    internal 
    override (Governor, GovernorTimelockControl)
    returns (uint48)

Description: Queues proposal operations in the timelock. Uses base logic from GovernorTimelockControl.


_executeOperations(...)

Signature:

function _executeOperations(
    uint256 proposalId_,
    address[] memory targets_,
    uint256[] memory values_,
    bytes[] memory calldatas_,
    bytes32 descriptionHash_
) 
    internal 
    override (Governor, GovernorTimelockControl)

Description: Executes proposal operations after a timelock delay. Calls _checkSelectors again to ensure compliance with protected selectors.


_cancel(...)

Signature:

function _cancel(
    address[] memory targets_,
    uint256[] memory values_,
    bytes[] memory calldatas_,
    bytes32 descriptionHash_
)
    internal
    override (Governor, GovernorTimelockControl)
    returns (uint256)

Description: Cancels a queued proposal. Uses base logic from GovernorTimelockControl.


_executor()

Signature:

function _executor() internal view override (Governor, GovernorTimelockControl) returns (address)

Description: Determines the address that executes proposals. Defaults to GovernorTimelockControl logic.


Internal/Private Helper Functions

_checkSelectors(address proposalCreator_, address[] memory targets_, bytes[] memory calldatas_)

Signature:

function _checkSelectors(
    address proposalCreator_,
    address[] memory targets_,
    bytes[] memory calldatas_
) private view

Description: Scans all proposed calls. If any call involves a protected function selector (based on s_protectedSelectorsByChainIdAndTarget), ensures proposalCreator_ is the owner. Otherwise reverts with ProtectedFunctionSelectorUsed().

Errors:

  • ProtectedFunctionSelectorUsed() if a protected call is made by non-owner.


_slice(bytes memory data_, uint256 start_, uint256 length_)

Signature:

function _slice(bytes memory data_, uint256 start_, uint256 length_) private pure returns (bytes memory result_)

Description: Returns a subarray of data_ starting at start_ with length_ bytes.

Errors:

  • InvalidSliceParameters() if out-of-bounds.


Events

BridgeUpdated(address indexed oldBridge, address indexed newBridge)

Emitted When: The bridge address is changed by the owner.

Parameters:

  • oldBridge: The previous bridge contract address.

  • newBridge: The new bridge contract address.


Errors

  • ChainIdAlreadyExists(uint256 chainId)

  • ChainIdDoesNotExist(uint256 chainId)

  • SelectorAlreadyExists(bytes4 selector)

  • SelectorDoesNotExist(bytes4 selector)

  • InvalidChainId(uint256 chainId)

  • ProtectedFunctionSelectorUsed()

  • InvalidSliceParameters()

These errors ensure correct chain ID and selector management and prevent unauthorized protected calls.


Summary

ProposalManager extends OpenZeppelin’s governance contracts to manage proposals within the EYWA ecosystem. By incorporating a timelock, quorum logic, adjustable quorum fraction, and protected function selectors, it ensures that certain governance actions are restricted, preventing misuse. Integration with the escrow manager for votes and a bridge for cross-chain calls, along with owner-managed lists of chain IDs and selectors, provides a flexible and secure governance framework for EYWA.

Last updated