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 asIEscrowManager
) 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:
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_)
setBridge(address bridge_)
Signature:
Description: Updates the bridge contract address. Only callable by the contract owner.
Events:
Emits
BridgeUpdated(oldBridge, newBridge)
.
addChainId(uint256 chainId_)
addChainId(uint256 chainId_)
Signature:
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_)
removeChainId(uint256 chainId_)
Signature:
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()
getChainIdsLength()
Signature:
Description: Returns the number of chain IDs currently recognized.
Return:
uint256
: The length of the_chainIds
set.
getChainIdAtIndex(uint256 index_)
getChainIdAtIndex(uint256 index_)
Signature:
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_)
addProtectedSelector(uint256 chainId_, address target_, bytes4 selector_)
Signature:
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_)
removeProtectedSelector(uint256 chainId_, address target_, bytes4 selector_)
Signature:
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_)
updateQuorumNumerator(uint256 quorumNumerator_)
Signature:
Description: Adjusts the quorum fraction within allowed bounds (25% to 80%).
Errors:
GovernorInvalidQuorumFraction(quorumNumerator_, quorumDenominator())
if outside the allowed range.
updateTimelock(TimelockController)
updateTimelock(TimelockController)
Signature:
Description:
This function is required by GovernorTimelockControl
but left empty as no dynamic timelock updates are performed.
propose(...)
propose(...)
Signature:
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()
clock()
Signature:
Description:
Returns the current time as a uint48
, overriding multiple inherited interfaces.
state(uint256 proposalId_)
state(uint256 proposalId_)
Signature:
Description: Returns the current state of a proposal, integrating both Governor and Timelock logic.
quorum(uint256 timepoint_)
quorum(uint256 timepoint_)
Signature:
Description:
Calculates quorum based on timepoint_
, the total supply at that time, and the current quorum fraction.
CLOCK_MODE()
CLOCK_MODE()
Signature:
Description: Returns the βmode=timestampβ indicating that block timestamps are used for time measurement.
proposalThreshold()
proposalThreshold()
Signature:
Description:
Sets a static proposal threshold of 2,500e18
votes required to create a proposal.
votingDelay()
votingDelay()
Signature:
Description: Returns a 2-day voting delay after a proposal is created before voting starts.
votingPeriod()
votingPeriod()
Signature:
Description: Returns a 5-day voting period for each proposal.
proposalNeedsQueuing(uint256)
proposalNeedsQueuing(uint256)
Signature:
Description: Indicates that proposals must be queued in the timelock after passing and before execution.
Internal Functions (Overrides from Governor/Timelock)
_queueOperations(...)
_queueOperations(...)
Signature:
Description:
Queues proposal operations in the timelock. Uses base logic from GovernorTimelockControl
.
_executeOperations(...)
_executeOperations(...)
Signature:
Description:
Executes proposal operations after a timelock delay. Calls _checkSelectors
again to ensure compliance with protected selectors.
_cancel(...)
_cancel(...)
Signature:
Description:
Cancels a queued proposal. Uses base logic from GovernorTimelockControl
.
_executor()
_executor()
Signature:
Description:
Determines the address that executes proposals. Defaults to GovernorTimelockControl
logic.
Internal/Private Helper Functions
_checkSelectors(address proposalCreator_, address[] memory targets_, bytes[] memory calldatas_)
_checkSelectors(address proposalCreator_, address[] memory targets_, bytes[] memory calldatas_)
Signature:
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_)
_slice(bytes memory data_, uint256 start_, uint256 length_)
Signature:
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)
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