TimelockController
Contract module which acts as a timelocked controller. When set as theowner of an Ownable
smart contract, it enforces a timelock on allonlyOwner
maintenance operations. This gives time for users of thecontrolled contract to exit before a potentially dangerous maintenanceoperation is applied.
By default, this contract is self administered, meaning administration taskshave to go through the timelock process. The proposer (resp executor) roleis in charge of proposing (resp executing) operations. A common use case isto position this TimelockController as the owner of a smart contract, witha multisig or a DAO as the sole proposer.
Available since v3.3.
Modifiers
onlyRole(role)
Functions
constructor(minDelay, proposers, executors)
receive()
isOperation(id)
isOperationPending(id)
isOperationReady(id)
isOperationDone(id)
getTimestamp(id)
getMinDelay()
hashOperation(target, value, data, predecessor, salt)
hashOperationBatch(targets, values, datas, predecessor, salt)
schedule(target, value, data, predecessor, salt, delay)
scheduleBatch(targets, values, datas, predecessor, salt, delay)
cancel(id)
execute(target, value, data, predecessor, salt)
executeBatch(targets, values, datas, predecessor, salt)
updateDelay(newDelay)
AccessControl
hasRole(role, account)
getRoleMemberCount(role)
getRoleMember(role, index)
getRoleAdmin(role)
grantRole(role, account)
revokeRole(role, account)
renounceRole(role, account)
_setupRole(role, account)
_setRoleAdmin(role, adminRole)
Events
CallScheduled(id, index, target, value, data, predecessor, delay)
CallExecuted(id, index, target, value, data)
Cancelled(id)
MinDelayChange(oldDuration, newDuration)
AccessControl
RoleAdminChanged(role, previousAdminRole, newAdminRole)
RoleGranted(role, account, sender)
RoleRevoked(role, account, sender)
onlyRole(bytes32 role)
modifier
Modifier to make a function callable only by a certain role. Inaddition to checking the sender’s role, address(0)
's role is alsoconsidered. Granting a role to address(0)
is equivalent to enablingthis role for everyone.
constructor(uint256 minDelay, address[] proposers, address[] executors)
public
Initializes the contract with a given minDelay
.
receive()
external
Contract might receive/hold ETH as part of the maintenance process.
isOperation(bytes32 id) → bool pending
public
Returns whether an id correspond to a registered operation. Thisincludes both Pending, Ready and Done operations.
isOperationPending(bytes32 id) → bool pending
public
Returns whether an operation is pending or not.
isOperationReady(bytes32 id) → bool ready
public
Returns whether an operation is ready or not.
isOperationDone(bytes32 id) → bool done
public
Returns whether an operation is done or not.
getTimestamp(bytes32 id) → uint256 timestamp
public
Returns the timestamp at with an operation becomes ready (0 forunset operations, 1 for done operations).
getMinDelay() → uint256 duration
public
Returns the minimum delay for an operation to become valid.
This value can be changed by executing an operation that calls updateDelay
.
hashOperation(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt) → bytes32 hash
public
Returns the identifier of an operation containing a singletransaction.
hashOperationBatch(address[] targets, uint256[] values, bytes[] datas, bytes32 predecessor, bytes32 salt) → bytes32 hash
public
Returns the identifier of an operation containing a batch oftransactions.
schedule(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt, uint256 delay)
public
Schedule an operation containing a single transaction.
Emits a CallScheduled event.
Requirements:
the caller must have the 'proposer' role.
scheduleBatch(address[] targets, uint256[] values, bytes[] datas, bytes32 predecessor, bytes32 salt, uint256 delay)
public
Schedule an operation containing a batch of transactions.
Emits one CallScheduled event per transaction in the batch.
Requirements:
the caller must have the 'proposer' role.
cancel(bytes32 id)
public
Cancel an operation.
Requirements:
the caller must have the 'proposer' role.
execute(address target, uint256 value, bytes data, bytes32 predecessor, bytes32 salt)
public
Execute an (ready) operation containing a single transaction.
Emits a CallExecuted event.
Requirements:
the caller must have the 'executor' role.
executeBatch(address[] targets, uint256[] values, bytes[] datas, bytes32 predecessor, bytes32 salt)
public
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.
updateDelay(uint256 newDelay)
external
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 executingan operation where the timelock is the target and the data is the ABI-encoded call to this function.
CallScheduled(bytes32 id, uint256 index, address target, uint256 value, bytes data, bytes32 predecessor, uint256 delay)
event
Emitted when a call is scheduled as part of operation id
.
CallExecuted(bytes32 id, uint256 index, address target, uint256 value, bytes data)
event
Emitted when a call is performed as part of operation id
.
Cancelled(bytes32 id)
event
Emitted when operation id
is cancelled.
MinDelayChange(uint256 oldDuration, uint256 newDuration)
event
Emitted when the minimum delay for future operations is modified.
Terminology
Operation: A transaction (or a set of transactions) that is the subject of the timelock. It has to be scheduled by a proposer and executed by an executor. The timelock enforces a minimum delay between the proposition and the execution (see operation lifecycle). If the operation contains multiple transactions (batch mode), they are executed atomically. Operations are identified by the hash of their content.
Operation status:
Unset: An operation that is not part of the timelock mechanism.
Pending: An operation that has been scheduled, before the timer expires.
Ready: An operation that has been scheduled, after the timer expires.
Done: An operation that has been executed.
Predecessor: An (optional) dependency between operations. An operation can depend on another operation (its predecessor), forcing the execution order of these two operations.
Role:
Proposer: An address (smart contract or EOA) that is in charge of scheduling (and cancelling) operations.
Executor: An address (smart contract or EOA) that is in charge of executing operations.
Operation structure
Operation executed by the TimelockControler can contain one or multiple subsequent calls. Depending on whether you need to multiple calls to be executed atomically, you can either use simple or batched operations.
Both operations contain:
Target, the address of the smart contract that the timelock should operate on.
Value, in wei, that should be sent with the transaction. Most of the time this will be 0. Ether can be deposited before-end or passed along when executing the transaction.
Data, containing the encoded function selector and parameters of the call. This can be produced using a number of tools. For example, a maintenance operation granting role
ROLE
toACCOUNT
can be encode using web3js as follows:
const data = timelock.contract.methods.grantRole(ROLE, ACCOUNT).encodeABI()
Predecessor, that specifies a dependency between operations. This dependency is optional. Use
bytes32(0)
if the operation does not have any dependency.Salt, used to disambiguate two otherwise identical operations. This can be any random value.
In the case of batched operations, target
, value
and data
are specified as arrays, which must be of the same length.
Operation lifecycle
Timelocked operations are identified by a unique id (their hash) and follow a specific lifecycle:
Unset
→ Pending
→ Pending
+ Ready
→ Done
By calling schedule (or scheduleBatch), a proposer moves the operation from the
Unset
to thePending
state. This starts a timer that must be longer than the minimum delay. The timer expires at a timestamp accessible through the getTimestamp method.Once the timer expires, the operation automatically gets the
Ready
state. At this point, it can be executed.By calling execute (or executeBatch), an executor triggers the operation’s underlying transactions and moves it to the
Done
state. If the operation has a predecessor, it has to be in theDone
state for this transition to succeed.cancel allows proposers to cancel any
Pending
operation. This resets the operation to theUnset
state. It is thus possible for a proposer to re-schedule an operation that has been cancelled. In this case, the timer restarts when the operation is re-scheduled.
Operations status can be queried using the functions:
isOperationPending(bytes32)
isOperationReady(bytes32)
isOperationDone(bytes32)
Roles
Admin
The admins are in charge of managing proposers and executors. For the timelock to be self-governed, this role should only be given to the timelock itself. Upon deployment, both the timelock and the deployer have this role. After further configuration and testing, the deployer can renounce this role such that all further maintenance operations have to go through the timelock process.
This role is identified by the TIMELOCK_ADMIN_ROLE value: 0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5
Proposer
The proposers are in charge of scheduling (and cancelling) operations. This is a critical role, that should be given to governing entities. This could be an EOA, a multisig, or a DAO.
Proposer fight: Having multiple proposers, while providing redundancy in case one becomes unavailable, can be dangerous. As proposer have their say on all operations, they could cancel operations they disagree with, including operations to remove them for the proposers. |
This role is identified by the PROPOSER_ROLE value: 0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1
Executor
The executors are in charge of executing the operations scheduled by the proposers once the timelock expires. Logic dictates that multisig or DAO that are proposers should also be executors in order to guarantee operations that have been scheduled will eventually be executed. However, having additional executor can reduce the cost (the executing transaction does not require validation by the multisig or DAO that proposed it), while ensuring whoever is in charge of execution cannot trigger actions that have not been scheduled by the proposers.
This role is identified by the EXECUTOR_ROLE value: 0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63
A live contract without at least one proposer and one executor is locked. Make sure these roles are filled by reliable entities before the deployer renounces its administrative rights in favour of the timelock contract itself. See the AccessControl documentation to learn more about role management. |