Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
828 changes: 828 additions & 0 deletions content/contracts-sui/1.x/api/timelock.mdx

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions content/contracts-sui/1.x/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
title: Contracts for Sui 1.x
---

**OpenZeppelin Contracts for Sui v1.x** ships four core packages:
**OpenZeppelin Contracts for Sui v1.x** ships five core packages:

- `openzeppelin_math` for deterministic integer arithmetic, configurable rounding, and decimal scaling.
- `openzeppelin_fp_math` for 9-decimal fixed-point arithmetic (`UD30x9`, `SD29x9`) backed by `u128`.
- `openzeppelin_access` for role-based authorization and ownership-transfer wrappers around privileged `key + store` objects.
- `openzeppelin_timelock` for enforcing a minimum on-chain delay between scheduling and executing privileged operations.
- `openzeppelin_utils` for embeddable building blocks; its first module, `rate_limiter`, provides multi-strategy rate limiting.

## Quickstart
Expand Down Expand Up @@ -37,7 +38,7 @@ You only need the dependencies your app actually uses. Add what you need and dro

### 3. Verify `Move.toml`

`mvr add` updates `Move.toml` automatically. With all three installed it should include:
`mvr add` updates `Move.toml` automatically. With all four installed it should include:

```toml
[dependencies]
Expand Down Expand Up @@ -82,14 +83,15 @@ sui move test
- Need role-based authorization for privileged functions or controlled transfer of admin/treasury/upgrade capabilities? Use [Access](/contracts-sui/1.x/access).
- Need fractional values like prices, fees, rates, or signed deltas? Use [Fixed-Point Math](/contracts-sui/1.x/fixed-point).
- Need integer arithmetic with safe overflow and explicit rounding? Use [Integer Math](/contracts-sui/1.x/math).
- Need to enforce a minimum delay before privileged operations execute? Use [Timelock](/contracts-sui/1.x/timelock).
- Need to throttle withdrawals, meter per-user budgets, gate action reuse, or delay an action? Use [Rate Limiter](/contracts-sui/1.x/rate-limiter).

The packages compose. A typical protocol module imports `openzeppelin_math` for share math, `openzeppelin_fp_math` for rate and fee math, and `openzeppelin_access` for the admin capability that governs both.

## Next steps

- Package guides: [Integer Math](/contracts-sui/1.x/math), [Fixed-Point Math](/contracts-sui/1.x/fixed-point), [Access](/contracts-sui/1.x/access), [Utilities](/contracts-sui/1.x/utils).
- Package guides: [Integer Math](/contracts-sui/1.x/math), [Fixed-Point Math](/contracts-sui/1.x/fixed-point), [Access](/contracts-sui/1.x/access), [Timelock](/contracts-sui/1.x/timelock), [Utilities](/contracts-sui/1.x/utils).
- Access modules: [RBAC](/contracts-sui/1.x/access-control), [Two-Step Transfer](/contracts-sui/1.x/two-step-transfer), [Delayed Transfer](/contracts-sui/1.x/delayed-transfer).
- Utilities modules: [Rate Limiter](/contracts-sui/1.x/rate-limiter).
- Learn: [Role Based Access Control](/contracts-sui/1.x/guides/access-control).
- API reference: [Integer Math](/contracts-sui/1.x/api/math), [Fixed-Point Math](/contracts-sui/1.x/api/fixed-point), [Access](/contracts-sui/1.x/api/access), [Utilities](/contracts-sui/1.x/api/utils).
- API reference: [Integer Math](/contracts-sui/1.x/api/math), [Fixed-Point Math](/contracts-sui/1.x/api/fixed-point), [Access](/contracts-sui/1.x/api/access), [Timelock](/contracts-sui/1.x/api/timelock), [Utilities](/contracts-sui/1.x/api/utils).
120 changes: 120 additions & 0 deletions content/contracts-sui/1.x/timelock.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
title: Timelock
---

The `openzeppelin_timelock` package is a Sui-native take on OpenZeppelin's `TimelockController`: it enforces a minimum on-chain delay between scheduling a privileged operation and executing it, giving users a window to react before the change takes effect.

Because Move has no `target.call(data)`, the timelock stores each operation's typed parameters on-chain and, at execution time, hands them back through a no-ability `ExecutionTicket<Action, Params>` hot potato that the consumer redeems in the same transaction. The consumer dispatches to itself. An integration binds to a specific `Timelock` through a stored `OperationCap`, so the canonical-timelock check is enforced by the library rather than by hand-written asserts. Roles (proposer, executor, canceller, admin) come from a hard dependency on `openzeppelin_access::access_control`.

<Callout type="warn">
Prefer the `OperationCap`-bound entries (`schedule_with`, `execute_with`, `cancel_with`). The raw `schedule` / `execute` / `cancel` entries do not bind the timelock id: if you use them you must assert `object::id(timelock)` against your canonical timelock in every wrapper, and forgetting it silently disables the delay. Also, `ExecutionTicket` has no abilities. Always follow `execute` with `consume` in the same PTB, or the transaction aborts.
</Callout>

## Usage

The package is not yet published to the Move Registry (MVR), so install it as a git dependency pinned to the release tag in `Move.toml`:

```toml
[dependencies]
openzeppelin_timelock = { git = "https://github.com/OpenZeppelin/contracts-sui.git", subdir = "contracts/timelock", rev = "v1.5.0" }
openzeppelin_access = { git = "https://github.com/OpenZeppelin/contracts-sui.git", subdir = "contracts/access", rev = "v1.5.0" }
```

The `openzeppelin_access` dependency is needed whenever your module names its types directly (e.g. `Auth`), as the example below does.

Import the module:

```move
use openzeppelin_timelock::timelock::{Self, Timelock, OperationCap};
```

## Examples

A single `Timelock` gating an AMM fee change. The `OperationCap` lives on the `Pool`, so the canonical-timelock binding is structural and call sites carry zero explicit type arguments:

```move
module my_protocol::amm;

use openzeppelin_access::access_control::{Self, Auth};
use openzeppelin_timelock::timelock::{Self, Timelock, OperationCap};
use sui::clock::Clock;

public struct AMM has drop {}

public struct ProposerRole {}
public struct ExecutorRole {}
public struct CancellerRole {}
public struct TimelockAdminRole {}

// Drop-only witness: construction never leaves this module.
public struct FeeChangeAction has drop {}

public struct Pool has key {
id: UID,
fee_bps: u16,
// Binds fee-change operations to the canonical timelock.
fee_cap: OperationCap<FeeChangeAction, u16>,
}

fun init(otw: AMM, ctx: &mut TxContext) {
// Roles default to root-role (`AMM`) administration: the default admin
// grants ProposerRole / ExecutorRole / CancellerRole memberships
// post-publish via `grant_role`.
let ac = access_control::new(otw, 7 * 86_400_000, ctx);

// `new` + `share` so the cap can be minted before the timelock is shared.
let tl = timelock::new<ProposerRole, ExecutorRole, CancellerRole, TimelockAdminRole>(
86_400_000, // 24h minimum delay
604_800_000, // 7d grace period
ctx,
);
let fee_cap = tl.new_operation_cap<FeeChangeAction, u16>();
tl.share();
transfer::share_object(Pool { id: object::new(ctx), fee_bps: 30, fee_cap });
transfer::public_share_object(ac);
}

// A proposer schedules; the OperationCap binds the call to the canonical timelock.
public fun schedule_fee_change(
tl: &mut Timelock,
pool: &Pool,
proposer: &Auth<ProposerRole>,
new_fee_bps: u16,
salt: vector<u8>,
delay_ms: u64,
clock: &Clock,
ctx: &mut TxContext,
): vector<u8> {
// Empty predecessor: this operation depends on no other.
tl.schedule_with(&pool.fee_cap, proposer, new_fee_bps, vector[], salt, delay_ms, clock, ctx)
}

// An executor cranks the ready operation; `consume` hands back the typed params.
public fun execute_fee_change(
tl: &mut Timelock,
pool: &mut Pool,
executor: &Auth<ExecutorRole>,
id: vector<u8>,
clock: &Clock,
ctx: &mut TxContext,
) {
let ticket = tl.execute_with(&pool.fee_cap, executor, id, clock, ctx);
// The ticket MUST be consumed in the same PTB.
let (_op_id, new_fee_bps) = tl.consume(ticket, FeeChangeAction {});
pool.fee_bps = new_fee_bps;
}
```

More complete integrations (an AMM with self-administered config, dual governance with two timelocks, and a timelocked package `UpgradeCap`) live in [`examples/timelock`](https://github.com/OpenZeppelin/contracts-sui/tree/v1.5.0/contracts/timelock/examples/timelock) on GitHub.

## Key concepts

- `Timelock` (shared object): the per-protocol registry holding each operation's state machine and typed params. Operation ids are deterministic keccak256 hashes, reproducible off-chain and isolated per instance.
- `OperationCap<Action, Params>` (stored): binds one operation kind to one `Timelock`. Minted at `init` and stored in the object you protect; carries no authority on its own.
- `ExecutionTicket<Action, Params>` (hot potato): minted by `execute` / `execute_with`, carries the typed params, and must be redeemed with `consume` (witness-gated) in the same PTB.
- Roles: every gated entry takes `&Auth<Role>` from `openzeppelin_access` and checks it against the proposer / executor / canceller / admin role type bound at creation. Optional open-executor mode makes execution permissionless.
- Delay and expiry: an operation is executable in the half-open window `[ready_at_ms, expires_at_ms)`, locked at schedule time. Later config changes never move an in-flight op, and `Done` is sticky, so operations are non-replayable. `min_delay_ms`, `grace_period_ms`, and `open_executor` are self-administered: every change is itself timelocked.

## API Reference

Use the full function-level reference here: [Timelock API](/contracts-sui/1.x/api/timelock).
6 changes: 6 additions & 0 deletions content/contracts-sui/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import { latestStable } from "./latest-versions.js";
<Card href={`/contracts-sui/${latestStable}/math`} title="Integer Math">
Overflow-safe integer arithmetic with explicit rounding and decimal scaling helpers.
</Card>
<Card href={`/contracts-sui/${latestStable}/timelock`} title="Timelock">
Package-level guide for the delayed-operation controller, including scheduling model, operation caps, and execution tickets.
</Card>
<Card href={`/contracts-sui/${latestStable}/utils`} title="Utilities">
Embeddable rate limiters for throttling on-chain actions.
</Card>
Expand All @@ -46,6 +49,9 @@ import { latestStable } from "./latest-versions.js";
<Card href={`/contracts-sui/${latestStable}/api/math`} title="Integer Math">
Explore the complete integer math API, including its functions, types, and errors.
</Card>
<Card href={`/contracts-sui/${latestStable}/api/timelock`} title="Timelock">
Explore the complete timelock API, including all functions, types, emitted events, and expected error conditions for integration.
</Card>
<Card href={`/contracts-sui/${latestStable}/api/utils`} title="Utilities">
Explore the complete utilities API, including the rate limiter types, functions, and errors.
</Card>
Expand Down
2 changes: 2 additions & 0 deletions public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -316,11 +316,13 @@ Each ecosystem section lists the smart-contract libraries and language-specific
- [Packages — Access — RBAC](https://docs.openzeppelin.com/contracts-sui/1.x/access-control)
- [Packages — Access — Two-Step Transfer](https://docs.openzeppelin.com/contracts-sui/1.x/two-step-transfer)
- [Packages — Access — Delayed Transfer](https://docs.openzeppelin.com/contracts-sui/1.x/delayed-transfer)
- [Packages — Timelock](https://docs.openzeppelin.com/contracts-sui/1.x/timelock)
- [Packages — Utils — Overview](https://docs.openzeppelin.com/contracts-sui/1.x/utils)
- [Packages — Utils — Rate Limiter](https://docs.openzeppelin.com/contracts-sui/1.x/rate-limiter)
- [API Reference — Integer Math](https://docs.openzeppelin.com/contracts-sui/1.x/api/math)
- [API Reference — Fixed-Point Math](https://docs.openzeppelin.com/contracts-sui/1.x/api/fixed-point)
- [API Reference — Access](https://docs.openzeppelin.com/contracts-sui/1.x/api/access)
- [API Reference — Timelock](https://docs.openzeppelin.com/contracts-sui/1.x/api/timelock)
- [API Reference — Utils](https://docs.openzeppelin.com/contracts-sui/1.x/api/utils)

## Midnight
Expand Down
10 changes: 10 additions & 0 deletions src/navigation/sui/current.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
}
]
},
{
"type": "page",
"name": "Timelock",
"url": "/contracts-sui/1.x/timelock"
},
{
"type": "folder",
"name": "Utils",
Expand Down Expand Up @@ -103,6 +108,11 @@
"name": "Access",
"url": "/contracts-sui/1.x/api/access"
},
{
"type": "page",
"name": "Timelock",
"url": "/contracts-sui/1.x/api/timelock"
},
{
"type": "page",
"name": "Utils",
Expand Down