> For the complete documentation index, see [llms.txt](https://docs.trilobyte.finance/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.trilobyte.finance/technical-documentation/timelock-contract.md).

# 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.

{% hint style="warning" %}
**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`.
{% endhint %}

## Operation Lifecycle

```
Unset → Waiting → Ready → Done
```

| State       | Description                                           |
| ----------- | ----------------------------------------------------- |
| **Unset**   | 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

| Role          | Description                                                                                                                |
| ------------- | -------------------------------------------------------------------------------------------------------------------------- |
| **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

| Function                                                                                                 | Role       | Description                                                                                              |
| -------------------------------------------------------------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------- |
| `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 authorisation
* To 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

{% hint style="info" %}
`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.
{% endhint %}

## 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 `CustomAccountInterface` self-admin path
* This creates a fully decentralised governance loop

## Deployment

```
__constructor(min_delay, proposers, executors, admin: None)
```

* `min_delay` — Minimum number of **ledgers** between scheduling and execution
* `proposers` — 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)

{% hint style="warning" %}
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.
{% endhint %}

## 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)
