From 7dec14367cf4ec24506c3ec004d65850eb05112d Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Tue, 14 Jan 2025 16:26:23 +0100 Subject: [PATCH 01/31] ICRC-107 initial draft --- ICRCs/ICRC-107/ICRC-107.md | 175 +++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 ICRCs/ICRC-107/ICRC-107.md diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md new file mode 100644 index 00000000..65fbb270 --- /dev/null +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -0,0 +1,175 @@ +| Status | +|:------:| +| Draft | + +# ICRC-107: Fee Collection + +## 1. Introduction + +Many ICRC-based ledgers (such as **ckBTC**) already implement partial fee collection, often charging fees for **transfer** transactions but not for **approve** transactions. However, there is currently no standardized way to: + +1. Indicate and configure **who** should collect fees (or if fees are burned). +2. Record fee collection information on existing ledgers. +3. Provide clear semantics for wallets, explorers, and other integrations to **interpret** fee information. + +**ICRC-107** aims to address this gap by defining: +- A set of **metadata entries** that indicate how fees are collected. +- A **minimal interface** for setting and enabling fee collection. +- **A backward-compatible standard** for **populating and understanding fee-collection fields** in ICRC-3 blocks corresponding to ICRC-1 and ICRC-2 transactions. +- Guidance on how to **interpret fee collection fields**, as recorded in blocks, in a consistent way. + +### Non-Requirements +This standard does **not** address or require: +- Unsetting or removing a previously configured fee collector. +- Having multiple fee collectors (e.g., distinct collectors for transfers vs. approvals). +- Generalizing the mechanism for future block types beyond transfers/approvals. +- Prescribing detailed behavior for ledger upgrades that enable fee collection. + +By focusing on **backward-compatible** enhancements and clear metadata, this standard allows ledgers to adopt fee collection with minimal disruption, while giving external tools a reliable way to parse and display fee-related data. + +--- + +## 2. Metadata + +A ledger implementing **ICRC-107** **MUST** include the following entry in the output of the `icrc1_supported_standards` method (indented for display): + + record { + name = "ICRC-107"; + url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-107" + } + +Additionally, the ledger **MUST** provide (at minimum) these metadata entries, retrievable via the `icrc1_metadata` method: + +1. **`icrc107_fee_collector` (text)** + The textual representation of the principal (or account) designated as the fee collector. + +2. **`icrc107_fee_collection_transfers` (bool)** + Indicates whether fees are collected on **transfer** transactions (`true`) or not (`false`). + +3. **`icrc107_fee_collection_approvals` (bool)** + Indicates whether fees are collected on **approve** transactions (`true`) or not (`false`). + +If a ledger has not set a fee collector, it may return an empty string or omit the metadata key for `icrc107_fee_collector`. Similarly, if fees are never collected for transfers or approvals, the corresponding booleans should be `false` or omitted. + +--- + +## 3. Fee Collection Management Interface + +A ledger **implementing ICRC-107** **SHOULD** expose the following methods to programmatically configure and enable fee collection. + + + // Sets the fee collector principal/account. + // If not set, or set to an empty string, fees are effectively burned. + icrc107_set_fee_collector: (text) -> (); + + // Enables fee collection for transfer transactions. + // Returns the currently set fee collector if successful, or an error if none is set. + icrc107_turn_on_fee_collector_transfers: () -> (variant { + Ok : text; // The fee collector principal + Err : text; // Error message + }); + + // Enables fee collection for approve transactions. + // Returns the currently set fee collector if successful, or an error if none is set. + icrc107_turn_on_fee_collector_approvals: () -> (variant { + Ok : text; // The fee collector principal + Err : text; // Error message + }); + +### Method Semantics + +- **`icrc107_set_fee_collector(fee_collector: text)`** + Updates the ledger’s metadata (`icrc107_fee_collector`) to the specified principal/account. + If this method is never called, fees are assumed to be burned by default. + +- **`icrc107_turn_on_fee_collector_transfers()`** + Sets `icrc107_fee_collection_transfers` to `true`. + If a valid fee collector has been set, returns `Ok(fee_collector)`. Otherwise, returns `Err` describing why it cannot be enabled. + +- **`icrc107_turn_on_fee_collector_approvals()`** + Sets `icrc107_fee_collection_approvals` to `true`. + If a valid fee collector has been set, returns `Ok(fee_collector)`. Otherwise, returns `Err`. + +These endpoints allow ledger administrators (or canister controllers) to enable fee collection for different transaction types once the fee collector account is specified. + +--- + +## 4. Fee Information in Blocks + +This section defines how **ICRC-107** determines whether a fee is collected or burned **exclusively** from information **contained in the blocks themselves**. No reliance on external metadata (e.g., `icrc107_fee_collector`) is assumed. + +### 4.1 Determining the Fee + +When reading or processing a block that may carry a fee, the ledger or external tools **SHOULD** apply the following logic to identify the final fee amount: + +1. **Check for an Explicit Transaction Fee (`tx.fee`)** + - If the transaction itself contains a `fee` field (e.g., `tx.fee`), that value **SHOULD** take precedence as the fee to be applied. + +2. **Fallback to `effective_fee`** + - If no `tx.fee` is provided, the ledger or tool **SHOULD** refer to the block’s `effective_fee` field (or an equivalent ledger-defined field) to determine the fee. + +3. **No Explicit Fee** + - If neither `tx.fee` nor `effective_fee` is available, then the ledger MAY default the fee to zero or burn any implied fee as per its internal policy. + - An **ICRC-107**-compliant ledger interprets the absence of a specified fee as no fee collected, or a fee of zero. + +### 4.2 Fee Collection Logic + +After determining the fee amount, **ICRC-107** specifies how to decide **whether** a fee is burned or collected, and if collected, **who** receives it, **based solely on block contents**: + +1. **Check for Fee Collector Fields** + - If the block contains a direct account field (e.g., `fee_col`) or a reference field (e.g., `fee_col_block`, `app_fee_col_block`), the fee is be collected by the referenced account. + - If **neither** a direct account field nor a reference field is present, the fee is burned. + +2. **Resolving a Reference to a Previous Block** + - If a reference field is used (`fee_col_block` or `app_fee_col_block`), the ledger or tool **MUST** locate that earlier block and extract the account specified there (e.g., `fee_col`). + - If the referenced block does not contain a valid collector the fee is burned. + + +### 4.3 Recording Fee Collection in Blocks for ICRC-1 and ICRC-2 Transactions + +The block schema for ICRC-1 and ICRC-2 transactions are specified in the ICRC-3 standard. This section explains how to extend those schemas to include backwards compatible fee collection information. + +#### 4.3.1 Transfer Blocks + +Transfer blocks in ICRC-based ledgers can be identified by one of the following: +- **`btype = "1xfer"`** (ICRC-1 style), +- **`btype = "2xfer"`** (ICRC-2 style), +- or a **transfer transaction** (`tx.op = "xfer"`). + +To **record and interpret** fee-collection information in these blocks: + +- **`fee_col` (Account)** + - A direct indicator of which account receives the collected fee. +- **`fee_col_block` (nat)** + - A pointer to an earlier block where `fee_col` is explicitly set. + +**How to Determine Who Collects the Fee** +1. If `fee_col` is present in the block, that account is the collector. +2. If `fee_col` is absent but `fee_col_block` is present, fetch the referenced block; the `fee_col` in that block is the collector. +3. If neither is present, **no** collector can be inferred, and the fee is burned. + +#### 4.3.2 Approve Blocks + +Approve blocks in ICRC-based ledgers can be identified by: +- **`btype = "2approve"`** (ICRC-2 style), +- or a **transaction object** (`tx.op = "approve"`). + +To **record and interpret** fee-collection information in these blocks: + +- **`fee_col` (Account)** + - A direct indicator of which account receives the collected fee for the approval operation. +- **`app_fee_col_block` (nat)** + - A pointer to an earlier block where `fee_col` is explicitly set for approves. + +**How to Determine Who Collects the Fee** +1. If `fee_col` is present in the block, that account is the collector. +2. If `fee_col` is absent but `app_fee_col_block` is present, fetch the referenced block; the `fee_col` in that block is the collector. +3. If neither is present, **no** collector can be inferred, and the fee is burned. + +--- + +By strictly focusing on **block-level fields**: + +- **Ledgers** remain free to implement or omit references in blocks while still following a consistent approach for collecting or burning fees. +- **External tools** (wallets, explorers) can determine the collector without requiring out-of-band metadata or additional method calls. If no collector info is embedded (directly or via a reference), the fee is considered burned. +- This specification thereby ensures **ICRC-1**, **ICRC-2**, and **ICRC-3** ledgers can remain consistent with **ICRC-107** using only the fields present in each block. From f45dbed581b1c7bf4b19d04e841fcb8d2949ef11 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:46:31 +0100 Subject: [PATCH 02/31] bool -> text for metadata --- ICRCs/ICRC-107/ICRC-107.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 65fbb270..e7239409 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -43,13 +43,13 @@ Additionally, the ledger **MUST** provide (at minimum) these metadata entries, r 1. **`icrc107_fee_collector` (text)** The textual representation of the principal (or account) designated as the fee collector. -2. **`icrc107_fee_collection_transfers` (bool)** - Indicates whether fees are collected on **transfer** transactions (`true`) or not (`false`). +2. **`icrc107_fee_collection_transfers` (text)** + Indicates whether fees are collected on **transfer** transactions ("true") or not ("false"). -3. **`icrc107_fee_collection_approvals` (bool)** - Indicates whether fees are collected on **approve** transactions (`true`) or not (`false`). +3. **`icrc107_fee_collection_approvals` (text)** + Indicates whether fees are collected on **approve** transactions ("true") or not ("false"). -If a ledger has not set a fee collector, it may return an empty string or omit the metadata key for `icrc107_fee_collector`. Similarly, if fees are never collected for transfers or approvals, the corresponding booleans should be `false` or omitted. +If a ledger has not set a fee collector, it may return an empty string or omit the metadata key for `icrc107_fee_collector`. Similarly, if fees are not collected for transfers or approvals, the values for the corresponding metadata should be "false" or omitted. --- From 63bff2088d18667661e160145804079e4babd8b1 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:48:16 +0100 Subject: [PATCH 03/31] bool -> text for metadata --- ICRCs/ICRC-107/ICRC-107.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index e7239409..73ec422e 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -83,11 +83,11 @@ A ledger **implementing ICRC-107** **SHOULD** expose the following methods to pr If this method is never called, fees are assumed to be burned by default. - **`icrc107_turn_on_fee_collector_transfers()`** - Sets `icrc107_fee_collection_transfers` to `true`. + Sets `icrc107_fee_collection_transfers` to "true". If a valid fee collector has been set, returns `Ok(fee_collector)`. Otherwise, returns `Err` describing why it cannot be enabled. - **`icrc107_turn_on_fee_collector_approvals()`** - Sets `icrc107_fee_collection_approvals` to `true`. + Sets `icrc107_fee_collection_approvals` to "true". If a valid fee collector has been set, returns `Ok(fee_collector)`. Otherwise, returns `Err`. These endpoints allow ledger administrators (or canister controllers) to enable fee collection for different transaction types once the fee collector account is specified. From b5bae30907b2ccb0ef4ea971116e9fec9fda7937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BW=C2=B0MacAir?= Date: Tue, 4 Feb 2025 11:18:27 +0100 Subject: [PATCH 04/31] option 1 from the discussion --- ICRCs/ICRC-107/ICRC-107.md | 280 ++++++++++++++++++++----------------- 1 file changed, 151 insertions(+), 129 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 73ec422e..fdccee4d 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -1,175 +1,197 @@ -| Status | -|:------:| -| Draft | -# ICRC-107: Fee Collection +# ICRC-107: Fee Collection (Purely Block-Based) -## 1. Introduction +## 1. Introduction & Motivation Many ICRC-based ledgers (such as **ckBTC**) already implement partial fee collection, often charging fees for **transfer** transactions but not for **approve** transactions. However, there is currently no standardized way to: 1. Indicate and configure **who** should collect fees (or if fees are burned). -2. Record fee collection information on existing ledgers. -3. Provide clear semantics for wallets, explorers, and other integrations to **interpret** fee information. +2. Record fee collection information directly in ledger blocks. +3. Provide clear semantics for wallets, explorers, and other integrations to **interpret** fee information in a consistent way. **ICRC-107** aims to address this gap by defining: -- A set of **metadata entries** that indicate how fees are collected. -- A **minimal interface** for setting and enabling fee collection. -- **A backward-compatible standard** for **populating and understanding fee-collection fields** in ICRC-3 blocks corresponding to ICRC-1 and ICRC-2 transactions. -- Guidance on how to **interpret fee collection fields**, as recorded in blocks, in a consistent way. +- A mechanism to specify **fee collection details** (collector account and applicable operations) directly in blocks. +- A **backward-compatible standard** for **recording and interpreting fee-collection fields** in ICRC-3 blocks corresponding to ICRC-1 and ICRC-2 transactions. +- Clear rules on how fee collection information evolves over time and how subsequent blocks rely on prior updates. -### Non-Requirements -This standard does **not** address or require: -- Unsetting or removing a previously configured fee collector. -- Having multiple fee collectors (e.g., distinct collectors for transfers vs. approvals). -- Generalizing the mechanism for future block types beyond transfers/approvals. -- Prescribing detailed behavior for ledger upgrades that enable fee collection. - -By focusing on **backward-compatible** enhancements and clear metadata, this standard allows ledgers to adopt fee collection with minimal disruption, while giving external tools a reliable way to parse and display fee-related data. +This design ensures that **all** fee-related information is stored entirely **on-chain** in the block history, without reliance on external metadata or specialized block types. It provides a fully self-contained, transparent standard for fee collection that simplifies integration with wallets, explorers, and other tools. --- -## 2. Metadata - -A ledger implementing **ICRC-107** **MUST** include the following entry in the output of the `icrc1_supported_standards` method (indented for display): - - record { - name = "ICRC-107"; - url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-107" - } +## 2. Fee Collection Mechanism -Additionally, the ledger **MUST** provide (at minimum) these metadata entries, retrievable via the `icrc1_metadata` method: +### 2.1 Fields in Blocks -1. **`icrc107_fee_collector` (text)** - The textual representation of the principal (or account) designated as the fee collector. +A block **MAY** include the following fields to define or update fee collection settings: -2. **`icrc107_fee_collection_transfers` (text)** - Indicates whether fees are collected on **transfer** transactions ("true") or not ("false"). +- **`fee_col`**: An **account** (ICRC account format) to which fees are paid. + **Format**: + ```candid + record { + principal: principal; + subaccount: opt blob; + } + ``` +- **`fee_ops`**: An **array of block types** (strings) for which fees **should** be collected. This standard specifies two types of blocks for which fee collection can occur: "transfer" and "approve". -3. **`icrc107_fee_collection_approvals` (text)** - Indicates whether fees are collected on **approve** transactions ("true") or not ("false"). +When a block includes either or both fields, it **overrides** the previously known settings from that point onward. -If a ledger has not set a fee collector, it may return an empty string or omit the metadata key for `icrc107_fee_collector`. Similarly, if fees are not collected for transfers or approvals, the values for the corresponding metadata should be "false" or omitted. +#### 2.1.1 Defaults +- If `fee_ops` is **omitted**, it defaults to `["transfer"]`. +- If `fee_col` is **omitted**, the previously known collector remains in effect (or none if it was never set). --- -## 3. Fee Collection Management Interface - -A ledger **implementing ICRC-107** **SHOULD** expose the following methods to programmatically configure and enable fee collection. - +## 3. Fee Computation - // Sets the fee collector principal/account. - // If not set, or set to an empty string, fees are effectively burned. - icrc107_set_fee_collector: (text) -> (); +### 3.1 Determining the Fee Amount - // Enables fee collection for transfer transactions. - // Returns the currently set fee collector if successful, or an error if none is set. - icrc107_turn_on_fee_collector_transfers: () -> (variant { - Ok : text; // The fee collector principal - Err : text; // Error message - }); +The fee amount in any block can be derived via: +- An explicit `tx.fee` field in the block’s transaction. +- A fallback `effective_fee` or other ledger-defined field if `tx.fee` is absent. +- Zero if neither is present. - // Enables fee collection for approve transactions. - // Returns the currently set fee collector if successful, or an error if none is set. - icrc107_turn_on_fee_collector_approvals: () -> (variant { - Ok : text; // The fee collector principal - Err : text; // Error message - }); +### 3.2 Collected or Burned -### Method Semantics - -- **`icrc107_set_fee_collector(fee_collector: text)`** - Updates the ledger’s metadata (`icrc107_fee_collector`) to the specified principal/account. - If this method is never called, fees are assumed to be burned by default. - -- **`icrc107_turn_on_fee_collector_transfers()`** - Sets `icrc107_fee_collection_transfers` to "true". - If a valid fee collector has been set, returns `Ok(fee_collector)`. Otherwise, returns `Err` describing why it cannot be enabled. - -- **`icrc107_turn_on_fee_collector_approvals()`** - Sets `icrc107_fee_collection_approvals` to "true". - If a valid fee collector has been set, returns `Ok(fee_collector)`. Otherwise, returns `Err`. - -These endpoints allow ledger administrators (or canister controllers) to enable fee collection for different transaction types once the fee collector account is specified. +After determining the fee amount, check the **current** configuration (from the most recent update): +1. **Operation Present**: If the block’s operation (e.g., `"transfer"`, `"approve"`, etc.) is in the active list (`fee_ops`), the fee is collected by the active `fee_col`. +2. **Operation Absent**: If the operation is **not** in the active list, the fee is burned. +3. **No Collector**: If no collector has ever been set, fees are burned by default. --- -## 4. Fee Information in Blocks - -This section defines how **ICRC-107** determines whether a fee is collected or burned **exclusively** from information **contained in the blocks themselves**. No reliance on external metadata (e.g., `icrc107_fee_collector`) is assumed. +## 4. Determining the Current Fee Configuration -### 4.1 Determining the Fee +At any block height `h`, to figure out **who** collects fees and for **which** operations: +1. **Traverse blocks** backward (or maintain an in-memory state) until you find the most recent block `< h` that included `fee_col` or `fee_ops`. +2. The fee-collector account from that block is **active**, and the operation list from that block is **active** (defaulting to `["transfer"]` if omitted). +3. If no such block is found, no collector is active and **all fees are burned**. -When reading or processing a block that may carry a fee, the ledger or external tools **SHOULD** apply the following logic to identify the final fee amount: - -1. **Check for an Explicit Transaction Fee (`tx.fee`)** - - If the transaction itself contains a `fee` field (e.g., `tx.fee`), that value **SHOULD** take precedence as the fee to be applied. - -2. **Fallback to `effective_fee`** - - If no `tx.fee` is provided, the ledger or tool **SHOULD** refer to the block’s `effective_fee` field (or an equivalent ledger-defined field) to determine the fee. - -3. **No Explicit Fee** - - If neither `tx.fee` nor `effective_fee` is available, then the ledger MAY default the fee to zero or burn any implied fee as per its internal policy. - - An **ICRC-107**-compliant ledger interprets the absence of a specified fee as no fee collected, or a fee of zero. - -### 4.2 Fee Collection Logic - -After determining the fee amount, **ICRC-107** specifies how to decide **whether** a fee is burned or collected, and if collected, **who** receives it, **based solely on block contents**: - -1. **Check for Fee Collector Fields** - - If the block contains a direct account field (e.g., `fee_col`) or a reference field (e.g., `fee_col_block`, `app_fee_col_block`), the fee is be collected by the referenced account. - - If **neither** a direct account field nor a reference field is present, the fee is burned. - -2. **Resolving a Reference to a Previous Block** - - If a reference field is used (`fee_col_block` or `app_fee_col_block`), the ledger or tool **MUST** locate that earlier block and extract the account specified there (e.g., `fee_col`). - - If the referenced block does not contain a valid collector the fee is burned. +--- +## 5. Examples + +### 5.1 Setting a Collector (Defaults to Transfers Only) + +```json +{ + "block_index": 100, + "timestamp": 1690000000, + "kind": "transaction", + "fee_col": { + "principal": "aaaaa-aa", + "subaccount": null + }, + "transaction": { + "operation": "transfer", + "from": { + "principal": "bbbbb-bb", + "subaccount": null + }, + "to": { + "principal": "ccccc-cc", + "subaccount": null + }, + "amount": 5000, + "fee": 10 + } +} +``` + +- Future blocks that perform a **transfer** pay fees to `aaaaa-aa`. +- **Approve** or other operations are not in the default set, so their fees are burned. + +### 5.2 Block with Approve but No Update + +```json +{ + "block_index": 101, + "timestamp": 1690000500, + "kind": "transaction", + "transaction": { + "operation": "approve", + "from": { + "principal": "bbbbb-bb", + "subaccount": null + }, + "spender": { + "principal": "ccccc-cc", + "subaccount": null + }, + "amount": 3000 + } +} +``` + +- No new fee settings. The **previous** block #100 had `["transfer"]` as the fee-bearing operation. +- This is an `"approve"` operation, **not** in `["transfer"]`, so the fee (if any) is burned. + +### 5.3 Updating Collector and Adding Approves + +```json +{ + "block_index": 150, + "timestamp": 1690001000, + "kind": "transaction", + "fee_col": { + "principal": "zzzzz-zz", + "subaccount": null + }, + "fee_ops": ["transfer", "approve"], + "transaction": { + "operation": "approve", + "from": { + "principal": "bbbbb-bb", + "subaccount": null + }, + "spender": { + "principal": "ccccc-cc", + "subaccount": null + }, + "amount": 6000, + "fee": 5 + } +} +``` + +- At block #150, the collector is changed to `zzzzz-zz`, **and** we explicitly list `["transfer", "approve"]`. +- From block #150 onward, **both** transfers and approves incur fees, credited to `zzzzz-zz`. +- The **approve** operation in this same block has a fee of 5 tokens. -### 4.3 Recording Fee Collection in Blocks for ICRC-1 and ICRC-2 Transactions +--- -The block schema for ICRC-1 and ICRC-2 transactions are specified in the ICRC-3 standard. This section explains how to extend those schemas to include backwards compatible fee collection information. +## 6. API for Managing Fee Collection -#### 4.3.1 Transfer Blocks +### 6.1 Methods -Transfer blocks in ICRC-based ledgers can be identified by one of the following: -- **`btype = "1xfer"`** (ICRC-1 style), -- **`btype = "2xfer"`** (ICRC-2 style), -- or a **transfer transaction** (`tx.op = "xfer"`). +#### 6.1.1 `set_fee_collection` -To **record and interpret** fee-collection information in these blocks: +Sets or updates the fee collector account and/or the set of fee-bearing operations. -- **`fee_col` (Account)** - - A direct indicator of which account receives the collected fee. -- **`fee_col_block` (nat)** - - A pointer to an earlier block where `fee_col` is explicitly set. +**Candid Definition:** +```candid +set_fee_collection: (opt Account, opt vec text) -> (); +``` -**How to Determine Who Collects the Fee** -1. If `fee_col` is present in the block, that account is the collector. -2. If `fee_col` is absent but `fee_col_block` is present, fetch the referenced block; the `fee_col` in that block is the collector. -3. If neither is present, **no** collector can be inferred, and the fee is burned. +#### 6.1.2 `get_fee_collection` -#### 4.3.2 Approve Blocks +Retrieves the current fee collector account and the list of fee-bearing operations. -Approve blocks in ICRC-based ledgers can be identified by: -- **`btype = "2approve"`** (ICRC-2 style), -- or a **transaction object** (`tx.op = "approve"`). +**Candid Definition:** +```candid +get_fee_collection: () -> (opt Account, vec text) query; +``` -To **record and interpret** fee-collection information in these blocks: +--- -- **`fee_col` (Account)** - - A direct indicator of which account receives the collected fee for the approval operation. -- **`app_fee_col_block` (nat)** - - A pointer to an earlier block where `fee_col` is explicitly set for approves. +## 7. Interaction with New Standards -**How to Determine Who Collects the Fee** -1. If `fee_col` is present in the block, that account is the collector. -2. If `fee_col` is absent but `app_fee_col_block` is present, fetch the referenced block; the `fee_col` in that block is the collector. -3. If neither is present, **no** collector can be inferred, and the fee is burned. +Any new standard that introduces **additional block types** **MUST** specify how those block types interact with the fee collection mechanism defined in **ICRC-107**. --- -By strictly focusing on **block-level fields**: +## 8. Summary -- **Ledgers** remain free to implement or omit references in blocks while still following a consistent approach for collecting or burning fees. -- **External tools** (wallets, explorers) can determine the collector without requiring out-of-band metadata or additional method calls. If no collector info is embedded (directly or via a reference), the fee is considered burned. -- This specification thereby ensures **ICRC-1**, **ICRC-2**, and **ICRC-3** ledgers can remain consistent with **ICRC-107** using only the fields present in each block. +- **Purely On-Chain**: Fee collection settings are managed entirely within the block history. +- **Flexible Defaults**: Default fee-bearing operations are `["transfer"]` if not explicitly specified. +- **Transparent Evolution**: The chain’s history defines how fee collection evolves over time, ensuring full traceability. From ef25f9e63a7b077eef6580910ab3237130ea39d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BW=C2=B0MacAir?= Date: Fri, 7 Feb 2025 11:14:36 +0100 Subject: [PATCH 05/31] new versions --- ICRCs/ICRC-107/ICRC-107.md.old | 123 +++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 ICRCs/ICRC-107/ICRC-107.md.old diff --git a/ICRCs/ICRC-107/ICRC-107.md.old b/ICRCs/ICRC-107/ICRC-107.md.old new file mode 100644 index 00000000..55c64892 --- /dev/null +++ b/ICRCs/ICRC-107/ICRC-107.md.old @@ -0,0 +1,123 @@ +# ICRC-107: Fee Collection + +## 1. Introduction & Motivation + +Many ICRC-based ledgers (such as **ckBTC**) already implement partial fee collection, often charging fees for **transfer** transactions but not for **approve** transactions. However, there is currently no standardized way to: + +1. Indicate and configure **who** should collect fees (or if fees are burned). +2. Record fee collection settings directly in ledger blocks. +3. Provide clear semantics for wallets, explorers, and other integrations to **interpret** fee settings in a consistent way. + +**ICRC-107** aims to address this gap by defining: + +- A mechanism to specify **fee collection settings** (collector account and applicable operations) directly in blocks. +- A **backward-compatible standard** for **recording and interpreting fee-collection fields** in ICRC-3 blocks corresponding to ICRC-1 and ICRC-2 transactions. +- Clear rules on how fee collection settings evolve over time and how subsequent blocks rely on prior updates. + +This design ensures that **all** fee-related information is stored entirely **on-chain** in the block history, without reliance on external metadata or specialized block types. It provides a fully self-contained, transparent standard for fee collection that simplifies integration with wallets, explorers, and other tools. + +--- + +## 2. Overview + +For each block, the fee amount and the account paying the fee are determined according to rules specific to the block type. +The fee collection settings then determine if the fee is **collected** (removed from the payer account and added to a fee collector account) +or **burned** (removed from the payer account and from the total supply): + + +--- + +## 4. Fee Collection Settings + +Fee collection settings determine the procedures for handling transaction fees within the ledger. These settings specify whether a fee is collected by an account or burned. When a transaction incurs a fee, the active fee collection settings at the time the block is created dictate how the fee is handled: + +- If a **fee collector account** (`fee_col`) is set, the fee is transferred to that account. +- If no **fee collector account** is set, the fee is burned (removed from circulation). +- The **operations list** (`col_ops`) defines which transaction types are subject to fee collection. +- Changes to fee collection settings are recorded directly in blocks, ensuring an **on-chain history** of modifications. + +The following subsections describe the fields in blocks that manage fee collection settings and how they are used in transactions. + +### 4.1 Fields in Blocks + +A block **MAY** include the following fields to define or update fee collection settings: + +1. **fee_col** (optional, `Map`) + - `principal`: `Blob` - The principal of the fee collector. + - `subaccount`: `Blob` (optional) - The subaccount identifier, if applicable. + +2. **col_ops** (optional, `Vec`) An array of operation types (strings) for which fees **should** be collected. If `col_ops` is omitted, it defaults to `["transfer"]` (fee is collected for transfers only). + +3. **prev_fee_col_info** (optional, `Nat`) A natural number (`Nat`) referencing the block index at which the ledger last updated `fee_col` or `col_ops`. If present, it helps external tools quickly locate the previous fee collection settings. + +--- + +## 5. Determining Fee Collection for ICRC-1 and ICRC-2 Blocks + +### 5.1 How to Calculate the Fee for ICRC-1 and ICRC-2 blocks + +The format of blocks follows the ICRC-3 standard. For a complete specification of block structure and fields, refer to [ICRC-3 Standard](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-3.md). + +To determine the **final fee amount** for a given block: + +1. **Check `tx.fee`** + - If present, the value of `tx.fee` is the fee for this block. + +2. **Else, check the top-level `fee` field** + - If `tx.fee` is **not set**, and a top-level field `fee` exists in the block, then that is the fee. + +3. **Else, fallback to `0`** + - If **neither** `tx.fee` nor `fee` is present, the default fee is `0`. + +The paying account is the source account for both transfers and approve transactions. + + +### 5.2 How `col_ops` Determines Fee Collection + +The `col_ops` field defines which block types incur a fee that is collected (instead of burned). For ICRC-1 and ICRC-2 blocks, the mapping from a `col_ops` entry to actual block types is: + +| **col_ops Entry** | **Block Types Affected** | +|-------------------|----------------------------------------------------------------------------------------------------------| +| **"transfer"** | Blocks with `btype = "1xfer"` or `btype = "2xfer"`. If `btype` is not set, then `tx.op = "xfer"` or `"2xfer"`. | +| **"approve"** | Blocks with `btype = "2approve"`. If `btype` is not set, then `tx.op = "approve"`. | + +**Note**: By merging the `"transfer"` and `"icrc2_transfer"` concept, any reference to `"transfer"` in `col_ops` applies to **both** ICRC-1 (`1xfer`) **and** ICRC-2 (`2xfer`) blocks. + +Concretely, + +1. **Identify Block Type** + - If `btype` is present, it takes precedence. + - Otherwise, use `tx.op`. + +2. **Check `col_ops`** + - If the block type is in `col_ops`, the fee is collected by the active `fee_col`. + - Otherwise, the fee is burned. + +If no `fee_col` is set, the fee is burned by default. + +--- + +## 6. Minimal API for Fee Collection Settings + +### 6.1 `set_fee_collection` + +``` +set_fee_collection: (opt Map, opt Vec) -> (); +``` + +### 6.2 `get_fee_collection` + +``` +get_fee_collection: () -> (opt Map, Vec) query; +``` + +--- + +## 7. Summary + +- **On-Chain Configuration**: Fee collection settings (`fee_col`, `col_ops`) are updated via blocks, with no external metadata needed. +- **Fee Collection & Burning**: If `fee_col` is set, the fee is credited to the collector; otherwise, it is burned. +- **Governance**: Only the **ledger controller** can update fee collection settings. +- **Backward-Compatible**: Works seamlessly with ICRC-1, ICRC-2, and ICRC-3 block definitions. + +--- From fc15a30f20f3a8e621405ed93f2b2de9cf692c5c Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Wed, 12 Feb 2025 10:51:02 +0100 Subject: [PATCH 06/31] pass --- ICRCs/ICRC-107/ICRC-107.md | 245 +++++++++++++-------------------- ICRCs/ICRC-107/ICRC-107.md.old | 49 +++---- 2 files changed, 122 insertions(+), 172 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index fdccee4d..2c26ff4d 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -1,197 +1,146 @@ - -# ICRC-107: Fee Collection (Purely Block-Based) +# ICRC-107: Fee Collection ## 1. Introduction & Motivation -Many ICRC-based ledgers (such as **ckBTC**) already implement partial fee collection, often charging fees for **transfer** transactions but not for **approve** transactions. However, there is currently no standardized way to: +Fee collection in ICRC-based ledgers (e.g., ckBTC) is inconsistently implemented. While transfer transactions often incur fees, approve transactions typically do not. Currently, there is no standardized way to: + +- Define fee collection rules—who receives fees, which operations are charged, and whether fees are burned. +- Record fee collection settings directly on-chain in ledger blocks. +- Provide consistent semantics for wallets, explorers, and other integrations to interpret fee structures. + +ICRC-107 extends [ICRC-3](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-3.md), adding semantics for fee collection while ensuring full compatibility with the existing block format. + +### ICRC-107 Proposal -1. Indicate and configure **who** should collect fees (or if fees are burned). -2. Record fee collection information directly in ledger blocks. -3. Provide clear semantics for wallets, explorers, and other integrations to **interpret** fee information in a consistent way. -**ICRC-107** aims to address this gap by defining: -- A mechanism to specify **fee collection details** (collector account and applicable operations) directly in blocks. -- A **backward-compatible standard** for **recording and interpreting fee-collection fields** in ICRC-3 blocks corresponding to ICRC-1 and ICRC-2 transactions. -- Clear rules on how fee collection information evolves over time and how subsequent blocks rely on prior updates. +ICRC-107 introduces a standardized mechanism for on-chain fee collection, ensuring clarity and interoperability across different ICRC-based ledgers. It defines: -This design ensures that **all** fee-related information is stored entirely **on-chain** in the block history, without reliance on external metadata or specialized block types. It provides a fully self-contained, transparent standard for fee collection that simplifies integration with wallets, explorers, and other tools. +- A fee collection configuration specifying the collector account (`fee_col`) and the operations (`col_ops`) subject to fees. +- A backward-compatible extension to ICRC-3 blocks, allowing fee collection settings to be recorded and modified within ledger history. +- Clear rules governing fee distribution: If `fee_col` is set, fees are transferred to the collector for the operations specified via `col_ops`; otherwise, they are burned. + +By embedding fee collection settings entirely on-chain, ICRC-107 eliminates reliance on off-chain metadata, simplifies integration with wallets and explorers, and ensures full transparency in fee handling. --- -## 2. Fee Collection Mechanism +## 2. Fee Collection + +### 2.1 Overview + +For each block added to the ledger, some party must pay a fee. The amount and payer of the fee depend on the specific transaction type. + +Fee collection settings determine how fees are processed. These settings include: -### 2.1 Fields in Blocks +- A fee collector account (`fee_col`). If `fee_col` is not set, all fees are burned. +- A list of operations (`col_ops`) for which fees are collected. If an operation is not in `col_ops`, the fee is burned. + +Changes to fee collection settings are recorded on-chain, ensuring a transparent history of modifications. + +### 2.2 Fee Collection Settings A block **MAY** include the following fields to define or update fee collection settings: -- **`fee_col`**: An **account** (ICRC account format) to which fees are paid. - **Format**: - ```candid - record { - principal: principal; - subaccount: opt blob; - } - ``` -- **`fee_ops`**: An **array of block types** (strings) for which fees **should** be collected. This standard specifies two types of blocks for which fee collection can occur: "transfer" and "approve". +- **`fee_col`** (optional, `Map`) + - `principal`: `Blob` — The principal of the fee collector. + - `subaccount`: `Blob` (optional) — The subaccount identifier, if applicable. -When a block includes either or both fields, it **overrides** the previously known settings from that point onward. +- **`col_ops`** (optional, `Vec`) + - A list of operation types for which fees **should** be collected. + - If omitted, defaults to `"transfer"` (fees are collected only for transfers). -#### 2.1.1 Defaults -- If `fee_ops` is **omitted**, it defaults to `["transfer"]`. -- If `fee_col` is **omitted**, the previously known collector remains in effect (or none if it was never set). +- **`prev_fee_col_info`** (optional, `Nat`) + - The block index at which `fee_col` or `col_ops` was last updated. + - Enables quick lookup of previous fee collection settings. + +**Note:** The block format follows the [ICRC-3 standard](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-3.md). --- -## 3. Fee Computation +## 3. Handling Fee Collection for ICRC-1 and ICRC-2 Blocks -### 3.1 Determining the Fee Amount +### 3.1 How to Calculate the Fee for ICRC-1 and ICRC-2 Blocks -The fee amount in any block can be derived via: -- An explicit `tx.fee` field in the block’s transaction. -- A fallback `effective_fee` or other ledger-defined field if `tx.fee` is absent. -- Zero if neither is present. +Blocks follow the ICRC-3 standard. To determine the **final fee amount** for a block: -### 3.2 Collected or Burned +1. **Check `tx.fee`** + - If present, `tx.fee` is the fee for this block. -After determining the fee amount, check the **current** configuration (from the most recent update): -1. **Operation Present**: If the block’s operation (e.g., `"transfer"`, `"approve"`, etc.) is in the active list (`fee_ops`), the fee is collected by the active `fee_col`. -2. **Operation Absent**: If the operation is **not** in the active list, the fee is burned. -3. **No Collector**: If no collector has ever been set, fees are burned by default. +2. **Else, check the top-level `fee` field** + - If `tx.fee` is **not set**, and a top-level field `fee` exists in the block, then that is the fee. ---- +3. **Else, fallback to `0`** + - If **neither** `tx.fee` nor `fee` is present, the default fee is `0`. -## 4. Determining the Current Fee Configuration +The paying account is the source account for both transfer and approve transactions. -At any block height `h`, to figure out **who** collects fees and for **which** operations: -1. **Traverse blocks** backward (or maintain an in-memory state) until you find the most recent block `< h` that included `fee_col` or `fee_ops`. -2. The fee-collector account from that block is **active**, and the operation list from that block is **active** (defaulting to `["transfer"]` if omitted). -3. If no such block is found, no collector is active and **all fees are burned**. +### 3.2 How `col_ops` Determines Fee Collection ---- +The `col_ops` field defines which block types incur fees that are collected instead of burned. For ICRC-1 and ICRC-2 blocks, `col_ops` entries map to block types as follows: -## 5. Examples - -### 5.1 Setting a Collector (Defaults to Transfers Only) - -```json -{ - "block_index": 100, - "timestamp": 1690000000, - "kind": "transaction", - "fee_col": { - "principal": "aaaaa-aa", - "subaccount": null - }, - "transaction": { - "operation": "transfer", - "from": { - "principal": "bbbbb-bb", - "subaccount": null - }, - "to": { - "principal": "ccccc-cc", - "subaccount": null - }, - "amount": 5000, - "fee": 10 - } -} -``` +| **col_ops Entry** | **Block Types Affected** | +|-------------------|--------------------------| +| **"transfer"** | Blocks with `btype = "1xfer"` or `btype = "2xfer"`. If `btype` is not set, use `tx.op = "xfer"` or `"2xfer"`. | +| **"approve"** | Blocks with `btype = "2approve"`. If `btype` is not set, use `tx.op = "approve"`. | -- Future blocks that perform a **transfer** pay fees to `aaaaa-aa`. -- **Approve** or other operations are not in the default set, so their fees are burned. - -### 5.2 Block with Approve but No Update - -```json -{ - "block_index": 101, - "timestamp": 1690000500, - "kind": "transaction", - "transaction": { - "operation": "approve", - "from": { - "principal": "bbbbb-bb", - "subaccount": null - }, - "spender": { - "principal": "ccccc-cc", - "subaccount": null - }, - "amount": 3000 - } -} -``` +#### Key Rules: -- No new fee settings. The **previous** block #100 had `["transfer"]` as the fee-bearing operation. -- This is an `"approve"` operation, **not** in `["transfer"]`, so the fee (if any) is burned. - -### 5.3 Updating Collector and Adding Approves - -```json -{ - "block_index": 150, - "timestamp": 1690001000, - "kind": "transaction", - "fee_col": { - "principal": "zzzzz-zz", - "subaccount": null - }, - "fee_ops": ["transfer", "approve"], - "transaction": { - "operation": "approve", - "from": { - "principal": "bbbbb-bb", - "subaccount": null - }, - "spender": { - "principal": "ccccc-cc", - "subaccount": null - }, - "amount": 6000, - "fee": 5 - } -} -``` +1. **Determine Block Type** + - If `btype` is present, it takes precedence. + - Otherwise, use `tx.op`. -- At block #150, the collector is changed to `zzzzz-zz`, **and** we explicitly list `["transfer", "approve"]`. -- From block #150 onward, **both** transfers and approves incur fees, credited to `zzzzz-zz`. -- The **approve** operation in this same block has a fee of 5 tokens. +2. **Check if `col_ops` applies to the block type** + - If the block type corresponds to an entry in `col_ops`, the fee is collected by `fee_col`. + - Otherwise, the fee is burned. ---- +If no `fee_col` is set, all fees are burned by default. -## 6. API for Managing Fee Collection +--- -### 6.1 Methods +## 4. Minimal API for Fee Collection Settings -#### 6.1.1 `set_fee_collection` +### 4.1 `set_fee_collection` -Sets or updates the fee collector account and/or the set of fee-bearing operations. +Updates fee collection settings. Only callable by the ledger controller. -**Candid Definition:** -```candid -set_fee_collection: (opt Account, opt vec text) -> (); +``` +set_fee_collection: (opt Map, opt Vec) -> (); ``` -#### 6.1.2 `get_fee_collection` +### 4.2 `get_fee_collection` -Retrieves the current fee collector account and the list of fee-bearing operations. +Returns the current fee collection settings and the block index of the last update. -**Candid Definition:** -```candid -get_fee_collection: () -> (opt Account, vec text) query; +``` +get_fee_collection: () -> (opt Map, Vec, Nat) query; ``` --- -## 7. Interaction with New Standards +## 5. Interaction with Future Standards + +Any new standard that introduces additional block types **MUST** specify how those block types interact with the fee collection mechanism defined in **ICRC-107**. Specifically: + +1. **Fee Payer Determination** + - Define how the fee amount is determined and who is responsible for paying the fee. + +2. **Fee Applicability** + - Clarify which new block types, if any, are subject to fee collection. -Any new standard that introduces **additional block types** **MUST** specify how those block types interact with the fee collection mechanism defined in **ICRC-107**. +3. **Fee Collection Rules** + - Define entries for `fee_col` corresponding to the new block types. --- -## 8. Summary -- **Purely On-Chain**: Fee collection settings are managed entirely within the block history. -- **Flexible Defaults**: Default fee-bearing operations are `["transfer"]` if not explicitly specified. -- **Transparent Evolution**: The chain’s history defines how fee collection evolves over time, ensuring full traceability. + + + +## 6. Summary + +- **On-Chain Configuration**: Fee collection settings (`fee_col`, `col_ops`) are recorded and modified within ledger blocks. +- **Fee Collection & Burning**: If `fee_col` is set and a block type appears in `fee_ops` then fees for that block type are collected by `fee_col`. Otherwise, the fee is burned. +- **Governance**: Only the **ledger controller** can update fee collection settings. +- **Backward-Compatible**: Works seamlessly with ICRC-1, ICRC-2, and ICRC-3 block definitions. + + +--- diff --git a/ICRCs/ICRC-107/ICRC-107.md.old b/ICRCs/ICRC-107/ICRC-107.md.old index 55c64892..032df1fd 100644 --- a/ICRCs/ICRC-107/ICRC-107.md.old +++ b/ICRCs/ICRC-107/ICRC-107.md.old @@ -14,31 +14,28 @@ Many ICRC-based ledgers (such as **ckBTC**) already implement partial fee collec - A **backward-compatible standard** for **recording and interpreting fee-collection fields** in ICRC-3 blocks corresponding to ICRC-1 and ICRC-2 transactions. - Clear rules on how fee collection settings evolve over time and how subsequent blocks rely on prior updates. -This design ensures that **all** fee-related information is stored entirely **on-chain** in the block history, without reliance on external metadata or specialized block types. It provides a fully self-contained, transparent standard for fee collection that simplifies integration with wallets, explorers, and other tools. +This design ensures that **all** fee-related information is stored entirely **on-chain** in the block history, without reliance on external metadata or specialized block types. +It provides a fully self-contained, transparent standard for fee collection that simplifies integration with wallets, explorers, and other tools. --- -## 2. Overview +## 2. Fee Collection -For each block, the fee amount and the account paying the fee are determined according to rules specific to the block type. -The fee collection settings then determine if the fee is **collected** (removed from the payer account and added to a fee collector account) -or **burned** (removed from the payer account and from the total supply): +### 2.1 Overview +For each block added to the ledgers, some party pays fees. The fee amount and the account paying the fee are determined according to rules specific to the block type. +Fee collection settings determine how fees are handled for specific block types. These settings include a fee collector account (`fee_col`) and +a list of operations (`col_ops`) for which fees are collected. If `fee_col` is not set, then fees for all operations are burned. +If no fee collector is set or the operation is not in `col_ops`, the fee is burned. +Changes to fee collection settings are recorded directly in blocks, ensuring an **on-chain history** of modifications. ---- - -## 4. Fee Collection Settings - -Fee collection settings determine the procedures for handling transaction fees within the ledger. These settings specify whether a fee is collected by an account or burned. When a transaction incurs a fee, the active fee collection settings at the time the block is created dictate how the fee is handled: - -- If a **fee collector account** (`fee_col`) is set, the fee is transferred to that account. -- If no **fee collector account** is set, the fee is burned (removed from circulation). -- The **operations list** (`col_ops`) defines which transaction types are subject to fee collection. -- Changes to fee collection settings are recorded directly in blocks, ensuring an **on-chain history** of modifications. +For each block corresponding to some operation, if that operation is not subject to fee collection or a fee collector is not set then the fee is burned (i.e. removed from +the account that pays the fee and from the total supply). +Otherwise, the fee is transferred to the fee collector account. The following subsections describe the fields in blocks that manage fee collection settings and how they are used in transactions. -### 4.1 Fields in Blocks +## 2.2 Fee Collection Settings A block **MAY** include the following fields to define or update fee collection settings: @@ -52,9 +49,9 @@ A block **MAY** include the following fields to define or update fee collection --- -## 5. Determining Fee Collection for ICRC-1 and ICRC-2 Blocks +## 3. Determining Fee Collection for ICRC-1 and ICRC-2 Blocks -### 5.1 How to Calculate the Fee for ICRC-1 and ICRC-2 blocks +### 3.1 How to Calculate the Fee for ICRC-1 and ICRC-2 blocks The format of blocks follows the ICRC-3 standard. For a complete specification of block structure and fields, refer to [ICRC-3 Standard](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-3.md). @@ -72,7 +69,7 @@ To determine the **final fee amount** for a given block: The paying account is the source account for both transfers and approve transactions. -### 5.2 How `col_ops` Determines Fee Collection +### 3.2 How `col_ops` Determines Fee Collection The `col_ops` field defines which block types incur a fee that is collected (instead of burned). For ICRC-1 and ICRC-2 blocks, the mapping from a `col_ops` entry to actual block types is: @@ -97,23 +94,27 @@ If no `fee_col` is set, the fee is burned by default. --- -## 6. Minimal API for Fee Collection Settings +## 4. Minimal API for Fee Collection Settings + + +### 4.1 `set_fee_collection` -### 6.1 `set_fee_collection` +This method sets or updates the fee collection settings. It can only be called by the controller of the canister. ``` set_fee_collection: (opt Map, opt Vec) -> (); ``` -### 6.2 `get_fee_collection` +### 4.2 `get_fee_collection` +This method returns the current `fee_col`, `col_ops`, and the block index of the last update (`prev_fee_col_info`). ``` -get_fee_collection: () -> (opt Map, Vec) query; +get_fee_collection: () -> (opt Map, Vec, Nat) query; ``` --- -## 7. Summary +## 5. Summary - **On-Chain Configuration**: Fee collection settings (`fee_col`, `col_ops`) are updated via blocks, with no external metadata needed. - **Fee Collection & Burning**: If `fee_col` is set, the fee is credited to the collector; otherwise, it is burned. From d2a50c3dc94f5c2777fb6ef4d434df2dc6d6a4d0 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 17 Feb 2025 17:09:07 +0100 Subject: [PATCH 07/31] final draft icrc-107 --- ICRCs/ICRC-107/ICRC-107.md | 106 ++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 32 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 2c26ff4d..55aa0eb3 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -2,22 +2,21 @@ ## 1. Introduction & Motivation -Fee collection in ICRC-based ledgers (e.g., ckBTC) is inconsistently implemented. While transfer transactions often incur fees, approve transactions typically do not. Currently, there is no standardized way to: +Fee collection in ICRC-based ledgers (e.g., ckBTC) is inconsistently implemented. While transfer transactions often incur fees, approve transactions typically do not. However, there is no standardized way to: - Define fee collection rules—who receives fees, which operations are charged, and whether fees are burned. - Record fee collection settings directly on-chain in ledger blocks. - Provide consistent semantics for wallets, explorers, and other integrations to interpret fee structures. -ICRC-107 extends [ICRC-3](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-3.md), adding semantics for fee collection while ensuring full compatibility with the existing block format. +ICRC-107 extends **ICRC-3**, adding semantics for fee collection while ensuring full compatibility with the existing block format. This proposal eliminates reliance on off-chain metadata, simplifies integration with wallets and explorers, and ensures full transparency in fee handling. ### ICRC-107 Proposal - ICRC-107 introduces a standardized mechanism for on-chain fee collection, ensuring clarity and interoperability across different ICRC-based ledgers. It defines: -- A fee collection configuration specifying the collector account (`fee_col`) and the operations (`col_ops`) subject to fees. +- A fee collection configuration specifying the collector account (`fee_col`) subject to fees. - A backward-compatible extension to ICRC-3 blocks, allowing fee collection settings to be recorded and modified within ledger history. -- Clear rules governing fee distribution: If `fee_col` is set, fees are transferred to the collector for the operations specified via `col_ops`; otherwise, they are burned. +- Clear rules governing fee distribution: If `fee_col` is set, fees are transferred to the collector; otherwise, they are burned. By embedding fee collection settings entirely on-chain, ICRC-107 eliminates reliance on off-chain metadata, simplifies integration with wallets and explorers, and ensures full transparency in fee handling. @@ -38,18 +37,39 @@ Changes to fee collection settings are recorded on-chain, ensuring a transparent ### 2.2 Fee Collection Settings +**Backwards Compatibility:** +To ensure compatibility with existing ICRC-3 implementations, `fee_col` **MUST** continue to be recorded using the existing array format: + +``` +fee_col; +variant { + Array = vec { + variant { Blob = principal_blob }; + variant { Blob = subaccount_blob }; + } +} +``` + +- The **first Blob** represents the **principal** of the fee collector. +- The **second Blob** represents the **subaccount**, or a zeroed-out Blob if no subaccount is used. + +This ensures that existing tools and explorers relying on the current `fee_col` format remain functional. + A block **MAY** include the following fields to define or update fee collection settings: -- **`fee_col`** (optional, `Map`) - - `principal`: `Blob` — The principal of the fee collector. - - `subaccount`: `Blob` (optional) — The subaccount identifier, if applicable. +- **`fee_col`** (optional, `Map`) -- **`col_ops`** (optional, `Vec`) - - A list of operation types for which fees **should** be collected. - - If omitted, defaults to `"transfer"` (fees are collected only for transfers). + - `principal`: `Blob` — The principal of the fee collector. + - `subaccount`: `Blob` (optional) — The subaccount identifier, if applicable. -- **`prev_fee_col_info`** (optional, `Nat`) - - The block index at which `fee_col` or `col_ops` was last updated. +- **`col_ops`** (optional, `Vec`) + + - A list of operation types for which fees **should** be collected. + - If omitted, defaults to `"transfer"` (fees are collected only for transfers). + +- **`prev_fee_col_info`** (optional, `Nat`) + + - The block index at which `fee_col` or `col_ops` was last updated. - Enables quick lookup of previous fee collection settings. **Note:** The block format follows the [ICRC-3 standard](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-3.md). @@ -98,49 +118,71 @@ If no `fee_col` is set, all fees are burned by default. ## 4. Minimal API for Fee Collection Settings -### 4.1 `set_fee_collection` +### 4.1 `icrc107_set_fee_collection` + +This method allows the ledger controller to update the fee collection settings. It modifies the fee_col account, which determines where collected fees are sent, and updates the col_ops list, which specifies the transaction types for which fees should be collected. The updated settings are recorded in the next block added to the ledger. + +``` +icrc107_set_fee_collection: (SetFeeCollectionRequest) -> (); +``` -Updates fee collection settings. Only callable by the ledger controller. +with ``` -set_fee_collection: (opt Map, opt Vec) -> (); +type Account = record { + owner: principal; + subaccount: opt blob; +}; + +type SetFeeCollectionRequest = record { + fee_col: Account; + fee_ops: Vec +} ``` -### 4.2 `get_fee_collection` -Returns the current fee collection settings and the block index of the last update. +### 4.2 `icrc107_get_fee_collection` +This method retrieves the currently active fee collection settings, including the fee_col account (if set), the list of transaction types (col_ops) for which fees are collected, and the block index of the last recorded update. This allows external systems, such as wallets and explorers, to determine how fee collection is configured. ``` -get_fee_collection: () -> (opt Map, Vec, Nat) query; +icrc107_get_fee_collection: () -> (opt Account, Vec, Nat) query; ``` +**Note:** The block returned reflects the last block where a change in fee collection information was recorded. However, this block may not contain the most recent settings (as returned in the first part of the response) if no block was created between setting fee collection information and retrieving it. The latest fee collection settings will be recorded in the first block that will be added to the ledger. + --- ## 5. Interaction with Future Standards Any new standard that introduces additional block types **MUST** specify how those block types interact with the fee collection mechanism defined in **ICRC-107**. Specifically: -1. **Fee Payer Determination** - - Define how the fee amount is determined and who is responsible for paying the fee. - -2. **Fee Applicability** +1. **Fee Applicability** - Clarify which new block types, if any, are subject to fee collection. -3. **Fee Collection Rules** - - Define entries for `fee_col` corresponding to the new block types. +2. **Fee Payer Determination** + - Define how the fee amount is determined and who is responsible for paying the fee. ---- +3. **Fee Collection Rules** + - Define new entry types for `fee_col` corresponding to the new block types. + - Specify how `fee_col` should be applied to determine whether fees for the new block types are collected or burned. +By ensuring that any future standards explicitly define their interaction with fee collection, ICRC-107 remains a robust and extensible framework. +--- +## 6. Reporting Compliance with ICRC-Supported Standards +Ledgers implementing ICRC-107 **MUST** indicate their compliance through the `icrc1_supported_standards` and `icrc10_supported_standards` methods, as defined in ICRC-1 and ICRC-10 by including the following record in the output of these methods: -## 6. Summary +``` +record { + name = "ICRC-107"; + url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-107.md" +} +``` -- **On-Chain Configuration**: Fee collection settings (`fee_col`, `col_ops`) are recorded and modified within ledger blocks. -- **Fee Collection & Burning**: If `fee_col` is set and a block type appears in `fee_ops` then fees for that block type are collected by `fee_col`. Otherwise, the fee is burned. -- **Governance**: Only the **ledger controller** can update fee collection settings. -- **Backward-Compatible**: Works seamlessly with ICRC-1, ICRC-2, and ICRC-3 block definitions. +--- +## 7. Summary ---- +ICRC-107 provides a self-contained, transparent, and interoperable framework for managing transaction fees across ICRC-based ledgers. From b24b937071a864fd262e45ec61d857a373f3dbcf Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 17 Feb 2025 17:10:50 +0100 Subject: [PATCH 08/31] deleted old version of icrc107 --- ICRCs/ICRC-107/ICRC-107.md.old | 124 --------------------------------- 1 file changed, 124 deletions(-) delete mode 100644 ICRCs/ICRC-107/ICRC-107.md.old diff --git a/ICRCs/ICRC-107/ICRC-107.md.old b/ICRCs/ICRC-107/ICRC-107.md.old deleted file mode 100644 index 032df1fd..00000000 --- a/ICRCs/ICRC-107/ICRC-107.md.old +++ /dev/null @@ -1,124 +0,0 @@ -# ICRC-107: Fee Collection - -## 1. Introduction & Motivation - -Many ICRC-based ledgers (such as **ckBTC**) already implement partial fee collection, often charging fees for **transfer** transactions but not for **approve** transactions. However, there is currently no standardized way to: - -1. Indicate and configure **who** should collect fees (or if fees are burned). -2. Record fee collection settings directly in ledger blocks. -3. Provide clear semantics for wallets, explorers, and other integrations to **interpret** fee settings in a consistent way. - -**ICRC-107** aims to address this gap by defining: - -- A mechanism to specify **fee collection settings** (collector account and applicable operations) directly in blocks. -- A **backward-compatible standard** for **recording and interpreting fee-collection fields** in ICRC-3 blocks corresponding to ICRC-1 and ICRC-2 transactions. -- Clear rules on how fee collection settings evolve over time and how subsequent blocks rely on prior updates. - -This design ensures that **all** fee-related information is stored entirely **on-chain** in the block history, without reliance on external metadata or specialized block types. -It provides a fully self-contained, transparent standard for fee collection that simplifies integration with wallets, explorers, and other tools. - ---- - -## 2. Fee Collection - -### 2.1 Overview - -For each block added to the ledgers, some party pays fees. The fee amount and the account paying the fee are determined according to rules specific to the block type. -Fee collection settings determine how fees are handled for specific block types. These settings include a fee collector account (`fee_col`) and -a list of operations (`col_ops`) for which fees are collected. If `fee_col` is not set, then fees for all operations are burned. -If no fee collector is set or the operation is not in `col_ops`, the fee is burned. -Changes to fee collection settings are recorded directly in blocks, ensuring an **on-chain history** of modifications. - -For each block corresponding to some operation, if that operation is not subject to fee collection or a fee collector is not set then the fee is burned (i.e. removed from -the account that pays the fee and from the total supply). -Otherwise, the fee is transferred to the fee collector account. - -The following subsections describe the fields in blocks that manage fee collection settings and how they are used in transactions. - -## 2.2 Fee Collection Settings - -A block **MAY** include the following fields to define or update fee collection settings: - -1. **fee_col** (optional, `Map`) - - `principal`: `Blob` - The principal of the fee collector. - - `subaccount`: `Blob` (optional) - The subaccount identifier, if applicable. - -2. **col_ops** (optional, `Vec`) An array of operation types (strings) for which fees **should** be collected. If `col_ops` is omitted, it defaults to `["transfer"]` (fee is collected for transfers only). - -3. **prev_fee_col_info** (optional, `Nat`) A natural number (`Nat`) referencing the block index at which the ledger last updated `fee_col` or `col_ops`. If present, it helps external tools quickly locate the previous fee collection settings. - ---- - -## 3. Determining Fee Collection for ICRC-1 and ICRC-2 Blocks - -### 3.1 How to Calculate the Fee for ICRC-1 and ICRC-2 blocks - -The format of blocks follows the ICRC-3 standard. For a complete specification of block structure and fields, refer to [ICRC-3 Standard](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-3.md). - -To determine the **final fee amount** for a given block: - -1. **Check `tx.fee`** - - If present, the value of `tx.fee` is the fee for this block. - -2. **Else, check the top-level `fee` field** - - If `tx.fee` is **not set**, and a top-level field `fee` exists in the block, then that is the fee. - -3. **Else, fallback to `0`** - - If **neither** `tx.fee` nor `fee` is present, the default fee is `0`. - -The paying account is the source account for both transfers and approve transactions. - - -### 3.2 How `col_ops` Determines Fee Collection - -The `col_ops` field defines which block types incur a fee that is collected (instead of burned). For ICRC-1 and ICRC-2 blocks, the mapping from a `col_ops` entry to actual block types is: - -| **col_ops Entry** | **Block Types Affected** | -|-------------------|----------------------------------------------------------------------------------------------------------| -| **"transfer"** | Blocks with `btype = "1xfer"` or `btype = "2xfer"`. If `btype` is not set, then `tx.op = "xfer"` or `"2xfer"`. | -| **"approve"** | Blocks with `btype = "2approve"`. If `btype` is not set, then `tx.op = "approve"`. | - -**Note**: By merging the `"transfer"` and `"icrc2_transfer"` concept, any reference to `"transfer"` in `col_ops` applies to **both** ICRC-1 (`1xfer`) **and** ICRC-2 (`2xfer`) blocks. - -Concretely, - -1. **Identify Block Type** - - If `btype` is present, it takes precedence. - - Otherwise, use `tx.op`. - -2. **Check `col_ops`** - - If the block type is in `col_ops`, the fee is collected by the active `fee_col`. - - Otherwise, the fee is burned. - -If no `fee_col` is set, the fee is burned by default. - ---- - -## 4. Minimal API for Fee Collection Settings - - -### 4.1 `set_fee_collection` - -This method sets or updates the fee collection settings. It can only be called by the controller of the canister. - -``` -set_fee_collection: (opt Map, opt Vec) -> (); -``` - -### 4.2 `get_fee_collection` - -This method returns the current `fee_col`, `col_ops`, and the block index of the last update (`prev_fee_col_info`). -``` -get_fee_collection: () -> (opt Map, Vec, Nat) query; -``` - ---- - -## 5. Summary - -- **On-Chain Configuration**: Fee collection settings (`fee_col`, `col_ops`) are updated via blocks, with no external metadata needed. -- **Fee Collection & Burning**: If `fee_col` is set, the fee is credited to the collector; otherwise, it is burned. -- **Governance**: Only the **ledger controller** can update fee collection settings. -- **Backward-Compatible**: Works seamlessly with ICRC-1, ICRC-2, and ICRC-3 block definitions. - ---- From 88617a334d0bae91b05335acdba427a524548a6e Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Tue, 25 Feb 2025 16:10:47 +0100 Subject: [PATCH 09/31] first pass over Thomas' comments --- ICRCs/ICRC-107/ICRC-107.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 55aa0eb3..c4befbf4 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -2,7 +2,7 @@ ## 1. Introduction & Motivation -Fee collection in ICRC-based ledgers (e.g., ckBTC) is inconsistently implemented. While transfer transactions often incur fees, approve transactions typically do not. However, there is no standardized way to: +Fee collection in ICRC-based ledgers (e.g., ckBTC) is inconsistently implemented. While transfer transactions often incur fees that is collected or burned, approve transactions typically do not. However, there is no standardized way to: - Define fee collection rules—who receives fees, which operations are charged, and whether fees are burned. - Record fee collection settings directly on-chain in ledger blocks. @@ -86,10 +86,8 @@ Blocks follow the ICRC-3 standard. To determine the **final fee amount** for a b - If present, `tx.fee` is the fee for this block. 2. **Else, check the top-level `fee` field** - - If `tx.fee` is **not set**, and a top-level field `fee` exists in the block, then that is the fee. + - If `tx.fee` is **not set**, then a top-level field `fee` exists in the block, and that is the fee. -3. **Else, fallback to `0`** - - If **neither** `tx.fee` nor `fee` is present, the default fee is `0`. The paying account is the source account for both transfer and approve transactions. @@ -136,16 +134,16 @@ type Account = record { type SetFeeCollectionRequest = record { fee_col: Account; - fee_ops: Vec + col_ops: Vec } ``` ### 4.2 `icrc107_get_fee_collection` -This method retrieves the currently active fee collection settings, including the fee_col account (if set), the list of transaction types (col_ops) for which fees are collected, and the block index of the last recorded update. This allows external systems, such as wallets and explorers, to determine how fee collection is configured. +This method retrieves the currently active fee collection settings, including the `fee_col` account (if set), the list of transaction types (`col_ops`) for which fees are collected, and the block index of the last recorded update. This allows external systems, such as wallets and explorers, to determine how fee collection is configured. ``` -icrc107_get_fee_collection: () -> (opt Account, Vec, Nat) query; +icrc107_get_fee_collection: () -> (opt Account, Vec, opt Nat) query; ``` **Note:** The block returned reflects the last block where a change in fee collection information was recorded. However, this block may not contain the most recent settings (as returned in the first part of the response) if no block was created between setting fee collection information and retrieving it. The latest fee collection settings will be recorded in the first block that will be added to the ledger. @@ -164,7 +162,7 @@ Any new standard that introduces additional block types **MUST** specify how tho 3. **Fee Collection Rules** - Define new entry types for `fee_col` corresponding to the new block types. - - Specify how `fee_col` should be applied to determine whether fees for the new block types are collected or burned. + - Specify how `col_ops` should be applied to determine whether fees for the new block types are collected or burned. By ensuring that any future standards explicitly define their interaction with fee collection, ICRC-107 remains a robust and extensible framework. From d58a8210ea6949b7d45a169c12f5748602d1625d Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Thu, 27 Feb 2025 23:40:44 +0100 Subject: [PATCH 10/31] implemented WG decisions --- ICRCs/ICRC-107/ICRC-107.md | 81 +++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index c4befbf4..098f6e04 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -2,7 +2,7 @@ ## 1. Introduction & Motivation -Fee collection in ICRC-based ledgers (e.g., ckBTC) is inconsistently implemented. While transfer transactions often incur fees that is collected or burned, approve transactions typically do not. However, there is no standardized way to: +The absence of a unified approach has resulted in inconsistencies in fee collection across ICRC-based ledgers (e.g., ckBTC). In the ckBTC ledger, the fee collector collects fees for transfer operations, while fees for approve operations are burned, demonstrating the need for a unified standard. However, there is no standardized way to: - Define fee collection rules—who receives fees, which operations are charged, and whether fees are burned. - Record fee collection settings directly on-chain in ledger blocks. @@ -16,7 +16,7 @@ ICRC-107 introduces a standardized mechanism for on-chain fee collection, ensuri - A fee collection configuration specifying the collector account (`fee_col`) subject to fees. - A backward-compatible extension to ICRC-3 blocks, allowing fee collection settings to be recorded and modified within ledger history. -- Clear rules governing fee distribution: If `fee_col` is set, fees are transferred to the collector; otherwise, they are burned. +- Clear rules governing fee distribution: If `fee_col` is set, fees are transferred from the paying account to the collector; otherwise, they are burned, —meaning they are debited from the paying account and removed from the total supply. By embedding fee collection settings entirely on-chain, ICRC-107 eliminates reliance on off-chain metadata, simplifies integration with wallets and explorers, and ensures full transparency in fee handling. @@ -26,19 +26,21 @@ By embedding fee collection settings entirely on-chain, ICRC-107 eliminates reli ### 2.1 Overview -For each block added to the ledger, some party must pay a fee. The amount and payer of the fee depend on the specific transaction type. +For each block added to the ledger, some party may have to pay a fee. The amount and payer of the fee depend on the specific operation type. Fee collection settings determine how fees are processed. These settings include: -- A fee collector account (`fee_col`). If `fee_col` is not set, all fees are burned. -- A list of operations (`col_ops`) for which fees are collected. If an operation is not in `col_ops`, the fee is burned. -Changes to fee collection settings are recorded on-chain, ensuring a transparent history of modifications. +- A fee collector account (`fee_col`). If `fee_col` is not set, all fees are burned. Burning occurs by removing the fee from the paying account and reducing the total token supply by that amount. +- A list of operations (`fee_ops`) for which fees are collected. If an operation is not in `fee_ops`, the fee is burned. +- A flag `fee_burn` explicitly determines that all fees are burned. If set to "true", fees are always burned, regardless of `fee_col`. + +Changes to fee collection settings are recorded on-chain and take effect immediately in the block where they appear. ### 2.2 Fee Collection Settings **Backwards Compatibility:** -To ensure compatibility with existing ICRC-3 implementations, `fee_col` **MUST** continue to be recorded using the existing array format: +To ensure compatibility with existing ICRC-3 implementations, `fee_col` **MUST** be recorded using the existing array format: ``` fee_col; @@ -50,7 +52,7 @@ variant { } ``` -- The **first Blob** represents the **principal** of the fee collector. +- The **first Blob** represents the **principal** of the fee collector; if this is the anonymous principal (0x04) then fees for all operations are burned. - The **second Blob** represents the **subaccount**, or a zeroed-out Blob if no subaccount is used. This ensures that existing tools and explorers relying on the current `fee_col` format remain functional. @@ -58,19 +60,20 @@ This ensures that existing tools and explorers relying on the current `fee_col` A block **MAY** include the following fields to define or update fee collection settings: - **`fee_col`** (optional, `Map`) - - `principal`: `Blob` — The principal of the fee collector. - `subaccount`: `Blob` (optional) — The subaccount identifier, if applicable. -- **`col_ops`** (optional, `Vec`) +- **`fee_burn`** (optional, `Text`) + - If `"true"`, fees are burned (removed from supply), irrespective of `fee_col` settings. + - If `"false"`, fees are transferred to `fee_col`. + - Defaults to `"true"` if `fee_col` is unset. - - A list of operation types for which fees **should** be collected. - - If omitted, defaults to `"transfer"` (fees are collected only for transfers). +- **`fee_ops`** (optional, `Vec`) -- **`prev_fee_col_info`** (optional, `Nat`) + - A list of operation types for which fees **should** be collected. + - If omitted, defaults to `["1xfer","2xfer"]` (fees are collected only for transfers). - - The block index at which `fee_col` or `col_ops` was last updated. - - Enables quick lookup of previous fee collection settings. +Blocks where `fee_ops` is specified, or `fee_burn` is set to "false" MUST also specify `fee_col`. **Note:** The block format follows the [ICRC-3 standard](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-3.md). @@ -91,26 +94,36 @@ Blocks follow the ICRC-3 standard. To determine the **final fee amount** for a b The paying account is the source account for both transfer and approve transactions. -### 3.2 How `col_ops` Determines Fee Collection +### 3.2 How to Determine the Fee Collector for a Block + +Find the most recent block where fee collection settings are present. +* If no such block exists, the fees in the current block are burned. +* If the most recent block with fee settings has `fee_burn` = "true", then fees in the current block are burned. +* Otherwise, the active fee collector is the account specified by `fee_col`, and fees are collected for the operations listed in `fee_ops` (see the next section). + -The `col_ops` field defines which block types incur fees that are collected instead of burned. For ICRC-1 and ICRC-2 blocks, `col_ops` entries map to block types as follows: +### 3.3 How `fee_ops` Determines Fee Collection for a Block -| **col_ops Entry** | **Block Types Affected** | +The `fee_ops` field defines which block types incur fees that are collected instead of burned. For ICRC-1 and ICRC-2 blocks, `fee_ops` entries map to block types as follows: + +| **fee_ops Entry** | **Block Types Affected** | |-------------------|--------------------------| -| **"transfer"** | Blocks with `btype = "1xfer"` or `btype = "2xfer"`. If `btype` is not set, use `tx.op = "xfer"` or `"2xfer"`. | -| **"approve"** | Blocks with `btype = "2approve"`. If `btype` is not set, use `tx.op = "approve"`. | +| "1xfer" | Blocks with `btype = "1xfer"` and blocks where `btype` is not set and `tx.op = "xfer"` and there is no `tx.spender` field | +| "2xfer" | Blocks with `btype = "2xfer"` and blocks where `btype` is not set and `tx.op = "xfer"` and the field `tx.spender` is set. | +| "2approve" | Blocks with `btype = "2approve"` and blocks where `btype` is not set and `tx.op = "approve"`. | #### Key Rules: 1. **Determine Block Type** - If `btype` is present, it takes precedence. - - Otherwise, use `tx.op`. + - Otherwise, use `tx.op` to determine the block type. -2. **Check if `col_ops` applies to the block type** - - If the block type corresponds to an entry in `col_ops`, the fee is collected by `fee_col`. +2. **Check if `fee_ops` applies to the block type** + - If the block type corresponds to an entry in `fee_ops`, the fee is collected by `fee_col`. - Otherwise, the fee is burned. -If no `fee_col` is set, all fees are burned by default. +If `fee_col` is set but `fee_ops` is not, `fee_ops` defaults to `["1xfer, 2xfer"]`, i.e. fees are only collected for transfer operations. + --- @@ -118,7 +131,7 @@ If no `fee_col` is set, all fees are burned by default. ### 4.1 `icrc107_set_fee_collection` -This method allows the ledger controller to update the fee collection settings. It modifies the fee_col account, which determines where collected fees are sent, and updates the col_ops list, which specifies the transaction types for which fees should be collected. The updated settings are recorded in the next block added to the ledger. +This method allows a ledger controller to update the fee collection settings. It modifies the `fee_col` account, which determines where collected fees are sent, and updates the `fee_ops` list, which specifies the transaction types for which fees should be collected. The updated settings are recorded in the next block added to the ledger. ``` icrc107_set_fee_collection: (SetFeeCollectionRequest) -> (); @@ -133,20 +146,26 @@ type Account = record { }; type SetFeeCollectionRequest = record { - fee_col: Account; - col_ops: Vec + fee_col: opt Account; + fee_ops: Vec } ``` +If `fee_col` is not provided then fee collection reverts to fee burning. + ### 4.2 `icrc107_get_fee_collection` -This method retrieves the currently active fee collection settings, including the `fee_col` account (if set), the list of transaction types (`col_ops`) for which fees are collected, and the block index of the last recorded update. This allows external systems, such as wallets and explorers, to determine how fee collection is configured. +This method retrieves the currently active fee collection settings. Unless they are changed they would apply to the next block added to the ledger. + ``` -icrc107_get_fee_collection: () -> (opt Account, Vec, opt Nat) query; +icrc107_get_fee_collection: () -> (opt Account, Vec) query; ``` -**Note:** The block returned reflects the last block where a change in fee collection information was recorded. However, this block may not contain the most recent settings (as returned in the first part of the response) if no block was created between setting fee collection information and retrieving it. The latest fee collection settings will be recorded in the first block that will be added to the ledger. +`icrc107_get_fee_collection` returns two values: + +* `opt Account` (`fee_col`) – The active fee collector account. If `null`, fees are burned (`fee_burn` = "true"). +* `Vec` (`fee_ops`) – The list of operation types for which fees are collected. If `fee_col` is set but `fee_ops` is empty, no fees are collected and all fees are burned. --- @@ -162,7 +181,7 @@ Any new standard that introduces additional block types **MUST** specify how tho 3. **Fee Collection Rules** - Define new entry types for `fee_col` corresponding to the new block types. - - Specify how `col_ops` should be applied to determine whether fees for the new block types are collected or burned. + - Specify how `fee_ops` should be applied to determine whether fees for the new block types are collected or burned. By ensuring that any future standards explicitly define their interaction with fee collection, ICRC-107 remains a robust and extensible framework. From 19764d9b9911a70ecb2b6ae1ab2af367ad0064ae Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Fri, 28 Feb 2025 10:53:38 +0100 Subject: [PATCH 11/31] minor edits --- ICRCs/ICRC-107/ICRC-107.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 098f6e04..8fbdf4eb 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -16,7 +16,7 @@ ICRC-107 introduces a standardized mechanism for on-chain fee collection, ensuri - A fee collection configuration specifying the collector account (`fee_col`) subject to fees. - A backward-compatible extension to ICRC-3 blocks, allowing fee collection settings to be recorded and modified within ledger history. -- Clear rules governing fee distribution: If `fee_col` is set, fees are transferred from the paying account to the collector; otherwise, they are burned, —meaning they are debited from the paying account and removed from the total supply. +- Clear rules governing fee distribution: If `fee_col` is set, fees are transferred from the paying account to the collector; otherwise, they are burned — meaning they are debited from the paying account and removed from the total supply. By embedding fee collection settings entirely on-chain, ICRC-107 eliminates reliance on off-chain metadata, simplifies integration with wallets and explorers, and ensures full transparency in fee handling. @@ -72,6 +72,7 @@ A block **MAY** include the following fields to define or update fee collection - A list of operation types for which fees **should** be collected. - If omitted, defaults to `["1xfer","2xfer"]` (fees are collected only for transfers). +- If `fee_ops` is explicitly set to an empty list (`[]`), no fees are collected, and all fees are burned. Blocks where `fee_ops` is specified, or `fee_burn` is set to "false" MUST also specify `fee_col`. @@ -152,6 +153,7 @@ type SetFeeCollectionRequest = record { ``` If `fee_col` is not provided then fee collection reverts to fee burning. +The fee collections settings will be recorded in the first block that is created after calling this endpoint. ### 4.2 `icrc107_get_fee_collection` @@ -165,7 +167,9 @@ icrc107_get_fee_collection: () -> (opt Account, Vec) query; `icrc107_get_fee_collection` returns two values: * `opt Account` (`fee_col`) – The active fee collector account. If `null`, fees are burned (`fee_burn` = "true"). -* `Vec` (`fee_ops`) – The list of operation types for which fees are collected. If `fee_col` is set but `fee_ops` is empty, no fees are collected and all fees are burned. +* `Vec` (`fee_ops`) – The list of operation types for which fees are collected. If `fee_col` is set but `fee_ops` is an empty list (`[]`), no fees are collected and all fees are burned. + + --- @@ -197,9 +201,3 @@ record { url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-107.md" } ``` - ---- - -## 7. Summary - -ICRC-107 provides a self-contained, transparent, and interoperable framework for managing transaction fees across ICRC-based ledgers. From 023326efd2ed1fbb3a33f89cf5fb149b2a1ddf73 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Fri, 28 Feb 2025 11:16:28 +0100 Subject: [PATCH 12/31] minor edits --- ICRCs/ICRC-107/ICRC-107.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 8fbdf4eb..d52ef1dc 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -2,7 +2,8 @@ ## 1. Introduction & Motivation -The absence of a unified approach has resulted in inconsistencies in fee collection across ICRC-based ledgers (e.g., ckBTC). In the ckBTC ledger, the fee collector collects fees for transfer operations, while fees for approve operations are burned, demonstrating the need for a unified standard. However, there is no standardized way to: +The lack of a unified approach has led to inconsistencies in fee collection across ICRC-based ledgers (e.g., ckBTC). For instance, in the ckBTC ledger, fees for transfer operations are collected by a designated fee collector, whereas fees for approve operations are burned—even when some fee collection details are present in approve blocks. However, there is no standardized way to: + - Define fee collection rules—who receives fees, which operations are charged, and whether fees are burned. - Record fee collection settings directly on-chain in ledger blocks. From d33d2af8cc591ec8845abe68f16e00dbe508a3bb Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 3 Mar 2025 15:54:48 +0100 Subject: [PATCH 13/31] eliminated fee_burn --- ICRCs/ICRC-107/ICRC-107.md | 64 +++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index d52ef1dc..77d1b7b4 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -29,12 +29,10 @@ By embedding fee collection settings entirely on-chain, ICRC-107 eliminates reli For each block added to the ledger, some party may have to pay a fee. The amount and payer of the fee depend on the specific operation type. -Fee collection settings determine how fees are processed. These settings include: +Fee collection settings determine how fees are processed. These settings consist of: - -- A fee collector account (`fee_col`). If `fee_col` is not set, all fees are burned. Burning occurs by removing the fee from the paying account and reducing the total token supply by that amount. +- A fee collector account (`fee_col`). If `fee_col` has never been set block, all fees are burned. Burning occurs by removing the fee from the paying account and reducing the total token supply by that amount. - A list of operations (`fee_ops`) for which fees are collected. If an operation is not in `fee_ops`, the fee is burned. -- A flag `fee_burn` explicitly determines that all fees are burned. If set to "true", fees are always burned, regardless of `fee_col`. Changes to fee collection settings are recorded on-chain and take effect immediately in the block where they appear. @@ -64,18 +62,12 @@ A block **MAY** include the following fields to define or update fee collection - `principal`: `Blob` — The principal of the fee collector. - `subaccount`: `Blob` (optional) — The subaccount identifier, if applicable. -- **`fee_burn`** (optional, `Text`) - - If `"true"`, fees are burned (removed from supply), irrespective of `fee_col` settings. - - If `"false"`, fees are transferred to `fee_col`. - - Defaults to `"true"` if `fee_col` is unset. - **`fee_ops`** (optional, `Vec`) - - A list of operation types for which fees **should** be collected. - If omitted, defaults to `["1xfer","2xfer"]` (fees are collected only for transfers). -- If `fee_ops` is explicitly set to an empty list (`[]`), no fees are collected, and all fees are burned. + - If `fee_ops` is explicitly set to an empty list (`[]`), no fees are collected, and all fees are burned. -Blocks where `fee_ops` is specified, or `fee_burn` is set to "false" MUST also specify `fee_col`. **Note:** The block format follows the [ICRC-3 standard](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-3.md). @@ -83,7 +75,7 @@ Blocks where `fee_ops` is specified, or `fee_burn` is set to "false" MUST also s ## 3. Handling Fee Collection for ICRC-1 and ICRC-2 Blocks -### 3.1 How to Calculate the Fee for ICRC-1 and ICRC-2 Blocks +### 3.1 Determining the Fee for ICRC-1 and ICRC-2 Blocks Blocks follow the ICRC-3 standard. To determine the **final fee amount** for a block: @@ -94,25 +86,41 @@ Blocks follow the ICRC-3 standard. To determine the **final fee amount** for a b - If `tx.fee` is **not set**, then a top-level field `fee` exists in the block, and that is the fee. -The paying account is the source account for both transfer and approve transactions. +The fee payer is always the source account in both transfer and approve transactions. + + +## **3.2 Determining the Active Fee Settings for a Block** + +To determine the **active fee settings** (who receives the fee and whether it is collected or burned), follow this algorithm: -### 3.2 How to Determine the Fee Collector for a Block +1. **Find the most recent block (including the current block) that defines `fee_col`** + - If `fee_col = []`, all fees in the block are **burned**, and `fee_ops` is ignored. + - Otherwise, use this value as the **active fee collector** (`active_fee_col`). + - If no `fee_col` is found in any prior block, all fees **are burned by default**. -Find the most recent block where fee collection settings are present. -* If no such block exists, the fees in the current block are burned. -* If the most recent block with fee settings has `fee_burn` = "true", then fees in the current block are burned. -* Otherwise, the active fee collector is the account specified by `fee_col`, and fees are collected for the operations listed in `fee_ops` (see the next section). +2. **Determine `fee_ops` from the same block where `fee_col` was set** + - If `fee_ops` is set in that block, use it as the **active fee_ops**. + - If `fee_ops` is **not set** in that block, **default to `["1xfer", "2xfer"]`**, meaning only transfer operations collect fees. +3. **Determine whether the fee should be collected or burned** + - Identify the **block type**: + - If `btype` is present, use it. + - Otherwise, infer from `tx.op`. + - If the **block type is in `fee_ops`**, fees are **collected** by `active_fee_col`. + - Otherwise, fees are **burned**. -### 3.3 How `fee_ops` Determines Fee Collection for a Block -The `fee_ops` field defines which block types incur fees that are collected instead of burned. For ICRC-1 and ICRC-2 blocks, `fee_ops` entries map to block types as follows: -| **fee_ops Entry** | **Block Types Affected** | -|-------------------|--------------------------| -| "1xfer" | Blocks with `btype = "1xfer"` and blocks where `btype` is not set and `tx.op = "xfer"` and there is no `tx.spender` field | -| "2xfer" | Blocks with `btype = "2xfer"` and blocks where `btype` is not set and `tx.op = "xfer"` and the field `tx.spender` is set. | -| "2approve" | Blocks with `btype = "2approve"` and blocks where `btype` is not set and `tx.op = "approve"`. | + ## **3.3 Mapping `fee_ops` to Block Types** + + The `fee_ops` field specifies which block types **have their fees collected** instead of burned. For ICRC-1 and ICRC-2 blocks, `fee_ops` maps to block types as follows: + + | **fee_ops Entry** | **Block Types Affected** | + |-------------------|--------------------------| + | `"1xfer"` | Blocks with `btype = "1xfer"` or where `btype` is not set, `tx.op = "xfer"`, and `tx.spender` is **not** set. | + | `"2xfer"` | Blocks with `btype = "2xfer"` or where `btype` is not set, `tx.op = "xfer"`, and `tx.spender` **is** set. | + | `"2approve"` | Blocks with `btype = "2approve"` or where `btype` is not set and `tx.op = "approve"`. | + #### Key Rules: @@ -124,7 +132,6 @@ The `fee_ops` field defines which block types incur fees that are collected inst - If the block type corresponds to an entry in `fee_ops`, the fee is collected by `fee_col`. - Otherwise, the fee is burned. -If `fee_col` is set but `fee_ops` is not, `fee_ops` defaults to `["1xfer, 2xfer"]`, i.e. fees are only collected for transfer operations. --- @@ -167,10 +174,9 @@ icrc107_get_fee_collection: () -> (opt Account, Vec) query; `icrc107_get_fee_collection` returns two values: -* `opt Account` (`fee_col`) – The active fee collector account. If `null`, fees are burned (`fee_burn` = "true"). -* `Vec` (`fee_ops`) – The list of operation types for which fees are collected. If `fee_col` is set but `fee_ops` is an empty list (`[]`), no fees are collected and all fees are burned. - +* `opt Account` (`fee_col`) – The active fee collector account. If `null`, fees are burned (this corresponds to `fee_col=[]` on-chain). +* `Vec` (`fee_ops`) – The list of operation types for which fees are collected. If `fee_ops=[]` no fees are collected. This is equivalent to `fee_col=[]`, meaning all fees are burned. --- From 199280be01910660a96662399783c7ddaa9ffaaa Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:04:28 +0100 Subject: [PATCH 14/31] minor changes --- ICRCs/ICRC-107/ICRC-107.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 77d1b7b4..5731494d 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -31,7 +31,7 @@ For each block added to the ledger, some party may have to pay a fee. The amount Fee collection settings determine how fees are processed. These settings consist of: -- A fee collector account (`fee_col`). If `fee_col` has never been set block, all fees are burned. Burning occurs by removing the fee from the paying account and reducing the total token supply by that amount. +- A fee collector account (`fee_col`). If `fee_col` has never been set in any block, fees are burned by default. Burning occurs by removing the fee from the paying account and reducing the total token supply by that amount. - A list of operations (`fee_ops`) for which fees are collected. If an operation is not in `fee_ops`, the fee is burned. Changes to fee collection settings are recorded on-chain and take effect immediately in the block where they appear. @@ -174,9 +174,13 @@ icrc107_get_fee_collection: () -> (opt Account, Vec) query; `icrc107_get_fee_collection` returns two values: -* `opt Account` (`fee_col`) – The active fee collector account. If `null`, fees are burned (this corresponds to `fee_col=[]` on-chain). +* `opt Account` (`fee_col`) – The active fee collector account. + - If `null`, fees are burned (this corresponds to `fee_col=[]` on-chain). + - Otherwise, fees are collected by the returned `Account` -* `Vec` (`fee_ops`) – The list of operation types for which fees are collected. If `fee_ops=[]` no fees are collected. This is equivalent to `fee_col=[]`, meaning all fees are burned. +* `Vec` (`fee_ops`) – The list of operation types for which fees are collected. + - If `fee_ops=[]`, no fees are collected, and all fees are burned. This is equivalent to `fee_col=[]`, meaning that all transactions incur a fee that is removed from the supply. + - The API **does not apply a default** — it always returns the explicitly set value. The defaulting behavior (`["1xfer", "2xfer"]`) applies only at the block processing level when `fee_ops` is missing from a block. --- From 86b676a142ad53f3cbb8cad2c0b51cb92df8441b Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:33:45 +0100 Subject: [PATCH 15/31] clarified what fee_ops = [] means --- ICRCs/ICRC-107/ICRC-107.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 5731494d..a0e0d23e 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -51,10 +51,12 @@ variant { } ``` -- The **first Blob** represents the **principal** of the fee collector; if this is the anonymous principal (0x04) then fees for all operations are burned. +- The **first Blob** represents the **principal** of the fee collector. - The **second Blob** represents the **subaccount**, or a zeroed-out Blob if no subaccount is used. - -This ensures that existing tools and explorers relying on the current `fee_col` format remain functional. +- Burning fees (`fee_col = []`): + * Fees are burned if `fee_col` is not set in any prior vlock. + * To explicitly indicate burning, set `fee_col` to `variant {Array = vec {} }`, meaning the array is empty. + * This ensures that existing tools and explorers relying on the current `fee_col` format remain functional. A block **MAY** include the following fields to define or update fee collection settings: @@ -89,7 +91,7 @@ Blocks follow the ICRC-3 standard. To determine the **final fee amount** for a b The fee payer is always the source account in both transfer and approve transactions. -## **3.2 Determining the Active Fee Settings for a Block** +### **3.2 Determining the Active Fee Settings for a Block** To determine the **active fee settings** (who receives the fee and whether it is collected or burned), follow this algorithm: @@ -110,8 +112,7 @@ To determine the **active fee settings** (who receives the fee and whether it is - Otherwise, fees are **burned**. - - ## **3.3 Mapping `fee_ops` to Block Types** +### **3.3 Mapping `fee_ops` to Block Types** The `fee_ops` field specifies which block types **have their fees collected** instead of burned. For ICRC-1 and ICRC-2 blocks, `fee_ops` maps to block types as follows: From e6e4122c373f6d034b3a8c59d90e90460fb665cc Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Thu, 6 Mar 2025 14:06:49 +0100 Subject: [PATCH 16/31] new summary --- ICRCs/ICRC-107/ICRC-107.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index a0e0d23e..ec5acf68 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -5,7 +5,7 @@ The lack of a unified approach has led to inconsistencies in fee collection across ICRC-based ledgers (e.g., ckBTC). For instance, in the ckBTC ledger, fees for transfer operations are collected by a designated fee collector, whereas fees for approve operations are burned—even when some fee collection details are present in approve blocks. However, there is no standardized way to: -- Define fee collection rules—who receives fees, which operations are charged, and whether fees are burned. +- Define fee collection rules — who receives fees, which operations are charged, and whether fees are burned. - Record fee collection settings directly on-chain in ledger blocks. - Provide consistent semantics for wallets, explorers, and other integrations to interpret fee structures. @@ -15,12 +15,13 @@ ICRC-107 extends **ICRC-3**, adding semantics for fee collection while ensuring ICRC-107 introduces a standardized mechanism for on-chain fee collection, ensuring clarity and interoperability across different ICRC-based ledgers. It defines: -- A fee collection configuration specifying the collector account (`fee_col`) subject to fees. -- A backward-compatible extension to ICRC-3 blocks, allowing fee collection settings to be recorded and modified within ledger history. -- Clear rules governing fee distribution: If `fee_col` is set, fees are transferred from the paying account to the collector; otherwise, they are burned — meaning they are debited from the paying account and removed from the total supply. +- A fee collection configuration specifying the collector account (icrc107_fee_col), which applies to all subsequent blocks. -By embedding fee collection settings entirely on-chain, ICRC-107 eliminates reliance on off-chain metadata, simplifies integration with wallets and explorers, and ensures full transparency in fee handling. +- A new block type for setting `icrc107_fee_col` to designate the fee collector. + +- A backward-compatible mechanism where, if `icrc107_fee_col` has never been set, legacy fee_col logic applies. +By embedding fee collection settings entirely on-chain, ICRC-107 eliminates reliance on off-chain metadata, simplifies integration with wallets and explorers, and ensures full transparency in fee handling. --- ## 2. Fee Collection @@ -55,7 +56,7 @@ variant { - The **second Blob** represents the **subaccount**, or a zeroed-out Blob if no subaccount is used. - Burning fees (`fee_col = []`): * Fees are burned if `fee_col` is not set in any prior vlock. - * To explicitly indicate burning, set `fee_col` to `variant {Array = vec {} }`, meaning the array is empty. + * To explicitly indicate burning, set `fee_col` to `variant {Array = vec {} }`, meaning the array is empty. * This ensures that existing tools and explorers relying on the current `fee_col` format remain functional. A block **MAY** include the following fields to define or update fee collection settings: From 4654feb04089c1355984e6a2043883dc396301ac Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Fri, 7 Mar 2025 18:13:42 +0100 Subject: [PATCH 17/31] included examples, legacy fee collection logic, compliance reporting --- ICRCs/ICRC-107/ICRC-107.md | 257 ++++++++++++++++++------------------- 1 file changed, 124 insertions(+), 133 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index ec5acf68..02e5b725 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -2,215 +2,206 @@ ## 1. Introduction & Motivation -The lack of a unified approach has led to inconsistencies in fee collection across ICRC-based ledgers (e.g., ckBTC). For instance, in the ckBTC ledger, fees for transfer operations are collected by a designated fee collector, whereas fees for approve operations are burned—even when some fee collection details are present in approve blocks. However, there is no standardized way to: +Different ICRC-based ledgers (e.g., ckBTC) handle fee collection inconsistently. Some ledgers assign a designated fee collector for transfer operations, while others burn fees for approve operations—even when fee collection details appear in approve blocks. This inconsistency leads to challenges in defining fee collection rules, recording fee settings on-chain, and ensuring clear semantics for wallets and explorers. ICRC-107 builds on the ICRC-3 block schema, introducing a new block type (`107feecol`) to configure fee collection settings on-chain. This ensures compatibility with existing ICRC-3 implementations while extending functionality for fee handling. +By embedding fee collection settings on-chain, ICRC-107 provides the following benefits: +- Transparency: Fee collection rules are visible and auditable on the ledger. -- Define fee collection rules — who receives fees, which operations are charged, and whether fees are burned. -- Record fee collection settings directly on-chain in ledger blocks. -- Provide consistent semantics for wallets, explorers, and other integrations to interpret fee structures. +- Interoperability: Wallets and explorers can rely on consistent fee handling across ICRC-based ledgers. -ICRC-107 extends **ICRC-3**, adding semantics for fee collection while ensuring full compatibility with the existing block format. This proposal eliminates reliance on off-chain metadata, simplifies integration with wallets and explorers, and ensures full transparency in fee handling. +- Simplified Integration: Eliminates the need for off-chain metadata to determine fee collection behavior -### ICRC-107 Proposal +ICRC-107 applies to all ICRC-based ensuring consistent fee collection behavior across transfers and approvals. This standard is backward-compatible with existing ICRC-based ledgers, allowing them to transition gradually to the new fee collection mechanism, while documenting legacy fee collection logic. -ICRC-107 introduces a standardized mechanism for on-chain fee collection, ensuring clarity and interoperability across different ICRC-based ledgers. It defines: +### ICRC-107 Proposal -- A fee collection configuration specifying the collector account (icrc107_fee_col), which applies to all subsequent blocks. +ICRC-107 establishes an on-chain fee collection mechanism that ensures clarity and interoperability across different ICRC-based ledgers. It defines: +- A fee collection configuration that specifies the collector account (`icrc107_fee_col`), applying to all subsequent blocks. - A new block type for setting `icrc107_fee_col` to designate the fee collector. +- A backward-compatible mechanism where, if `icrc107_fee_col` has never been set, legacy `fee_col` logic applies. -- A backward-compatible mechanism where, if `icrc107_fee_col` has never been set, legacy fee_col logic applies. +By embedding fee collection settings entirely on-chain, ICRC-107 ensures transparency and simplifies integration with external tools. -By embedding fee collection settings entirely on-chain, ICRC-107 eliminates reliance on off-chain metadata, simplifies integration with wallets and explorers, and ensures full transparency in fee handling. --- ## 2. Fee Collection ### 2.1 Overview -For each block added to the ledger, some party may have to pay a fee. The amount and payer of the fee depend on the specific operation type. - -Fee collection settings determine how fees are processed. These settings consist of: +Each block requires a fee, which a designated party must pay based on the operation type. -- A fee collector account (`fee_col`). If `fee_col` has never been set in any block, fees are burned by default. Burning occurs by removing the fee from the paying account and reducing the total token supply by that amount. -- A list of operations (`fee_ops`) for which fees are collected. If an operation is not in `fee_ops`, the fee is burned. +The fee collection configuration controls how the ledger processes fees. This configuration consists of a single global fee collector account (`icrc107_fee_col`). When a block updates this setting, the change takes effect immediately. -Changes to fee collection settings are recorded on-chain and take effect immediately in the block where they appear. +- If `icrc107_fee_col` is set to a ledger account, that account collects all subsequent fees. +- If `icrc107_fee_col` is set to the empty account (see below), the ledger burns all subsequent fees. +- The most recent `icrc107_fee_col` setting applies to each block. +- By default, the ledger burns all block fees until the first `icrc107_fee_col` setting is applied. If `icrc107_fee_col` has never been set, the ledger follows legacy `fee_col` logic (see Section 5). +- A **fee collector configuration block** records these settings on-chain, ensuring transparent fee collection. -### 2.2 Fee Collection Settings +Once `icrc107_fee_col` is set, it overrides any legacy fee collection logic may be in place (See Section 5). -**Backwards Compatibility:** -To ensure compatibility with existing ICRC-3 implementations, `fee_col` **MUST** be recorded using the existing array format: - -``` -fee_col; -variant { - Array = vec { - variant { Blob = principal_blob }; - variant { Blob = subaccount_blob }; - } -} -``` +This standard uses the Account representation defined in ICRC-3, as an array of blobs. +The empty account is represented as an empty array (`[]`) in the `icrc107_fee_col` field. The empty account (`[]`) is a reserved value that explicitly indicates fee burning, ensuring unambiguous interpretation across implementations. +When `icrc107_fee_col` is set to the empty account, all subsequent fees are burned. This ensures a clear and unambiguous mechanism for fee burning. -- The **first Blob** represents the **principal** of the fee collector. -- The **second Blob** represents the **subaccount**, or a zeroed-out Blob if no subaccount is used. -- Burning fees (`fee_col = []`): - * Fees are burned if `fee_col` is not set in any prior vlock. - * To explicitly indicate burning, set `fee_col` to `variant {Array = vec {} }`, meaning the array is empty. - * This ensures that existing tools and explorers relying on the current `fee_col` format remain functional. +### 2.2 ICRC-107 Block Schema -A block **MAY** include the following fields to define or update fee collection settings: +ICRC-107 introduces a new block type that follows the ICRC-3 specification to configure `icrc107_fee_col`. -- **`fee_col`** (optional, `Map`) - - `principal`: `Blob` — The principal of the fee collector. - - `subaccount`: `Blob` (optional) — The subaccount identifier, if applicable. +In addition to the ICRC-3 specific requirements, a fee collector configuration block: +- **MUST** contain a field `btype` with value `"107feecol"`. +- **MUST** contain a field `icrc107_fee_col` of type `Array`, specifying the owner and, optionally, a subaccount, following the ICRC-3 standard. -- **`fee_ops`** (optional, `Vec`) - - A list of operation types for which fees **should** be collected. - - If omitted, defaults to `["1xfer","2xfer"]` (fees are collected only for transfers). - - If `fee_ops` is explicitly set to an empty list (`[]`), no fees are collected, and all fees are burned. +#### Example Block +Below is an example of a valid **fee collector configuration block** that sets the fee collector to a specific account: -**Note:** The block format follows the [ICRC-3 standard](https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-3.md). +``` +variant { Map = vec { + // Block type: indicates this is a fee collector configuration block + record { "btype"; variant { Text = "107feecol" }}; + // Fee collector account: specifies the account that will collect fees + record { "icrc107_fee_col"; variant { Array = vec { + variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01"}; // Owner principal + variant { Blob = blob "\06\ec\cd\3a\97\fb\a8\5f\bc\8d\a3\3e\5d\ba\bc\2f\38\69\60\5d\c7\a1\c9\53\1f\70\a3\66\c5\a7\e4\21" }; // Subaccount + }} }; + // Timestamp: indicates when the block was created + record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; + // Parent hash: links this block to the previous block in the chain + record { "phash"; + variant { + Blob = blob "\d5\c7\eb\57\a2\4e\fa\d4\8b\d1\cc\54\9e\49\c6\9f\d1\93\8d\e8\02\d4\60\65\e2\f2\3c\00\04\3b\2e\51" + }}; +}}; +``` ---- +A block that unsets the fee collector, i.e. from this point onward all fees are burned: -## 3. Handling Fee Collection for ICRC-1 and ICRC-2 Blocks +``` +variant { Map = vec { + record { "btype"; variant { Text = "107feecol" }}; + record { "icrc107_fee_col"; variant { Array = vec { + }}}; + record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; + record { "phash"; + variant { + Blob = blob "\2d\86\7f\34\c7\2d\1e\2d\00\84\10\a4\00\b0\b6\4c\3e\02\96\c9\e8\55\6f\dd\72\68\e8\df\8d\8e\8a\ee" + }}; +}}; +``` -### 3.1 Determining the Fee for ICRC-1 and ICRC-2 Blocks -Blocks follow the ICRC-3 standard. To determine the **final fee amount** for a block: -1. **Check `tx.fee`** - - If present, `tx.fee` is the fee for this block. +--- -2. **Else, check the top-level `fee` field** - - If `tx.fee` is **not set**, then a top-level field `fee` exists in the block, and that is the fee. +# ICRC-107: Methods for Setting and Getting Fee Collection +## 3. Methods for Setting and Getting Fee Collection -The fee payer is always the source account in both transfer and approve transactions. +### 3.1 `icrc107_set_fee_collection` +This method allows a ledger controller to update the fee collection settings. It modifies the `icrc107_fee_col` account, which determines where collected fees are sent. The updated settings are recorded in the next block added to the ledger. -### **3.2 Determining the Active Fee Settings for a Block** +``` +icrc107_set_fee_collection: (SetFeeCollectionRequest) -> (); +``` -To determine the **active fee settings** (who receives the fee and whether it is collected or burned), follow this algorithm: +#### Request Type: +``` +type Account = record { + owner: principal; + subaccount: opt blob; +}; -1. **Find the most recent block (including the current block) that defines `fee_col`** - - If `fee_col = []`, all fees in the block are **burned**, and `fee_ops` is ignored. - - Otherwise, use this value as the **active fee collector** (`active_fee_col`). - - If no `fee_col` is found in any prior block, all fees **are burned by default**. +type SetFeeCollectionRequest = record { + icrc107_fee_col: opt Account; +}; +``` -2. **Determine `fee_ops` from the same block where `fee_col` was set** - - If `fee_ops` is set in that block, use it as the **active fee_ops**. - - If `fee_ops` is **not set** in that block, **default to `["1xfer", "2xfer"]`**, meaning only transfer operations collect fees. +This method MUST only be callable by the ledger controller or authorized principals. Unauthorized calls SHOULD result in an error. -3. **Determine whether the fee should be collected or burned** - - Identify the **block type**: - - If `btype` is present, use it. - - Otherwise, infer from `tx.op`. - - If the **block type is in `fee_ops`**, fees are **collected** by `active_fee_col`. - - Otherwise, fees are **burned**. +- If `icrc107_fee_col` is set to an account, all subsequent fees are collected by that account. +- If `icrc107_fee_col` is not provided (or set to `null`), all subsequent fees are burned. +The `icrc107_set_fee_collection` method MUST return an error in the following cases: -### **3.3 Mapping `fee_ops` to Block Types** +- The caller is not authorized to modify fee collection settings. +- The provided `Account` is invalid (e.g., malformed principal or subaccount)." - The `fee_ops` field specifies which block types **have their fees collected** instead of burned. For ICRC-1 and ICRC-2 blocks, `fee_ops` maps to block types as follows: - | **fee_ops Entry** | **Block Types Affected** | - |-------------------|--------------------------| - | `"1xfer"` | Blocks with `btype = "1xfer"` or where `btype` is not set, `tx.op = "xfer"`, and `tx.spender` is **not** set. | - | `"2xfer"` | Blocks with `btype = "2xfer"` or where `btype` is not set, `tx.op = "xfer"`, and `tx.spender` **is** set. | - | `"2approve"` | Blocks with `btype = "2approve"` or where `btype` is not set and `tx.op = "approve"`. | +### 3.2 `icrc107_get_fee_collection` +This method retrieves the currently active fee collection settings. Unless changed, these settings apply to the next block added to the ledger. -#### Key Rules: +``` +icrc107_get_fee_collection: () -> (opt Account) query; +``` -1. **Determine Block Type** - - If `btype` is present, it takes precedence. - - Otherwise, use `tx.op` to determine the block type. -2. **Check if `fee_ops` applies to the block type** - - If the block type corresponds to an entry in `fee_ops`, the fee is collected by `fee_col`. - - Otherwise, the fee is burned. +This method should return the currently active fee collection settings: + - If the response is `null`, fees are burned + - If the response is a valid `Account`, fees are collected by that account. --- -## 4. Minimal API for Fee Collection Settings -### 4.1 `icrc107_set_fee_collection` +## 4. Reporting Compliance -This method allows a ledger controller to update the fee collection settings. It modifies the `fee_col` account, which determines where collected fees are sent, and updates the `fee_ops` list, which specifies the transaction types for which fees should be collected. The updated settings are recorded in the next block added to the ledger. +Compliance with the current standard is reported as follows. -``` -icrc107_set_fee_collection: (SetFeeCollectionRequest) -> (); -``` +### 4.1 Supported Standards -with +Ledgers implementing ICRC-107 **MUST** indicate their compliance through the `icrc1_supported_standards` and `icrc10_supported_standards` methods, by including in the output of these methods: ``` -type Account = record { - owner: principal; - subaccount: opt blob; -}; - -type SetFeeCollectionRequest = record { - fee_col: opt Account; - fee_ops: Vec -} +variant { Vec = vec { + record { + "name"; variant { Text = "ICRC-107" }; + "url"; variant { Text = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-107.md" }; + } +}}; ``` -If `fee_col` is not provided then fee collection reverts to fee burning. -The fee collections settings will be recorded in the first block that is created after calling this endpoint. - +### 4.2 Supported Block Types -### 4.2 `icrc107_get_fee_collection` - -This method retrieves the currently active fee collection settings. Unless they are changed they would apply to the next block added to the ledger. +Ledgers implementing ICRC-107 **MUST** report the new block type in response to `icrc3_supported_block_types`. The output of the call must include ``` -icrc107_get_fee_collection: () -> (opt Account, Vec) query; +variant { Vec = vec { + record { "name"; variant { Text = "107feecol" }; "url"; variant { Text = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-107.md" } }; +}}; ``` -`icrc107_get_fee_collection` returns two values: - -* `opt Account` (`fee_col`) – The active fee collector account. - - If `null`, fees are burned (this corresponds to `fee_col=[]` on-chain). - - Otherwise, fees are collected by the returned `Account` +## 5. Legacy Fee Collection Mechanisms -* `Vec` (`fee_ops`) – The list of operation types for which fees are collected. - - If `fee_ops=[]`, no fees are collected, and all fees are burned. This is equivalent to `fee_col=[]`, meaning that all transactions incur a fee that is removed from the supply. - - The API **does not apply a default** — it always returns the explicitly set value. The defaulting behavior (`["1xfer", "2xfer"]`) applies only at the block processing level when `fee_ops` is missing from a block. +The Dfinity maintained ICRC ledgers include a fee colelction mechanism which, for completeness is described below. ---- +### 5.1 Legacy Behavior (`fee_col`) -## 5. Interaction with Future Standards +- If `fee_col` is set in a block, the designated account collects **only transfer fees** (`1xfer` and `2xfer`) from that block onward. +- Other fees (e.g., `2approve`) are burned. +- If `icrc107_fee_col` is not set, the ledger follows this legacy behavior, using `fee_col` only for transfers. -Any new standard that introduces additional block types **MUST** specify how those block types interact with the fee collection mechanism defined in **ICRC-107**. Specifically: -1. **Fee Applicability** - - Clarify which new block types, if any, are subject to fee collection. +New implementations SHOULD avoid using fee_col and instead use icrc107_fee_col for all fee collection settings. Legacy behavior is provided for backward compatibility only and MAY be deprecated in future versions of this standard. -2. **Fee Payer Determination** - - Define how the fee amount is determined and who is responsible for paying the fee. +### 5.2 Handling Fee Collection for ICRC-1 and ICRC-2 Blocks -3. **Fee Collection Rules** - - Define new entry types for `fee_col` corresponding to the new block types. - - Specify how `fee_ops` should be applied to determine whether fees for the new block types are collected or burned. +To determine who collects the fee in a block: -By ensuring that any future standards explicitly define their interaction with fee collection, ICRC-107 remains a robust and extensible framework. +1. Check for fee collection configuration + - If a previous block set `icrc107_fee_col`, the ledger uses the behavior specified by this standard. ---- -## 6. Reporting Compliance with ICRC-Supported Standards +2. If no `icrc107_fee_col` setting exists (legacy behavior): -Ledgers implementing ICRC-107 **MUST** indicate their compliance through the `icrc1_supported_standards` and `icrc10_supported_standards` methods, as defined in ICRC-1 and ICRC-10 by including the following record in the output of these methods: + - If the block is of type `2approve` then the fee is burned or no `fee_col` + - If the block is a transfer block, i.e. of type `1xfer` or `2xfer`: + - If `fee_col` is specified in the block the fee is collected by `fee_col`. + - If `fee_col_block` is specified in the block the fee is collected by the account specified in the `fee_col` field of the block with index `fee_col_block`. -``` -record { - name = "ICRC-107"; - url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-107.md" -} -``` +--- From 06e6d5dd6628752fb2b5a7e870fd2a8b80144b01 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:06:01 +0100 Subject: [PATCH 18/31] one more pass --- ICRCs/ICRC-107/ICRC-107.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 02e5b725..50dd65a8 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -9,9 +9,9 @@ By embedding fee collection settings on-chain, ICRC-107 provides the following b - Interoperability: Wallets and explorers can rely on consistent fee handling across ICRC-based ledgers. -- Simplified Integration: Eliminates the need for off-chain metadata to determine fee collection behavior +- Simplified Integration: Eliminates the need for off-chain metadata to determine fee collection behavior. -ICRC-107 applies to all ICRC-based ensuring consistent fee collection behavior across transfers and approvals. This standard is backward-compatible with existing ICRC-based ledgers, allowing them to transition gradually to the new fee collection mechanism, while documenting legacy fee collection logic. +ICRC-107 applies to all ICRC-based ledgers ensuring consistent fee collection behavior across transfers and approvals. This standard is backward-compatible with existing ICRC-based ledgers, allowing them to transition gradually to the new fee collection mechanism, while documenting legacy fee collection logic. ### ICRC-107 Proposal @@ -41,9 +41,7 @@ The fee collection configuration controls how the ledger processes fees. This co Once `icrc107_fee_col` is set, it overrides any legacy fee collection logic may be in place (See Section 5). -This standard uses the Account representation defined in ICRC-3, as an array of blobs. -The empty account is represented as an empty array (`[]`) in the `icrc107_fee_col` field. The empty account (`[]`) is a reserved value that explicitly indicates fee burning, ensuring unambiguous interpretation across implementations. -When `icrc107_fee_col` is set to the empty account, all subsequent fees are burned. This ensures a clear and unambiguous mechanism for fee burning. +The empty account is represented as an empty array (`[]`) in the `icrc107_fee_col` field and it is a reserved value that explicitly indicates fee burning, ensuring unambiguous interpretation across implementations. ### 2.2 ICRC-107 Block Schema @@ -168,7 +166,7 @@ variant { Vec = vec { ### 4.2 Supported Block Types -Ledgers implementing ICRC-107 **MUST** report the new block type in response to `icrc3_supported_block_types`. The output of the call must include +Ledgers implementing ICRC-107 **MUST** report the new block type in response to `icrc3_supported_block_types`. The output of the call must include ``` variant { Vec = vec { @@ -178,7 +176,7 @@ variant { Vec = vec { ## 5. Legacy Fee Collection Mechanisms -The Dfinity maintained ICRC ledgers include a fee colelction mechanism which, for completeness is described below. +The Dfinity maintained ICRC ledgers include a fee collection mechanism which, for completeness is described below. ### 5.1 Legacy Behavior (`fee_col`) @@ -186,8 +184,7 @@ The Dfinity maintained ICRC ledgers include a fee colelction mechanism which, fo - Other fees (e.g., `2approve`) are burned. - If `icrc107_fee_col` is not set, the ledger follows this legacy behavior, using `fee_col` only for transfers. - -New implementations SHOULD avoid using fee_col and instead use icrc107_fee_col for all fee collection settings. Legacy behavior is provided for backward compatibility only and MAY be deprecated in future versions of this standard. +New implementations SHOULD avoid using fee_col and instead use `icrc107_fee_col` for all fee collection settings. Legacy behavior is provided for backward compatibility only and MAY be deprecated in future versions of this standard. ### 5.2 Handling Fee Collection for ICRC-1 and ICRC-2 Blocks @@ -202,6 +199,6 @@ To determine who collects the fee in a block: - If the block is of type `2approve` then the fee is burned or no `fee_col` - If the block is a transfer block, i.e. of type `1xfer` or `2xfer`: - If `fee_col` is specified in the block the fee is collected by `fee_col`. - - If `fee_col_block` is specified in the block the fee is collected by the account specified in the `fee_col` field of the block with index `fee_col_block`. + - If `fee_col_block` is specified use the `fee_col` from the referenced block index. --- From eb7d6a4c4228621e0593e6fa473c9b9ba2530c6f Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:44:15 +0100 Subject: [PATCH 19/31] further clarifications on legacy behaviour --- ICRCs/ICRC-107/ICRC-107.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 50dd65a8..019c4917 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -41,7 +41,8 @@ The fee collection configuration controls how the ledger processes fees. This co Once `icrc107_fee_col` is set, it overrides any legacy fee collection logic may be in place (See Section 5). -The empty account is represented as an empty array (`[]`) in the `icrc107_fee_col` field and it is a reserved value that explicitly indicates fee burning, ensuring unambiguous interpretation across implementations. +Fee burning is explicitly recorded on-chain by setting `icrc_107_fee_col = variant { Array = vec {} }`. This ensures unambiguous representation accross implementations. + ### 2.2 ICRC-107 Block Schema @@ -51,6 +52,7 @@ In addition to the ICRC-3 specific requirements, a fee collector configuration b - **MUST** contain a field `btype` with value `"107feecol"`. - **MUST** contain a field `icrc107_fee_col` of type `Array`, specifying the owner and, optionally, a subaccount, following the ICRC-3 standard. +- To explicitly burn fees, `icrc107_fee_col` **MUST** be set to `variant { Array = vec {}}`. #### Example Block @@ -75,7 +77,7 @@ variant { Map = vec { }}; ``` -A block that unsets the fee collector, i.e. from this point onward all fees are burned: +A block that explicitly sets fee burning by removing the fee collector (i.e., all fees are burned from this point onward): ``` variant { Map = vec { @@ -140,9 +142,10 @@ icrc107_get_fee_collection: () -> (opt Account) query; This method should return the currently active fee collection settings: - - If the response is `null`, fees are burned - - If the response is a valid `Account`, fees are collected by that account. + - If the response is `null`, fees are burned. This corresponds to `icrc107_fee_col = variant { Array = vec {}}` on-chain. + - If the response is a valid `Account`, fees are collected by that account. This corresponds to `icrc_107_fee_col` being set to the ICRC-3 representation of the account on-chain. +This method strictly returns the last explicitly set value of `icrc107_fee_col`. It does not infer defaults, and if no fee collector was ever set, it returns null. --- @@ -174,14 +177,13 @@ variant { Vec = vec { }}; ``` -## 5. Legacy Fee Collection Mechanisms +## 5. Note on Legacy Fee Collection Mechanisms The Dfinity maintained ICRC ledgers include a fee collection mechanism which, for completeness is described below. ### 5.1 Legacy Behavior (`fee_col`) -- If `fee_col` is set in a block, the designated account collects **only transfer fees** (`1xfer` and `2xfer`) from that block onward. -- Other fees (e.g., `2approve`) are burned. +- If `fee_col` is set in a block, the designated account collects only transfer fees (`1xfer`, `2xfer`). Fees for all other operations (e.g., `2approve`) were always burned in legacy behavior - If `icrc107_fee_col` is not set, the ledger follows this legacy behavior, using `fee_col` only for transfers. New implementations SHOULD avoid using fee_col and instead use `icrc107_fee_col` for all fee collection settings. Legacy behavior is provided for backward compatibility only and MAY be deprecated in future versions of this standard. From d3accbf0c778115b7cd99f1bfea58f6de1dc99f6 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:57:11 +0100 Subject: [PATCH 20/31] typos --- ICRCs/ICRC-107/ICRC-107.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 019c4917..9d2aa862 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -41,7 +41,7 @@ The fee collection configuration controls how the ledger processes fees. This co Once `icrc107_fee_col` is set, it overrides any legacy fee collection logic may be in place (See Section 5). -Fee burning is explicitly recorded on-chain by setting `icrc_107_fee_col = variant { Array = vec {} }`. This ensures unambiguous representation accross implementations. +Fee burning is explicitly recorded on-chain by setting `icrc107_fee_col = variant { Array = vec {} }`. This ensures unambiguous representation accross implementations. ### 2.2 ICRC-107 Block Schema @@ -143,9 +143,9 @@ icrc107_get_fee_collection: () -> (opt Account) query; This method should return the currently active fee collection settings: - If the response is `null`, fees are burned. This corresponds to `icrc107_fee_col = variant { Array = vec {}}` on-chain. - - If the response is a valid `Account`, fees are collected by that account. This corresponds to `icrc_107_fee_col` being set to the ICRC-3 representation of the account on-chain. + - If the response is a valid `Account`, fees are collected by that account. This corresponds to `icrc107_fee_col` being set to the ICRC-3 representation of the account on-chain. -This method strictly returns the last explicitly set value of `icrc107_fee_col`. It does not infer defaults, and if no fee collector was ever set, it returns null. +This method strictly returns the last explicitly set value of `icrc107_fee_col`. It does not infer defaults, and if no fee collector was ever set, it returns `opt Account = null`. --- @@ -183,7 +183,7 @@ The Dfinity maintained ICRC ledgers include a fee collection mechanism which, fo ### 5.1 Legacy Behavior (`fee_col`) -- If `fee_col` is set in a block, the designated account collects only transfer fees (`1xfer`, `2xfer`). Fees for all other operations (e.g., `2approve`) were always burned in legacy behavior +- If `fee_col` is set in a block, the designated account collects only transfer fees (`1xfer`, `2xfer`). Fees for all other operations (e.g., `2approve`) were always burned in legacy behavior as implemented in Dfinity-maintained ICRC-3 ledgers. - If `icrc107_fee_col` is not set, the ledger follows this legacy behavior, using `fee_col` only for transfers. New implementations SHOULD avoid using fee_col and instead use `icrc107_fee_col` for all fee collection settings. Legacy behavior is provided for backward compatibility only and MAY be deprecated in future versions of this standard. From 12eb7caaba8d4f30a95700e93b34421c898405e2 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 10 Mar 2025 12:19:46 +0100 Subject: [PATCH 21/31] typo --- ICRCs/ICRC-107/ICRC-107.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 9d2aa862..a37dd6d4 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -39,9 +39,9 @@ The fee collection configuration controls how the ledger processes fees. This co - By default, the ledger burns all block fees until the first `icrc107_fee_col` setting is applied. If `icrc107_fee_col` has never been set, the ledger follows legacy `fee_col` logic (see Section 5). - A **fee collector configuration block** records these settings on-chain, ensuring transparent fee collection. -Once `icrc107_fee_col` is set, it overrides any legacy fee collection logic may be in place (See Section 5). +Once `icrc107_fee_col` is set, it overrides any legacy fee collection logic that may be in place (See Section 5). -Fee burning is explicitly recorded on-chain by setting `icrc107_fee_col = variant { Array = vec {} }`. This ensures unambiguous representation accross implementations. +Fee burning is explicitly recorded on-chain by setting `icrc107_fee_col = variant { Array = vec {} }`. This ensures unambiguous representation across implementations. ### 2.2 ICRC-107 Block Schema From 9582f01ce251d7c07b564ab65b9acb4c859036a8 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Tue, 18 Mar 2025 16:02:31 +0100 Subject: [PATCH 22/31] Update ICRCs/ICRC-107/ICRC-107.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mathias Björkqvist --- ICRCs/ICRC-107/ICRC-107.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index a37dd6d4..d312aee0 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -186,7 +186,7 @@ The Dfinity maintained ICRC ledgers include a fee collection mechanism which, fo - If `fee_col` is set in a block, the designated account collects only transfer fees (`1xfer`, `2xfer`). Fees for all other operations (e.g., `2approve`) were always burned in legacy behavior as implemented in Dfinity-maintained ICRC-3 ledgers. - If `icrc107_fee_col` is not set, the ledger follows this legacy behavior, using `fee_col` only for transfers. -New implementations SHOULD avoid using fee_col and instead use `icrc107_fee_col` for all fee collection settings. Legacy behavior is provided for backward compatibility only and MAY be deprecated in future versions of this standard. +New implementations SHOULD avoid using `fee_col` and instead use `icrc107_fee_col` for all fee collection settings. Legacy behavior is provided for backward compatibility only and MAY be deprecated in future versions of this standard. ### 5.2 Handling Fee Collection for ICRC-1 and ICRC-2 Blocks From 0944115f4455bd33747eb260adb9db36c37c74d4 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Tue, 18 Mar 2025 16:05:29 +0100 Subject: [PATCH 23/31] Update ICRCs/ICRC-107/ICRC-107.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mathias Björkqvist From 100b5322f1554c0020bab24d89c4c81d3086e048 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Thu, 20 Mar 2025 11:21:59 +0100 Subject: [PATCH 24/31] addressed comments --- ICRCs/ICRC-107/ICRC-107.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index d312aee0..7feb9e77 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -19,7 +19,7 @@ ICRC-107 establishes an on-chain fee collection mechanism that ensures clarity a - A fee collection configuration that specifies the collector account (`icrc107_fee_col`), applying to all subsequent blocks. - A new block type for setting `icrc107_fee_col` to designate the fee collector. -- A backward-compatible mechanism where, if `icrc107_fee_col` has never been set, legacy `fee_col` logic applies. +- A backward-compatible mechanism where, until `icrc107_fee_col` is set, legacy `fee_col` logic applies. By embedding fee collection settings entirely on-chain, ICRC-107 ensures transparency and simplifies integration with external tools. @@ -102,7 +102,7 @@ variant { Map = vec { ### 3.1 `icrc107_set_fee_collection` -This method allows a ledger controller to update the fee collection settings. It modifies the `icrc107_fee_col` account, which determines where collected fees are sent. The updated settings are recorded in the next block added to the ledger. +This method allows a ledger controller to update the fee collection settings. It modifies the `icrc107_fee_col` account, which determines where collected fees are sent. The updated settings are recorded in a new block (of type `107feecol`) added to the ledger. ``` icrc107_set_fee_collection: (SetFeeCollectionRequest) -> (); @@ -128,7 +128,7 @@ This method MUST only be callable by the ledger controller or authorized princip The `icrc107_set_fee_collection` method MUST return an error in the following cases: - The caller is not authorized to modify fee collection settings. -- The provided `Account` is invalid (e.g., malformed principal or subaccount)." +- The provided `Account` is invalid (e.g., the minting account on ledgers, anonymous principal, malformed principal or subaccount)." ### 3.2 `icrc107_get_fee_collection` From 597195c29834cc3cef0121c0493f21ed062fbb59 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Thu, 20 Mar 2025 11:25:07 +0100 Subject: [PATCH 25/31] rewrite legacy part --- ICRCs/ICRC-107/ICRC-107.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 7feb9e77..43b081a4 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -36,7 +36,7 @@ The fee collection configuration controls how the ledger processes fees. This co - If `icrc107_fee_col` is set to a ledger account, that account collects all subsequent fees. - If `icrc107_fee_col` is set to the empty account (see below), the ledger burns all subsequent fees. - The most recent `icrc107_fee_col` setting applies to each block. -- By default, the ledger burns all block fees until the first `icrc107_fee_col` setting is applied. If `icrc107_fee_col` has never been set, the ledger follows legacy `fee_col` logic (see Section 5). +- Until `icrc107_fee_col` is set fees are burned, unless legacy `fee_col` logic applies (see Section 5). - A **fee collector configuration block** records these settings on-chain, ensuring transparent fee collection. Once `icrc107_fee_col` is set, it overrides any legacy fee collection logic that may be in place (See Section 5). @@ -183,8 +183,11 @@ The Dfinity maintained ICRC ledgers include a fee collection mechanism which, fo ### 5.1 Legacy Behavior (`fee_col`) + +- Until `icrc107_fee_col` is set, the ledger follows this legacy behavior, using `fee_col` only for transfers. +- If `fee_col`is not set, all fees are burned. - If `fee_col` is set in a block, the designated account collects only transfer fees (`1xfer`, `2xfer`). Fees for all other operations (e.g., `2approve`) were always burned in legacy behavior as implemented in Dfinity-maintained ICRC-3 ledgers. -- If `icrc107_fee_col` is not set, the ledger follows this legacy behavior, using `fee_col` only for transfers. + New implementations SHOULD avoid using `fee_col` and instead use `icrc107_fee_col` for all fee collection settings. Legacy behavior is provided for backward compatibility only and MAY be deprecated in future versions of this standard. @@ -198,7 +201,7 @@ To determine who collects the fee in a block: 2. If no `icrc107_fee_col` setting exists (legacy behavior): - - If the block is of type `2approve` then the fee is burned or no `fee_col` + - If the block is of type `2approve` then the fee is burned - If the block is a transfer block, i.e. of type `1xfer` or `2xfer`: - If `fee_col` is specified in the block the fee is collected by `fee_col`. - If `fee_col_block` is specified use the `fee_col` from the referenced block index. From 1cef2a6bfd94ebfb579dc1887ccc36e3ec8ed054 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Thu, 20 Mar 2025 11:29:09 +0100 Subject: [PATCH 26/31] rewrite --- ICRCs/ICRC-107/ICRC-107.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 43b081a4..546a464d 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -35,7 +35,7 @@ The fee collection configuration controls how the ledger processes fees. This co - If `icrc107_fee_col` is set to a ledger account, that account collects all subsequent fees. - If `icrc107_fee_col` is set to the empty account (see below), the ledger burns all subsequent fees. -- The most recent `icrc107_fee_col` setting applies to each block. +- An `icrc107_fee_col` block configures the fee collection for all subsequent blocks, until superseded by another `icrc107_fee_col` block. - Until `icrc107_fee_col` is set fees are burned, unless legacy `fee_col` logic applies (see Section 5). - A **fee collector configuration block** records these settings on-chain, ensuring transparent fee collection. From 1f30ad30aa5deb6c115adef0c95a09460a1265fe Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Fri, 27 Jun 2025 18:30:32 +0200 Subject: [PATCH 27/31] introduced tx structure --- ICRCs/ICRC-107/ICRC-107.md | 124 ++++++++++++++++++++++++++----------- 1 file changed, 89 insertions(+), 35 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 546a464d..de184272 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -1,6 +1,6 @@ # ICRC-107: Fee Collection -## 1. Introduction & Motivation +## Introduction & Motivation Different ICRC-based ledgers (e.g., ckBTC) handle fee collection inconsistently. Some ledgers assign a designated fee collector for transfer operations, while others burn fees for approve operations—even when fee collection details appear in approve blocks. This inconsistency leads to challenges in defining fee collection rules, recording fee settings on-chain, and ensuring clear semantics for wallets and explorers. ICRC-107 builds on the ICRC-3 block schema, introducing a new block type (`107feecol`) to configure fee collection settings on-chain. This ensures compatibility with existing ICRC-3 implementations while extending functionality for fee handling. By embedding fee collection settings on-chain, ICRC-107 provides the following benefits: @@ -23,11 +23,20 @@ ICRC-107 establishes an on-chain fee collection mechanism that ensures clarity a By embedding fee collection settings entirely on-chain, ICRC-107 ensures transparency and simplifies integration with external tools. + +## Common Elements +This standard follows the conventions set by ICRC-3, inheriting key structural components. +- **Accounts** are represented using the ICRC-3 `Value` type, specifically as a `variant { Array = vec { V1 [, V2] } }` where `V1` is `variant { Blob = }` representing the account owner, and `V2` is `variant { Blob = }` representing the subaccount. If no subaccount is specified, the `Array` MUST contain only one element (`V1`, the owner's principal). +- **Principals** (such as the `caller`) are represented using the ICRC-3 `Value` type as `variant { Blob = }`. +- Each block includes `phash`, a `Blob` representing the hash of the parent block, and `ts`, a `Nat` representing the timestamp of the block. + + + --- -## 2. Fee Collection +## Fee Collection -### 2.1 Overview +### Overview Each block requires a fee, which a designated party must pay based on the operation type. @@ -35,7 +44,7 @@ The fee collection configuration controls how the ledger processes fees. This co - If `icrc107_fee_col` is set to a ledger account, that account collects all subsequent fees. - If `icrc107_fee_col` is set to the empty account (see below), the ledger burns all subsequent fees. -- An `icrc107_fee_col` block configures the fee collection for all subsequent blocks, until superseded by another `icrc107_fee_col` block. +- An `icrc107_fee_col` block configures the fee collection for all subsequent blocks, until superseded by another `icrc107_fee_col` block. - Until `icrc107_fee_col` is set fees are burned, unless legacy `fee_col` logic applies (see Section 5). - A **fee collector configuration block** records these settings on-chain, ensuring transparent fee collection. @@ -44,15 +53,34 @@ Once `icrc107_fee_col` is set, it overrides any legacy fee collection logic that Fee burning is explicitly recorded on-chain by setting `icrc107_fee_col = variant { Array = vec {} }`. This ensures unambiguous representation across implementations. -### 2.2 ICRC-107 Block Schema -ICRC-107 introduces a new block type that follows the ICRC-3 specification to configure `icrc107_fee_col`. -In addition to the ICRC-3 specific requirements, a fee collector configuration block: +### ICRC-107 Transaction and Block Schema + +Each `107feecol` block consists of the following top-level fields: + +| Field | Type (ICRC-3 `Value`) | Required | Description | +|-------------------|------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `btype` | `Text` | Yes | **MUST** be `"107feecol"`. | +| `ts` | `Nat` | Yes | Timestamp (in nanoseconds since the Unix epoch) when the block was added to the ledger. | +| `phash` | `Blob` | Yes | Hash of the parent block. | +| `tx` | `Map(Text, Value)` | Yes | Encodes information about the fee collection configuration transaction. See `tx` Field Schema below. | + + +### `tx` Field Schema + +The `tx` field for a `107feecol` block is a `Map` that contains the following fields: + +| Field | Type (ICRC-3 `Value`) | Required | Description | +|-----------------|------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------| +| `op` | `Text` | Yes | **MUST** be `"set107feecol"`. Explicitly identifies the operation and standard within the transaction, enabling self-contained transaction records and unambiguous hashing. | +| `fee_col` | `Array` (Account) | Yes | The target fee collector account. If `variant { Array = vec {} }`, it signifies that fees should be burned. See Common Elements for Account representation. | +| `created_at_time` | `Nat` | Yes | Timestamp (in nanoseconds since the Unix epoch) when the user created the transaction. This value **MUST** be provided by the caller to enable transaction deduplication as per ICRC-1. | +| `caller` | `Blob` | Yes | The principal of the user or canister that originated this transaction. | + + + -- **MUST** contain a field `btype` with value `"107feecol"`. -- **MUST** contain a field `icrc107_fee_col` of type `Array`, specifying the owner and, optionally, a subaccount, following the ICRC-3 standard. -- To explicitly burn fees, `icrc107_fee_col` **MUST** be set to `variant { Array = vec {}}`. #### Example Block @@ -62,13 +90,18 @@ Below is an example of a valid **fee collector configuration block** that sets t variant { Map = vec { // Block type: indicates this is a fee collector configuration block record { "btype"; variant { Text = "107feecol" }}; - // Fee collector account: specifies the account that will collect fees - record { "icrc107_fee_col"; variant { Array = vec { - variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01"}; // Owner principal - variant { Blob = blob "\06\ec\cd\3a\97\fb\a8\5f\bc\8d\a3\3e\5d\ba\bc\2f\38\69\60\5d\c7\a1\c9\53\1f\70\a3\66\c5\a7\e4\21" }; // Subaccount + // The user's intended transaction + record { "tx"; variant { Map = vec { + record { "op"; variant { Text = "107setfeecol" } }; // Operation name, as per schema + record { "fee_col"; variant { Array = vec { + variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01"}; // Owner principal + variant { Blob = blob "\06\ec\cd\3a\97\fb\a8\5f\bc\8d\a3\3e\5d\ba\bc\2f\38\69\60\5d\c7\a1\c9\53\1f\70\a3\66\c5\a7\e4\21" }; // Subaccount + }} }; + record { "created_at_time"; variant { Nat = 1_750_951_727_000_000_000 : nat } }; // Timestamp for deduplication (June 27, 2025, 15:28:47 UTC) + record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Example caller principal }} }; // Timestamp: indicates when the block was created - record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; + record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; // Existing block timestamp // Parent hash: links this block to the previous block in the chain record { "phash"; variant { @@ -76,15 +109,29 @@ variant { Map = vec { }}; }}; ``` +If one wanted to set the fee collector to be the default account of principal identified by `"\00\00\00\00\02\00\01\0d\01\01"`, then the "fee_col" field within `tx` should be set to variant ``{ Array = vec { + variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01"} } }``. + + A block that explicitly sets fee burning by removing the fee collector (i.e., all fees are burned from this point onward): ``` variant { Map = vec { + // Block type: indicates this is a fee collector configuration block record { "btype"; variant { Text = "107feecol" }}; - record { "icrc107_fee_col"; variant { Array = vec { + // The user's intended transaction, from which the hash is computed + record { "tx"; variant { Map = vec { + record { "op"; variant { Text = "set107feecol" } }; // Operation name, as per schema + record { "fee_col"; variant { Array = vec { + // Empty array to signify burning fees }}}; - record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; + record { "created_at_time"; variant { Nat = 1_750_951_728_000_000_000 : nat } }; // Timestamp for deduplication (incremented from previous example) + record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Example caller principal + }} }; + // Timestamp: indicates when the block was created (can be derived from tx.created_at_time or ledger time) + record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; // Existing block timestamp + // Parent hash: links this block to the previous block in the chain record { "phash"; variant { Blob = blob "\2d\86\7f\34\c7\2d\1e\2d\00\84\10\a4\00\b0\b6\4c\3e\02\96\c9\e8\55\6f\dd\72\68\e8\df\8d\8e\8a\ee" @@ -98,30 +145,38 @@ variant { Map = vec { # ICRC-107: Methods for Setting and Getting Fee Collection -## 3. Methods for Setting and Getting Fee Collection +## Methods for Setting and Getting Fee Collection -### 3.1 `icrc107_set_fee_collection` +### `icrc107_set_fee_collection` This method allows a ledger controller to update the fee collection settings. It modifies the `icrc107_fee_col` account, which determines where collected fees are sent. The updated settings are recorded in a new block (of type `107feecol`) added to the ledger. -``` -icrc107_set_fee_collection: (SetFeeCollectionRequest) -> (); ``` -#### Request Type: -``` type Account = record { owner: principal; subaccount: opt blob; }; -type SetFeeCollectionRequest = record { +type SetFeeCollectionArgs = record { icrc107_fee_col: opt Account; + created_at_time: opt nat64; }; + +type SetFeeCollectionError = variant { + AccessDenied : text; // The caller is not authorized to modify fee collection settings. + InvalidAccount : text; // The provided account for fee collection is invalid (e.g., minting account, anonymous principal, malformed). + Duplicate : record { duplicate_of : nat }; // A duplicate transaction already exists at position `duplicate_of` in the ledger. + GenericError : record { error_code : nat; message : text }; +}; + +icrc107_set_fee_collection: (SetFeeCollectionRequest) -> (variant { Ok: empty; Err: SetFeeCollectionError }); + ``` -This method MUST only be callable by the ledger controller or authorized principals. Unauthorized calls SHOULD result in an error. +This method MUST only be callable by a controller of the ledger or some other authorized principals. Unauthorized calls SHOULD result in an error. +- The ledger MUST construct the tx field (an ICRC-3 Value of type Map as described in the previous section) from this request, place it in the generated `107feecol` block and append the block to the ledger. - If `icrc107_fee_col` is set to an account, all subsequent fees are collected by that account. - If `icrc107_fee_col` is not provided (or set to `null`), all subsequent fees are burned. @@ -129,14 +184,15 @@ The `icrc107_set_fee_collection` method MUST return an error in the following ca - The caller is not authorized to modify fee collection settings. - The provided `Account` is invalid (e.g., the minting account on ledgers, anonymous principal, malformed principal or subaccount)." +- Transaction deduplication is enabled for the transaction (i.e. `created_at_time` is set) and an identical transaction already exist. -### 3.2 `icrc107_get_fee_collection` +### `icrc107_get_fee_collection` This method retrieves the currently active fee collection settings. Unless changed, these settings apply to the next block added to the ledger. ``` -icrc107_get_fee_collection: () -> (opt Account) query; +icrc107_get_fee_collection: () -> (variant { Ok: opt Account; Err: GetFeeCollectionError }) query; ``` @@ -150,11 +206,11 @@ This method strictly returns the last explicitly set value of `icrc107_fee_col`. --- -## 4. Reporting Compliance +## Reporting Compliance Compliance with the current standard is reported as follows. -### 4.1 Supported Standards +### Supported Standards Ledgers implementing ICRC-107 **MUST** indicate their compliance through the `icrc1_supported_standards` and `icrc10_supported_standards` methods, by including in the output of these methods: @@ -167,7 +223,7 @@ variant { Vec = vec { }}; ``` -### 4.2 Supported Block Types +### Supported Block Types Ledgers implementing ICRC-107 **MUST** report the new block type in response to `icrc3_supported_block_types`. The output of the call must include @@ -177,11 +233,11 @@ variant { Vec = vec { }}; ``` -## 5. Note on Legacy Fee Collection Mechanisms +## Note on Legacy Fee Collection Mechanisms The Dfinity maintained ICRC ledgers include a fee collection mechanism which, for completeness is described below. -### 5.1 Legacy Behavior (`fee_col`) +### Legacy Behavior (`fee_col`) - Until `icrc107_fee_col` is set, the ledger follows this legacy behavior, using `fee_col` only for transfers. @@ -191,7 +247,7 @@ The Dfinity maintained ICRC ledgers include a fee collection mechanism which, fo New implementations SHOULD avoid using `fee_col` and instead use `icrc107_fee_col` for all fee collection settings. Legacy behavior is provided for backward compatibility only and MAY be deprecated in future versions of this standard. -### 5.2 Handling Fee Collection for ICRC-1 and ICRC-2 Blocks +### Handling Fee Collection for ICRC-1 and ICRC-2 Blocks To determine who collects the fee in a block: @@ -205,5 +261,3 @@ To determine who collects the fee in a block: - If the block is a transfer block, i.e. of type `1xfer` or `2xfer`: - If `fee_col` is specified in the block the fee is collected by `fee_col`. - If `fee_col_block` is specified use the `fee_col` from the referenced block index. - ---- From aeac0560dcc3b24b6a57431b49a28e7aa0ab55aa Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Fri, 27 Jun 2025 18:31:21 +0200 Subject: [PATCH 28/31] fixed an error type --- ICRCs/ICRC-107/ICRC-107.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index de184272..ae7169d4 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -192,7 +192,7 @@ The `icrc107_set_fee_collection` method MUST return an error in the following ca This method retrieves the currently active fee collection settings. Unless changed, these settings apply to the next block added to the ledger. ``` -icrc107_get_fee_collection: () -> (variant { Ok: opt Account; Err: GetFeeCollectionError }) query; +icrc107_get_fee_collection: () -> (variant { Ok: opt Account; Err: record { error_code : nat; message : text } }) query; ``` From 92436c8624629b364e8954c72859ccae4c80f251 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 30 Jun 2025 09:55:11 +0200 Subject: [PATCH 29/31] fixed example --- ICRCs/ICRC-107/ICRC-107.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index ae7169d4..29418efa 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -98,10 +98,10 @@ variant { Map = vec { variant { Blob = blob "\06\ec\cd\3a\97\fb\a8\5f\bc\8d\a3\3e\5d\ba\bc\2f\38\69\60\5d\c7\a1\c9\53\1f\70\a3\66\c5\a7\e4\21" }; // Subaccount }} }; record { "created_at_time"; variant { Nat = 1_750_951_727_000_000_000 : nat } }; // Timestamp for deduplication (June 27, 2025, 15:28:47 UTC) - record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Example caller principal + record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Caller principal }} }; // Timestamp: indicates when the block was created - record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; // Existing block timestamp + record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; // Parent hash: links this block to the previous block in the chain record { "phash"; variant { @@ -126,11 +126,11 @@ variant { Map = vec { record { "fee_col"; variant { Array = vec { // Empty array to signify burning fees }}}; - record { "created_at_time"; variant { Nat = 1_750_951_728_000_000_000 : nat } }; // Timestamp for deduplication (incremented from previous example) - record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Example caller principal + record { "created_at_time"; variant { Nat = 1_750_951_728_000_000_000 : nat } }; // Timestamp for deduplication + record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Caller principal }} }; - // Timestamp: indicates when the block was created (can be derived from tx.created_at_time or ledger time) - record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; // Existing block timestamp + // Timestamp: indicates when the block was created + record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; // Parent hash: links this block to the previous block in the chain record { "phash"; variant { From b81f01315d6c35322258a9bc387f49fad88e86ce Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Mon, 30 Jun 2025 16:34:33 +0200 Subject: [PATCH 30/31] final(?) pass --- ICRCs/ICRC-107/ICRC-107.md | 77 ++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index 29418efa..d65fb287 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -38,20 +38,20 @@ This standard follows the conventions set by ICRC-3, inheriting key structural c ### Overview -Each block requires a fee, which a designated party must pay based on the operation type. +Each block may specify a fee which a designated party must pay based on the operation type. -The fee collection configuration controls how the ledger processes fees. This configuration consists of a single global fee collector account (`icrc107_fee_col`). When a block updates this setting, the change takes effect immediately. +The fee collection configuration controls how the ledger processes these fees. This configuration consists of a single global fee collector account (`icrc107_fee_col`). When a block updates this setting, the change takes effect immediately. -- If `icrc107_fee_col` is set to a ledger account, that account collects all subsequent fees. -- If `icrc107_fee_col` is set to the empty account (see below), the ledger burns all subsequent fees. -- An `icrc107_fee_col` block configures the fee collection for all subsequent blocks, until superseded by another `icrc107_fee_col` block. -- Until `icrc107_fee_col` is set fees are burned, unless legacy `fee_col` logic applies (see Section 5). -- A **fee collector configuration block** records these settings on-chain, ensuring transparent fee collection. +- A **fee collector configuration block** records changes to `icrc107_fee_col` value. +- If `icrc107_fee_col` is set to a ledger account, that account collects the fees in all subsequent blocks. +- If `icrc107_fee_col` is set to the empty account (see below), the ledger burns the fees in all subsequent blocks. +- An `107feecol` block configures the fee collection for all subsequent blocks, until superseded by another `107feecol` block. -Once `icrc107_fee_col` is set, it overrides any legacy fee collection logic that may be in place (See Section 5). -Fee burning is explicitly recorded on-chain by setting `icrc107_fee_col = variant { Array = vec {} }`. This ensures unambiguous representation across implementations. +### Interaction with Legacy Fee Collection +- Until the first `107feecol` block, legacy logic for fee collection applies (see the Legacy Fee Collection Mechanism section). +- Once a block `107feecol` block is added to the ledger, it overrides any legacy fee collection logic. @@ -73,10 +73,10 @@ The `tx` field for a `107feecol` block is a `Map` that contains the following fi | Field | Type (ICRC-3 `Value`) | Required | Description | |-----------------|------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------| -| `op` | `Text` | Yes | **MUST** be `"set107feecol"`. Explicitly identifies the operation and standard within the transaction, enabling self-contained transaction records and unambiguous hashing. | -| `fee_col` | `Array` (Account) | Yes | The target fee collector account. If `variant { Array = vec {} }`, it signifies that fees should be burned. See Common Elements for Account representation. | +| `op` | `Text` | Yes | **MUST** be `"107_set_fee_col"`. Explicitly identifies the operation and standard within the transaction, enabling self-contained transaction records and unambiguous hashing. | +| `icrc107_fee_col` | `Array` (Account) | Yes | The target fee collector account. If `variant { Array = vec {} }`, it signifies that fees should be burned. See Common Elements for Account representation. | | `created_at_time` | `Nat` | Yes | Timestamp (in nanoseconds since the Unix epoch) when the user created the transaction. This value **MUST** be provided by the caller to enable transaction deduplication as per ICRC-1. | -| `caller` | `Blob` | Yes | The principal of the user or canister that originated this transaction. | +| `caller` | `Blob` | Yes | The principal of the user or canister that originated this transaction. It is the principal of the ledger itself, if the fee collector is set within a ledger upgrade. | @@ -92,8 +92,8 @@ variant { Map = vec { record { "btype"; variant { Text = "107feecol" }}; // The user's intended transaction record { "tx"; variant { Map = vec { - record { "op"; variant { Text = "107setfeecol" } }; // Operation name, as per schema - record { "fee_col"; variant { Array = vec { + record { "op"; variant { Text = "107_set_fee_col" } }; // Operation name, as per schema + record { "icrc107_fee_col"; variant { Array = vec { variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01"}; // Owner principal variant { Blob = blob "\06\ec\cd\3a\97\fb\a8\5f\bc\8d\a3\3e\5d\ba\bc\2f\38\69\60\5d\c7\a1\c9\53\1f\70\a3\66\c5\a7\e4\21" }; // Subaccount }} }; @@ -122,14 +122,14 @@ variant { Map = vec { record { "btype"; variant { Text = "107feecol" }}; // The user's intended transaction, from which the hash is computed record { "tx"; variant { Map = vec { - record { "op"; variant { Text = "set107feecol" } }; // Operation name, as per schema - record { "fee_col"; variant { Array = vec { + record { "op"; variant { Text = "107_set_fee_col" } }; // Operation name, as per schema + record { "icrc107_fee_col"; variant { Array = vec { // Empty array to signify burning fees }}}; record { "created_at_time"; variant { Nat = 1_750_951_728_000_000_000 : nat } }; // Timestamp for deduplication record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Caller principal }} }; - // Timestamp: indicates when the block was created + // Timestamp: indicates when the block was created record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; // Parent hash: links this block to the previous block in the chain record { "phash"; @@ -140,16 +140,12 @@ variant { Map = vec { ``` +# ICRC-107: Methods for Setting and Getting Fee Collector Information ---- - -# ICRC-107: Methods for Setting and Getting Fee Collection -## Methods for Setting and Getting Fee Collection +### `icrc107_set_fee_collector` -### `icrc107_set_fee_collection` - -This method allows a ledger controller to update the fee collection settings. It modifies the `icrc107_fee_col` account, which determines where collected fees are sent. The updated settings are recorded in a new block (of type `107feecol`) added to the ledger. +This method allows a ledger controller to update the fee collector. It modifies the `icrc107_fee_col` account, which determines where collected fees are sent. The updated settings are recorded in a new block (of type `107feecol`) added to the ledger. ``` @@ -158,19 +154,19 @@ type Account = record { subaccount: opt blob; }; -type SetFeeCollectionArgs = record { +type SetFeeCollectorArgs = record { icrc107_fee_col: opt Account; - created_at_time: opt nat64; + created_at_time: nat64; }; -type SetFeeCollectionError = variant { - AccessDenied : text; // The caller is not authorized to modify fee collection settings. +type SetFeeCollectorError = variant { + AccessDenied : text; // The caller is not authorized to modify fee collector. InvalidAccount : text; // The provided account for fee collection is invalid (e.g., minting account, anonymous principal, malformed). Duplicate : record { duplicate_of : nat }; // A duplicate transaction already exists at position `duplicate_of` in the ledger. GenericError : record { error_code : nat; message : text }; }; -icrc107_set_fee_collection: (SetFeeCollectionRequest) -> (variant { Ok: empty; Err: SetFeeCollectionError }); +icrc107_set_fee_collector: (SetFeeCollectorArgs) -> (variant { Ok: empty; Err: SetFeeCollectorError }); ``` @@ -180,23 +176,23 @@ This method MUST only be callable by a controller of the ledger or some other au - If `icrc107_fee_col` is set to an account, all subsequent fees are collected by that account. - If `icrc107_fee_col` is not provided (or set to `null`), all subsequent fees are burned. -The `icrc107_set_fee_collection` method MUST return an error in the following cases: +The `icrc107_set_fee_collector` method MUST return an error in the following cases: -- The caller is not authorized to modify fee collection settings. -- The provided `Account` is invalid (e.g., the minting account on ledgers, anonymous principal, malformed principal or subaccount)." -- Transaction deduplication is enabled for the transaction (i.e. `created_at_time` is set) and an identical transaction already exist. +- The caller is not authorized to modify fee collector. +- The provided `Account` is invalid (e.g., the minting account on ledgers, anonymous principal, malformed principal or subaccount). +- The transaction is a duplicate (as determined by its tx hash when deduplication is enabled), resulting in a `SetFeeCollectorError::Duplicate`. -### `icrc107_get_fee_collection` +### `icrc107_get_fee_collector` -This method retrieves the currently active fee collection settings. Unless changed, these settings apply to the next block added to the ledger. +This method retrieves the currently active fee collector account. Unless changed, these settings apply to the next block added to the ledger. ``` -icrc107_get_fee_collection: () -> (variant { Ok: opt Account; Err: record { error_code : nat; message : text } }) query; +icrc107_get_fee_collector: () -> (variant { Ok: opt Account; Err: record { error_code : nat; message : text } }) query; ``` -This method should return the currently active fee collection settings: +This method should return the currently active fee collector account: - If the response is `null`, fees are burned. This corresponds to `icrc107_fee_col = variant { Array = vec {}}` on-chain. - If the response is a valid `Account`, fees are collected by that account. This corresponds to `icrc107_fee_col` being set to the ICRC-3 representation of the account on-chain. @@ -233,14 +229,15 @@ variant { Vec = vec { }}; ``` -## Note on Legacy Fee Collection Mechanisms +## Legacy Fee Collection Mechanism The Dfinity maintained ICRC ledgers include a fee collection mechanism which, for completeness is described below. ### Legacy Behavior (`fee_col`) -- Until `icrc107_fee_col` is set, the ledger follows this legacy behavior, using `fee_col` only for transfers. +Until the first block of type `107feecol` the ledger follows the following legacy behavior. + - If `fee_col`is not set, all fees are burned. - If `fee_col` is set in a block, the designated account collects only transfer fees (`1xfer`, `2xfer`). Fees for all other operations (e.g., `2approve`) were always burned in legacy behavior as implemented in Dfinity-maintained ICRC-3 ledgers. @@ -251,7 +248,7 @@ New implementations SHOULD avoid using `fee_col` and instead use `icrc107_fee_co To determine who collects the fee in a block: -1. Check for fee collection configuration +1. Check for fee collector configuration - If a previous block set `icrc107_fee_col`, the ledger uses the behavior specified by this standard. From dd064abf0e83a34125edeac084b504867d00ed5d Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Thu, 11 Sep 2025 13:02:00 +0200 Subject: [PATCH 31/31] alignment with ICRC-3 refinements --- ICRCs/ICRC-107/ICRC-107.md | 287 +++++++++++++++++++++++++------------ 1 file changed, 193 insertions(+), 94 deletions(-) diff --git a/ICRCs/ICRC-107/ICRC-107.md b/ICRCs/ICRC-107/ICRC-107.md index d65fb287..30100a62 100644 --- a/ICRCs/ICRC-107/ICRC-107.md +++ b/ICRCs/ICRC-107/ICRC-107.md @@ -1,57 +1,89 @@ -# ICRC-107: Fee Collection +# ICRC-107: Fee Management ## Introduction & Motivation -Different ICRC-based ledgers (e.g., ckBTC) handle fee collection inconsistently. Some ledgers assign a designated fee collector for transfer operations, while others burn fees for approve operations—even when fee collection details appear in approve blocks. This inconsistency leads to challenges in defining fee collection rules, recording fee settings on-chain, and ensuring clear semantics for wallets and explorers. ICRC-107 builds on the ICRC-3 block schema, introducing a new block type (`107feecol`) to configure fee collection settings on-chain. This ensures compatibility with existing ICRC-3 implementations while extending functionality for fee handling. -By embedding fee collection settings on-chain, ICRC-107 provides the following benefits: +Different ICRC-based ledgers (e.g., ckBTC) handle fee collection inconsistently. +Some assign a designated fee collector for transfer operations, while others burn fees for approve operations—even when fee collection details appear in approve blocks. -- Transparency: Fee collection rules are visible and auditable on the ledger. +This inconsistency creates challenges: -- Interoperability: Wallets and explorers can rely on consistent fee handling across ICRC-based ledgers. +- Fee collection rules are not uniformly defined or visible on-chain. +- Wallets and explorers cannot reliably predict where fees go. +- Integration requires off-chain metadata to interpret fee behavior. -- Simplified Integration: Eliminates the need for off-chain metadata to determine fee collection behavior. +**ICRC-107** standardizes fee collection across all ICRC-based ledgers by introducing: + +- A **global fee collector configuration** stored in the ledger state. +- A new block type (`107feecol`) for setting the fee collector on-chain. +- A consistent fee application model integrated into the ICRC-3 evaluation model. + +This ensures **transparency**, **interoperability**, and **simplified integration**. ICRC-107 applies to all ICRC-based ledgers ensuring consistent fee collection behavior across transfers and approvals. This standard is backward-compatible with existing ICRC-based ledgers, allowing them to transition gradually to the new fee collection mechanism, while documenting legacy fee collection logic. -### ICRC-107 Proposal +## Overview + +ICRC-107 establishes an on-chain fee collection mechanism that ensures clarity and interoperability across different ICRC-based ledgers. +It extends the ICRC-3 block model with a global fee collection configuration that determines whether fees are **credited to a designated account** or **burned**, and records all changes on-chain using a new block type (`107feecol`). + +Specifically, ICRC-107 defines: + +- A fee collection configuration that specifies the collector account (`icrc107_fee_col`), applying to all subsequent blocks. +- A new block type for setting `icrc107_fee_col` to designate the fee collector. +- A backward-compatible mechanism where, until `icrc107_fee_col` is set, legacy `fee_col` logic applies. + +By embedding fee collection settings entirely on-chain, ICRC-107 ensures **transparency**, **interoperability**, and **simplified integration** for wallets, explorers, and other tools. + +### Effective Fee Application + +- **Effective fee:** For any block that charges a fee, the ledger computes the **effective fee** according to ICRC-3. +- **Fee payer:** The block type (e.g., ICRC-1 transfer, ICRC-2 approve) defines which account pays the effective fee. +- **Fee collector:** + - If the current configuration specifies an account, that account is credited with the effective fee. + - If the configuration is empty (`[]`), the effective fee is burned. + +The global fee collector configuration is changed by appending a `107feecol` block: +- `tx.icrc107_fee_col = []` → all subsequent fees are burned. +- `tx.icrc107_fee_col = ` → all subsequent fees are credited to that account. -ICRC-107 establishes an on-chain fee collection mechanism that ensures clarity and interoperability across different ICRC-based ledgers. It defines: +This configuration applies to **all subsequent blocks** until replaced by another `107feecol`. -- A fee collection configuration that specifies the collector account (`icrc107_fee_col`), applying to all subsequent blocks. -- A new block type for setting `icrc107_fee_col` to designate the fee collector. -- A backward-compatible mechanism where, until `icrc107_fee_col` is set, legacy `fee_col` logic applies. +**Legacy compatibility:** +- Until the first `107feecol` block appears, ledgers follow the legacy `fee_col` mechanism (as described in the *Legacy Fee Collection* section). +- Once a `107feecol` block is present, it overrides all legacy fee collection logic. -By embedding fee collection settings entirely on-chain, ICRC-107 ensures transparency and simplifies integration with external tools. ## Common Elements -This standard follows the conventions set by ICRC-3, inheriting key structural components. -- **Accounts** are represented using the ICRC-3 `Value` type, specifically as a `variant { Array = vec { V1 [, V2] } }` where `V1` is `variant { Blob = }` representing the account owner, and `V2` is `variant { Blob = }` representing the subaccount. If no subaccount is specified, the `Array` MUST contain only one element (`V1`, the owner's principal). -- **Principals** (such as the `caller`) are represented using the ICRC-3 `Value` type as `variant { Blob = }`. -- Each block includes `phash`, a `Blob` representing the hash of the parent block, and `ts`, a `Nat` representing the timestamp of the block. +This standard follows the conventions set by ICRC-3, inheriting key structural components. +- **Accounts** + Represented using the ICRC-3 `Value` type as a + `variant { Array = vec { V1 [, V2] } }` where: + - `V1` = `variant { Blob = }` (the account owner), + - `V2` = `variant { Blob = <32-byte_subaccount_bytes> }` (optional). + If no subaccount is specified, the `Array` MUST contain only the owner’s principal. ---- +- **Principals** + Represented as `variant { Blob = }`. -## Fee Collection +- **Timestamps** + Both `ts` and `tx.created_at_time` are expressed in **nanoseconds since the Unix epoch**. + They are encoded as `Nat` in ICRC-3 `Value`, but MUST fit into a `nat64`. + - `ts` is set by the ledger when the block is created. + - `created_at_time` is provided by the caller for deduplication (per ICRC-1). -### Overview +- **Parent Hash** + Each block includes `phash : Blob`, the hash of its parent block, + unless it is the genesis block (where `phash` is omitted). -Each block may specify a fee which a designated party must pay based on the operation type. -The fee collection configuration controls how the ledger processes these fees. This configuration consists of a single global fee collector account (`icrc107_fee_col`). When a block updates this setting, the change takes effect immediately. -- A **fee collector configuration block** records changes to `icrc107_fee_col` value. -- If `icrc107_fee_col` is set to a ledger account, that account collects the fees in all subsequent blocks. -- If `icrc107_fee_col` is set to the empty account (see below), the ledger burns the fees in all subsequent blocks. -- An `107feecol` block configures the fee collection for all subsequent blocks, until superseded by another `107feecol` block. +## Fee Management -### Interaction with Legacy Fee Collection -- Until the first `107feecol` block, legacy logic for fee collection applies (see the Legacy Fee Collection Mechanism section). -- Once a block `107feecol` block is added to the ledger, it overrides any legacy fee collection logic. @@ -66,78 +98,57 @@ Each `107feecol` block consists of the following top-level fields: | `phash` | `Blob` | Yes | Hash of the parent block. | | `tx` | `Map(Text, Value)` | Yes | Encodes information about the fee collection configuration transaction. See `tx` Field Schema below. | +### Minimal `tx` Schema -### `tx` Field Schema +The minimal fields required to interpret a `107feecol` block: -The `tx` field for a `107feecol` block is a `Map` that contains the following fields: +| Field | Type (ICRC-3 `Value`) | Required | Description | +|-------------------|------------------------|----------|-------------| +| `op` | `Text` | Yes | MUST be `"107_set_fee_col"`. | +| `icrc107_fee_col` | `Array` (Account/Empty)| Yes | Target fee collector account, or empty (`[]`) to burn fees. | -| Field | Type (ICRC-3 `Value`) | Required | Description | -|-----------------|------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------| -| `op` | `Text` | Yes | **MUST** be `"107_set_fee_col"`. Explicitly identifies the operation and standard within the transaction, enabling self-contained transaction records and unambiguous hashing. | -| `icrc107_fee_col` | `Array` (Account) | Yes | The target fee collector account. If `variant { Array = vec {} }`, it signifies that fees should be burned. See Common Elements for Account representation. | -| `created_at_time` | `Nat` | Yes | Timestamp (in nanoseconds since the Unix epoch) when the user created the transaction. This value **MUST** be provided by the caller to enable transaction deduplication as per ICRC-1. | -| `caller` | `Blob` | Yes | The principal of the user or canister that originated this transaction. It is the principal of the ledger itself, if the fee collector is set within a ledger upgrade. | +## Semantics of `107feecol` Blocks +### Core State Transition +A `107feecol` block updates the ledger’s global fee collector configuration: +- If `tx.icrc107_fee_col = []` → all subsequent fees are **burned**. +- If `tx.icrc107_fee_col` encodes an account → all subsequent fees are **credited to that account**. -#### Example Block +This configuration applies **to all subsequent blocks** until replaced by another `107feecol`. -Below is an example of a valid **fee collector configuration block** that sets the fee collector to a specific account: +--- -``` -variant { Map = vec { - // Block type: indicates this is a fee collector configuration block - record { "btype"; variant { Text = "107feecol" }}; - // The user's intended transaction - record { "tx"; variant { Map = vec { - record { "op"; variant { Text = "107_set_fee_col" } }; // Operation name, as per schema - record { "icrc107_fee_col"; variant { Array = vec { - variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01"}; // Owner principal - variant { Blob = blob "\06\ec\cd\3a\97\fb\a8\5f\bc\8d\a3\3e\5d\ba\bc\2f\38\69\60\5d\c7\a1\c9\53\1f\70\a3\66\c5\a7\e4\21" }; // Subaccount - }} }; - record { "created_at_time"; variant { Nat = 1_750_951_727_000_000_000 : nat } }; // Timestamp for deduplication (June 27, 2025, 15:28:47 UTC) - record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Caller principal - }} }; - // Timestamp: indicates when the block was created - record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; - // Parent hash: links this block to the previous block in the chain - record { "phash"; - variant { - Blob = blob "\d5\c7\eb\57\a2\4e\fa\d4\8b\d1\cc\54\9e\49\c6\9f\d1\93\8d\e8\02\d4\60\65\e2\f2\3c\00\04\3b\2e\51" - }}; -}}; -``` -If one wanted to set the fee collector to be the default account of principal identified by `"\00\00\00\00\02\00\01\0d\01\01"`, then the "fee_col" field within `tx` should be set to variant ``{ Array = vec { - variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01"} } }``. +### Fee Application under ICRC-107 +For every block that charges a fee: + +1. Execute the **pre-fee state transition** (per ICRC-3 evaluation model). +2. Compute the **effective fee** (as defined in ICRC-3). +3. Debit the **effective fee** from the block’s **fee payer**. +4. Determine how the effective fee is handled: + + - **If at least one `107feecol` block exists:** + • If the most recent `107feecol` has `tx.icrc107_fee_col = []` → burn the effective fee. + • If the most recent `107feecol` has `tx.icrc107_fee_col = ` → credit the effective fee to that account. + + - **If no `107feecol` block exists yet (legacy mode):** + • Apply the legacy rules described in the *Legacy Fee Collection* section. + • In brief: if `fee_col` is unset, burn all fees; if `fee_col` is set, only transfer fees are credited to that account, and all other fees are burned. + +This ensures that fee handling is always well-defined, both before and after the introduction of ICRC-107. + +--- + +**Notes** + +- Invariants from ICRC-3 still apply (e.g., `effective fee ≤ amt` for mints; sufficient balance for `amt + effective fee`; allowance reductions include `amt + effective fee` where applicable). +- ICRC-107 does **not** define who the fee payer is — that comes from the semantics of each block type (e.g., ICRC-1/2 legacy rules in ICRC-3). -A block that explicitly sets fee burning by removing the fee collector (i.e., all fees are burned from this point onward): -``` -variant { Map = vec { - // Block type: indicates this is a fee collector configuration block - record { "btype"; variant { Text = "107feecol" }}; - // The user's intended transaction, from which the hash is computed - record { "tx"; variant { Map = vec { - record { "op"; variant { Text = "107_set_fee_col" } }; // Operation name, as per schema - record { "icrc107_fee_col"; variant { Array = vec { - // Empty array to signify burning fees - }}}; - record { "created_at_time"; variant { Nat = 1_750_951_728_000_000_000 : nat } }; // Timestamp for deduplication - record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Caller principal - }} }; - // Timestamp: indicates when the block was created - record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; - // Parent hash: links this block to the previous block in the chain - record { "phash"; - variant { - Blob = blob "\2d\86\7f\34\c7\2d\1e\2d\00\84\10\a4\00\b0\b6\4c\3e\02\96\c9\e8\55\6f\dd\72\68\e8\df\8d\8e\8a\ee" - }}; -}}; -``` # ICRC-107: Methods for Setting and Getting Fee Collector Information @@ -170,17 +181,39 @@ icrc107_set_fee_collector: (SetFeeCollectorArgs) -> (variant { Ok: empty; Err: S ``` -This method MUST only be callable by a controller of the ledger or some other authorized principals. Unauthorized calls SHOULD result in an error. +This method MUST only be callable by a controller of the ledger or other authorized principals. Unauthorized calls SHOULD result in an error. -- The ledger MUST construct the tx field (an ICRC-3 Value of type Map as described in the previous section) from this request, place it in the generated `107feecol` block and append the block to the ledger. -- If `icrc107_fee_col` is set to an account, all subsequent fees are collected by that account. -- If `icrc107_fee_col` is not provided (or set to `null`), all subsequent fees are burned. +- The ledger MUST construct the `tx` field (an ICRC-3 Value of type Map) from this request, place it in the generated `107feecol` block, and append the block to the ledger. +- If `icrc107_fee_col` is set to an account, all subsequent fees are collected by that account. +- If `icrc107_fee_col` is not provided (or set to `null`), all subsequent fees are burned. The `icrc107_set_fee_collector` method MUST return an error in the following cases: -- The caller is not authorized to modify fee collector. -- The provided `Account` is invalid (e.g., the minting account on ledgers, anonymous principal, malformed principal or subaccount). -- The transaction is a duplicate (as determined by its tx hash when deduplication is enabled), resulting in a `SetFeeCollectorError::Duplicate`. +- The caller is not authorized to modify fee collector. +- The provided `Account` is invalid (e.g., the minting account on ledgers, anonymous principal, malformed principal or subaccount). +- The transaction is a duplicate (as determined by its tx hash when deduplication is enabled), resulting in a `SetFeeCollectorError::Duplicate`. + + +### Canonical `tx` Mapping + +A successful call to `icrc107_set_fee_collector` MUST produce a `107feecol` block whose `tx` field is derived deterministically from the method arguments and the caller. The mapping is as follows: + +| Field | Type (ICRC-3 `Value`) | Source | +|-------------------|------------------------|--------| +| `op` | `Text` | Constant `"107_set_fee_col"`. | +| `icrc107_fee_col` | `Array` (Account/Empty)| From the `icrc107_fee_col` argument. If `null`, map to `Array = []`. | +| `created_at_time` | `Nat` | From the `created_at_time` argument (ns since Unix epoch, MUST fit in `nat64`). | +| `caller` | `Blob` | Principal of the caller. | + + + +#### System-Generated `107feecol` Blocks + +For blocks created by the ledger itself (e.g., during an upgrade): + +- MUST include `op` and `icrc107_fee_col`. +- MAY omit `created_at_time` and `caller`. +- If `caller` is included, it SHOULD be the ledger canister principal. ### `icrc107_get_fee_collector` @@ -199,7 +232,73 @@ This method should return the currently active fee collector account: This method strictly returns the last explicitly set value of `icrc107_fee_col`. It does not infer defaults, and if no fee collector was ever set, it returns `opt Account = null`. ---- + + + + + + +#### Example Block + +Below is an example of a valid **fee collector configuration block** that sets the fee collector to a specific account: + +``` +variant { Map = vec { + // Block type: indicates this is a fee collector configuration block + record { "btype"; variant { Text = "107feecol" }}; + // The user's intended transaction + record { "tx"; variant { Map = vec { + record { "op"; variant { Text = "107_set_fee_col" } }; // Operation name, as per schema + record { "icrc107_fee_col"; variant { Array = vec { + variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01"}; // Owner principal + variant { Blob = blob "\06\ec\cd\3a\97\fb\a8\5f\bc\8d\a3\3e\5d\ba\bc\2f\38\69\60\5d\c7\a1\c9\53\1f\70\a3\66\c5\a7\e4\21" }; // Subaccount + }} }; + record { "created_at_time"; variant { Nat = 1_750_951_727_000_000_000 : nat } }; // Timestamp for deduplication (June 27, 2025, 15:28:47 UTC) + record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Caller principal + }} }; + // Timestamp: indicates when the block was created + record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; + // Parent hash: links this block to the previous block in the chain + record { "phash"; + variant { + Blob = blob "\d5\c7\eb\57\a2\4e\fa\d4\8b\d1\cc\54\9e\49\c6\9f\d1\93\8d\e8\02\d4\60\65\e2\f2\3c\00\04\3b\2e\51" + }}; +}}; +``` +If one wanted to set the fee collector to be the default account of principal +identified by `"\00\00\00\00\02\00\01\0d\01\01"`, then the +`"icrc107_fee_col"` field within `tx` should be set to: + +variant { Array = vec { variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01"} } } + + + + +A block that explicitly sets fee burning by removing the fee collector (i.e., all fees are burned from this point onward): + +``` +variant { Map = vec { + // Block type: indicates this is a fee collector configuration block + record { "btype"; variant { Text = "107feecol" }}; + // The user's intended transaction, from which the hash is computed + record { "tx"; variant { Map = vec { + record { "op"; variant { Text = "107_set_fee_col" } }; // Operation name, as per schema + record { "icrc107_fee_col"; variant { Array = vec { + // Empty array to signify burning fees + }}}; + record { "created_at_time"; variant { Nat = 1_750_951_728_000_000_000 : nat } }; // Timestamp for deduplication + record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" } }; // Caller principal + }} }; + // Timestamp: indicates when the block was created + record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat } }; + // Parent hash: links this block to the previous block in the chain + record { "phash"; + variant { + Blob = blob "\2d\86\7f\34\c7\2d\1e\2d\00\84\10\a4\00\b0\b6\4c\3e\02\96\c9\e8\55\6f\dd\72\68\e8\df\8d\8e\8a\ee" + }}; +}}; +``` + ## Reporting Compliance