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
25 changes: 25 additions & 0 deletions ICRCs/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
load("//bazel:didc_test.bzl", "didc_subtype_test")

exports_files([
"ICRC-2.did",
])

genrule(
name = "candid",
srcs = [":README.md"],
outs = ["ICRC-2-generated.did"],
cmd_bash = "$(location @lmt) $(SRCS); mv ICRC-2.did $@",
exec_tools = ["@lmt"],
)

didc_subtype_test(
name = "check_generated_subtype",
did = ":ICRC-2-generated.did",
previous = "ICRC-2.did",
)

didc_subtype_test(
name = "check_source_subtype",
did = "ICRC-2.did",
previous = ":ICRC-2-generated.did",
)
102 changes: 102 additions & 0 deletions ICRCs/HASHINGVALUES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
## Representation independent hashing

The following pseudocode specifies how to calculate the (representation independent) hash of an element of the Value type. Some test vectors to check compliance of an implementation with this specification follow.

```
type Value = variant {
Blob : blob,
Text : text,
Nat : nat,
Int : int,
Array : vec Value,
Map : vec (text, Value)
};

Function hash_value(value)
Initialize hasher as a new instance of SHA256

Match value with
Nat:
Return SHA256_hash(LEB128_encode(value))
Int:
Return SHA256_hash(SLEB128_encode(value))
Text:
Return SHA256_hash(UTF8_encode(value))
Blob:
Return SHA256_hash(value)
Array:
For each element in value
Update hasher with hash_value(element)
Return hasher.finalize()
Map:
Initialize hashes as empty list
For each (key, val) in value
Add (SHA256_hash(UTF8_encode(key)), hash_value(val)) to hashes
Sort hashes in lexicographical order
For each (key_hash, val_hash) in hashes
Update hasher with key_hash
Update hasher with val_hash
Return hasher.finalize()
Else:
Return error "unsupported value type"
End Function

Function LEB128_encode(nat_input)
Convert nat_input to LEB128 byte encoding
End Function

Function SLEB128_encode(integer_input)
Convert integer_input to SLEB128 byte encoding
End Function

Function UTF8_encode(text)
Convert text to UTF-8 byte array and return it
End Function

Function SHA256_hash(data)
Initialize a new SHA256 hasher
Update hasher with data
Return hasher.finalize()
End Function

```

## Test vectors


```ignorelang
input: Nat(42)
expected output: 684888c0ebb17f374298b65ee2807526c066094c701bcc7ebbe1c1095f494fc1
```

```ignorelang
input: Int(-42)
expected output: de5a6f78116eca62d7fc5ce159d23ae6b889b365a1739ad2cf36f925a140d0cc
```


```ignorelang
input: Text("Hello, World!"),
expected output: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
```

```ignorelang
input: Blob(b'\x01\x02\x03\x04')
expected output: 9f64a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a
```

```ignorelang
input: Array([Nat(3), Text("foo"), Blob(b'\x05\x06')])
expected output: 514a04011caa503990d446b7dec5d79e19c221ae607fb08b2848c67734d468d6
```

```ignorelang
input: Map([("from", Blob(b'\x00\xab\xcd\xef\x00\x12\x34\x00\x56\x78\x9a\x00\xbc\xde\xf0\x00\x01\x23\x45\x67\x89\x00\xab\xcd\xef\x01')),
("to", Blob(b'\x00\xab\x0d\xef\x00\x12\x34\x00\x56\x78\x9a\x00\xbc\xde\xf0\x00\x01\x23\x45\x67\x89\x00\xab\xcd\xef\x01')),
("amount", Nat(42)),
("created_at", Nat(1699218263)),
("memo", Nat(0))
])

expected output: c56ece650e1de4269c5bdeff7875949e3e2033f85b2d193c2ff4f7f78bdcfc75
```
52 changes: 52 additions & 0 deletions ICRCs/ICRC-1.did
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Number of nanoseconds since the UNIX epoch in UTC timezone.
type Timestamp = nat64;

// Number of nanoseconds between two [Timestamp]s.
type Duration = nat64;

type Subaccount = blob;

type Account = record {
owner : principal;
subaccount : opt Subaccount;
};

type TransferArgs = record {
from_subaccount : opt Subaccount;
to : Account;
amount : nat;
fee : opt nat;
memo : opt blob;
created_at_time : opt Timestamp;
};

type TransferError = variant {
BadFee : record { expected_fee : nat };
BadBurn : record { min_burn_amount : nat };
InsufficientFunds : record { balance : nat };
TooOld;
CreatedInFuture: record { ledger_time : Timestamp };
Duplicate : record { duplicate_of : nat };
TemporarilyUnavailable;
GenericError : record { error_code : nat; message : text };
};

type Value = variant {
Nat : nat;
Int : int;
Text : text;
Blob : blob;
};

service : {
icrc1_metadata : () -> (vec record { text; Value; }) query;
icrc1_name : () -> (text) query;
icrc1_symbol : () -> (text) query;
icrc1_decimals : () -> (nat8) query;
icrc1_fee : () -> (nat) query;
icrc1_total_supply : () -> (nat) query;
icrc1_minting_account : () -> (opt Account) query;
icrc1_balance_of : (Account) -> (nat) query;
icrc1_transfer : (TransferArgs) -> (variant { Ok : nat; Err : TransferError });
icrc1_supported_standards : () -> (vec record { name : text; url : text }) query;
}
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: Pause, Unpause & Deactivate Blocks

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

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