Skip to content
Open
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
198 changes: 198 additions & 0 deletions ICRCs/ICRC-124/ICRC-124.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# ICRC-124: Ledger Pausing, Unpausing, and Deactivation

## Status

Draft

## Introduction

This standard defines new block types for recording administrative actions that control the operational state of an ICRC-compliant ledger: pausing (`124pause`), unpausing (`124unpause`), and deactivating (`124deactivate`). These actions allow ledger operations to be temporarily halted (e.g., for maintenance), resumed, or permanently stopped (making the ledger immutable). This standard provides a consistent, auditable way to represent these ledger-wide state transitions within the ledger's block history, ensuring transparency and enabling robust governance mechanisms.

## Motivation

Ledger lifecycle management may require administrative actions like pausing for upgrades, unpausing after checks, or deactivating at the end of a token's useful life. These significant events must be recorded transparently on-chain. This standard provides explicit block types for these actions, defining a minimal block structure sufficient for semantics, while allowing optional provenance for auditability.

## Common Elements

This standard follows the conventions set by ICRC-3, inheriting key structural components:

- **Principals** are represented using the ICRC-3 `Value` type as `variant { Blob = <principal_bytes> }`.
- **Timestamps:** `ts` (and any optional `created_at_time`) are **nanoseconds since the Unix epoch**, encoded as `Nat` but **MUST fit into `nat64`**.
- **Parent hash:** `phash : Blob` **MUST** be present if the block has a parent (omit for the genesis block).

## Block Types & Schema

Each block introduced by this standard MUST include a `tx` field containing a map. This map encodes the **minimal information** about the ledger state change. Additional provenance MAY be included but is not required for semantics.

Each block consists of the following top-level fields:

| Field | Type (ICRC-3 `Value`) | Required | Description |
|-------|------------------------|----------|-------------|
| `btype` | `Text` | Yes | MUST be one of: `"124pause"`, `"124unpause"`, or `"124deactivate"`. |
| `ts` | `Nat` | Yes | Timestamp (ns since Unix epoch) when the block was added to the ledger. MUST fit in `nat64`. |
| `phash` | `Blob` | Yes/No | Hash of the parent block; omitted only for the genesis block. |
| `tx` | `Map(Text, Value)` | Yes | Minimal operation details (see below). |

### `tx` Field Schema (minimal)

For all `124pause`, `124unpause`, and `124deactivate` blocks:

- No required fields are needed for semantics.
- The presence of the block type alone (`btype`) determines the state transition.

### Optional Provenance (non-semantic)

Producers MAY include fields such as:

- `caller : Blob` — principal that invoked the operation.
- `reason : Text` — human-readable context.
- `created_at_time : Nat` — caller-supplied timestamp (ns; MUST fit nat64).
- `policy_ref : Text` — identifier for proposal/vote/policy.
- `op : Text` — namespaced operation identifier, e.g. `148pause_ledger`.

These fields MUST NOT affect semantics or verification. Verifiers MUST ignore them.

> **Informative note (recoverability):** Implementations **SHOULD** provide mechanisms (e.g., archives or lookups) to retrieve extended invocation context not present in `tx` when useful for audits. The authorization model that permits these actions is implementation-defined.

---

## Semantics

### Pause Ledger (`124pause`)
- When a `124pause` block is recorded, the ledger MUST enter a "paused" state.
- While paused, the ledger MUST reject all state-changing operations except those required for governance or recovery (e.g., `124unpause`, optionally `124deactivate`, and operations like freeze/unfreeze if permitted by governance policy).
- Query calls SHOULD remain operational.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- An `124pause` block has no effect if the ledger is already paused or if it is in a terminal state due to deactivation.

### Unpause Ledger (`124unpause`)
- When a `124unpause` block is recorded, the ledger MUST exit the "paused" state and resume normal operation, unless it is already in the terminal state due to deactivation.
- An `124unpause` block has no effect if the ledger is already unpaused or deactivated.

### Deactivate Ledger (`124deactivate`)
- When a `124deactivate` block is recorded, the ledger MUST transition to a permanent "terminal" state.
- In this state:
- All ingress calls that modify state MUST be rejected (transfers, approvals, mints, burns, freezes, pauses, unpauses, etc.).
- Query calls retrieving historical data MUST remain available.
- The deactivated state is irreversible.

---

## Guidance for Standards That Define Methods

A standard that defines ledger methods which produce ICRC-124 blocks (e.g., “pause ledger” or “deactivate ledger”) SHOULD:

1. **Include `tx.op`** in the resulting block’s `tx` map.
- Use a namespaced value per ICRC-3: `<icrc_number><op_name>` (e.g., `148pause_ledger`).
- This makes the call uniquely identifiable and prevents collisions across standards.

2. **Define a canonical mapping** from the method’s call parameters to the block’s minimal `tx` fields.
- Since 124 blocks have no required fields, only provenance may be mapped.

3. **Document deduplication inputs** (if any). If the method uses a caller-supplied timestamp, put it in `tx.created_at_time`.

---

## Compliance Reporting

Ledgers implementing this standard MUST return the following entries (along with entries for other supported block types) from `icrc3_supported_block_types`:

```candid
vec {
record { block_type = "124pause"; url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-124.md" };
record { block_type = "124unpause"; url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-124.md" };
record { block_type = "124deactivate"; url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-124.md" };
}
```

## Example Blocks

### 124pause Example

```candid
variant { Map = vec {
    // Block type identifier
    record { "btype"; variant { Text = "124pause" }};

    // Timestamp when the block was recorded (nanoseconds since epoch)
    record { "ts"; variant { Nat = 1_747_774_560_000_000_000 : nat }}; // Example: 2025-05-19T12:56:00Z

    // Hash of the previous block in the ledger chain
    record { "phash"; variant {
        Blob = blob "\de\ad\be\ef\00\11\22\33\44\55\66\77\88\99\aa\bb\cc\dd\ee\ff\10\20\30\40\50\60\70\80\90\a0\b0\c0"
    }};

    // Pause transaction details
    record { "tx"; variant { Map = vec {
// The principal that invoked the pause_ledger operation
        record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\f0\0d\01\03" }}; // Example caller principal (e.g., a governance canister)
        // Optional reason
        record { "reason"; variant { Text = "DAO vote #78: pause for scheduled maintenance." }};
    }}};
}};

```

### 124unpause Example

```candid
variant { Map = vec {
    record { "btype"; variant { Text = "124unpause" }};
    record { "ts"; variant { Nat = 1_747_778_160_000_000_000 : nat }}; // Example: 2025-05-19T13:56:00Z
    record { "phash"; variant {
        Blob = blob "\be\ba\fe\ca\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f\10\11\12\13\14\15\16\17\18\19\1a\1b"
    }};
    // Unpause transaction details
    record { "tx"; variant { Map = vec {
// The principal that invoked the unpause_ledger operation
        record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\f0\0d\01\03" }}; // Example caller principal
        // Optional reason
        record { "reason"; variant { Text = "Ledger resumes after maintenance window (DAO vote #79)." }};
    }}};
}};
```

### 124deactivate Example

```candid
variant { Map = vec {
    record { "btype"; variant { Text = "124deactivate" }};
    record { "ts"; variant { Nat = 1_747_864_560_000_000_000 : nat }}; // Example: 2025-05-20T12:56:00Z
    record { "phash"; variant {
        Blob = blob "\c0\ff\ee\00\10\20\30\40\50\60\70\80\90\a0\b0\c0\d0\e0\f0\00\11\22\33\44\55\66\77\88\99\aa\bb\cc"
    }};
    // Deactivate transaction details
    record { "tx"; variant { Map = vec {
// The principal that invoked the deactivate_ledger operation
        record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\f0\0d\01\04" }}; // Example caller (e.g., project multisig or final DAO vote)
        // Optional reason
        record { "reason"; variant { Text = "Token project sunset. Ledger permanently archived as per SNS DAO proposal #314." }};
    }}};
}};

```

### Informative Example: Integration with a Standardized Method
ICRC-124 defines only block types and their semantics. It does not define any ledger methods.
However, future standards may specify methods that map directly to these block types.

For illustration, suppose a future standard (e.g., ICRC-148) introduces the method:
```
icrc148_pause_ledger : (opt text) -> result nat
```

Invoking this method with an optional reason could produce a `124pause` block:
```
variant { Map = vec {
record { "btype"; variant { Text = "124pause" }};
record { "ts"; variant { Nat = 1_747_900_000_000_000_000 : nat }};
record { "phash"; variant {
Blob = blob "\aa\bb\cc\dd\ee\ff\00\11\22\33\44\55\66\77\88\99"
}};
record { "tx"; variant { Map = vec {
record { "op"; variant { Text = "148pause_ledger" }};
record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\f0\0d\01\03" }};
record { "reason"; variant { Text = "DAO vote #101: emergency pause" }};
}}};
}};
```

This example is non-normative and illustrates how a standardized method can map into the ICRC-124 block structure while using a namespaced `tx.op` for unambiguous identification. The authoritative semantics remain defined by the ICRC-124 block types.