123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.20;
- import {IGovernorTimelock} from "./IGovernorTimelock.sol";
- import {IGovernor, Governor} from "../Governor.sol";
- import {IManaged} from "../../access/manager/IManaged.sol";
- import {IAuthority, safeCanCall} from "../../access/manager/IAuthority.sol";
- import {IAccessManager} from "../../access/manager/IAccessManager.sol";
- import {Address} from "../../utils/Address.sol";
- import {Math} from "../../utils/math/Math.sol";
- /**
- * @dev TODO
- *
- * _Available since v5.0._
- */
- abstract contract GovernorTimelock is IGovernorTimelock, Governor {
- struct ExecutionDetail {
- address authority;
- uint32 delay;
- }
- mapping(uint256 => ExecutionDetail[]) private _executionDetails;
- mapping(uint256 => uint256) private _proposalEta;
- /**
- * @dev Overridden version of the {Governor-state} function with added support for the `Queued` and `Expired` state.
- */
- function state(uint256 proposalId) public view virtual override(IGovernor, Governor) returns (ProposalState) {
- ProposalState currentState = super.state(proposalId);
- if (currentState == ProposalState.Succeeded && proposalEta(proposalId) != 0) {
- return ProposalState.Queued;
- } else {
- return currentState;
- }
- }
- /**
- * @dev Public accessor to check the eta of a queued proposal.
- */
- function proposalEta(uint256 proposalId) public view virtual override returns (uint256) {
- return _proposalEta[proposalId];
- }
- /**
- * @dev Public accessor to check the execution details.
- */
- function proposalExecutionDetails(uint256 proposalId) public view returns (ExecutionDetail[] memory) {
- return _executionDetails[proposalId];
- }
- /**
- * @dev See {IGovernor-propose}
- */
- function propose(
- address[] memory targets,
- uint256[] memory values,
- bytes[] memory calldatas,
- string memory description
- ) public virtual override(IGovernor, Governor) returns (uint256) {
- uint256 proposalId = super.propose(targets, values, calldatas, description);
- ExecutionDetail[] storage details = _executionDetails[proposalId];
- for (uint256 i = 0; i < targets.length; ++i) {
- details.push(_detectExecutionDetails(targets[i], bytes4(calldatas[i])));
- }
- return proposalId;
- }
- /**
- * @dev Function to queue a proposal to the timelock.
- *
- * NOTE: execution delay is estimated based on the delay information retrieved in {proposal}. This value may be
- * off if the delay were updated during the vote.
- */
- function queue(
- address[] memory targets,
- uint256[] memory values,
- bytes[] memory calldatas,
- bytes32 descriptionHash
- ) public virtual override returns (uint256) {
- uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
- ProposalState currentState = state(proposalId);
- if (currentState != ProposalState.Succeeded) {
- revert GovernorUnexpectedProposalState(
- proposalId,
- currentState,
- _encodeStateBitmap(ProposalState.Succeeded)
- );
- }
- ExecutionDetail[] storage details = _executionDetails[proposalId];
- ExecutionDetail memory detail;
- uint32 setback = 0;
- for (uint256 i = 0; i < targets.length; ++i) {
- detail = details[i];
- if (detail.authority != address(0)) {
- IAccessManager(detail.authority).schedule(targets[i], calldatas[i]);
- }
- setback = uint32(Math.max(setback, detail.delay)); // cast is safe, both parameters are uint32
- }
- uint256 eta = block.timestamp + setback;
- _proposalEta[proposalId] = eta;
- return eta;
- }
- /**
- * @dev See {IGovernor-_execute}
- */
- function _execute(
- uint256 proposalId,
- address[] memory targets,
- uint256[] memory values,
- bytes[] memory calldatas,
- bytes32 /*descriptionHash*/
- ) internal virtual override {
- ExecutionDetail[] storage details = _executionDetails[proposalId];
- ExecutionDetail memory detail;
- // TODO: enforce ETA (might include some _defaultDelaySeconds that are not enforced by any authority)
- for (uint256 i = 0; i < targets.length; ++i) {
- detail = details[i];
- if (detail.authority != address(0)) {
- IAccessManager(detail.authority).relay{value: values[i]}(targets[i], calldatas[i]);
- } else {
- (bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]);
- Address.verifyCallResult(success, returndata);
- }
- }
- delete _executionDetails[proposalId];
- }
- /**
- * @dev See {IGovernor-_cancel}
- */
- function _cancel(
- address[] memory targets,
- uint256[] memory values,
- bytes[] memory calldatas,
- bytes32 descriptionHash
- ) internal virtual override returns (uint256) {
- uint256 proposalId = super._cancel(targets, values, calldatas, descriptionHash);
- ExecutionDetail[] storage details = _executionDetails[proposalId];
- ExecutionDetail memory detail;
- for (uint256 i = 0; i < targets.length; ++i) {
- detail = details[i];
- if (detail.authority != address(0)) {
- IAccessManager(detail.authority).cancel(address(this), targets[i], calldatas[i]);
- }
- }
- delete _executionDetails[proposalId];
- return proposalId;
- }
- /**
- * @dev Default delay to apply to function calls that are not (scheduled and) executed through an AccessManager.
- *
- * NOTE: execution delays are processed by the AccessManager contracts. We expect these to always be in seconds.
- * Therefore, the default delay that is enforced for calls that don't go through an access manager is also in
- * seconds, regardless of the Governor's clock mode.
- */
- function _defaultDelaySeconds() internal view virtual returns (uint32) {
- return 0;
- }
- /**
- * @dev Check if the execution of a call needs to be performed through an AccessManager and what delay should be
- * applied to this call.
- *
- * Returns { manager: address(0), delay: _defaultDelaySeconds() } if:
- * - target does not have code
- * - target does not implement IManaged
- * - calling canCall on the target's manager returns a 0 delay
- * - calling canCall on the target's manager reverts
- * Otherwise (calling canCall on the target's manager returns a non 0 delay), return the address of the
- * AccessManager to use, and the delay for this call.
- */
- function _detectExecutionDetails(address target, bytes4 selector) private view returns (ExecutionDetail memory) {
- bool success;
- bytes memory returndata;
- // Get authority
- (success, returndata) = target.staticcall(abi.encodeCall(IManaged.authority, ()));
- if (success && returndata.length >= 0x20) {
- address authority = abi.decode(returndata, (address));
- // Check if governor can call, and try to detect a delay
- (bool authorized, uint32 delay) = safeCanCall(authority, address(this), target, selector);
- // If direct call is not authorized, and delayed call is possible
- if (!authorized && delay > 0) {
- return ExecutionDetail({authority: authority, delay: delay});
- }
- }
- return ExecutionDetail({authority: address(0), delay: _defaultDelaySeconds()});
- }
- }
|