Skip to content

feat(ICRC-Ledger): DEFI-1687: implement ICRC-107 fee collector#8217

Open
maciejdfinity wants to merge 103 commits intomasterfrom
maciej-ledger-feecol
Open

feat(ICRC-Ledger): DEFI-1687: implement ICRC-107 fee collector#8217
maciejdfinity wants to merge 103 commits intomasterfrom
maciej-ledger-feecol

Conversation

@maciejdfinity
Copy link
Contributor

@maciejdfinity maciejdfinity commented Jan 5, 2026

With this change the ICRC ledger now implements the ICRC-107 fee collector standard as described in https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-107/ICRC-107.md. The legacy fee collector is not longer supported by the ledger. This results in the following notable changes:

  • If a fee collector is present, approval fees are also collected.
  • If an existing ledger has a legacy fee collector configured, the legacy fee collector will be converted to the ICRC-107 fee collector during the upgrade to this or later version.

The fee collector can be configured using the existing fee_collector_account init argument and change_fee_collector upgrade argument.

Since the ICRC-107 fee collector change is not backwards compatible, the ledger upgraded to this version cannot be downgraded to an earlier version.

@maciejdfinity maciejdfinity changed the title add set fee collector arg and error feat(ICRC-Ledger): DEFI-1687: implement ICRC-107 fee collector Jan 5, 2026
@github-actions github-actions bot added the feat label Jan 5, 2026
Base automatically changed from maciej-schema-feecol to master January 7, 2026 14:03
withdrawal_id: cketh_block_index.clone(),
burn_in_block: ckerc20_block_index.clone(),
reimbursed_in_block: Nat::from(3_u8),
reimbursed_in_block: Nat::from(4_u8),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the ICRC ledger is installed with a 107 fee collector, the first block is the fee collector setting block and therefore all block indexes that are tested need to be increased by 1.

index_status,
ic_icrc1_index_ng::Status {
num_blocks_synced: Nat::from(0_u8)
num_blocks_synced: Nat::from(1_u8)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the ICRC ledger is installed with a 107 fee collector, the first block is the fee collector setting block and therefore even a ledger with no transactions will contain one block.

@maciejdfinity maciejdfinity marked this pull request as ready for review February 3, 2026 13:29
@maciejdfinity maciejdfinity requested review from a team as code owners February 3, 2026 13:29
calls: 1
instructions: 54416296904
heap_increase: 264
instructions: 54937060262
Copy link
Contributor Author

Choose a reason for hiding this comment

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

u64 diffs:
Only significant changes:

status name calls ins ins Δ% HI HI Δ% SMI SMI Δ%
+/- bench_icrc1_transfers::icrc2_approve 1 19.65B +6.57% 24 -4.00% 128 0.00%
- bench_icrc1_transfers::icrc3_get_blocks 1 7.84M -7.01% 0 0.00% 0 0.00%

u256 diffs:
Only significant changes:

status name calls ins ins Δ% HI HI Δ% SMI SMI Δ%
+/- bench_icrc1_transfers::icrc2_approve 1 20.55B +6.23% 25 -13.79% 128 0.00%
+ bench_icrc1_transfers::icrc2_transfer_from 1 21.03B -1.86% 4 +33.33% 0 0.00%
+/- bench_icrc1_transfers::icrc1_transfer 1 12.64B -2.20% 34 +6.25% 0 0.00%
- bench_icrc1_transfers::icrc3_get_blocks 1 8.22M -7.89% 0 0.00% 0 0.00%

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The u64 diffs make some sense, icrc2_approve now also collects fees, so it uses a bit more instructions. The created blocks don't contain the fee collector or fee collector block index, which results in less heap usage for icrc2_approve and less instructions for decoding the blocks in case of icrc3_get_blocks.

Copy link
Contributor Author

@maciejdfinity maciejdfinity Feb 4, 2026

Choose a reason for hiding this comment

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

For u256, the instruction diffs are similar for icrc2_approve and icrc3_get_blocks. We also see improvements for icrc1_transfer and icrc2_transfer_from (in case of u64 they were significant but below threshold). The latter might be caused by the fact that the fee collector is stored directly as Option<Account> and previously it was stored in Option<FeeCollector<Account>>. The heap diffs are less consistent, though.

Copy link
Contributor

@mbjorkqvist mbjorkqvist left a comment

Choose a reason for hiding this comment

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

Thanks @maciejdfinity! I have a few comments and questions, but overall this looks very good!

"ck_btc_ledger": "ic-icrc1-ledger.wasm.gz",
"ck_btc_ledger_v1": "ic-icrc1-ledger.wasm.gz",
"ck_btc_ledger_v3": "ic-icrc1-ledger.wasm.gz",
"ck_btc_ledger_v5": "ic-icrc1-ledger.wasm.gz",
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to rename this to something more descriptive, and/or perhaps add a comment somewhere (I think comments don't work in mainnet-canister-revisions.json, but here would be ok?)? Especially since the v5 doesn't correspond to the LEDGER_VERSION, so it can be confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! Maybe we could change it to correspond to the ledger versions, i.e.:
v1 -> no_mem_mgr
v3 -> v1
v5 -> v3
WDYT?

Comment on lines 30 to 33
"ck_btc_ledger_v5": {
"rev": "512cf412f33d430b79f42330518166d14fc6884e",
"sha256": "901bc548f901145bd15a1156487eed703705794ad6a23787eaa04b1c7bbdcf48"
},
Copy link
Contributor

Choose a reason for hiding this comment

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

This points to ledger-suite-icrc-2025-04-14. Is there a particular reason for this specific version, or could we use ledger-suite-icrc-2025-10-27, which is the minimum version we point to from the currently most recent version ledger-suite-icrc-2026-02-02?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No reason, I just used the same version as current mainnet ledger :) Changed!

Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to move all the fee collector-related tests to a separate file? rs/ledger_suite/icrc1/index-ng/tests/tests.rs is already over 2000 lines.

Also, would it make sense to add tests for the following (unless they already exist and I missed them):

  • Trying to set the fee collector account to the minting account, the anonymous account, and an invalid account (e.g., an account with an invalid principal). For the init, upgrade, and endpoint fee collector setting paths. IIUC, there's a check for the minting account in the init and upgrade paths, but not for the endpoint?

Comment on lines +152 to +164
// Downgrade to mainnet is currently not possible. The rest of the test can
// be uncommented again, once the PR that introduced this line is on mainnet.

// // Downgrade the ledger to the mainnet version that does not support ICRC-106
// env.upgrade_canister(canister_id, mainnet_ledger_wasm, encoded_empty_upgrade_args)
// .expect("should successfully downgrade ledger canister");
// assert_index_not_set(&env, canister_id, false);

// // Upgrade to a ledger version that supports ICRC-106, but do not set the index principal
// let encoded_empty_upgrade_args = Encode!(&encode_empty_upgrade_args()).unwrap();
// env.upgrade_canister(canister_id, ledger_wasm, encoded_empty_upgrade_args)
// .expect("should successfully upgrade ledger canister");
// assert_index_not_set(&env, canister_id, true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it help to have a ticket in the ICRC-107 epic to remember to uncomment this, and the other snippet in rs/ledger_suite/icrc1/tests/upgrade_downgrade.rs, once this has been released and deployed to mainnet? Or will the tests fail anyway once that happens, and uncommenting cannot be forgotten?

maciejdfinity and others added 2 commits February 5, 2026 15:29
Co-authored-by: Mathias Björkqvist <mathias.bjorkqvist@dfinity.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants