Timelock Contract
OpenZeppelin Timelock Controller (Stellar Contracts v0.7.2) for governance delay on critical protocol actions.
Source: contracts/timelock/
Overview
The Timelock enforces a minimum delay on governance operations. Critical protocol changes (fee updates, asset whitelisting, contract upgrades) must be scheduled, wait for the delay to pass, and then be executed. This gives protocol participants time to review and react.
Delays are measured in LEDGERS, not seconds. OZ Timelock v0.7 measures min_delay and per-operation delay in ledger-sequence units (~5 s/ledger). A 1-day delay is therefore ~17 280 ledgers, not 86400.
Operation Lifecycle
Unset → Waiting → Ready → DoneUnset
Operation doesn't exist yet
Waiting
Scheduled, waiting for the delay (in ledgers) to pass
Ready
Delay has passed, can be executed
Done
Successfully executed
Roles
Proposer
Can schedule new operations with a delay. Proposers are also automatically granted the Canceller role at construction.
Executor
Can execute operations once they are ready. If no executors are configured, anyone can execute ready operations.
Canceller
Can cancel pending operations before execution
Admin
Can manage roles and update the minimum delay. By default the contract is its own admin (self-administered).
Functions
schedule_op(target, function, args, predecessor, salt, delay, proposer)
Proposer
Schedule an operation; returns the operation id (hash). delay is in ledgers and must be ≥ min_delay.
execute_op(target, function, args, predecessor, salt, executor)
Executor*
Execute a ready operation (external targets). *Anyone if no executors configured.
cancel_op(operation_id, canceller)
Canceller
Cancel a pending operation.
update_delay(new_delay)
Admin
Update the minimum delay (self-administered → goes through the timelock).
get_min_delay()
—
Current minimum delay (ledgers).
hash_operation(...)
—
Compute an operation's id.
get_operation_state(id)
—
Returns the OperationState.
operation_exists(id) / is_operation_pending(id) / is_operation_ready(id) / is_operation_done(id)
—
State predicates.
The Timelock also exposes the OZ AccessControl interface (grant_role, revoke_role, has_role, …).
Integration with Globals
The Timelock is set as the admin of the Globals contract during deployment (via the two-step transfer_admin_role / accept_admin_transfer). This means:
All admin-gated functions on Globals (
whitelist_asset,set_factory,upgrade, etc.) require Timelock authorisationTo change such a parameter, a Proposer schedules the operation, waits for the delay (in ledgers), then an Executor executes it
This prevents instant, unilateral changes to critical protocol parameters
ops_adm-gated functions on Globals (fees, settings, pool-manager approval, delinquency) are not routed through the Timelock — they are gated by the operations-admin role directly.
Self-Administered
The Timelock is deployed self-administered (admin None → the contract is its own admin):
No single external address can bypass the time delay
The Timelock's own settings (minimum delay, roles) can only be changed through the Timelock itself, via the
CustomAccountInterfaceself-admin pathThis creates a fully decentralised governance loop
Deployment
min_delay— Minimum number of ledgers between scheduling and executionproposers— Addresses that can schedule operations (also granted the canceller role)executors— Addresses that can execute ready operations (empty ⇒ anyone can execute)admin: None— Self-administered (no external admin)
Upgrading the timelock WASM changes the operation storage key, so any operations scheduled under a prior version are dropped. Drain in-flight operations before upgrading a live timelock.
Test Coverage
14 tests covering:
Operation lifecycle (schedule → ready → execute)
Cancellation before execution
Delay enforcement (in ledgers)
Role-based access control (incl. proposer-auto-canceller and open-execution-when-no-executors)
Integration with the Globals contract (scheduling admin-gated operations)
Last updated