Deprecating the ledger_notify Flow for Minting Cycles in Favor of cmc_notify

TL;DR

Minting cycles from ICP to create or top up canisters can currently be done via two distinct flows:

  1. Legacy Flow: transferledger_notify
  2. New Flow: transfercmc_notify

The new flow (cmc_notify) offers superior security guarantees, greater flexibility, and eliminates a critical coupling between the ICP Ledger and subnet operations. This post announces our plan to deprecate and eventually disable the legacy ledger_notify flow.

Who Should Be Concerned?

If you are using the notify() endpoint on the ICP Ledger, this post is relevant to you. If you are not aware of this functionality, you most likely do not need to take any action.

Legacy Flow: ledger_notify (To Be Deprecated)

How It Works

  1. Transfer:
  • Use either the legacy send() method or the icrc1_transfer() method to transfer tokens to a specific subaccount of the Cycles Minting Canister (CMC).
  • The subaccount encodes a principal:
    1. If creating a new canister, this is the principal of the intended controller.
    2. If topping up an existing canister, this is the principal of the target canister.
  • The transaction memo specifies whether the transfer is for canister creation or top-up.
  • The transaction is recorded in the ledger at block index idx.
  1. Ledger Notify: The user (or anyone) calls notify() on the Ledger, passing the transaction index idx.
  • The Ledger performs checks and then forwards the transaction details to the CMC.
  1. CMC Processing: The CMC verifies the memo and the intended action. If valid, it mints cycles at the ICP/XDR exchange rate and executes the requested operation.
  2. Result Propagation: The CMC returns the result to the Ledger, which then relays it back to the notify() caller.

Problems with ledger_notify Flow

  1. Ledger Coupling to Downstream Canister Calls: The Ledger must wait for responses from the CMC (and potentially other subnetworks). If a subnet stalls, this leaves open call contexts on the Ledger, blocking safe upgrades.
  2. Limited Deduplication Window: To prevent double spending, the Ledger maintains a record of past notify() calls for only 24 hours. If a transaction is not successfully notified within this window, it cannot be used later, effectively causing the loss of those tokens unless manually recovered.
  3. Lost Responses are Unrecoverable: Since the result of notify() is returned via the Ledger, losing this response due to frontend failures or network issues means there is no way to recover the canister ID or cycle top-up result. The Ledger does not store the outcome persistently, making it impossible to retrieve the lost data.

New Flow: cmc_notify (The Superior Alternative)

How It Works

  1. Transfer: The user transfers ICP tokens to a specific subaccount of the CMC, as above.
  2. CMC Notify: The user directly notifies the CMC, specifying the transaction index idx via one of two distinct endpoints:
    • notify_create_canister() (for canister creation)
    • notify_top_up_canister() (for canister top-up)
  • The notification explicitly includes additional details, such as the intended controller or canister settings for canister creation or the principal of the canister to be topped up.
  1. CMC Processing:
  • The CMC fetches the transaction from the Ledger.
  • Ensures that the memo is valid and that the notifier is the transaction initiator.
  • Mints cycles and executes the requested operation.
  1. Result Propagation: The CMC returns the result directly to the caller without involving the Ledger.

Advantages of cmc_notify Flow

  1. Eliminates Ledger Coupling: The ICP Ledger is no longer involved in downstream calls, making ledger upgrades safer and more flexible.
  2. Better Deduplication Handling:
  • The CMC itself tracks which transactions have already been processed.
  • It retains records for 1 million transactions, significantly improving over the previous 24-hour limit.
  • If a notification is lost, the result can be recovered by simply retrying the notify call.
  1. Recoverable Results: The CMC tracks past transactions and their results, so if a response is lost due to frontend connectivity issues, a user can simply reissue cmc_notify call with the same transaction index to retrieve the original result.
  2. Enhanced Canister Creation Flexibility:
  • Users can specify the subnet type or even a specific subnet for canister creation.
  • Supports defining initial canister settings at creation time.

How to Migrate

To transition from the legacy ledger_notify flow to the new cmc_notify flow, simply replace the Ledger notify() call with one of the following end points on the CMC:

  • notify_create_canister() (for canister creation)
  • notify_top_up_canister() (for canister top-up)

Next Steps & Action Required

We encourage developers to migrate their integrations as soon as possible. A formal deprecation schedule will be announced, after which the legacy ledger_notify flow will be disabled.

Action Required:

If you are using the ledger_notify flow, please comment below or reach out to us with the following details:

  • Confirmation that you are currently using ledger_notify
  • Your estimated timeline for transitioning to cmc_notify
  • Any blockers or concerns regarding the migration

This will help us ensure a smooth transition for all affected users. For questions or assistance, feel free to engage in the discussion below!

1 Like

Final Steps Toward Deprecating ledger_notify: Callers Will Be Logged

As part of the deprecation of the legacy ledger_notify flow for minting cycles, we have now instrumented the notify() endpoint on the ICP Ledger to log the principal of each caller. If we can somehow identify the callers we will reach out directly to help with the transition.

Why We’re Doing This

The new cmc_notify flow—based on direct notification to the Cycles Minting Canister (CMC)—offers:

  • Stronger security guarantees
  • Improved reliability and resilience
  • A more flexible and decoupled architecture
    See our post above for a complete overview and migration guide.

Upcoming Timeline

We are targeting end of June for permanent removal of the notify() endpoint from the Ledger canister. After that point, the legacy flow will no longer function.

If you are still using ledger_notify, now is the time to switch to notify_create_canister() or notify_top_up_canister() on the CMC.

Help Us Reach You

If you’re using ledger_notify and haven’t already, please reply to this post (or the previous one) indicating:

  • That you are using ledger_notify
  • Your planned migration timeline
    This will help ensure we support you through the transition.

Thank you to everyone who has already updated their integration!

5 Likes

Just in case useful, small typo, I think it’s notify_top_up.

2 Likes

Thanks @peterparker; i’ve fixed it

1 Like

Has notify_mint_cycles been abandoned? I didn’t see it in ic-js’s cmc.

I don’t think the notify_mint_cycles function has ever been implemented in the ic-js cmc. Do you need it?

I want to convert ICP to cycles via the web page and create a container, but at the same time, considering that users may need to manage multiple containers, I want to store cycles in a place where they can be called upon.

Perhaps this logic can be supplemented with the notify_top_up method? But my first reaction is still to convert ICP to cycles first, and then call upon the cycles ledger.

I’ve actually never implemented that specific flow. I usually “just” do an ICP transfer followed by notify_top_up - e.g. see Juno’s (Rust) code or this (JS) implementation in NNS-dapp’s code). So I’m probably not the best person to answer your question.

The only thing I can confirm is that notify_mint_cycles is currently not implemented in ic-js. If it’s something you’d indeed need to use from the frontend, maybe you could open an issue in the repo?

I used ic-js/cmc.notifyCreateCanister in my testing.

This is indeed a very useful method, but why is the return value like this? Is it because the subnet has been upgraded recently?

 Caught exception while attempting to read state: Server returned an error:
  Code: 400 ()
  Body: Invalid request expiry: Specified ingress_expiry not within expected range: Minimum allowed expiry: 2025-06-26 09:13:20.128896697 UTC, Maximum allowed expiry: 2025-06-26 09:18:50.128896697 UTC, Provided expiry:        2025-06-26 09:13:00 UTC

Looks like you’re running into an error. Maybe start a new thread and share a bit of code and details about the setup — it’s a bit difficult to say exactly what’s going wrong without more details.

Hello, Peter, thank you for your reply.

I tested the interface today and found no problems.

I guess the problem was caused by yesterday’s subnet upgrade maintenance, because I encountered the same error yesterday when using the II-related interface, and then I logged back into II and found that II was showing temporary maintenance for 5-10 minutes.

1 Like

Oh great to hear, I did not thought about that! :+1:

1 Like