-
Notifications
You must be signed in to change notification settings - Fork 7
Bw/overview #160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
bogwar
wants to merge
39
commits into
main
Choose a base branch
from
bw/overview
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+443
−0
Draft
Bw/overview #160
Changes from all commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
7dec143
ICRC-107 initial draft
bogwar f45dbed
bool -> text for metadata
bogwar 63bff20
bool -> text for metadata
bogwar b5bae30
option 1 from the discussion
ef25f9e
new versions
fc15a30
pass
bogwar d2a50c3
final draft icrc-107
bogwar b24b937
deleted old version of icrc107
bogwar 88617a3
first pass over Thomas' comments
bogwar d58a821
implemented WG decisions
bogwar 19764d9
minor edits
bogwar 023326e
minor edits
bogwar d33d2af
eliminated fee_burn
bogwar 199280b
minor changes
bogwar 86b676a
clarified what fee_ops = [] means
bogwar e6e4122
new summary
bogwar 4654feb
included examples, legacy fee collection logic, compliance reporting
bogwar 06e6d5d
one more pass
bogwar eb7d6a4
further clarifications on legacy behaviour
bogwar d3accbf
typos
bogwar 12eb7ca
typo
bogwar 9582f01
Update ICRCs/ICRC-107/ICRC-107.md
bogwar 0944115
Update ICRCs/ICRC-107/ICRC-107.md
bogwar 100b532
addressed comments
bogwar 597195c
rewrite legacy part
bogwar 1cef2a6
rewrite
bogwar 1f30ad3
introduced tx structure
bogwar aeac056
fixed an error type
bogwar 92436c8
fixed example
bogwar b81f013
final(?) pass
bogwar dd064ab
alignment with ICRC-3 refinements
bogwar fcd51d3
clarified the sematnics and aligned wiht ICRC-3
bogwar 589b4bd
addressed comments from revies & added clarifications
bogwar 34ef399
restructured the semantics section for icrc107_set_fee_collector
bogwar 463fdbf
updated examples
bogwar b87d0db
minor typos
bogwar 968e4f1
icrc107_set_fee_col --> 107set_fee_col in blocks and icrc107_fee_col …
bogwar 9e178d3
standards overview
bogwar 6a44362
initial table/overview
bogwar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,385 @@ | ||
| # ICRC-107: Fee Management | ||
|
|
||
| ## Introduction & Motivation | ||
|
|
||
| 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. | ||
|
|
||
| This inconsistency creates challenges: | ||
|
|
||
| - 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. | ||
|
|
||
| **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. | ||
|
|
||
| ## Overview | ||
|
|
||
| ICRC-107 introduces a global on-chain fee collection setting recorded in the ledger via a new block type `btype = "107feecol"`. This setting determines whether the effective fee of fee-charging blocks is credited to a designated account or burned. All changes are recorded on-chain for transparent, interoperable processing by wallets and explorers. | ||
|
|
||
|
|
||
| Specifically, ICRC-107 defines: | ||
|
|
||
| - A fee collection configuration that specifies the collector account (`fee_collector`), applying to all subsequent blocks. | ||
| - A new block type (`btype = "107feecol"`) for setting `fee_collector` to designate the fee collector. | ||
| - A backward-compatible mechanism where, until the new mechanism is employed, the legacy collection 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 | ||
|
|
||
| For any block that charges a fee, handle the fee using this **checklist in order**: | ||
|
|
||
| 1) **Find the current `107feecol` setting for this block** | ||
| Look **backwards** from this block’s index to the most recent block with `btype = "107feecol"`. | ||
| - If you find one with `tx.fee_col = <Account>`, the current setting is **Account**. | ||
| - If you find one with `tx.fee_col = []`, the current setting is **Burn**. | ||
| - If you find **none**, then legacy collection logic applies. | ||
|
|
||
| 2) **Apply the setting** | ||
| - **Account found:** credit the **effective fee** to that account. | ||
| - **Burn found (`[]`):** burn the effective fee. | ||
| - **No 107feecol setting found:** apply **legacy `fee_col` logic (see Legacy Fee Collection Mechanism)**. | ||
| - If legacy config designates a collector, credit that account. | ||
| - If no legacy collector is configured, burn the fee. | ||
|
|
||
| > **Important:** “No block with `btype = "107feecol"` is found is **not** the same as finding one with `tx.feecol = []`. | ||
| > `[]` means “we explicitly burn from now on.” | ||
| > “No setting found” means “use legacy rules.” | ||
|
|
||
| 3) **Non-retroactivity** | ||
| A `107feecol` block affects only blocks at its index **and after**. Earlier blocks keep whatever applied at their own indices (legacy or prior `107feecol`). | ||
|
|
||
|
|
||
| ## Common Elements | ||
|
|
||
| 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 = <owner_principal_bytes> }` (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 = <principal_bytes> }`. | ||
|
|
||
| - **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). | ||
|
|
||
| - **Parent Hash** | ||
| Each block includes `phash : Blob`, the hash of its parent block, | ||
| unless it is the genesis block (where `phash` is omitted). | ||
|
|
||
|
|
||
|
|
||
| ## Fee Management | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| ### 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. | | ||
|
|
||
| ### Minimal `tx` Schema | ||
|
|
||
| The minimal fields required to interpret a `107feecol` block: | ||
|
|
||
| | Field | Type (ICRC-3 `Value`) | Required | Description | | ||
| |-------------------|------------------------|----------|-------------| | ||
| | `fee_col` | `Array` (Account/Empty)| Yes | Target fee collector account, or empty (`[]`) to burn fees. | | ||
|
|
||
|
|
||
| ## Semantics of `107feecol` Blocks | ||
|
|
||
| ### Core State Transition | ||
|
|
||
| A `107feecol` block updates the ledger’s global fee collector configuration: | ||
|
|
||
| - If `tx.107fee_col = []` → all subsequent fees are **burned**. | ||
| - If `tx.107fee_col` encodes an account → all subsequent fees are **credited to that account**. | ||
|
|
||
| This configuration applies **to all subsequent blocks** until replaced by another `107feecol`. | ||
|
|
||
|
|
||
| --- | ||
|
|
||
| ### 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.fee_collector = []` → burn the effective fee. | ||
| • If the most recent `107feecol` has `tx.fee_collector = <Account>` → 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). | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| # ICRC-107: Methods for Setting and Getting Fee Collector Information | ||
|
|
||
|
|
||
| ### `icrc107_set_fee_collector` | ||
|
|
||
| This method allows a ledger controller to update the fee collector. It modifies the `fee_collector` account, which determines where collected fees are sent. The updated settings are recorded in a new block (of type `107feecol`) added to the ledger. | ||
|
|
||
| ``` | ||
|
|
||
| type Account = record { | ||
| owner: principal; | ||
| subaccount: opt blob; | ||
| }; | ||
|
|
||
| type SetFeeCollectorArgs = record { | ||
| fee_collector: opt Account; | ||
| created_at_time: nat64; | ||
| }; | ||
|
|
||
| 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_collector: (SetFeeCollectorArgs) -> (variant { Ok: nat; Err: SetFeeCollectorError }); | ||
|
|
||
| ``` | ||
|
|
||
| ### `icrc107_set_fee_collector` — Semantics | ||
|
|
||
| - **Authorization** | ||
| The method **MUST** be callable only by a **controller** of the ledger or other explicitly authorized principals. Unauthorized calls **MUST** fail with `AccessDenied`. | ||
|
|
||
| - **Effect (on success, non-retroactive)** | ||
| The ledger **MUST** append a new block with `btype = "107feecol"`. | ||
| **The block’s `tx` is a top-level field of that `107feecol` block** and **MUST** be constructed **exactly** as defined in **Canonical `tx` Mapping** (same keys, types, and value encodings) and embedded in the block. | ||
| The value in `tx.fee_collector` becomes the active fee-collection setting **from that block’s index onward**; earlier blocks **MUST NOT** be affected. | ||
|
|
||
| - If `fee_collector` is **provided** (`Account`), all effective fees for this and later blocks **MUST** be **credited** to that account, until changed by a later `107feecol`. | ||
| - If `fee_collector` is **absent** (`null`), it **MUST** be encoded as an empty array `[]` in `tx`; all effective fees for this and later blocks **MUST** be **burned**, until changed by a later `107feecol`. | ||
|
|
||
| - **Return value** | ||
| On success, the method **MUST** return `Ok(index : nat)`, where `index` is the block index of the newly appended `107feecol` block. The new configuration **MUST** apply starting **at** `index` (non-retroactive). | ||
|
|
||
| - **Multiple updates over time** | ||
| If multiple `107feecol` blocks exist, the setting that applies to any block **MUST** be the **last** `107feecol` **at or before** that block’s index. | ||
|
|
||
| - **Deduplication & idempotency** | ||
| The ledger **MUST** perform deduplication (e.g., using `created_at_time` and any implementation-defined inputs). | ||
| If a duplicate is detected, the ledger **MUST NOT** append a new block and **MUST** return `Err(Duplicate { duplicate_of = <index> })`. | ||
|
|
||
| - **Error cases (normative)** | ||
| The method **MUST** return an error in these cases: | ||
| - **AccessDenied** — caller is not authorized to modify the fee collector. | ||
| - **InvalidAccount** — provided collector account is invalid (e.g., minting account, anonymous principal, malformed principal/subaccount). | ||
| - **Duplicate** — a semantically identical transaction was already accepted (per the ledger’s deduplication rules); response **MUST** include `duplicate_of` with the existing block index. | ||
| - **GenericError** — any other failure that prevents constructing or appending a valid `107feecol` block. | ||
|
|
||
| - **Clarifications** | ||
| `tx.fee_collector = []` (explicit **burn**) is **not** the same as “no `107feecol` has ever been set” (which implies **legacy behavior** applies until the first ICRC-107 setting appears). | ||
|
|
||
|
|
||
| --- | ||
|
|
||
| ### Canonical `tx` Mapping (normative and referenced above) | ||
|
|
||
| **Scope:** This mapping defines the **exact** content of the **top-level** `tx` field that MUST be embedded in every `107feecol` block created by `icrc107_set_fee_collector` (or by the ledger itself in the system-generated case). | ||
|
|
||
| | Field | Type (ICRC-3 `Value`) | Source / Encoding Rule | | ||
| |-------------------|-------------------------|-----------------------------------------------------------------------------------------| | ||
| | `op` | `Text` | **Constant** `"107set_fee_collector"`. | | ||
| | `fee_collector` | `Array` (Account/Empty) | From the `fee_collector` argument. If `null`, **encode as** `Array = []`. If present, encode the Account. | | ||
| | `created_at_time` | `Nat` | From the `created_at_time` argument (ns since Unix epoch; **MUST** fit in `nat64`). | | ||
| | `caller` | `Blob` | Principal of the caller. | | ||
|
|
||
| > **Conformance note:** Hashing uses **representation-independent hashing (RIH)**, so **field order and concrete map encoding do not affect the hash**. What **does** matter is: | ||
| > - The **presence** (or absence) of the fields listed above. | ||
| > - The **exact values** of those fields, encoded per ICRC-3 `Value`. | ||
|
|
||
|
|
||
| #### System-Generated `107feecol` Blocks | ||
|
|
||
| For blocks created by the ledger itself (e.g., during an upgrade/migration): | ||
|
|
||
| - MUST include top-level `tx.fee_collector` (encoded as above). | ||
| - MAY omit `tx.created_at_time` and `tx.caller`. | ||
| - If `tx.caller` is included, it SHOULD be the ledger canister principal. | ||
|
|
||
|
|
||
|
|
||
| ### `icrc107_get_fee_collector` | ||
|
|
||
| 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_collector: () -> (variant { Ok: opt Account; Err: record { error_code : nat; message : text } }) query; | ||
| ``` | ||
|
|
||
|
|
||
| This method should return the currently active fee collector account: | ||
|
|
||
| - If the response is `null`, fees are burned. This corresponds to `fee_collector = variant { Array = vec {}}` on-chain. | ||
| - If the response is a valid `Account`, fees are collected by that account. This corresponds to `fee_collector` being set to the ICRC-3 representation of the account on-chain. | ||
|
|
||
| This method strictly returns the last explicitly set value of `fee_collector`. It does not infer defaults, and if no fee collector was ever set, it returns `opt Account = null`. | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| #### Example: set fee collector to a specific account (produced by `icrc107_set_fee_collector`) | ||
|
|
||
| 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 | ||
| record { "btype"; variant { Text = "107feecol" }}; | ||
|
|
||
| // Top-level tx (constructed per Canonical `tx` Mapping) | ||
| record { "tx"; variant { Map = vec { | ||
| record { "op"; variant { Text = "107set_fee_collector" }}; | ||
| record { "fee_collector"; variant { Array = vec { | ||
| // Account = [owner, subaccount?] | ||
| variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01" }; // owner principal bytes | ||
| 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" } // 32-byte subaccount | ||
| }}}; | ||
| record { "created_at_time"; variant { Nat = 1_750_951_727_000_000_000 : nat }}; // ns since Unix epoch | ||
| record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" }}; // caller principal bytes | ||
| }}}; | ||
|
|
||
| // Standard block metadata | ||
| record { "ts"; variant { Nat = 1_741_312_737_184_874_392 : nat }}; | ||
| 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_collector"` field within `tx` should be set to: | ||
|
|
||
| `variant { Array = vec { variant { Blob = blob "\00\00\00\00\02\00\01\0d\01\01"} } }` | ||
|
|
||
|
|
||
| #### Example: explicitly burn all fees from this point onward (produced by `icrc107_set_fee_collector`) | ||
|
|
||
|
|
||
| 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 | ||
| record { "btype"; variant { Text = "107feecol" }}; | ||
|
|
||
| // Top-level tx (constructed per Canonical `tx` Mapping) | ||
| record { "tx"; variant { Map = vec { | ||
| record { "op"; variant { Text = "107set_fee_collector" }}; | ||
| record { "fee_collector"; variant { Array = vec { }}}; // [] means "burn from now on" | ||
| record { "created_at_time"; variant { Nat = 1_750_951_728_000_000_000 : nat }}; | ||
| record { "caller"; variant { Blob = blob "\00\00\00\00\00\00\00\00\01\01" }}; | ||
| }}}; | ||
|
|
||
| // Standard block metadata | ||
| 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" }}; | ||
| }} | ||
| ``` | ||
|
|
||
|
|
||
|
|
||
| ## Reporting Compliance | ||
|
|
||
| Compliance with the current standard is reported as follows. | ||
|
|
||
| ### 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: | ||
|
|
||
| ``` | ||
| variant { Vec = vec { | ||
| record { | ||
| "name"; variant { Text = "ICRC-107" }; | ||
| "url"; variant { Text = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-107.md" }; | ||
| } | ||
| }}; | ||
| ``` | ||
|
|
||
| ### 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 | ||
|
|
||
| ``` | ||
| variant { Vec = vec { | ||
| record { "name"; variant { Text = "107feecol" }; "url"; variant { Text = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-107.md" } }; | ||
| }}; | ||
| ``` | ||
|
|
||
| ## Legacy Fee Collection Mechanism | ||
|
|
||
| The **DFINITY-maintained** ICRC ledgers include a fee collection mechanism which, for completeness, is described below. | ||
|
|
||
| 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`) are burned in DFINITY-maintained ICRC-3 ledgers. | ||
|
|
||
|
|
||
| New implementations SHOULD avoid using `fee_col` and instead use `fee_collector` for all fee collection settings. Legacy behavior is provided for backward compatibility only and MAY be deprecated in future versions of this standard. | ||
|
|
||
| ### Handling Fee Collection for ICRC-1 and ICRC-2 Blocks | ||
|
|
||
| To determine who collects the fee in a block: | ||
|
|
||
| 1. Check for fee collector configuration | ||
|
|
||
| - If a previous block set `fee_collector`, the ledger uses the behavior specified by this standard. | ||
|
|
||
| 2. If no `fee_collector` setting exists (legacy behavior): | ||
|
|
||
| - 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. | ||
| - If neither `fee_col` nor `fee_col_block` are specified, then the fee is burned. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grammatical error with duplicated 'is'. Should be either 'No block with
btype = \"107feecol\"found is not' or 'Finding no block withbtype = \"107feecol\"is not'.