Skip to content

Conversation

@bogwar
Copy link
Contributor

@bogwar bogwar commented Mar 25, 2025

Thank you for your contribution to the IC Developer Portal. This repo contains the content for https://internetcomputer.org and the ICP Developer Documentation, https://internetcomputer.org/docs/.

If you are submitting a Pull Request for adding or changing content on the ICP Developer Documentation, please make sure that your contribution meets the following requirements:

@bogwar bogwar requested a review from a team as a code owner March 25, 2025 14:45
@github-actions
Copy link
Contributor

github-actions bot commented Mar 25, 2025

🤖 Here's your preview: https://tai3p-vaaaa-aaaam-abema-cai.icp0.io

@bogwar bogwar changed the title This is a specification for the cycles minting canister. A specification for the cycles minting canister. Mar 25, 2025

- Topping up existing canisters.
- Creating new canisters.
- Minting and depositing minted cycles into cycle ledger-managed accounts.

Choose a reason for hiding this comment

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

"cycle ledger-managed" has hyphenation that is confusing.

Perhaps "into accounts managed by the Cycles Ledger" would be clearer?

Copy link
Contributor

Choose a reason for hiding this comment

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

+1

- Minting and depositing minted cycles into cycle ledger-managed accounts.
- Fetching exchange rates and subnet configuration.

The CMC is governed solely by the [NNS (Network Nervous System)](https://internetcomputer.org/nns/) and receives configuration updates through proposals.

Choose a reason for hiding this comment

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

"receives configuration updates" - this is true, but not exhaustive. It also receives new functionality, bug fixes, etc.

Something more general, like "updates" or "updates to functionality and configuration" might be clearer to those who aren't already IC aficionados.


## Configuration parameters

- **Conversion rate source**: The ICP/XDR exchange rate is **pushed** into the CMC by a dedicated [exchange rate canister](/references/exchange-rate-canister). This data reflects live market pricing and determines the ICP→cycles conversion.

Choose a reason for hiding this comment

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

The CMC actually pulls the info from the XRC

- `get_principals_authorized_to_create_canisters_to_subnets`: Indicates which principals are permitted by governance to create canisters on specific, non-default subnets.
- `get_default_subnets`: Returns the subnets designated by governance for general-purpose public canister creation.
- `total_cycles_minted`: Returns the total number of cycles ever minted by the CMC.
- `get_build_metadata`: Displays internal version and build information for the CMC.

Choose a reason for hiding this comment

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

These are supposed to be removed at some point (get_build_metadata endpoints), as we now attach that information as metadata to the WASMs.

- The **memo field** must explicitly indicate the intent to create a canister. This can be expressed as:
- A legacy 64-bit unsigned integer memo with value (in decimal):
```
1095062083

Choose a reason for hiding this comment

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

How was this derived? It would be good to have an explanation, so it can be checked. I believe it should work out to "CREA" but in little endian if I remember correctly.

```
- Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to:
```
"\50\55\50\54\00\00\00\00" // ASCII "TUPT" + 4 zero bytes

Choose a reason for hiding this comment

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

Suggested change
"\50\55\50\54\00\00\00\00" // ASCII "TUPT" + 4 zero bytes
"\50\55\50\54\00\00\00\00" // ASCII "TPUP" + 4 zero bytes

Choose a reason for hiding this comment

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

Hex may also need to be updated but it actually looks right...

pub const MEMO_TOP_UP_CANISTER: Memo = Memo(0x50555054); // == 'TPUP'

- If a previous `notify_*` call has already successfully processed a transfer at the same block height, the new call returns the same cached result (Ok or a terminal Err like `InvalidTransaction` or `Refunded`).
- To support safe retries, the result of a successful call or a terminal error is **cached** for a bounded (but generous) number of blocks (`MAX_NOTIFY_HISTORY`). Calls referring to blocks older than the cache window will fail with `NotifyError::TransactionTooOld`.

These semantics ensure that all `notify_*` methods are idempotent. Clients can safely retry using the original parameters (memo, block height, destination account).

Choose a reason for hiding this comment

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

nit: idempotent within the supported block window.

- Topping up existing canisters.
- Creating new canisters.
- Minting and depositing minted cycles into cycle ledger-managed accounts.
- Fetching exchange rates and subnet configuration.
Copy link
Contributor

Choose a reason for hiding this comment

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

here I was wondering: is this exchange rate just between cycles:ICP or more? (I thought it used to be more but now we have a separate exchange rate canister. But since I am also not sure, I wonder if others might have the same question :)

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 maybe also be useful to already say here what subnet configuration is here (because it is very little and specific i.e., just the subnet types, right?)

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add links to the docs on the XRC canister and subnet types for more info


## `notify_create_canister`

Uses tokens from an ICP transfer to **mint cycles**, which are then used to **create a new canister**. The CMC first verifies that the transfer has been recorded in the ICP ledger and that it matches the expected structure. If the `subnet_selection` field is omitted, the CMC selects a suitable subnet at random from the list of subnets available to the `controller` (either default or authorized).
Copy link
Contributor

Choose a reason for hiding this comment

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

is the controller always the sender of the ICP? Maybe we could add how the controller is defined?

Copy link
Contributor

Choose a reason for hiding this comment

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

This might also make the first point below clearer

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Isn't this the change that Daniel implemented to deal with a security issue? I thought only the nns-dapp is allowed to break this rule.

- The **destination account** of the ICP transfer must be the CMC's account with a **subaccount derived from the intended controller's principal** (see the section on [Encoding a principal into a subaccount](#encoding-a-principal-into-a-subaccount)).
- The **principal encoded in the subaccount is the intended controller** of the created canister.
- Only that principal is authorized to call `notify_create_canister` and can set **arbitrary canister settings**, including additional controllers.
- **Exception**: The [NNS dapp](https://nns.ic0.app/) is permitted to call `notify_create_canister` on behalf of users and acts as a proxy in that case.
Copy link
Contributor

Choose a reason for hiding this comment

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

in this case, how is the controller determined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I only remember the old flow where the controller is the whatever is encoded in the subaccount. I don't think we've changed that.


The CMC expects the payment to follow a strict structure that encodes the **intent** and **destination**:

- The subaccount used in the transfer must be derived from the caller principal using the standard 32-byte encoding (see “Shared Logic” section).
Copy link
Contributor

Choose a reason for hiding this comment

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

do we want to add a link to the section here as in other places?


All `notify_*` methods (such as `notify_create_canister` and `notify_top_up`) follow the same core semantics:

- Before processing any specific operation (like create or top-up), the CMC checks the memo of the incoming ICP transfer identified by `block_index`. If the memo (either the legacy `u64` memo or the ICRC-1 `blob` memo interpreted as a little-endian `u64`) does **not** match one of the recognized operation memos (`1095062083` for create, `1347768404` for top-up, `1414416717` for mint), the CMC attempts to automatically refund the ICP amount (minus the standard ledger transfer fee) back to the sender's account (`from` account in the transfer). If the refund succeeds or fails terminally, the call will return a `NotifyError::Refunded` or `NotifyError::InvalidTransaction` respectively. If the refund attempt fails transiently (e.g., ledger unavailable), the status is cleared, allowing a retry.
Copy link
Contributor

Choose a reason for hiding this comment

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

the retry (last line here) has then to be triggered manually?
so the CMC just tries ones can then stops trying?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, just like the original notify_*, since the ICPs are still with the CMC

type NotifyError = variant {
Refunded: record { reason : text; block_index : opt nat64 }; // Request was invalid; ICP (minus fees) was refunded (or attempted). `block_index` is the refund transaction index, if successful.
InvalidTransaction: text; // Transaction details (e.g., destination, memo) don't match the request, or it was already processed for a different operation.
Processing: null; // The specified block_index is currently being processed by another call. Retry shortly.
Copy link
Contributor

Choose a reason for hiding this comment

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

"Retry shortly" is what we ask the caller who sees this error to do, right?
I was first not sure if this indicates "the canister will retry shortly", so maybe we can make this more precise?


**Key Points:**

- **`Refunded`**: This occurs if the initial ICP transfer had an unrecognized memo (automatic refund), or if the canister creation/top-up/minting failed *after* the ICP transfer was validated (e.g., subnet unavailable, deposit cycles failed).
Copy link
Contributor

Choose a reason for hiding this comment

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

what do these points here refer to? are all of them error reasons? If so, should there maybe be 1 sentence explaining this?

- **`InvalidTransaction`**: The provided `block_index` might correspond to a transaction that doesn't match the request (e.g., wrong memo for the operation, wrong destination subaccount) or was already processed under a *different* `notify_*` call.
- **`Processing`**: Indicates contention. Simply retrying the *exact same call* after a short delay is appropriate.
- **`TransactionTooOld`**: The CMC only keeps a history of recent notifications (`MAX_NOTIFY_HISTORY` entries, currently 1,000,000). Older transactions cannot be processed. The returned value is the oldest `block_index` the CMC *might* still know about.
- **`Other`**: Catches various issues, such as failure to fetch the block from the ledger (`FailedToFetchBlock` error code `1`), internal CMC errors, or authorization issues (`Unauthorized` error code `3` if `notify_create_canister` caller != creator and is not NNS Dapp), bad subnet selection (`BadSubnetSelection` error code `4`).
Copy link
Contributor

Choose a reason for hiding this comment

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

is internal CMC errror code '2' ? Just noticed that we don't give the code for this case and that we left out the nubmer 2 :D

Refunded : record { refund_amount: nat; create_error: text; };
};
```
- `create_error`: Text description of the failure (e.g., "Insufficient cycles attached", "No subnets in which to create a canister", or errors from the IC management canister).
Copy link
Contributor

Choose a reason for hiding this comment

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

do we still call this "IC management canister" even though we should use "ICP" instead of "IC" in most places?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will let @jessiemongeon1 take this one :)

Copy link
Contributor

Choose a reason for hiding this comment

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

It should be the ICP management canister, though in some other contexts we've been using "ICP system API"

- **Purpose**:
- Sets the **default list** of subnets where *any* principal can create canisters (when `who` parameter is `null`).
- Sets a specific list of subnets where a designated `who` principal is authorized to create canisters. If the `subnets` list is empty, the authorization for `who` is removed.
- **Preconditions**: Subnets added to the default or authorized lists cannot already be assigned to a named subnet type (see `update_subnet_type`).
Copy link
Contributor

Choose a reason for hiding this comment

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

is a "named subnet" the same as a "subnet type"? or do we mean sth else where?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess subnet type is accurate


## `get_principals_authorized_to_create_canisters_to_subnets`

Lists principals specifically authorized by NNS Governance to create canisters on non-default subnets, and the corresponding list of subnet principals they are permitted to use.
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if the relationship between default / non-default subnets (with special permissions) and the "named types" (where any principal can deploy AFAIK) is clear. If we want to be extra clear, maybe we could have a small section introducing these different concepts?

compute_allocation : opt nat; // Percentage (0-100) of guaranteed compute capacity
memory_allocation : opt nat; // Memory reserved for the canister, in bytes
freezing_threshold : opt nat; // Number of seconds the canister can go without being topped up before being frozen
// reserved_cycles_limit : opt nat; // (Note: This field might not be directly settable via CMC)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand the note here.
All of this cannot be set by CMC but needs to be set by its controller (NNS root), right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When you create a canister, you can decide on canister settings -- I'll check if reserved_cycles_limit can be set by the CMC canister creation mechanism.

bogwar and others added 6 commits May 13, 2025 14:19
Co-authored-by: Lara Schmid <73884086+LaraAS@users.noreply.github.com>
Co-authored-by: Lara Schmid <73884086+LaraAS@users.noreply.github.com>
Co-authored-by: Lara Schmid <73884086+LaraAS@users.noreply.github.com>
Co-authored-by: Lara Schmid <73884086+LaraAS@users.noreply.github.com>
@marc0olo
Copy link
Member

marc0olo commented Jul 4, 2025

@bogwar @LaraAS bumping this after the recent call I had with Bogdan 😅

@jessiemongeon1 context:

  • we were discussing whether we need to provide a more low-level guide how to deploy canisters programmatically using the cycles ledger without dealing with dfx. we came to the conclusion that such a guide should be added to the docs if possible

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants