|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// OpenZeppelin Contracts (last updated v4.9.0) (governance/TimelockController.sol)
|
|
|
|
|
|
|
|
pragma solidity ^0.8.19;
|
|
|
|
|
|
|
|
import {AccessControl} from "../access/AccessControl.sol";
|
|
|
|
import {ERC721Holder} from "../token/ERC721/utils/ERC721Holder.sol";
|
|
|
|
import {ERC1155Holder} from "../token/ERC1155/utils/ERC1155Holder.sol";
|
|
|
|
import {Address} from "../utils/Address.sol";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Contract module which acts as a timelocked controller. When set as the
|
|
|
|
* owner of an `Ownable` smart contract, it enforces a timelock on all
|
|
|
|
* `onlyOwner` maintenance operations. This gives time for users of the
|
|
|
|
* controlled contract to exit before a potentially dangerous maintenance
|
|
|
|
* operation is applied.
|
|
|
|
*
|
|
|
|
* By default, this contract is self administered, meaning administration tasks
|
|
|
|
* have to go through the timelock process. The proposer (resp executor) role
|
|
|
|
* is in charge of proposing (resp executing) operations. A common use case is
|
|
|
|
* to position this {TimelockController} as the owner of a smart contract, with
|
|
|
|
* a multisig or a DAO as the sole proposer.
|
|
|
|
*/
|
|
|
|
contract TimelockController is AccessControl, ERC721Holder, ERC1155Holder {
|
|
|
|
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
|
|
|
|
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
|
|
|
|
bytes32 public constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE");
|
|
|
|
uint256 internal constant _DONE_TIMESTAMP = uint256(1);
|
|
|
|
|
|
|
|
mapping(bytes32 => uint256) private _timestamps;
|
|
|
|
uint256 private _minDelay;
|
|
|
|
|
|
|
|
enum OperationState {
|
|
|
|
Unset,
|
|
|
|
Waiting,
|
|
|
|
Ready,
|
|
|
|
Done
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Mismatch between the parameters length for an operation call.
|
|
|
|
*/
|
|
|
|
error TimelockInvalidOperationLength(uint256 targets, uint256 payloads, uint256 values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev The schedule operation doesn't meet the minimum delay.
|
|
|
|
*/
|
|
|
|
error TimelockInsufficientDelay(uint256 delay, uint256 minDelay);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev The current state of an operation is not as required.
|
|
|
|
* The `expectedStates` is a bitmap with the bits enabled for each OperationState enum position
|
|
|
|
* counting from right to left.
|
|
|
|
*
|
|
|
|
* See {_encodeStateBitmap}.
|
|
|
|
*/
|
|
|
|
error TimelockUnexpectedOperationState(bytes32 operationId, bytes32 expectedStates);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev The predecessor to an operation not yet done.
|
|
|
|
*/
|
|
|
|
error TimelockUnexecutedPredecessor(bytes32 predecessorId);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev The caller account is not authorized.
|
|
|
|
*/
|
|
|
|
error TimelockUnauthorizedCaller(address caller);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Emitted when a call is scheduled as part of operation `id`.
|
|
|
|
*/
|
|
|
|
event CallScheduled(
|
|
|
|
bytes32 indexed id,
|
|
|
|
uint256 indexed index,
|
|
|
|
address target,
|
|
|
|
uint256 value,
|
|
|
|
bytes data,
|
|
|
|
bytes32 predecessor,
|
|
|
|
uint256 delay
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Emitted when a call is performed as part of operation `id`.
|
|
|
|
*/
|
|
|
|
event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Emitted when new proposal is scheduled with non-zero salt.
|
|
|
|
*/
|
|
|
|
event CallSalt(bytes32 indexed id, bytes32 salt);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Emitted when operation `id` is cancelled.
|
|
|
|
*/
|
|
|
|
event Cancelled(bytes32 indexed id);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Emitted when the minimum delay for future operations is modified.
|
|
|
|
*/
|
|
|
|
event MinDelayChange(uint256 oldDuration, uint256 newDuration);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Initializes the contract with the following parameters:
|
|
|
|
*
|
|
|
|
* - `minDelay`: initial minimum delay in seconds for operations
|
|
|
|
* - `proposers`: accounts to be granted proposer and canceller roles
|
|
|
|
* - `executors`: accounts to be granted executor role
|
|
|
|
* - `admin`: optional account to be granted admin role; disable with zero address
|
|
|
|
*
|
|
|
|
* IMPORTANT: The optional admin can aid with initial configuration of roles after deployment
|
|
|
|
* without being subject to delay, but this role should be subsequently renounced in favor of
|
|
|
|
* administration through timelocked proposals. Previous versions of this contract would assign
|
|
|
|
* this admin to the deployer automatically and should be renounced as well.
|
|
|
|
*/
|
|
|
|
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors, address admin) {
|
|
|
|
// self administration
|
|
|
|
_grantRole(DEFAULT_ADMIN_ROLE, address(this));
|
|
|
|
|
|
|
|
// optional admin
|
|
|
|
if (admin != address(0)) {
|
|
|
|
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
|
|
|
}
|
|
|
|
|
|
|
|
// register proposers and cancellers
|
|
|
|
for (uint256 i = 0; i < proposers.length; ++i) {
|
|
|
|
_grantRole(PROPOSER_ROLE, proposers[i]);
|
|
|
|
_grantRole(CANCELLER_ROLE, proposers[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// register executors
|
|
|
|
for (uint256 i = 0; i < executors.length; ++i) {
|
|
|
|
_grantRole(EXECUTOR_ROLE, executors[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
_minDelay = minDelay;
|
|
|
|
emit MinDelayChange(0, minDelay);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Modifier to make a function callable only by a certain role. In
|
|
|
|
* addition to checking the sender's role, `address(0)` 's role is also
|
|
|
|
* considered. Granting a role to `address(0)` is equivalent to enabling
|
|
|
|
* this role for everyone.
|
|
|
|
*/
|
|
|
|
modifier onlyRoleOrOpenRole(bytes32 role) {
|
|
|
|
if (!hasRole(role, address(0))) {
|
|
|
|
_checkRole(role, _msgSender());
|
|
|
|
}
|
|
|
|
_;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Contract might receive/hold ETH as part of the maintenance process.
|
|
|
|
*/
|
|
|
|
receive() external payable {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev See {IERC165-supportsInterface}.
|
|
|
|
*/
|
|
|
|
function supportsInterface(
|
|
|
|
bytes4 interfaceId
|
|
|
|
) public view virtual override(AccessControl, ERC1155Holder) returns (bool) {
|
|
|
|
return super.supportsInterface(interfaceId);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Returns whether an id correspond to a registered operation. This
|
|
|
|
* includes both Pending, Ready and Done operations.
|
|
|
|
*/
|
|
|
|
function isOperation(bytes32 id) public view returns (bool) {
|
|
|
|
return getOperationState(id) != OperationState.Unset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Returns whether an operation is pending or not. Note that a "pending" operation may also be "ready".
|
|
|
|
*/
|
|
|
|
function isOperationPending(bytes32 id) public view returns (bool) {
|
|
|
|
OperationState state = getOperationState(id);
|
|
|
|
return state == OperationState.Waiting || state == OperationState.Ready;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Returns whether an operation is ready for execution. Note that a "ready" operation is also "pending".
|
|
|
|
*/
|
|
|
|
function isOperationReady(bytes32 id) public view returns (bool) {
|
|
|
|
return getOperationState(id) == OperationState.Ready;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Returns whether an operation is done or not.
|
|
|
|
*/
|
|
|
|
function isOperationDone(bytes32 id) public view returns (bool) {
|
|
|
|
return getOperationState(id) == OperationState.Done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Returns the timestamp at which an operation becomes ready (0 for
|
|
|
|
* unset operations, 1 for done operations).
|
|
|
|
*/
|
|
|
|
function getTimestamp(bytes32 id) public view virtual returns (uint256) {
|
|
|
|
return _timestamps[id];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Returns operation state.
|
|
|
|
*/
|
|
|
|
function getOperationState(bytes32 id) public view virtual returns (OperationState) {
|
|
|
|
uint256 timestamp = getTimestamp(id);
|
|
|
|
if (timestamp == 0) {
|
|
|
|
return OperationState.Unset;
|
|
|
|
} else if (timestamp == _DONE_TIMESTAMP) {
|
|
|
|
return OperationState.Done;
|
|
|
|
} else if (timestamp > block.timestamp) {
|
|
|
|
return OperationState.Waiting;
|
|
|
|
} else {
|
|
|
|
return OperationState.Ready;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Returns the minimum delay in seconds for an operation to become valid.
|
|
|
|
*
|
|
|
|
* This value can be changed by executing an operation that calls `updateDelay`.
|
|
|
|
*/
|
|
|
|
function getMinDelay() public view virtual returns (uint256) {
|
|
|
|
return _minDelay;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Returns the identifier of an operation containing a single
|
|
|
|
* transaction.
|
|
|
|
*/
|
|
|
|
function hashOperation(
|
|
|
|
address target,
|
|
|
|
uint256 value,
|
|
|
|
bytes calldata data,
|
|
|
|
bytes32 predecessor,
|
|
|
|
bytes32 salt
|
|
|
|
) public pure virtual returns (bytes32) {
|
|
|
|
return keccak256(abi.encode(target, value, data, predecessor, salt));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Returns the identifier of an operation containing a batch of
|
|
|
|
* transactions.
|
|
|
|
*/
|
|
|
|
function hashOperationBatch(
|
|
|
|
address[] calldata targets,
|
|
|
|
uint256[] calldata values,
|
|
|
|
bytes[] calldata payloads,
|
|
|
|
bytes32 predecessor,
|
|
|
|
bytes32 salt
|
|
|
|
) public pure virtual returns (bytes32) {
|
|
|
|
return keccak256(abi.encode(targets, values, payloads, predecessor, salt));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Schedule an operation containing a single transaction.
|
|
|
|
*
|
|
|
|
* Emits {CallSalt} if salt is nonzero, and {CallScheduled}.
|
|
|
|
*
|
|
|
|
* Requirements:
|
|
|
|
*
|
|
|
|
* - the caller must have the 'proposer' role.
|
|
|
|
*/
|
|
|
|
function schedule(
|
|
|
|
address target,
|
|
|
|
uint256 value,
|
|
|
|
bytes calldata data,
|
|
|
|
bytes32 predecessor,
|
|
|
|
bytes32 salt,
|
|
|
|
uint256 delay
|
|
|
|
) public virtual onlyRole(PROPOSER_ROLE) {
|
|
|
|
bytes32 id = hashOperation(target, value, data, predecessor, salt);
|
|
|
|
_schedule(id, delay);
|
|
|
|
emit CallScheduled(id, 0, target, value, data, predecessor, delay);
|
|
|
|
if (salt != bytes32(0)) {
|
|
|
|
emit CallSalt(id, salt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Schedule an operation containing a batch of transactions.
|
|
|
|
*
|
|
|
|
* Emits {CallSalt} if salt is nonzero, and one {CallScheduled} event per transaction in the batch.
|
|
|
|
*
|
|
|
|
* Requirements:
|
|
|
|
*
|
|
|
|
* - the caller must have the 'proposer' role.
|
|
|
|
*/
|
|
|
|
function scheduleBatch(
|
|
|
|
address[] calldata targets,
|
|
|
|
uint256[] calldata values,
|
|
|
|
bytes[] calldata payloads,
|
|
|
|
bytes32 predecessor,
|
|
|
|
bytes32 salt,
|
|
|
|
uint256 delay
|
|
|
|
) public virtual onlyRole(PROPOSER_ROLE) {
|
|
|
|
if (targets.length != values.length || targets.length != payloads.length) {
|
|
|
|
revert TimelockInvalidOperationLength(targets.length, payloads.length, values.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt);
|
|
|
|
_schedule(id, delay);
|
|
|
|
for (uint256 i = 0; i < targets.length; ++i) {
|
|
|
|
emit CallScheduled(id, i, targets[i], values[i], payloads[i], predecessor, delay);
|
|
|
|
}
|
|
|
|
if (salt != bytes32(0)) {
|
|
|
|
emit CallSalt(id, salt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Schedule an operation that is to become valid after a given delay.
|
|
|
|
*/
|
|
|
|
function _schedule(bytes32 id, uint256 delay) private {
|
|
|
|
if (isOperation(id)) {
|
|
|
|
revert TimelockUnexpectedOperationState(id, _encodeStateBitmap(OperationState.Unset));
|
|
|
|
}
|
|
|
|
uint256 minDelay = getMinDelay();
|
|
|
|
if (delay < minDelay) {
|
|
|
|
revert TimelockInsufficientDelay(delay, minDelay);
|
|
|
|
}
|
|
|
|
_timestamps[id] = block.timestamp + delay;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Cancel an operation.
|
|
|
|
*
|
|
|
|
* Requirements:
|
|
|
|
*
|
|
|
|
* - the caller must have the 'canceller' role.
|
|
|
|
*/
|
|
|
|
function cancel(bytes32 id) public virtual onlyRole(CANCELLER_ROLE) {
|
|
|
|
if (!isOperationPending(id)) {
|
|
|
|
revert TimelockUnexpectedOperationState(
|
|
|
|
id,
|
|
|
|
_encodeStateBitmap(OperationState.Waiting) | _encodeStateBitmap(OperationState.Ready)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
delete _timestamps[id];
|
|
|
|
|
|
|
|
emit Cancelled(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Execute an (ready) operation containing a single transaction.
|
|
|
|
*
|
|
|
|
* Emits a {CallExecuted} event.
|
|
|
|
*
|
|
|
|
* Requirements:
|
|
|
|
*
|
|
|
|
* - the caller must have the 'executor' role.
|
|
|
|
*/
|
|
|
|
// This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending,
|
|
|
|
// thus any modifications to the operation during reentrancy should be caught.
|
|
|
|
// slither-disable-next-line reentrancy-eth
|
|
|
|
function execute(
|
|
|
|
address target,
|
|
|
|
uint256 value,
|
|
|
|
bytes calldata payload,
|
|
|
|
bytes32 predecessor,
|
|
|
|
bytes32 salt
|
|
|
|
) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) {
|
|
|
|
bytes32 id = hashOperation(target, value, payload, predecessor, salt);
|
|
|
|
|
|
|
|
_beforeCall(id, predecessor);
|
|
|
|
_execute(target, value, payload);
|
|
|
|
emit CallExecuted(id, 0, target, value, payload);
|
|
|
|
_afterCall(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Execute an (ready) operation containing a batch of transactions.
|
|
|
|
*
|
|
|
|
* Emits one {CallExecuted} event per transaction in the batch.
|
|
|
|
*
|
|
|
|
* Requirements:
|
|
|
|
*
|
|
|
|
* - the caller must have the 'executor' role.
|
|
|
|
*/
|
|
|
|
// This function can reenter, but it doesn't pose a risk because _afterCall checks that the proposal is pending,
|
|
|
|
// thus any modifications to the operation during reentrancy should be caught.
|
|
|
|
// slither-disable-next-line reentrancy-eth
|
|
|
|
function executeBatch(
|
|
|
|
address[] calldata targets,
|
|
|
|
uint256[] calldata values,
|
|
|
|
bytes[] calldata payloads,
|
|
|
|
bytes32 predecessor,
|
|
|
|
bytes32 salt
|
|
|
|
) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE) {
|
|
|
|
if (targets.length != values.length || targets.length != payloads.length) {
|
|
|
|
revert TimelockInvalidOperationLength(targets.length, payloads.length, values.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt);
|
|
|
|
|
|
|
|
_beforeCall(id, predecessor);
|
|
|
|
for (uint256 i = 0; i < targets.length; ++i) {
|
|
|
|
address target = targets[i];
|
|
|
|
uint256 value = values[i];
|
|
|
|
bytes calldata payload = payloads[i];
|
|
|
|
_execute(target, value, payload);
|
|
|
|
emit CallExecuted(id, i, target, value, payload);
|
|
|
|
}
|
|
|
|
_afterCall(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Execute an operation's call.
|
|
|
|
*/
|
|
|
|
function _execute(address target, uint256 value, bytes calldata data) internal virtual {
|
|
|
|
(bool success, bytes memory returndata) = target.call{value: value}(data);
|
|
|
|
Address.verifyCallResult(success, returndata);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Checks before execution of an operation's calls.
|
|
|
|
*/
|
|
|
|
function _beforeCall(bytes32 id, bytes32 predecessor) private view {
|
|
|
|
if (!isOperationReady(id)) {
|
|
|
|
revert TimelockUnexpectedOperationState(id, _encodeStateBitmap(OperationState.Ready));
|
|
|
|
}
|
|
|
|
if (predecessor != bytes32(0) && !isOperationDone(predecessor)) {
|
|
|
|
revert TimelockUnexecutedPredecessor(predecessor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Checks after execution of an operation's calls.
|
|
|
|
*/
|
|
|
|
function _afterCall(bytes32 id) private {
|
|
|
|
if (!isOperationReady(id)) {
|
|
|
|
revert TimelockUnexpectedOperationState(id, _encodeStateBitmap(OperationState.Ready));
|
|
|
|
}
|
|
|
|
_timestamps[id] = _DONE_TIMESTAMP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Changes the minimum timelock duration for future operations.
|
|
|
|
*
|
|
|
|
* Emits a {MinDelayChange} event.
|
|
|
|
*
|
|
|
|
* Requirements:
|
|
|
|
*
|
|
|
|
* - the caller must be the timelock itself. This can only be achieved by scheduling and later executing
|
|
|
|
* an operation where the timelock is the target and the data is the ABI-encoded call to this function.
|
|
|
|
*/
|
|
|
|
function updateDelay(uint256 newDelay) external virtual {
|
|
|
|
address sender = _msgSender();
|
|
|
|
if (sender != address(this)) {
|
|
|
|
revert TimelockUnauthorizedCaller(sender);
|
|
|
|
}
|
|
|
|
emit MinDelayChange(_minDelay, newDelay);
|
|
|
|
_minDelay = newDelay;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dev Encodes a `OperationState` into a `bytes32` representation where each bit enabled corresponds to
|
|
|
|
* the underlying position in the `OperationState` enum. For example:
|
|
|
|
*
|
|
|
|
* 0x000...1000
|
|
|
|
* ^^^^^^----- ...
|
|
|
|
* ^---- Done
|
|
|
|
* ^--- Ready
|
|
|
|
* ^-- Waiting
|
|
|
|
* ^- Unset
|
|
|
|
*/
|
|
|
|
function _encodeStateBitmap(OperationState operationState) internal pure returns (bytes32) {
|
|
|
|
return bytes32(1 << uint8(operationState));
|
|
|
|
}
|
|
|
|
}
|