4

I want to use the openzeppelin timelock controller contract in a way that it calls the functions of other deployed contracts at some time. The thing can be done with the openzeppelin timelock controller smart contract.

The TimelockController contract contains a function that I think would do my work.

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);
    }

I need to understand a few things,

The contract says

The proposer (resp executor) role
 * is in charge of proposing (resp executing) operations.

So, should I also set the prosper and executor(I think they are already implemented in TimelockController.sol)

second, the parameters of schedule()... what will be the value, data, predecessor, salt, delay...

Can anyone please help? There are no guides regarding this library and documentation is not helping much.

1

2 Answers 2

2

The TimelockController contract has a simple but powerful design, in order to enable an address (Proposer) to schedule an operation, that will be executed by another address (Executor), after some time (minimum delay).

In order to properly use this library you should understand:

  • The terminology used by the contract
  • The operation structure
  • The operation lifecycle
  • The involved roles

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:

  • Admin: An address (smart contract or EOA) that is in charge of granting the roles of Proposer and Executor.
  • 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 once the timelock has expired. This role can be given to the zero address to allow anyone to execute operations.

Operation structure

Operation executed by the TimelockController 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 to ACCOUNT can be encoded using web3js as follows:

Example:

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 the Pending 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 the Done state for this transition to succeed.

  • cancel allows proposers to cancel any Pending operation. This resets the operation to the Unset 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:

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, the admin role can be granted to any address (in addition to the timelock itself). After further configuration and testing, this optional admin should renounce its 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.

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 executors 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. Alternatively, it is possible to allow any address to execute a proposal once the timelock has expired by granting the executor role to the zero address.

This role is identified by the EXECUTOR_ROLE value: 0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63

From Openzeppelin documentation

1

So, should I also set the prosper and executor(I think they are already implemented in TimelockController.sol)

To answer your first question -- YES, you should absolutely set the proposer and executer. You can initialize these roles to the null address 0x000 if you don't know the current address. Just make sure you also set the role of admin to your address so that you can later grant the role of executor or proposer to someone else if you decide


second, the parameters of schedule()... what will be the value, data, predecessor, salt, delay...

Below is a short explanation of each argument in the schedule() function:

  • target - Address of the contract you want to execute the transaction on.

  • value - a value that can be assigned with this, normally it will be 0.

  • data - this is the data of a transaction

  • predecessor - if this transaction relies on one before it, you will put it’s Operation ID here. Each Operation performed by the TimeLock has an ID attached to it. This can be used in conjunction with the predecessor argument. If you do not have a predecessor use 0x0000000000000000000000000000000000000000000000000000000000000000

  • salt - this is a password that you can use when scheduling transactions, and must be input during the execution function to actually execute the function. This could be useful if you were sharing a TimeLock with many different people or had many users and wanted to safeguard your transactions from being executed by other individuals.

  • delay - this is the delay you would like. It must be equal to or greater than the minimum delay.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.