From 93332e58da6c29a9b6fc437f5d3b5bd1c3398b61 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Mon, 24 Mar 2025 12:20:26 +0100 Subject: [PATCH 01/24] initial cmc spec file --- docs/references/cmcspec.mdx | 92 +++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/references/cmcspec.mdx diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx new file mode 100644 index 0000000000..49e6b47a4b --- /dev/null +++ b/docs/references/cmcspec.mdx @@ -0,0 +1,92 @@ +import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow"; + +# Cycles Minting Canister (CMC) API + + + +## CMC API Endpoints +The Cycles Minting Canister (CMC) is a system canister on the Internet Computer responsible for minting and managing cycles. It allows users to convert ICP into cycles, top up existing canisters, and create new canisters with an initial cycle balance. The CMC is controlled directly by the NNS. It is upgraded and configured via NNS proposals and has no independent control authority outside of NNS governance. + +## Configuration Parameters +- **Conversion Rate Source**: The conversion rate used by the CMC is dynamically fetched via HTTPS outcalls from an external source. +- **Governance Control**: CMC operations are governed by NNS proposals. + +## API Endpoints +The CMC exposes the following API endpoints for interaction: + + +- [`get_icp_xdr_conversion_rate`](#get_icp_xdr_conversion_rate): Returns the latest ICP/XDR exchange rate. +- [`notify_top_up`](#notify_top_upblock_index-nat-canister_id-principal): Notifies the CMC of an ICP top-up transaction. +- [`notify_mint_cycles`](#notify_mint_cyclesblock_index-nat-to_subaccount-opt-blob-deposit_memo-opt-blob): Mints cycles into a cycles ledger account. +- [`create_canister`](#create_canistersettings-opt-canistersettings-subnet_type-opt-text-subnet_selection-opt-subnetselection): Creates a new canister using attached cycles. + +- [`get_subnet_types_to_subnets`](#get_subnet_types_to_subnets): Returns the mapping of subnet types to subnets. +- [`get_principals_authorized_to_create_canisters_to_subnets`](#get_principals_authorized_to_create_canisters_to_subnets): Returns which principals are authorized to create canisters in which subnets. +- [`get_default_subnets`](#get_default_subnets): Returns the list of default subnets. +- [`get_build_metadata`](#get_build_metadata): Returns versioning and build metadata for the CMC canister. + +### `create_canister(settings: opt CanisterSettings, subnet_type: opt text, subnet_selection: opt SubnetSelection)` +Creates a new canister using cycles attached to the call. The caller becomes the controller unless otherwise specified in settings. + +- **Parameters:** + - `arg: record {` + - `settings: opt CanisterSettings` + - `subnet_type: opt text` (deprecated) + - `subnet_selection: opt SubnetSelection` + - `}` +- **Returns:** + - `variant { Ok: principal; Err: CreateCanisterError }` + +### `get_icp_xdr_conversion_rate()` +Returns the current ICP/XDR conversion rate along with certified data. + +- **Returns:** + - `record { data: IcpXdrConversionRate; hash_tree: blob; certificate: blob }` + +### `get_subnet_types_to_subnets()` +Returns the mapping from subnet types to subnets. + +- **Returns:** + - `record { data: vec (text, vec principal) }` + +### `get_principals_authorized_to_create_canisters_to_subnets()` +Returns principals and which subnets they are allowed to create canisters on. + +- **Returns:** + - `record { data: vec (principal, vec principal) }` + +### `get_default_subnets()` +Returns the default subnet list. + +- **Returns:** + - `vec principal` + +### `get_build_metadata()` +Returns build and version metadata for the CMC. + +- **Returns:** + - `text` + +### `notify_mint_cycles(block_index: nat, canister_id: principal)` +Notifies the CMC that an ICP transfer has occurred with the intent to mint cycles into the specified canister. + +- **Parameters:** + - `arg: record {` + - `block_index: nat` — The block index of the ICP transfer. + - `canister_id: principal` — The principal ID of the canister to receive the minted cycles. + - `}` +- **Returns:** + - `Result` — The number of cycles minted on success. + - `NotifyError` — If the operation fails due to invalid memo, ownership mismatch, or transaction duplication. + +### `notify_top_up(block_index: nat, canister_id: principal)` +Notifies the CMC that an ICP transfer intended to top up a canister has occurred. The CMC verifies and processes the payment before crediting the cycles to the specified canister. + +- **Parameters:** + - `arg: record {` + - `block_index: nat` — The height of the ledger block containing the top-up transaction. + - `canister_id: principal` — The identifier of the canister to be topped up. + - `}` +- **Returns:** + - `Result` — The number of cycles credited on success. + - `NotifyError` — If the notification fails due to an invalid transaction, old transaction, or other issues. From 5fcf45a63560305bb67ba63147e327b0b3fa2986 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Mon, 24 Mar 2025 12:23:18 +0100 Subject: [PATCH 02/24] API overview --- docs/references/cmcspec.mdx | 100 +++++++++--------------------------- 1 file changed, 24 insertions(+), 76 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 49e6b47a4b..4f9b78f0a5 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -4,89 +4,37 @@ import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow"; -## CMC API Endpoints -The Cycles Minting Canister (CMC) is a system canister on the Internet Computer responsible for minting and managing cycles. It allows users to convert ICP into cycles, top up existing canisters, and create new canisters with an initial cycle balance. The CMC is controlled directly by the NNS. It is upgraded and configured via NNS proposals and has no independent control authority outside of NNS governance. +## Overview -## Configuration Parameters -- **Conversion Rate Source**: The conversion rate used by the CMC is dynamically fetched via HTTPS outcalls from an external source. -- **Governance Control**: CMC operations are governed by NNS proposals. - -## API Endpoints -The CMC exposes the following API endpoints for interaction: - - -- [`get_icp_xdr_conversion_rate`](#get_icp_xdr_conversion_rate): Returns the latest ICP/XDR exchange rate. -- [`notify_top_up`](#notify_top_upblock_index-nat-canister_id-principal): Notifies the CMC of an ICP top-up transaction. -- [`notify_mint_cycles`](#notify_mint_cyclesblock_index-nat-to_subaccount-opt-blob-deposit_memo-opt-blob): Mints cycles into a cycles ledger account. -- [`create_canister`](#create_canistersettings-opt-canistersettings-subnet_type-opt-text-subnet_selection-opt-subnetselection): Creates a new canister using attached cycles. - -- [`get_subnet_types_to_subnets`](#get_subnet_types_to_subnets): Returns the mapping of subnet types to subnets. -- [`get_principals_authorized_to_create_canisters_to_subnets`](#get_principals_authorized_to_create_canisters_to_subnets): Returns which principals are authorized to create canisters in which subnets. -- [`get_default_subnets`](#get_default_subnets): Returns the list of default subnets. -- [`get_build_metadata`](#get_build_metadata): Returns versioning and build metadata for the CMC canister. - -### `create_canister(settings: opt CanisterSettings, subnet_type: opt text, subnet_selection: opt SubnetSelection)` -Creates a new canister using cycles attached to the call. The caller becomes the controller unless otherwise specified in settings. - -- **Parameters:** - - `arg: record {` - - `settings: opt CanisterSettings` - - `subnet_type: opt text` (deprecated) - - `subnet_selection: opt SubnetSelection` - - `}` -- **Returns:** - - `variant { Ok: principal; Err: CreateCanisterError }` +The Cycles Minting Canister (CMC) is a system canister on the Internet Computer responsible for converting ICP tokens into **cycles**. It supports: -### `get_icp_xdr_conversion_rate()` -Returns the current ICP/XDR conversion rate along with certified data. +- Topping up existing canisters +- Creating new canisters +- Depositing minted cycles into ledger-managed accounts +- Fetching exchange rates and subnet configuration -- **Returns:** - - `record { data: IcpXdrConversionRate; hash_tree: blob; certificate: blob }` +The CMC is governed entirely by the **NNS (Network Nervous System)** and receives configuration updates through proposals. -### `get_subnet_types_to_subnets()` -Returns the mapping from subnet types to subnets. - -- **Returns:** - - `record { data: vec (text, vec principal) }` - -### `get_principals_authorized_to_create_canisters_to_subnets()` -Returns principals and which subnets they are allowed to create canisters on. - -- **Returns:** - - `record { data: vec (principal, vec principal) }` - -### `get_default_subnets()` -Returns the default subnet list. - -- **Returns:** - - `vec principal` +## Configuration Parameters -### `get_build_metadata()` -Returns build and version metadata for the CMC. +- **Conversion Rate Source**: The ICP/XDR exchange rate is **pushed** into the CMC by a dedicated *exchange rate canister*. This data reflects live market pricing and determines the ICP→cycles conversion. -- **Returns:** - - `text` +- **Governance Control**: All operations of the CMC — including upgrades, configuration, and authorized callers — are managed through NNS proposals. -### `notify_mint_cycles(block_index: nat, canister_id: principal)` -Notifies the CMC that an ICP transfer has occurred with the intent to mint cycles into the specified canister. -- **Parameters:** - - `arg: record {` - - `block_index: nat` — The block index of the ICP transfer. - - `canister_id: principal` — The principal ID of the canister to receive the minted cycles. - - `}` -- **Returns:** - - `Result` — The number of cycles minted on success. - - `NotifyError` — If the operation fails due to invalid memo, ownership mismatch, or transaction duplication. +## API Endpoints +The CMC exposes the following API endpoints for interaction: -### `notify_top_up(block_index: nat, canister_id: principal)` -Notifies the CMC that an ICP transfer intended to top up a canister has occurred. The CMC verifies and processes the payment before crediting the cycles to the specified canister. +## API Endpoints -- **Parameters:** - - `arg: record {` - - `block_index: nat` — The height of the ledger block containing the top-up transaction. - - `canister_id: principal` — The identifier of the canister to be topped up. - - `}` -- **Returns:** - - `Result` — The number of cycles credited on success. - - `NotifyError` — If the notification fails due to an invalid transaction, old transaction, or other issues. +The CMC exposes a set of core methods for converting ICP into cycles and interacting with subnet configuration. These include: + +- `notify_create_canister`: Processes an ICP payment to create a new canister with a designated controller and optional settings. +- `notify_top_up`: Tops up an existing canister with cycles, based on a prior ICP transfer. +- `notify_mint_cycles`: Mints cycles into a cycles ledger account, enabling off-chain or flexible use of cycles. +- `create_canister`: Creates a canister using cycles directly attached to the call. +- `get_icp_xdr_conversion_rate`: Returns the current ICP/XDR exchange rate with certification. +- `get_subnet_types_to_subnets`: Lists available subnets grouped by their types. +- `get_principals_authorized_to_create_canisters_to_subnets`: Indicates which principals are permitted to create canisters on which subnets. +- `get_default_subnets`: Returns the fallback subnets for general-purpose canister creation. +- `get_build_metadata`: Displays internal version and build information for the CMC. From 05e0039e502a4578ba6cb643ae183157209c0236 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Mon, 24 Mar 2025 12:33:19 +0100 Subject: [PATCH 03/24] notify_create_canister --- docs/references/cmcspec.mdx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 4f9b78f0a5..0f6e581d36 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -22,19 +22,16 @@ The CMC is governed entirely by the **NNS (Network Nervous System)** and receive - **Governance Control**: All operations of the CMC — including upgrades, configuration, and authorized callers — are managed through NNS proposals. -## API Endpoints -The CMC exposes the following API endpoints for interaction: - ## API Endpoints The CMC exposes a set of core methods for converting ICP into cycles and interacting with subnet configuration. These include: -- `notify_create_canister`: Processes an ICP payment to create a new canister with a designated controller and optional settings. -- `notify_top_up`: Tops up an existing canister with cycles, based on a prior ICP transfer. -- `notify_mint_cycles`: Mints cycles into a cycles ledger account, enabling off-chain or flexible use of cycles. +- `notify_create_canister`: Processes an ICP payment by minting cycles and using them to create a new canister, assigning control to the specified principal and applying optional settings. +- `notify_top_up`: Processes an ICP payment by minting cycles and sending them to an existing canister to increase its available balance. +- `notify_mint_cycles`: Processes an ICP payment by minting cycles and depositing them into a cycles ledger account associated with a subaccount. - `create_canister`: Creates a canister using cycles directly attached to the call. - `get_icp_xdr_conversion_rate`: Returns the current ICP/XDR exchange rate with certification. - `get_subnet_types_to_subnets`: Lists available subnets grouped by their types. - `get_principals_authorized_to_create_canisters_to_subnets`: Indicates which principals are permitted to create canisters on which subnets. -- `get_default_subnets`: Returns the fallback subnets for general-purpose canister creation. +- `get_default_subnets`: Returns the subnets for general-purpose canister creation. - `get_build_metadata`: Displays internal version and build information for the CMC. From 8b357f7c13868beab0e5213d2c29160ba74393e1 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Mon, 24 Mar 2025 12:54:32 +0100 Subject: [PATCH 04/24] notify_create_canister --- docs/references/cmcspec.mdx | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 0f6e581d36..f4ad877953 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -35,3 +35,42 @@ The CMC exposes a set of core methods for converting ICP into cycles and interac - `get_principals_authorized_to_create_canisters_to_subnets`: Indicates which principals are permitted to create canisters on which subnets. - `get_default_subnets`: Returns the subnets for general-purpose canister creation. - `get_build_metadata`: Displays internal version and build information for the CMC. + + + +## `notify_create_canister` + +Creates a new canister after verifying an ICP payment recorded in the ICP Ledger. +If the `subnet_selection field is omitted, the CMC will automatically choose a suitable subnet at random from the available options. + +The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **recipient**: + +- The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the intended controller's principal. +- 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 `0x41455243` (ASCII `"CREA"`) + - Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to: + ```candid + "\43\52\45\41\00\00\00\00" + ``` + which represents `"CREA"` padded to 8 bytes. + +- **Parameters:** + ```candid + record { + block_index: nat64; // The ledger block containing the ICP payment + controller: principal; // The principal who will control the new canister + settings: opt CanisterSettings; // Optional settings like controllers, memory limits, etc. + subnet_type: opt text; // Deprecated: legacy subnet selection + subnet_selection: opt SubnetSelection; // Preferred subnet selection method + } + ``` + +- **Returns:** + ```candid + variant { Ok: principal; Err: NotifyError } + ``` + +- **Notes:** + - The call is idempotent with respect to `block_index`; repeating it returns the same result. + - The controller must match the one encoded in the subaccount used for the payment. + - Only transactions that follow the exact memo format will be processed successfully. From c615079aec28713c01f873c9bed72964fb571a08 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Mon, 24 Mar 2025 13:01:47 +0100 Subject: [PATCH 05/24] notify_topup_canister --- docs/references/cmcspec.mdx | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index f4ad877953..ff786a40fb 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -37,8 +37,9 @@ The CMC exposes a set of core methods for converting ICP into cycles and interac - `get_build_metadata`: Displays internal version and build information for the CMC. +## API Reference -## `notify_create_canister` +### `notify_create_canister` Creates a new canister after verifying an ICP payment recorded in the ICP Ledger. If the `subnet_selection field is omitted, the CMC will automatically choose a suitable subnet at random from the available options. @@ -74,3 +75,37 @@ The CMC expects the payment to follow a strict structure that encodes both the * - The call is idempotent with respect to `block_index`; repeating it returns the same result. - The controller must match the one encoded in the subaccount used for the payment. - Only transactions that follow the exact memo format will be processed successfully. + + + ## `notify_top_up` + +Tops up an existing canister by minting cycles based on an ICP payment recorded in the ICP Ledger. + +The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **target canister**: + +- The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the target canister’s principal. +- The **memo field** must explicitly indicate the intent to top up a canister. This can be expressed as: + - A legacy 64-bit unsigned integer memo with value `0x544f5055` (ASCII `"TOPU"`) + - Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to: + ```candid + "\54\4f\50\55\00\00\00\00" + ``` + which represents `"TOPU"` padded to 8 bytes. + +- **Parameters:** + ```candid + record { + block_index: nat64; // Block index of the ICP ledger payment + canister_id: principal; // Canister to be topped up + } + ``` + +- **Returns:** + ```candid + variant { Ok: nat; Err: NotifyError } // Number of cycles minted and sent + ``` + +- **Notes:** + - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see “Shared Logic” section). + - This method is idempotent by `block_index`: retrying the same request will return the same result if it already succeeded. + - Only transfers matching the expected structure and memo will be accepted. From 4a3502e2b13139ccd4671648357b6300b3273b27 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Tue, 25 Mar 2025 11:38:48 +0100 Subject: [PATCH 06/24] fixed memo for topup --- docs/references/cmcspec.mdx | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index ff786a40fb..b88086a654 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -79,33 +79,33 @@ The CMC expects the payment to follow a strict structure that encodes both the * ## `notify_top_up` -Tops up an existing canister by minting cycles based on an ICP payment recorded in the ICP Ledger. + Tops up an existing canister by minting cycles based on an ICP payment recorded in the ICP Ledger. -The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **target canister**: + The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **target canister**: -- The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the target canister’s principal. -- The **memo field** must explicitly indicate the intent to top up a canister. This can be expressed as: - - A legacy 64-bit unsigned integer memo with value `0x544f5055` (ASCII `"TOPU"`) - - Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to: + - The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the target canister’s principal. + - The **memo field** must explicitly indicate the intent to top up a canister. This can be expressed as: + - A legacy 64-bit unsigned integer memo with value `0x50555054` (ASCII `"PUPT"`) + - Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to: + ```candid + "\50\55\50\54\00\00\00\00" + ``` + which represents `"PUPT"` padded to 8 bytes. + + - **Parameters:** ```candid - "\54\4f\50\55\00\00\00\00" + record { + block_index: nat64; // Block index of the ICP ledger payment + canister_id: principal; // Canister to be topped up + } ``` - which represents `"TOPU"` padded to 8 bytes. - -- **Parameters:** - ```candid - record { - block_index: nat64; // Block index of the ICP ledger payment - canister_id: principal; // Canister to be topped up - } - ``` -- **Returns:** - ```candid - variant { Ok: nat; Err: NotifyError } // Number of cycles minted and sent - ``` + - **Returns:** + ```candid + variant { Ok: nat; Err: NotifyError } // Number of cycles minted and sent + ``` -- **Notes:** - - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see “Shared Logic” section). - - This method is idempotent by `block_index`: retrying the same request will return the same result if it already succeeded. - - Only transfers matching the expected structure and memo will be accepted. + - **Notes:** + - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see “Shared Logic” section). + - This method is idempotent by `block_index`: retrying the same request will return the same result if it already succeeded. + - Only transfers matching the expected structure and memo will be accepted. From 37ff838949ae4e1b2ed586db620c41838a70bb57 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Tue, 25 Mar 2025 12:01:32 +0100 Subject: [PATCH 07/24] create_canister --- docs/references/cmcspec.mdx | 74 ++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index b88086a654..6352a0b9d7 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -77,7 +77,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * - Only transactions that follow the exact memo format will be processed successfully. - ## `notify_top_up` + ### `notify_top_up` Tops up an existing canister by minting cycles based on an ICP payment recorded in the ICP Ledger. @@ -109,3 +109,75 @@ The CMC expects the payment to follow a strict structure that encodes both the * - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see “Shared Logic” section). - This method is idempotent by `block_index`: retrying the same request will return the same result if it already succeeded. - Only transfers matching the expected structure and memo will be accepted. + + ### `notify_mint_cycles` + + Mints cycles into a **cycles ledger account** based on an ICP payment recorded in the ICP Ledger. + + This method is used for depositing cycles into a ledger-managed subaccount, not directly into a canister. It supports minting only from **ICRC-1-compatible transfers**, and not from legacy `transfer` calls. + + The CMC expects the payment to follow a strict structure that encodes the **intent**: + + - The **destination account** of the ICP transfer must be the CMC's account with a subaccount matching the `to_subaccount` parameter. + - The **memo field** must be an ICRC-1-style `blob`, and must equal: + ```candid + "\4d\49\4e\54\00\00\00\00" + ``` + which represents `"MINT"` padded to 8 bytes (no legacy `u64` memo is accepted). + + - **Parameters:** + ```candid + record { + block_index: nat64; // Block index of the ICP ledger payment + to_subaccount: opt blob; // 32-byte subaccount to credit in the cycles ledger + deposit_memo: opt blob; // Optional application-specific memo + } + ``` + + - **Returns:** + ```candid + variant { + Ok: record { + block_index: nat; // Block index in the cycles ledger + minted: nat; // Amount of cycles minted + balance: nat; // Final balance of the cycles ledger account + }; + Err: NotifyError; + } + ``` + + - **Notes:** + - Only authorized principals may call this method. + - The `to_subaccount` must match the subaccount used in the ICP destination address. + - Idempotent by `block_index`. + - This method only processes transfers with a valid ICRC-1 memo and does not support legacy-style memos. + + + ### `create_canister` + +Creates a new canister using cycles attached directly to the call (not from an ICP ledger payment). + +Unlike `notify_create_canister`, this method does not rely on an external ICP transaction. Instead, the calling canister must attach enough cycles to cover the creation cost. + +- If `subnet_selection` is omitted, the CMC will select a suitable subnet at random from the available subnets. +- If `settings` is provided, it will override default configuration for the new canister. +- If no `settings` are given, the calling principal becomes the sole controller. + +- **Parameters:** + ```candid + record { + settings: opt CanisterSettings; // Optional settings: controller(s), allocations, limits, etc. + subnet_type: opt text; // (Deprecated) Legacy subnet type selection + subnet_selection: opt SubnetSelection; // Preferred way to select the subnet + } + ``` + +- **Returns:** + ```candid + variant { Ok: principal; Err: CreateCanisterError } + ``` + +- **Notes:** + - This method requires cycles to be attached to the call. + - Returns the principal of the newly created canister. + - The result is **not idempotent** — calling it again will consume cycles and create a different canister. From e95b8c6166f31ca5bf7d7fbff2f411a880fe60a6 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Tue, 25 Mar 2025 12:06:51 +0100 Subject: [PATCH 08/24] added shared logic section for settings and encoding of principal as subaccount --- docs/references/cmcspec.mdx | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 6352a0b9d7..84a3991956 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -181,3 +181,56 @@ Unlike `notify_create_canister`, this method does not rely on an external ICP tr - This method requires cycles to be attached to the call. - Returns the principal of the newly created canister. - The result is **not idempotent** — calling it again will consume cycles and create a different canister. + + + ## Shared Logic + +### Canister Settings + +The `CanisterSettings` record allows configuration of the canister at the time of creation or through governance. Each field is optional and will default to system-provided values if not specified. + +```candid +record { + controllers : opt vec principal; // List of principals allowed to control the canister + 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; // Reserved minimum cycle balance for execution + log_visibility : opt variant { + controllers; + public; + }; // Visibility of canister logs + wasm_memory_limit : opt nat; // Cap on wasm memory usage, in bytes + wasm_memory_threshold : opt nat; // Threshold to trigger memory alerts or actions +} +``` + +Unspecified fields are interpreted as: +- `controllers`: Defaults to the caller of the method +- Other fields: Use system-wide default configuration values + +--- + +### Encoding a Principal into a Subaccount + +Several CMC methods require an ICP payment to be sent to a subaccount that encodes a principal (e.g., the controller of a new canister or the canister being topped up). + +To derive this subaccount, the principal is serialized and packed into a 32-byte array using the following logic: + +```motoko +public func principalToSubAccount(id: Principal) : [Nat8] { + let p = Blob.toArray(Principal.toBlob(id)); + Array.tabulate(32, func(i : Nat) : Nat8 { + if (i >= p.size() + 1) 0 + else if (i == 0) (Nat8.fromNat(p.size())) + else (p[i - 1]) + }) +}; +``` + +This produces a 32-byte subaccount where: +- The first byte contains the length of the principal +- The next bytes contain the principal's raw byte encoding +- The remainder is zero-padded to reach 32 bytes + +This convention ensures the CMC can determine which principal or canister an incoming payment was intended for. From 61680ad02a76b9b340883fab28f585c0d82a5bbc Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Tue, 25 Mar 2025 16:09:13 +0100 Subject: [PATCH 09/24] added legacy memo for notify_mint --- docs/references/cmcspec.mdx | 73 ++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 84a3991956..9c69c53668 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -110,47 +110,52 @@ The CMC expects the payment to follow a strict structure that encodes both the * - This method is idempotent by `block_index`: retrying the same request will return the same result if it already succeeded. - Only transfers matching the expected structure and memo will be accepted. - ### `notify_mint_cycles` + ## `notify_mint_cycles` - Mints cycles into a **cycles ledger account** based on an ICP payment recorded in the ICP Ledger. +Mints cycles into a **cycles ledger account** based on an ICP payment recorded in the ICP Ledger. - This method is used for depositing cycles into a ledger-managed subaccount, not directly into a canister. It supports minting only from **ICRC-1-compatible transfers**, and not from legacy `transfer` calls. +This method is used to deposit minted cycles into a specific subaccount in the **cycles ledger**. It supports minting from both legacy and ICRC-1-style transfers. - The CMC expects the payment to follow a strict structure that encodes the **intent**: +The CMC expects the payment to follow a strict structure that encodes the **intent** and **destination**: - - The **destination account** of the ICP transfer must be the CMC's account with a subaccount matching the `to_subaccount` parameter. - - The **memo field** must be an ICRC-1-style `blob`, and must equal: - ```candid - "\4d\49\4e\54\00\00\00\00" - ``` - which represents `"MINT"` padded to 8 bytes (no legacy `u64` memo is accepted). +- The **destination account** must be the CMC’s account with the subaccount matching the `to_subaccount` parameter. +- The **memo field** must explicitly indicate the intent to mint cycles. This can be: + - For legacy `transfer` calls: a `u64` memo with value: + ```candid + 0x544e494d // ASCII "MINT" + ``` + - For ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`): a `blob` memo equal to: + ```candid + "\4d\49\4e\54\00\00\00\00" + ``` + which represents `"MINT"` padded to 8 bytes - - **Parameters:** - ```candid - record { - block_index: nat64; // Block index of the ICP ledger payment - to_subaccount: opt blob; // 32-byte subaccount to credit in the cycles ledger - deposit_memo: opt blob; // Optional application-specific memo - } - ``` +- **Parameters:** + ```candid + record { + block_index: nat64; // Block index of the ICP ledger payment + to_subaccount: opt blob; // 32-byte subaccount to credit in the cycles ledger + deposit_memo: opt blob; // Optional application-specific memo + } + ``` - - **Returns:** - ```candid - variant { - Ok: record { - block_index: nat; // Block index in the cycles ledger - minted: nat; // Amount of cycles minted - balance: nat; // Final balance of the cycles ledger account - }; - Err: NotifyError; - } - ``` +- **Returns:** + ```candid + variant { + Ok: record { + block_index: nat; // Block index in the cycles ledger + minted: nat; // Amount of cycles minted + balance: nat; // Final balance of the cycles ledger account + }; + Err: NotifyError; + } + ``` - - **Notes:** - - Only authorized principals may call this method. - - The `to_subaccount` must match the subaccount used in the ICP destination address. - - Idempotent by `block_index`. - - This method only processes transfers with a valid ICRC-1 memo and does not support legacy-style memos. +- **Notes:** + - The caller must be authorized to invoke this method. + - The subaccount in the payment must match `to_subaccount` exactly. + - This method is idempotent by `block_index`. + - Both legacy and ICRC-1 memo formats are accepted if they correctly encode `"MINT"`. ### `create_canister` From a16f639e304c729503b4a07adbd3c2ce487a4f62 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Wed, 26 Mar 2025 18:30:45 +0100 Subject: [PATCH 10/24] Apply Jesse's changes Co-authored-by: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com> --- docs/references/cmcspec.mdx | 79 ++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 9c69c53668..20e14f6117 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -4,25 +4,24 @@ import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow"; -## Overview The Cycles Minting Canister (CMC) is a system canister on the Internet Computer responsible for converting ICP tokens into **cycles**. It supports: -- Topping up existing canisters -- Creating new canisters -- Depositing minted cycles into ledger-managed accounts -- Fetching exchange rates and subnet configuration +- Topping up existing canisters. +- Creating new canisters. +- Depositing minted cycles into ledger-managed accounts. +- Fetching exchange rates and subnet configuration. -The CMC is governed entirely by the **NNS (Network Nervous System)** and receives configuration updates through proposals. +The CMC is governed entirely by the [NNS (Network Nervous System)](https://learn.internetcomputer.org/hc/en-us/articles/33692645961236-Overview) and receives configuration updates through proposals. -## Configuration Parameters +## Configuration parameters -- **Conversion Rate Source**: The ICP/XDR exchange rate is **pushed** into the CMC by a dedicated *exchange rate canister*. This data reflects live market pricing and determines the ICP→cycles conversion. +- **Conversion rate source**: The ICP/XDR exchange rate is **pushed** into the CMC by a dedicated [exchange rate canister](/docs/references/system-canisters/xrc). This data reflects live market pricing and determines the ICP→cycles conversion. -- **Governance Control**: All operations of the CMC — including upgrades, configuration, and authorized callers — are managed through NNS proposals. +- **Governance control**: All operations of the CMC — including upgrades, configuration, and authorized callers — are managed through NNS proposals. -## API Endpoints +## API endpoints The CMC exposes a set of core methods for converting ICP into cycles and interacting with subnet configuration. These include: @@ -39,10 +38,10 @@ The CMC exposes a set of core methods for converting ICP into cycles and interac ## API Reference -### `notify_create_canister` +## `notify_create_canister` -Creates a new canister after verifying an ICP payment recorded in the ICP Ledger. -If the `subnet_selection field is omitted, the CMC will automatically choose a suitable subnet at random from the available options. +Creates a new canister after verifying an ICP payment has been recorded in the ICP Ledger. +If the `subnet_selection` field is omitted, the CMC will automatically choose a suitable subnet at random from the available options. The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **recipient**: @@ -55,7 +54,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * ``` which represents `"CREA"` padded to 8 bytes. -- **Parameters:** +#### Parameters ```candid record { block_index: nat64; // The ledger block containing the ICP payment @@ -66,18 +65,18 @@ The CMC expects the payment to follow a strict structure that encodes both the * } ``` -- **Returns:** +#### Returns ```candid variant { Ok: principal; Err: NotifyError } ``` -- **Notes:** +#### Notes - The call is idempotent with respect to `block_index`; repeating it returns the same result. - The controller must match the one encoded in the subaccount used for the payment. - Only transactions that follow the exact memo format will be processed successfully. - ### `notify_top_up` +## `notify_top_up` Tops up an existing canister by minting cycles based on an ICP payment recorded in the ICP Ledger. @@ -92,7 +91,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * ``` which represents `"PUPT"` padded to 8 bytes. - - **Parameters:** +#### Parameters ```candid record { block_index: nat64; // Block index of the ICP ledger payment @@ -100,21 +99,21 @@ The CMC expects the payment to follow a strict structure that encodes both the * } ``` - - **Returns:** +#### Returns ```candid variant { Ok: nat; Err: NotifyError } // Number of cycles minted and sent ``` - **Notes:** - - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see “Shared Logic” section). + - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see [“Shared logic”](#shared-logic) section). - This method is idempotent by `block_index`: retrying the same request will return the same result if it already succeeded. - Only transfers matching the expected structure and memo will be accepted. - ## `notify_mint_cycles` +## `notify_mint_cycles` -Mints cycles into a **cycles ledger account** based on an ICP payment recorded in the ICP Ledger. +Mints cycles into a [cycles ledger account](/docs/defi/token-ledgers/cycles-ledger) based on an ICP payment recorded in the ICP Ledger. -This method is used to deposit minted cycles into a specific subaccount in the **cycles ledger**. It supports minting from both legacy and ICRC-1-style transfers. +This method is used to deposit minted cycles into a specific subaccount in the [cycles ledger*](/docs/defi/token-ledgers/cycles-ledger) It supports minting from both legacy and ICRC-1-style transfers. The CMC expects the payment to follow a strict structure that encodes the **intent** and **destination**: @@ -130,7 +129,7 @@ The CMC expects the payment to follow a strict structure that encodes the **inte ``` which represents `"MINT"` padded to 8 bytes -- **Parameters:** +#### Parameters ```candid record { block_index: nat64; // Block index of the ICP ledger payment @@ -139,7 +138,7 @@ The CMC expects the payment to follow a strict structure that encodes the **inte } ``` -- **Returns:** +#### Returns ```candid variant { Ok: record { @@ -151,24 +150,24 @@ The CMC expects the payment to follow a strict structure that encodes the **inte } ``` -- **Notes:** +#### Notes - The caller must be authorized to invoke this method. - The subaccount in the payment must match `to_subaccount` exactly. - This method is idempotent by `block_index`. - Both legacy and ICRC-1 memo formats are accepted if they correctly encode `"MINT"`. - ### `create_canister` +## `create_canister` Creates a new canister using cycles attached directly to the call (not from an ICP ledger payment). Unlike `notify_create_canister`, this method does not rely on an external ICP transaction. Instead, the calling canister must attach enough cycles to cover the creation cost. - If `subnet_selection` is omitted, the CMC will select a suitable subnet at random from the available subnets. -- If `settings` is provided, it will override default configuration for the new canister. +- If `settings` is provided, it will override the default configuration for the new canister. - If no `settings` are given, the calling principal becomes the sole controller. -- **Parameters:** +#### Parameters ```candid record { settings: opt CanisterSettings; // Optional settings: controller(s), allocations, limits, etc. @@ -177,22 +176,22 @@ Unlike `notify_create_canister`, this method does not rely on an external ICP tr } ``` -- **Returns:** +#### Returns ```candid variant { Ok: principal; Err: CreateCanisterError } ``` -- **Notes:** +#### Notes - This method requires cycles to be attached to the call. - Returns the principal of the newly created canister. - The result is **not idempotent** — calling it again will consume cycles and create a different canister. - ## Shared Logic +## Shared logic -### Canister Settings +### `CanisterSettings` -The `CanisterSettings` record allows configuration of the canister at the time of creation or through governance. Each field is optional and will default to system-provided values if not specified. +The `CanisterSettings` record is used to configure the canister at the time of creation or through governance. Each field is optional and will default to system-provided values if not specified. ```candid record { @@ -211,12 +210,12 @@ record { ``` Unspecified fields are interpreted as: -- `controllers`: Defaults to the caller of the method -- Other fields: Use system-wide default configuration values +- `controllers`: Defaults to the caller of the method. +- Other fields: Use system-wide default configuration values. --- -### Encoding a Principal into a Subaccount +## Encoding a principal into a subaccount Several CMC methods require an ICP payment to be sent to a subaccount that encodes a principal (e.g., the controller of a new canister or the canister being topped up). @@ -234,8 +233,8 @@ public func principalToSubAccount(id: Principal) : [Nat8] { ``` This produces a 32-byte subaccount where: -- The first byte contains the length of the principal -- The next bytes contain the principal's raw byte encoding -- The remainder is zero-padded to reach 32 bytes +- The first byte contains the length of the principal. +- The next bytes contain the principal's raw byte encoding. +- The remainder is zero-padded to reach 32 bytes. This convention ensures the CMC can determine which principal or canister an incoming payment was intended for. From cfe15e4bc158b2c3fab6da206b539bd3e7ef00ee Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Wed, 26 Mar 2025 18:32:04 +0100 Subject: [PATCH 11/24] Apply Severin's changes Co-authored-by: Severin Siffert Co-authored-by: Jessie Mongeon <133128541+jessiemongeon1@users.noreply.github.com> --- docs/references/cmcspec.mdx | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 20e14f6117..59deb2169f 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -27,16 +27,15 @@ The CMC exposes a set of core methods for converting ICP into cycles and interac - `notify_create_canister`: Processes an ICP payment by minting cycles and using them to create a new canister, assigning control to the specified principal and applying optional settings. - `notify_top_up`: Processes an ICP payment by minting cycles and sending them to an existing canister to increase its available balance. -- `notify_mint_cycles`: Processes an ICP payment by minting cycles and depositing them into a cycles ledger account associated with a subaccount. +- `notify_mint_cycles`: Processes an ICP payment by minting cycles and depositing them into the caller's cycles ledger account associated with a subaccount. - `create_canister`: Creates a canister using cycles directly attached to the call. - `get_icp_xdr_conversion_rate`: Returns the current ICP/XDR exchange rate with certification. -- `get_subnet_types_to_subnets`: Lists available subnets grouped by their types. +- `get_subnet_types_to_subnets`: Lists publicly available subnets grouped by their types. Only covers subnets that don't have standard settings. - `get_principals_authorized_to_create_canisters_to_subnets`: Indicates which principals are permitted to create canisters on which subnets. -- `get_default_subnets`: Returns the subnets for general-purpose canister creation. +- `get_default_subnets`: Returns the subnets for general-purpose public canister creation. These subnets typically have standard settings. - `get_build_metadata`: Displays internal version and build information for the CMC. -## API Reference ## `notify_create_canister` @@ -58,7 +57,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * ```candid record { block_index: nat64; // The ledger block containing the ICP payment - controller: principal; // The principal who will control the new canister + controller: principal; // The creator of the canister. Must match caller; otherwise, Err is returned. This is also used when checking that the creator is authorized to create canisters in subnets where authorization is required. This is also used when `settings` does not specify `controllers`. settings: opt CanisterSettings; // Optional settings like controllers, memory limits, etc. subnet_type: opt text; // Deprecated: legacy subnet selection subnet_selection: opt SubnetSelection; // Preferred subnet selection method @@ -72,7 +71,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * #### Notes - The call is idempotent with respect to `block_index`; repeating it returns the same result. - - The controller must match the one encoded in the subaccount used for the payment. + - The caller principal must match the one encoded in the subaccount used for the payment. - Only transactions that follow the exact memo format will be processed successfully. @@ -101,10 +100,10 @@ The CMC expects the payment to follow a strict structure that encodes both the * #### Returns ```candid - variant { Ok: nat; Err: NotifyError } // Number of cycles minted and sent + variant { Ok: nat; Err: NotifyError } // Number of cycles minted and deposited ``` - - **Notes:** +#### Notes - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see [“Shared logic”](#shared-logic) section). - This method is idempotent by `block_index`: retrying the same request will return the same result if it already succeeded. - Only transfers matching the expected structure and memo will be accepted. @@ -117,8 +116,8 @@ This method is used to deposit minted cycles into a specific subaccount in the [ The CMC expects the payment to follow a strict structure that encodes the **intent** and **destination**: -- The **destination account** must be the CMC’s account with the subaccount matching the `to_subaccount` parameter. -- The **memo field** must explicitly indicate the intent to mint cycles. This can be: +- The subaccount used in the transfer must be derived from the caller principal using the standard 32-byte encoding (see “Shared Logic” section). +- The **ICP transfer's memo field** must explicitly indicate the intent to mint cycles. This can be: - For legacy `transfer` calls: a `u64` memo with value: ```candid 0x544e494d // ASCII "MINT" @@ -134,7 +133,7 @@ The CMC expects the payment to follow a strict structure that encodes the **inte record { block_index: nat64; // Block index of the ICP ledger payment to_subaccount: opt blob; // 32-byte subaccount to credit in the cycles ledger - deposit_memo: opt blob; // Optional application-specific memo + deposit_memo: opt blob; // Optional application-specific memo that will be used in the cycles ledger's deposit transaction } ``` @@ -151,8 +150,6 @@ The CMC expects the payment to follow a strict structure that encodes the **inte ``` #### Notes - - The caller must be authorized to invoke this method. - - The subaccount in the payment must match `to_subaccount` exactly. - This method is idempotent by `block_index`. - Both legacy and ICRC-1 memo formats are accepted if they correctly encode `"MINT"`. @@ -163,9 +160,9 @@ Creates a new canister using cycles attached directly to the call (not from an I Unlike `notify_create_canister`, this method does not rely on an external ICP transaction. Instead, the calling canister must attach enough cycles to cover the creation cost. -- If `subnet_selection` is omitted, the CMC will select a suitable subnet at random from the available subnets. +- If `subnet_selection` and the (deprecated) `subnet_type` are omitted, the CMC will select a suitable subnet at random from the available subnets. - If `settings` is provided, it will override the default configuration for the new canister. -- If no `settings` are given, the calling principal becomes the sole controller. +- If no controllers are given in `settings`, the calling principal becomes the sole controller. #### Parameters ```candid @@ -213,7 +210,6 @@ Unspecified fields are interpreted as: - `controllers`: Defaults to the caller of the method. - Other fields: Use system-wide default configuration values. ---- ## Encoding a principal into a subaccount From 4c033e78ee98e678bb78a5638c0cc17615e07852 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Wed, 26 Mar 2025 18:37:59 +0100 Subject: [PATCH 12/24] memo fix --- docs/references/cmcspec.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 9c69c53668..6a7a1b4515 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -48,7 +48,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * - The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the intended controller's principal. - 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 `0x41455243` (ASCII `"CREA"`) + - A legacy 64-bit unsigned integer memo with value `0x41455243` (ASCII `"AERC"`) - Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to: ```candid "\43\52\45\41\00\00\00\00" From cc22a9fcfe355e8431222739507452270717f5e8 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Fri, 4 Apr 2025 15:54:02 +0200 Subject: [PATCH 13/24] added some clarifications --- docs/references/cmcspec.mdx | 45 ++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index e7d06b0db0..a58baa396a 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -9,10 +9,10 @@ The Cycles Minting Canister (CMC) is a system canister on the Internet Computer - Topping up existing canisters. - Creating new canisters. -- Depositing minted cycles into ledger-managed accounts. +- Minting and depositing minted cycles into cycle ledger-managed accounts. - Fetching exchange rates and subnet configuration. -The CMC is governed entirely by the [NNS (Network Nervous System)](https://learn.internetcomputer.org/hc/en-us/articles/33692645961236-Overview) and receives configuration updates through proposals. +The CMC is governed solely by the [NNS (Network Nervous System)](https://learn.internetcomputer.org/hc/en-us/articles/33692645961236-Overview) and receives configuration updates through proposals. ## Configuration parameters @@ -64,11 +64,16 @@ The CMC expects the payment to follow a strict structure that encodes both the * } ``` -#### Returns + #### Returns ```candid variant { Ok: principal; Err: NotifyError } ``` + - **Ok**: The principal of the newly created canister. + - **Err**: Indicates why the request was rejected (e.g., incorrect memo, controller mismatch, payment already processed, etc.). + See [NotifyError](#error-handling-and-retry-semantics) for all possible errors. + + #### Notes - The call is idempotent with respect to `block_index`; repeating it returns the same result. - The caller principal must match the one encoded in the subaccount used for the payment. @@ -98,11 +103,16 @@ The CMC expects the payment to follow a strict structure that encodes both the * } ``` -#### Returns + #### Returns ```candid - variant { Ok: nat; Err: NotifyError } // Number of cycles minted and deposited + variant { Ok: nat; Err: NotifyError } ``` + - **Ok**: Number of cycles minted and deposited into the specified canister. + - **Err**: Describes why the top-up failed (e.g., invalid transaction, wrong memo, too old, already processed, etc.). + See [NotifyError](#error-handling-and-retry-semantics). + + #### Notes - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see [“Shared logic”](#shared-logic) section). - This method is idempotent by `block_index`: retrying the same request will return the same result if it already succeeded. @@ -112,7 +122,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * Mints cycles into a [cycles ledger account](/docs/defi/token-ledgers/cycles-ledger) based on an ICP payment recorded in the ICP Ledger. -This method is used to deposit minted cycles into a specific subaccount in the [cycles ledger*](/docs/defi/token-ledgers/cycles-ledger) It supports minting from both legacy and ICRC-1-style transfers. +This method is used to deposit minted cycles into a specific subaccount in the [cycles ledger.](/docs/defi/token-ledgers/cycles-ledger) It supports minting from both legacy and ICRC-1-style transfers. The CMC expects the payment to follow a strict structure that encodes the **intent** and **destination**: @@ -137,18 +147,25 @@ The CMC expects the payment to follow a strict structure that encodes the **inte } ``` -#### Returns + #### Returns ```candid variant { Ok: record { - block_index: nat; // Block index in the cycles ledger - minted: nat; // Amount of cycles minted - balance: nat; // Final balance of the cycles ledger account + block_index: nat; + minted: nat; + balance: nat; }; Err: NotifyError; } ``` + - **Ok**: A record that includes: + - `block_index`: The cycles ledger block index of the minting transaction + - `minted`: The amount of cycles created from the ICP payment + - `balance`: The new balance of the target cycles ledger subaccount + - **Err**: Describes why the minting failed. See [NotifyError](#error-handling-and-retry-semantics). + + #### Notes - This method is idempotent by `block_index`. - Both legacy and ICRC-1 memo formats are accepted if they correctly encode `"MINT"`. @@ -173,11 +190,17 @@ Unlike `notify_create_canister`, this method does not rely on an external ICP tr } ``` -#### Returns + #### Returns ```candid variant { Ok: principal; Err: CreateCanisterError } ``` + - **Ok**: The principal ID of the newly created canister. + - **Err**: An error indicating why the creation failed. + May include a refund amount if cycles were consumed but the canister was not created. + See `CreateCanisterError` for structured error types. + + #### Notes - This method requires cycles to be attached to the call. - Returns the principal of the newly created canister. From fa16af218b6f3fad60b30271d58f612cc9892174 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Wed, 9 Apr 2025 10:31:14 +0200 Subject: [PATCH 14/24] removed mnemonics for memo fields, added remaining methods --- docs/references/cmcspec.mdx | 93 +++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index a58baa396a..04555726b9 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -46,15 +46,18 @@ The CMC expects the payment to follow a strict structure that encodes both the * - The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the intended controller's principal. - 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 `0x41455243` (ASCII `"AERC"`) + - A legacy 64-bit unsigned integer memo with value (in decimal) + ``` + 1095062083 + ``` - Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to: - ```candid + ``` "\43\52\45\41\00\00\00\00" ``` - which represents `"CREA"` padded to 8 bytes. + #### Parameters - ```candid + ``` record { block_index: nat64; // The ledger block containing the ICP payment controller: principal; // The creator of the canister. Must match caller; otherwise, Err is returned. This is also used when checking that the creator is authorized to create canisters in subnets where authorization is required. This is also used when `settings` does not specify `controllers`. @@ -65,7 +68,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * ``` #### Returns - ```candid + ``` variant { Ok: principal; Err: NotifyError } ``` @@ -88,15 +91,18 @@ The CMC expects the payment to follow a strict structure that encodes both the * - The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the target canister’s principal. - The **memo field** must explicitly indicate the intent to top up a canister. This can be expressed as: - - A legacy 64-bit unsigned integer memo with value `0x50555054` (ASCII `"PUPT"`) + - A legacy 64-bit unsigned integer memo with value (in decimal) + ``` + 1347768404 + ``` - Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to: - ```candid + ``` "\50\55\50\54\00\00\00\00" ``` which represents `"PUPT"` padded to 8 bytes. #### Parameters - ```candid + ``` record { block_index: nat64; // Block index of the ICP ledger payment canister_id: principal; // Canister to be topped up @@ -104,7 +110,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * ``` #### Returns - ```candid + ``` variant { Ok: nat; Err: NotifyError } ``` @@ -128,18 +134,19 @@ The CMC expects the payment to follow a strict structure that encodes the **inte - The subaccount used in the transfer must be derived from the caller principal using the standard 32-byte encoding (see “Shared Logic” section). - The **ICP transfer's memo field** must explicitly indicate the intent to mint cycles. This can be: - - For legacy `transfer` calls: a `u64` memo with value: - ```candid - 0x544e494d // ASCII "MINT" + - For legacy `transfer` calls: a `u64` memo with value (in decimal): + + ``` + 1414416717 ``` - For ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`): a `blob` memo equal to: - ```candid + ``` "\4d\49\4e\54\00\00\00\00" ``` which represents `"MINT"` padded to 8 bytes #### Parameters - ```candid + ``` record { block_index: nat64; // Block index of the ICP ledger payment to_subaccount: opt blob; // 32-byte subaccount to credit in the cycles ledger @@ -148,7 +155,7 @@ The CMC expects the payment to follow a strict structure that encodes the **inte ``` #### Returns - ```candid + ``` variant { Ok: record { block_index: nat; @@ -182,7 +189,7 @@ Unlike `notify_create_canister`, this method does not rely on an external ICP tr - If no controllers are given in `settings`, the calling principal becomes the sole controller. #### Parameters - ```candid + ``` record { settings: opt CanisterSettings; // Optional settings: controller(s), allocations, limits, etc. subnet_type: opt text; // (Deprecated) Legacy subnet type selection @@ -191,13 +198,13 @@ Unlike `notify_create_canister`, this method does not rely on an external ICP tr ``` #### Returns - ```candid + ``` variant { Ok: principal; Err: CreateCanisterError } ``` - **Ok**: The principal ID of the newly created canister. - **Err**: An error indicating why the creation failed. - May include a refund amount if cycles were consumed but the canister was not created. + May include a refund amount if cycles were consumed but the canister was not created. See `CreateCanisterError` for structured error types. @@ -206,6 +213,52 @@ Unlike `notify_create_canister`, this method does not rely on an external ICP tr - Returns the principal of the newly created canister. - The result is **not idempotent** — calling it again will consume cycles and create a different canister. + ## `get_icp_xdr_conversion_rate` + + Returns the current ICP/XDR exchange rate used to compute how many cycles a given amount of ICP buys. + + #### Parameters + + ```did + record {} +``` + +#### Returns +``` +record { + data: opt record { + xdr_permyriad_per_icp: nat64; + timestamp_seconds: nat64; + }; + certificate: blob; +} +``` + +* `xdr_permyriad_per_icp`: The exchange rate, in hundredths of a percent. For example, 12500 means 1 ICP = 1.25 XDR. + +* `timestamp_seconds`: Time the rate was recorded, in seconds since the Unix epoch. + +* `certificate`: Certified data that can be verified by canister clients in query calls. + +#### Notes +This method can be called as a query. If the exchange rate is not available, data will be null. + + + +--- + +``` +## `get_subnet_types_to_subnets` + +Returns a mapping from subnet types (e.g., `"application"`, `"verified_application"`) to the list of subnets of that type. + +#### Parameters + +```did +record {} +``` + + ## Shared logic @@ -213,7 +266,7 @@ Unlike `notify_create_canister`, this method does not rely on an external ICP tr The `CanisterSettings` record is used to configure the canister at the time of creation or through governance. Each field is optional and will default to system-provided values if not specified. -```candid +``` record { controllers : opt vec principal; // List of principals allowed to control the canister compute_allocation : opt nat; // Percentage (0-100) of guaranteed compute capacity @@ -240,7 +293,7 @@ Several CMC methods require an ICP payment to be sent to a subaccount that encod To derive this subaccount, the principal is serialized and packed into a 32-byte array using the following logic: -```motoko +``` public func principalToSubAccount(id: Principal) : [Nat8] { let p = Blob.toArray(Principal.toBlob(id)); Array.tabulate(32, func(i : Nat) : Nat8 { From 6b841fadf0c72ab26dc4347ce1fee3f35adce818 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Wed, 9 Apr 2025 18:04:07 +0200 Subject: [PATCH 15/24] new section + shared behaviour for notify-* methods --- docs/references/cmcspec.mdx | 149 +++++++++++++++++++++++++++++++----- 1 file changed, 128 insertions(+), 21 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 04555726b9..0e51dd1339 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -78,10 +78,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * #### Notes - - The call is idempotent with respect to `block_index`; repeating it returns the same result. - The caller principal must match the one encoded in the subaccount used for the payment. - - Only transactions that follow the exact memo format will be processed successfully. - ## `notify_top_up` @@ -99,7 +96,6 @@ The CMC expects the payment to follow a strict structure that encodes both the * ``` "\50\55\50\54\00\00\00\00" ``` - which represents `"PUPT"` padded to 8 bytes. #### Parameters ``` @@ -121,8 +117,7 @@ The CMC expects the payment to follow a strict structure that encodes both the * #### Notes - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see [“Shared logic”](#shared-logic) section). - - This method is idempotent by `block_index`: retrying the same request will return the same result if it already succeeded. - - Only transfers matching the expected structure and memo will be accepted. + ## `notify_mint_cycles` @@ -143,7 +138,6 @@ The CMC expects the payment to follow a strict structure that encodes the **inte ``` "\4d\49\4e\54\00\00\00\00" ``` - which represents `"MINT"` padded to 8 bytes #### Parameters ``` @@ -173,9 +167,20 @@ The CMC expects the payment to follow a strict structure that encodes the **inte - **Err**: Describes why the minting failed. See [NotifyError](#error-handling-and-retry-semantics). -#### Notes - - This method is idempotent by `block_index`. - - Both legacy and ICRC-1 memo formats are accepted if they correctly encode `"MINT"`. + + ## Notify Methods – Shared Behavior + + All `notify_*` methods (`notify_create_canister`, `notify_top_up`, `notify_mint_cycles`) process ICP payments recorded in the ledger and mint cycles accordingly. They follow the same core logic and safeguards: + + - **Memo validation**: The ICP transfer must include a memo indicating the intended operation. If the memo does not match the expected value for the given `notify_*` method, the transfer is **automatically refunded** to the sender, minus the ICP ledger’s transfer fee. + + - **Idempotency**: These methods are **idempotent** with respect to the `block_index` provided. If a `notify_*` call has already been processed for a given ledger block, repeated calls with the same parameters will return the previously computed result. + + - **Result caching**: The results of successful calls are **cached** in memory for a generous but bounded duration to allow retrying without recomputation. Once the cache expires, duplicate calls may return a new error if the ledger transaction is no longer considered valid (e.g., too old). + + - **Concurrency handling**: If a `notify_*` method is invoked while a previous call for the same ledger block is still in progress, the method returns an error indicating that the call is currently being processed. This prevents redundant computation or double processing. + + ## `create_canister` @@ -215,15 +220,16 @@ Unlike `notify_create_canister`, this method does not rely on an external ICP tr ## `get_icp_xdr_conversion_rate` - Returns the current ICP/XDR exchange rate used to compute how many cycles a given amount of ICP buys. +Returns the current ICP/XDR exchange rate used to compute how many cycles a given amount of ICP buys. - #### Parameters +#### Parameters - ```did - record {} +``` +record {} ``` #### Returns + ``` record { data: opt record { @@ -234,30 +240,131 @@ record { } ``` -* `xdr_permyriad_per_icp`: The exchange rate, in hundredths of a percent. For example, 12500 means 1 ICP = 1.25 XDR. +- `xdr_permyriad_per_icp`: The exchange rate, in hundredths of a percent. For example, 12500 means 1 ICP = 1.25 XDR. +- `timestamp_seconds`: Time the rate was recorded, in seconds since the Unix epoch. +- `certificate`: Certified data that can be verified by canister clients in query calls. + +#### Notes + +This method can be called as a query. If the exchange rate is not available, `data` will be `null`. + +--- + +## `get_subnet_types_to_subnets` + +Returns a mapping from subnet types (e.g., `"application"`, `"verified_application"`) to the list of subnets of that type. + +#### Parameters + +``` +record {} +``` + +#### Returns -* `timestamp_seconds`: Time the rate was recorded, in seconds since the Unix epoch. +``` +record { + subnets: vec record { + subnet_type: text; + subnet_ids: vec principal; + }; +} +``` -* `certificate`: Certified data that can be verified by canister clients in query calls. +- Each entry in `subnets` represents a mapping from a subnet type to the list of subnets that belong to that type. #### Notes -This method can be called as a query. If the exchange rate is not available, data will be null. +This information can be used by dapps to select appropriate subnets for canister creation. + +--- + +## `get_principals_authorized_to_create_canisters_to_subnets` + +Lists the principals that are authorized to create canisters on which subnets. +#### Parameters + +``` +record {} +``` + +#### Returns + +``` +record { + mappings: vec record { + principal: principal; + subnet_ids: vec principal; + }; +} +``` + +- Each entry represents a principal and the list of subnet IDs on which they are allowed to create canisters. + +#### Notes + +This is typically governed by proposals on the NNS. Only some principals are explicitly authorized. --- +## `get_default_subnets` + +Returns the list of default subnets for general-purpose canister creation. + +#### Parameters + +``` +record {} ``` -## `get_subnet_types_to_subnets` -Returns a mapping from subnet types (e.g., `"application"`, `"verified_application"`) to the list of subnets of that type. +#### Returns + +``` +record { + subnets: vec principal; +} +``` + +- `subnets`: The default subnets that a user may be assigned when no subnet is specified during canister creation. + +#### Notes + +These are usually `"application"` type subnets and serve as a fallback option. + +--- + +## `get_build_metadata` + +Returns metadata about the current build of the Cycles Minting Canister. #### Parameters -```did +``` record {} ``` +#### Returns + +``` +record { + repo: opt text; + git_revision: opt text; + rust_version: opt text; + cargo_version: opt text; + build_time: opt text; +} +``` + +- `repo`: URL of the source code repository. +- `git_revision`: Git commit hash. +- `rust_version`: Version of Rust used. +- `cargo_version`: Version of Cargo used. +- `build_time`: UTC string of when the build was performed. + +#### Notes + +This is primarily used for debugging, reproducibility, and audits. Fields may be `null` depending on build environment. ## Shared logic From d0289ccaf9df1cfe41a48e3639781efd7fadafae Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Tue, 15 Apr 2025 11:21:17 +0200 Subject: [PATCH 16/24] pass --- docs/references/cmcspec.mdx | 49 +++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 0e51dd1339..ed522945f7 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -39,17 +39,19 @@ The CMC exposes a set of core methods for converting ICP into cycles and interac ## `notify_create_canister` -Creates a new canister after verifying an ICP payment has been recorded in the ICP Ledger. -If the `subnet_selection` field is omitted, the CMC will automatically choose a suitable subnet at random from the available options. +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. The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **recipient**: -- The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the intended controller's principal. +- The **destination account** of the ICP transfer must be the CMC's account with a **subaccount derived from the intended controller's principal**. +- 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. - 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 - ``` + - A legacy 64-bit unsigned integer memo with value (in decimal): + ``` + 1095062083 + ``` - Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to: ``` "\43\52\45\41\00\00\00\00" @@ -74,11 +76,11 @@ The CMC expects the payment to follow a strict structure that encodes both the * - **Ok**: The principal of the newly created canister. - **Err**: Indicates why the request was rejected (e.g., incorrect memo, controller mismatch, payment already processed, etc.). - See [NotifyError](#error-handling-and-retry-semantics) for all possible errors. + For information about retry semantics, error handling, and caching behavior, see [Notify Methods – Shared Behavior](#notify-methods-shared-behavior-retry-error-handling-and-caching) +. + -#### Notes - - The caller principal must match the one encoded in the subaccount used for the payment. ## `notify_top_up` @@ -111,9 +113,9 @@ The CMC expects the payment to follow a strict structure that encodes both the * ``` - **Ok**: Number of cycles minted and deposited into the specified canister. - - **Err**: Describes why the top-up failed (e.g., invalid transaction, wrong memo, too old, already processed, etc.). - See [NotifyError](#error-handling-and-retry-semantics). - + - **Err**: Indicates why the top-up failed (e.g., incorrect memo, controller mismatch, payment already processed, etc.). + For information about retry semantics, error handling, and caching behavior, see [Notify Methods – Shared Behavior](#notify-methods-shared-behavior-retry-error-handling-and-caching) + . #### Notes - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see [“Shared logic”](#shared-logic) section). @@ -164,22 +166,21 @@ The CMC expects the payment to follow a strict structure that encodes the **inte - `block_index`: The cycles ledger block index of the minting transaction - `minted`: The amount of cycles created from the ICP payment - `balance`: The new balance of the target cycles ledger subaccount - - **Err**: Describes why the minting failed. See [NotifyError](#error-handling-and-retry-semantics). - - - - ## Notify Methods – Shared Behavior - - All `notify_*` methods (`notify_create_canister`, `notify_top_up`, `notify_mint_cycles`) process ICP payments recorded in the ledger and mint cycles accordingly. They follow the same core logic and safeguards: + - **Err**: Indicates why the minting failed (e.g., incorrect memo, controller mismatch, payment already processed, etc.). + For information about retry semantics, error handling, and caching behavior, see [Notify Methods – Shared Behavior](#notify-methods-shared-behavior-retry-error-handling-and-caching) +. - - **Memo validation**: The ICP transfer must include a memo indicating the intended operation. If the memo does not match the expected value for the given `notify_*` method, the transfer is **automatically refunded** to the sender, minus the ICP ledger’s transfer fee. - - **Idempotency**: These methods are **idempotent** with respect to the `block_index` provided. If a `notify_*` call has already been processed for a given ledger block, repeated calls with the same parameters will return the previously computed result. + ## Notify Methods – Shared Behavior (Retry, Error Handling, and Caching) - - **Result caching**: The results of successful calls are **cached** in memory for a generous but bounded duration to allow retrying without recomputation. Once the cache expires, duplicate calls may return a new error if the ledger transaction is no longer considered valid (e.g., too old). + All `notify_*` methods (such as `notify_create_canister` and `notify_top_up`) follow the same core semantics: - - **Concurrency handling**: If a `notify_*` method is invoked while a previous call for the same ledger block is still in progress, the method returns an error indicating that the call is currently being processed. This prevents redundant computation or double processing. + - If the ICP transfer does not use the correct memo, the tokens are refunded (minus the ledger’s transfer fee). + - If a concurrent `notify_*` call is already processing for the same transfer, the new call returns an error indicating that the operation is in progress. + - If a previous `notify_*` call has already successfully processed a transfer at the same block height, the new call returns the same result. + - To support safe retries, the result of a successful call is **cached** for a bounded (but generous) amount of time. + These semantics ensure that all `notify_*` methods are idempotent. Clients can safely retry using the original parameters (memo, block height, destination account). From f6a79cf51144fd45bd29cd391fe5a973c0c950dd Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Tue, 15 Apr 2025 12:06:32 +0200 Subject: [PATCH 17/24] added more methods --- docs/references/cmcspec.mdx | 541 ++++++++++++++++++++++-------------- 1 file changed, 337 insertions(+), 204 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index ed522945f7..eb126cda01 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -4,185 +4,205 @@ import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow"; +The Cycles Minting Canister (CMC) is a system canister on the Internet Computer responsible for converting ICP tokens into **cycles**, tracking the total cycles minted, and providing configuration and exchange rate data. It supports: -The Cycles Minting Canister (CMC) is a system canister on the Internet Computer responsible for converting ICP tokens into **cycles**. It supports: +- Topping up existing canisters. +- Creating new canisters. +- Minting and depositing minted cycles into cycle ledger-managed accounts. +- Fetching exchange rates and subnet configuration. -- Topping up existing canisters. -- Creating new canisters. -- 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://learn.internetcomputer.org/hc/en-us/articles/33692645961236-Overview) and receives configuration updates through proposals. +The CMC is governed solely by the [NNS (Network Nervous System)](https://internetcomputer.org/nns/) and receives configuration updates through proposals. ## Configuration parameters -- **Conversion rate source**: The ICP/XDR exchange rate is **pushed** into the CMC by a dedicated [exchange rate canister](/docs/references/system-canisters/xrc). This data reflects live market pricing and determines the ICP→cycles conversion. - -- **Governance control**: All operations of the CMC — including upgrades, configuration, and authorized callers — are managed through NNS proposals. - +- **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. +- **Governance control**: All operations of the CMC — including upgrades, configuration, and authorized callers — are managed through NNS proposals. ## API endpoints The CMC exposes a set of core methods for converting ICP into cycles and interacting with subnet configuration. These include: -- `notify_create_canister`: Processes an ICP payment by minting cycles and using them to create a new canister, assigning control to the specified principal and applying optional settings. -- `notify_top_up`: Processes an ICP payment by minting cycles and sending them to an existing canister to increase its available balance. -- `notify_mint_cycles`: Processes an ICP payment by minting cycles and depositing them into the caller's cycles ledger account associated with a subaccount. -- `create_canister`: Creates a canister using cycles directly attached to the call. -- `get_icp_xdr_conversion_rate`: Returns the current ICP/XDR exchange rate with certification. -- `get_subnet_types_to_subnets`: Lists publicly available subnets grouped by their types. Only covers subnets that don't have standard settings. -- `get_principals_authorized_to_create_canisters_to_subnets`: Indicates which principals are permitted to create canisters on which subnets. -- `get_default_subnets`: Returns the subnets for general-purpose public canister creation. These subnets typically have standard settings. -- `get_build_metadata`: Displays internal version and build information for the CMC. - +- `notify_create_canister`: Processes an ICP payment by minting cycles and using them to create a new canister, assigning control to the specified principal and applying optional settings. +- `notify_top_up`: Processes an ICP payment by minting cycles and sending them to an existing canister to increase its available balance. +- `notify_mint_cycles`: Processes an ICP payment by minting cycles and depositing them into the caller's cycles ledger account associated with a subaccount. +- `create_canister`: Creates a canister using cycles directly attached to the call. +- `get_icp_xdr_conversion_rate`: Returns the current ICP/XDR exchange rate with certification. +- `get_subnet_types_to_subnets`: Lists publicly available subnets grouped by their *named types* (assigned by governance). +- `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. +- `http_request`: Provides canister metrics via a standard HTTP interface (useful for monitoring). ## `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. +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). The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **recipient**: -- The **destination account** of the ICP transfer must be the CMC's account with a **subaccount derived from the intended controller's principal**. -- 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. -- 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 - ``` - - Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to: - ``` - "\43\52\45\41\00\00\00\00" - ``` - +- The **destination account** of the ICP transfer must be the CMC's account with a **subaccount derived from the intended controller's principal**. +- 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. +- 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 + ``` + - Or, for ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`), a memo blob equal to: + ``` + "\43\52\45\41\00\00\00\00" // ASCII "CREA" + 4 zero bytes + ``` #### Parameters - ``` - record { - block_index: nat64; // The ledger block containing the ICP payment - controller: principal; // The creator of the canister. Must match caller; otherwise, Err is returned. This is also used when checking that the creator is authorized to create canisters in subnets where authorization is required. This is also used when `settings` does not specify `controllers`. - settings: opt CanisterSettings; // Optional settings like controllers, memory limits, etc. - subnet_type: opt text; // Deprecated: legacy subnet selection - subnet_selection: opt SubnetSelection; // Preferred subnet selection method - } - ``` - - #### Returns - ``` - variant { Ok: principal; Err: NotifyError } - ``` - - **Ok**: The principal of the newly created canister. - - **Err**: Indicates why the request was rejected (e.g., incorrect memo, controller mismatch, payment already processed, etc.). - For information about retry semantics, error handling, and caching behavior, see [Notify Methods – Shared Behavior](#notify-methods-shared-behavior-retry-error-handling-and-caching) -. +```candid +record { + block_index: nat64; // The ledger block containing the ICP payment + controller: principal; // The creator of the canister. Must match caller; otherwise, Err is returned (unless caller is NNS Dapp). This is also used when checking that the creator is authorized to create canisters in subnets where authorization is required. This is also used when `settings` does not specify `controllers`. + settings: opt CanisterSettings; // Optional settings like controllers, memory limits, etc. + subnet_type: opt text; // Deprecated: legacy subnet selection + subnet_selection: opt SubnetSelection; // Preferred subnet selection method +} +``` +#### Returns +```candid +variant { Ok: principal; Err: NotifyError } +``` +- **Ok**: The principal of the newly created canister. +- **Err**: Indicates why the request was rejected (e.g., incorrect memo, controller mismatch, payment already processed, etc.). + For information about retry semantics, error handling, and caching behavior, see [Notify Methods – Shared Behavior](#notify-methods--shared-behavior-retry-error-handling-and-caching). ## `notify_top_up` - Tops up an existing canister by minting cycles based on an ICP payment recorded in the ICP Ledger. +Tops up an existing canister by minting cycles based on an ICP payment recorded in the ICP Ledger. - The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **target canister**: +The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **target canister**: - - The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the target canister’s principal. - - The **memo field** must explicitly indicate the intent to top up a canister. This can be expressed as: - - A legacy 64-bit unsigned integer memo with value (in decimal) - ``` - 1347768404 - ``` - - 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" - ``` +- The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the target canister’s principal. +- The **memo field** must explicitly indicate the intent to top up a canister. This can be expressed as: + - A legacy 64-bit unsigned integer memo with value (in decimal) + ``` + 1347768404 + ``` + - 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 + ``` #### Parameters - ``` - record { - block_index: nat64; // Block index of the ICP ledger payment - canister_id: principal; // Canister to be topped up - } - ``` - #### Returns - ``` - variant { Ok: nat; Err: NotifyError } - ``` +```candid +record { + block_index: nat64; // Block index of the ICP ledger payment + canister_id: principal; // Canister to be topped up +} +``` - - **Ok**: Number of cycles minted and deposited into the specified canister. - - **Err**: Indicates why the top-up failed (e.g., incorrect memo, controller mismatch, payment already processed, etc.). - For information about retry semantics, error handling, and caching behavior, see [Notify Methods – Shared Behavior](#notify-methods-shared-behavior-retry-error-handling-and-caching) - . +#### Returns + +```candid +variant { Ok: nat; Err: NotifyError } +``` + +- **Ok**: Number of cycles minted and deposited into the specified canister. +- **Err**: Indicates why the top-up failed (e.g., incorrect memo, controller mismatch, payment already processed, etc.). + For information about retry semantics, error handling, and caching behavior, see [Notify Methods – Shared Behavior](#notify-methods--shared-behavior-retry-error-handling-and-caching). #### Notes - - The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see [“Shared logic”](#shared-logic) section). +- The subaccount used in the transfer must be derived from the target canister’s principal using the standard 32-byte encoding (see [“Shared logic”](#shared-logic) section). ## `notify_mint_cycles` -Mints cycles into a [cycles ledger account](/docs/defi/token-ledgers/cycles-ledger) based on an ICP payment recorded in the ICP Ledger. +Mints cycles into a [cycles ledger account](/docs/concepts/tokens/cycles/cycles-ledger) based on an ICP payment recorded in the ICP Ledger. -This method is used to deposit minted cycles into a specific subaccount in the [cycles ledger.](/docs/defi/token-ledgers/cycles-ledger) It supports minting from both legacy and ICRC-1-style transfers. +This method is used to deposit minted cycles into a specific subaccount in the [cycles ledger.](/docs/concepts/tokens/cycles/cycles-ledger) It supports minting from both legacy and ICRC-1-style transfers. 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). -- The **ICP transfer's memo field** must explicitly indicate the intent to mint cycles. This can be: - - For legacy `transfer` calls: a `u64` memo with value (in decimal): - - ``` - 1414416717 - ``` - - For ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`): a `blob` memo equal to: - ``` - "\4d\49\4e\54\00\00\00\00" - ``` +- The subaccount used in the transfer must be derived from the caller principal using the standard 32-byte encoding (see “Shared Logic” section). +- The **ICP transfer's memo field** must explicitly indicate the intent to mint cycles. This can be: + - For legacy `transfer` calls: a `u64` memo with value (in decimal): + ``` + 1414416717 + ``` + - For ICRC-1-compatible transfers (e.g., `icrc1_transfer`, `icrc2_transfer_from`): a `blob` memo equal to: + ``` + "\4d\49\4e\54\00\00\00\00" // ASCII "MINT" + 4 zero bytes + ``` #### Parameters - ``` - record { - block_index: nat64; // Block index of the ICP ledger payment - to_subaccount: opt blob; // 32-byte subaccount to credit in the cycles ledger - deposit_memo: opt blob; // Optional application-specific memo that will be used in the cycles ledger's deposit transaction - } - ``` - - #### Returns - ``` - variant { - Ok: record { - block_index: nat; - minted: nat; - balance: nat; - }; - Err: NotifyError; - } - ``` - - **Ok**: A record that includes: - - `block_index`: The cycles ledger block index of the minting transaction - - `minted`: The amount of cycles created from the ICP payment - - `balance`: The new balance of the target cycles ledger subaccount - - **Err**: Indicates why the minting failed (e.g., incorrect memo, controller mismatch, payment already processed, etc.). - For information about retry semantics, error handling, and caching behavior, see [Notify Methods – Shared Behavior](#notify-methods-shared-behavior-retry-error-handling-and-caching) -. +```candid +record { + block_index: nat64; // Block index of the ICP ledger payment + to_subaccount: opt blob; // 32-byte subaccount to credit in the cycles ledger + deposit_memo: opt blob; // Optional application-specific memo that will be used in the cycles ledger's deposit transaction +} +``` + +#### Returns + +```candid +variant { + Ok: record { + block_index: nat; + minted: nat; + balance: nat; + }; + Err: NotifyError; +} +``` + +- **Ok**: A record that includes: + - `block_index`: The cycles ledger block index of the minting transaction + - `minted`: The amount of cycles created from the ICP payment + - `balance`: The new balance of the target cycles ledger subaccount +- **Err**: Indicates why the minting failed (e.g., incorrect memo, controller mismatch, payment already processed, etc.). + For information about retry semantics, error handling, and caching behavior, see [Notify Methods – Shared Behavior](#notify-methods--shared-behavior-retry-error-handling-and-caching). + +## Notify Methods – Shared Behavior (Retry, Error Handling, and Caching) + +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. +- If a concurrent `notify_*` call is already processing for the same transfer, the new call returns an error indicating that the operation is in progress (`NotifyError::Processing`). +- 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`. - ## Notify Methods – Shared Behavior (Retry, Error Handling, and Caching) +These semantics ensure that all `notify_*` methods are idempotent. Clients can safely retry using the original parameters (memo, block height, destination account). - All `notify_*` methods (such as `notify_create_canister` and `notify_top_up`) follow the same core semantics: +### Error Handling and Refunds (`NotifyError`) - - If the ICP transfer does not use the correct memo, the tokens are refunded (minus the ledger’s transfer fee). - - If a concurrent `notify_*` call is already processing for the same transfer, the new call returns an error indicating that the operation is in progress. - - If a previous `notify_*` call has already successfully processed a transfer at the same block height, the new call returns the same result. - - To support safe retries, the result of a successful call is **cached** for a bounded (but generous) amount of time. +Calls to `notify_*` methods return a `variant { Ok: ...; Err: NotifyError }`. The `NotifyError` type indicates why an operation failed: - These semantics ensure that all `notify_*` methods are idempotent. Clients can safely retry using the original parameters (memo, block height, destination account). +```candid +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. + TransactionTooOld: nat64; // The block_index is older than the CMC's cached history (value returned is the minimum valid block_index). Cannot be retried. + Other: record { error_code: nat64; description: text; }; // A miscellaneous error occurred. +}; +``` +**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). + - **Fees:** When a refund occurs, the standard ICP Ledger transfer fee (`10_000` e8s) is always deducted by the Ledger itself. Additionally, the CMC may burn a specific refund fee from the amount before initiating the refund transfer: + - Create Canister Refund Fee: `10_000` e8s (`CREATE_CANISTER_REFUND_FEE`) + - Top Up Refund Fee: `10_000` e8s (`TOP_UP_CANISTER_REFUND_FEE`) + - Mint Cycles Refund Fee: `10_000` e8s (`MINT_CYCLES_REFUND_FEE`) + - The `reason` field provides details. The `block_index` indicates the ledger block of the successful refund transfer, if applicable. +- **`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`). ## `create_canister` @@ -190,80 +210,131 @@ Creates a new canister using cycles attached directly to the call (not from an I Unlike `notify_create_canister`, this method does not rely on an external ICP transaction. Instead, the calling canister must attach enough cycles to cover the creation cost. -- If `subnet_selection` and the (deprecated) `subnet_type` are omitted, the CMC will select a suitable subnet at random from the available subnets. -- If `settings` is provided, it will override the default configuration for the new canister. -- If no controllers are given in `settings`, the calling principal becomes the sole controller. +- If `subnet_selection` and the (deprecated) `subnet_type` are omitted, the CMC will select a suitable subnet at random from the available subnets (either default subnets or those specifically authorized for the caller principal). +- If `settings` is provided, it will override the default configuration for the new canister. +- If no controllers are given in `settings`, the calling principal becomes the sole controller. #### Parameters - ``` - record { - settings: opt CanisterSettings; // Optional settings: controller(s), allocations, limits, etc. - subnet_type: opt text; // (Deprecated) Legacy subnet type selection - subnet_selection: opt SubnetSelection; // Preferred way to select the subnet - } - ``` - #### Returns - ``` - variant { Ok: principal; Err: CreateCanisterError } - ``` +```candid +record { + settings: opt CanisterSettings; // Optional settings: controller(s), allocations, limits, etc. + subnet_type: opt text; // (Deprecated) Legacy subnet type selection + subnet_selection: opt SubnetSelection; // Preferred way to select the subnet +} +``` - - **Ok**: The principal ID of the newly created canister. - - **Err**: An error indicating why the creation failed. - May include a refund amount if cycles were consumed but the canister was not created. - See `CreateCanisterError` for structured error types. +#### Returns +```candid +variant { Ok: principal; Err: CreateCanisterError } +``` + +- **Ok**: The principal ID of the newly created canister. +- **Err**: A `CreateCanisterError` indicating why creation failed. This is currently always a `Refunded` variant: + ```candid + type CreateCanisterError = variant { + 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). + - `refund_amount`: The amount of cycles refunded to the caller. If the request failed due to issues *before* attempting creation on a subnet (e.g., bad arguments, insufficient cycles attached initially), the full attached amount is usually refunded. If it failed *during* the cross-net call to a subnet (e.g., subnet rejected creation), a penalty (`1,000,000,000` cycles, `BAD_REQUEST_CYCLES_PENALTY`) is deducted before refunding the rest. #### Notes - - This method requires cycles to be attached to the call. - - Returns the principal of the newly created canister. - - The result is **not idempotent** — calling it again will consume cycles and create a different canister. - ## `get_icp_xdr_conversion_rate` +- This method requires cycles to be attached to the call. +- Returns the principal of the newly created canister. +- The result is **not idempotent** — calling it again will consume cycles and create a different canister. + +## `get_icp_xdr_conversion_rate` Returns the current ICP/XDR exchange rate used to compute how many cycles a given amount of ICP buys. #### Parameters -``` +```candid record {} ``` #### Returns -``` +```candid record { - data: opt record { + data: record { // Note: In current implementation, this is never null if the call succeeds. xdr_permyriad_per_icp: nat64; timestamp_seconds: nat64; }; certificate: blob; + hash_tree: blob; // CBOR encoded hash tree witness } + ``` -- `xdr_permyriad_per_icp`: The exchange rate, in hundredths of a percent. For example, 12500 means 1 ICP = 1.25 XDR. -- `timestamp_seconds`: Time the rate was recorded, in seconds since the Unix epoch. -- `certificate`: Certified data that can be verified by canister clients in query calls. +- `xdr_permyriad_per_icp`: The exchange rate, in 1/10000ths of an XDR per ICP (Permyriad). For example, 12500 means 1 ICP = 1.25 XDR. +- `timestamp_seconds`: Time the rate was recorded, in seconds since the Unix epoch. +- `certificate`: The IC data certificate authenticating the response. +- `hash_tree`: The CBOR-encoded hash tree proving the inclusion of the `data` in the certificate. #### Notes -This method can be called as a query. If the exchange rate is not available, `data` will be `null`. +This method can be called as a query. Clients should verify the certificate and hash_tree against the root subnet's public key to ensure authenticity. + +## Subnet Configuration (Governance Methods) + +The following methods are used by the NNS Governance canister to configure how canisters are created across different subnets. Developers typically do not call these directly but may query the results using methods like `get_default_subnets`, `get_principals_authorized_to_create_canisters_to_subnets`, and `get_subnet_types_to_subnets`. + +### `set_authorized_subnetwork_list` (Governance Only) + +Configures which principals are authorized to create canisters on specific lists of subnets. + +- **Caller**: Must be the NNS Governance canister. +- **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`). + +*(Args: `record { who: opt principal; subnets: vec principal; }`, Returns: `()`) * + +### `update_subnet_type` (Governance Only) + +Adds or removes named subnet types (e.g., "Fiduciary", "Storage"). These types provide logical groupings for subnets with specific characteristics. + +- **Caller**: Must be the NNS Governance canister. +- **Purpose**: + - **Add**: Creates a new, empty named type. Fails if the type already exists. + - **Remove**: Deletes an existing named type. Fails if the type does not exist or if any subnets are currently assigned to that type. + +*(Args: `variant { Add: text; Remove: text; }`, Returns: `variant { Ok: (); Err: text }`)* + +### `change_subnet_type_assignment` (Governance Only) + +Assigns or unassigns specific subnets to a named subnet type. + +- **Caller**: Must be the NNS Governance canister. +- **Purpose**: + - **Add**: Assigns a list of subnets to an existing named type. Fails if the type doesn't exist, if any of the subnets are already assigned to *any* type, or if any of the subnets are part of the *default* or per-principal *authorized* lists. + - **Remove**: Unassigns a list of subnets from a specific named type. Fails if the type doesn't exist or if any of the specified subnets are not currently assigned to *that specific* type. + +*(Args: `variant { Add: SubnetListWithType; Remove: SubnetListWithType; }` where `SubnetListWithType = record { subnets: vec principal; subnet_type: text; }`, Returns: `variant { Ok: (); Err: text }`)* + + ---- ## `get_subnet_types_to_subnets` -Returns a mapping from subnet types (e.g., `"application"`, `"verified_application"`) to the list of subnets of that type. +Returns a mapping from named subnet types (e.g., `"Fiduciary"`) assigned by NNS Governance to the list of subnet principals belonging to that type. Subnets listed here are typically reserved for specific purposes and are separate from the default and authorized lists. #### Parameters -``` +```candid record {} ``` #### Returns -``` +```candid +// Current type: SubnetTypesToSubnetsResponse = record { data: vec record { text; vec principal } } +// Draft type shown for simplicity: record { subnets: vec record { subnet_type: text; @@ -272,27 +343,29 @@ record { } ``` -- Each entry in `subnets` represents a mapping from a subnet type to the list of subnets that belong to that type. +- Each entry in `subnets` represents a mapping from a subnet type to the list of subnets that belong to that type. #### Notes -This information can be used by dapps to select appropriate subnets for canister creation. +This information can be used by dapps to select appropriate subnets for canister creation when a specific type is desired via `SubnetSelection::Filter`. --- ## `get_principals_authorized_to_create_canisters_to_subnets` -Lists the principals that are authorized to create canisters on which 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. #### Parameters -``` +```candid record {} ``` #### Returns -``` +```candid +// Current type: AuthorizedSubnetsResponse = record { data: vec record { principal; vec principal } } +// Draft type shown for simplicity: record { mappings: vec record { principal: principal; @@ -301,33 +374,35 @@ record { } ``` -- Each entry represents a principal and the list of subnet IDs on which they are allowed to create canisters. +- Each entry represents a principal and the list of subnet IDs on which they are allowed to create canisters. #### Notes -This is typically governed by proposals on the NNS. Only some principals are explicitly authorized. +This is typically governed by proposals on the NNS. Only some principals are explicitly authorized for specific subnets beyond the default list. --- ## `get_default_subnets` -Returns the list of default subnets for general-purpose canister creation. +Returns the list of subnet principals designated by NNS Governance as default targets for canister creation. Any principal can typically create canisters on these subnets without specific authorization. #### Parameters -``` +```candid record {} ``` #### Returns -``` +```candid +// Current type: vec principal +// Draft type shown for simplicity: record { subnets: vec principal; } ``` -- `subnets`: The default subnets that a user may be assigned when no subnet is specified during canister creation. +- `subnets`: The default subnets that a user may be assigned when no specific subnet is requested during canister creation. #### Notes @@ -335,19 +410,39 @@ These are usually `"application"` type subnets and serve as a fallback option. --- +## `total_cycles_minted` + +Returns the total number of cycles ever minted by the CMC since its genesis. This provides a measure of the overall cycles created via the CMC mechanism. + +#### Parameters + +```candid +record {} +``` + +#### Returns + +```candid +nat // Total cycles minted as an arbitrary-precision natural number. +``` + +--- + ## `get_build_metadata` Returns metadata about the current build of the Cycles Minting Canister. #### Parameters -``` +```candid record {} ``` #### Returns -``` +```candid +// Current type from define_get_build_metadata_candid_method!: text +// Draft type shown for simplicity: record { repo: opt text; git_revision: opt text; @@ -357,16 +452,50 @@ record { } ``` -- `repo`: URL of the source code repository. -- `git_revision`: Git commit hash. -- `rust_version`: Version of Rust used. -- `cargo_version`: Version of Cargo used. -- `build_time`: UTC string of when the build was performed. +- `repo`: URL of the source code repository. +- `git_revision`: Git commit hash. +- `rust_version`: Version of Rust used. +- `cargo_version`: Version of Cargo used. +- `build_time`: UTC string of when the build was performed. + +#### Notes + +This is primarily used for debugging, reproducibility, and audits. Fields may be `null` depending on build environment (though typically returned as a single formatted string). + +--- + +## `http_request` (Metrics Endpoint) + +Standard HTTP interface for fetching canister metrics in Prometheus format. + +#### Parameters + +```candid +record { + url: text; + method: text; // Should be "GET" + headers: vec record { name: text; value: text }; + body: blob; +} +``` + +#### Returns + +```candid +record { + status_code: nat16; + headers: vec record { name: text; value: text }; + body: blob; // Prometheus metrics data + // streaming_strategy : opt ... // Optional streaming fields omitted +} +``` #### Notes -This is primarily used for debugging, reproducibility, and audits. Fields may be `null` depending on build environment. +- Call with method "GET" and URL "/metrics". +- Provides metrics like `cmc_cycles_minted_total`, `cmc_icp_xdr_conversion_rate`, `cmc_limiter_cycles`, `cmc_cycles_limit`, etc. Useful for monitoring CMC activity and capacity. Requires standard HTTP request structure. +--- ## Shared logic @@ -374,47 +503,51 @@ This is primarily used for debugging, reproducibility, and audits. Fields may be The `CanisterSettings` record is used to configure the canister at the time of creation or through governance. Each field is optional and will default to system-provided values if not specified. -``` +```candid record { - controllers : opt vec principal; // List of principals allowed to control the canister - 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; // Reserved minimum cycle balance for execution - log_visibility : opt variant { - controllers; - public; - }; // Visibility of canister logs - wasm_memory_limit : opt nat; // Cap on wasm memory usage, in bytes - wasm_memory_threshold : opt nat; // Threshold to trigger memory alerts or actions + controllers : opt vec principal; // List of principals allowed to control the canister + 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) + log_visibility : opt variant { controllers; public; }; // Visibility of canister logs + wasm_memory_limit : opt nat; // Cap on wasm memory usage, in bytes } ``` Unspecified fields are interpreted as: -- `controllers`: Defaults to the caller of the method. -- Other fields: Use system-wide default configuration values. +- `controllers`: Defaults to the caller of the method (`notify_create_canister`'s controller or `create_canister`'s caller). +- Other fields: Use system-wide default configuration values set by the IC replica. -## Encoding a principal into a subaccount +### Encoding a principal into a subaccount Several CMC methods require an ICP payment to be sent to a subaccount that encodes a principal (e.g., the controller of a new canister or the canister being topped up). To derive this subaccount, the principal is serialized and packed into a 32-byte array using the following logic: -``` +```motoko +// Motoko Example +import Principal "mo:base/Principal"; +import Blob "mo:base/Blob"; +import Nat8 "mo:base/Nat8"; +import Array "mo:base/Array"; + public func principalToSubAccount(id: Principal) : [Nat8] { - let p = Blob.toArray(Principal.toBlob(id)); - Array.tabulate(32, func(i : Nat) : Nat8 { - if (i >= p.size() + 1) 0 - else if (i == 0) (Nat8.fromNat(p.size())) - else (p[i - 1]) - }) + let p = Blob.toArray(Principal.toBlob(id)); + let p_len = p.size(); + Array.tabulate(32, func(i : Nat) : Nat8 { + if (i == 0) { Nat8.fromNat(p_len) } + else if (i <= p_len) { p[i - 1] } + else { 0 } + }) }; ``` This produces a 32-byte subaccount where: -- The first byte contains the length of the principal. -- The next bytes contain the principal's raw byte encoding. -- The remainder is zero-padded to reach 32 bytes. + +- The first byte contains the length of the principal's byte representation. +- The next bytes contain the principal's raw byte encoding. +- The remainder is zero-padded to reach 32 bytes. This convention ensures the CMC can determine which principal or canister an incoming payment was intended for. From 0c5976e026d318cda58e6e8b74abba3ceb0dbe66 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Tue, 15 Apr 2025 12:16:02 +0200 Subject: [PATCH 18/24] spourious lines --- docs/references/cmcspec.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index eb126cda01..5c087e1179 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -349,7 +349,7 @@ record { This information can be used by dapps to select appropriate subnets for canister creation when a specific type is desired via `SubnetSelection::Filter`. ---- + ## `get_principals_authorized_to_create_canisters_to_subnets` @@ -380,7 +380,7 @@ record { This is typically governed by proposals on the NNS. Only some principals are explicitly authorized for specific subnets beyond the default list. ---- + ## `get_default_subnets` @@ -408,7 +408,7 @@ record { These are usually `"application"` type subnets and serve as a fallback option. ---- + ## `total_cycles_minted` @@ -426,7 +426,7 @@ record {} nat // Total cycles minted as an arbitrary-precision natural number. ``` ---- + ## `get_build_metadata` @@ -462,7 +462,7 @@ record { This is primarily used for debugging, reproducibility, and audits. Fields may be `null` depending on build environment (though typically returned as a single formatted string). ---- + ## `http_request` (Metrics Endpoint) @@ -495,7 +495,7 @@ record { - Call with method "GET" and URL "/metrics". - Provides metrics like `cmc_cycles_minted_total`, `cmc_icp_xdr_conversion_rate`, `cmc_limiter_cycles`, `cmc_cycles_limit`, etc. Useful for monitoring CMC activity and capacity. Requires standard HTTP request structure. ---- + ## Shared logic From da413113f06e2dd231889b9bea57f3de1434e2f2 Mon Sep 17 00:00:00 2001 From: Bogdan Warinschi Date: Tue, 15 Apr 2025 12:30:09 +0200 Subject: [PATCH 19/24] pointers to encoding of principals into subaccounts --- docs/references/cmcspec.mdx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 5c087e1179..127092253b 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -17,7 +17,6 @@ The CMC is governed solely by the [NNS (Network Nervous System)](https://interne - **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. - **Governance control**: All operations of the CMC — including upgrades, configuration, and authorized callers — are managed through NNS proposals. - ## API endpoints The CMC exposes a set of core methods for converting ICP into cycles and interacting with subnet configuration. These include: @@ -41,7 +40,7 @@ Uses tokens from an ICP transfer to **mint cycles**, which are then used to **cr The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **recipient**: -- The **destination account** of the ICP transfer must be the CMC's account with a **subaccount derived from the intended controller's principal**. +- 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. @@ -83,7 +82,7 @@ Tops up an existing canister by minting cycles based on an ICP payment recorded The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **target canister**: -- The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the target canister’s principal. +- The **destination account** of the ICP transfer must be the CMC's account with a subaccount derived from the target canister’s principal (see the section on [Encoding a principal into a subaccount](#encoding-a-principal-into-a-subaccount)). - The **memo field** must explicitly indicate the intent to top up a canister. This can be expressed as: - A legacy 64-bit unsigned integer memo with value (in decimal) ``` @@ -283,7 +282,7 @@ This method can be called as a query. Clients should verify the certificate and The following methods are used by the NNS Governance canister to configure how canisters are created across different subnets. Developers typically do not call these directly but may query the results using methods like `get_default_subnets`, `get_principals_authorized_to_create_canisters_to_subnets`, and `get_subnet_types_to_subnets`. -### `set_authorized_subnetwork_list` (Governance Only) +### `set_authorized_subnetwork_list` (Governance only) Configures which principals are authorized to create canisters on specific lists of subnets. @@ -295,7 +294,7 @@ Configures which principals are authorized to create canisters on specific lists *(Args: `record { who: opt principal; subnets: vec principal; }`, Returns: `()`) * -### `update_subnet_type` (Governance Only) +### `update_subnet_type` (Governance only) Adds or removes named subnet types (e.g., "Fiduciary", "Storage"). These types provide logical groupings for subnets with specific characteristics. @@ -306,7 +305,7 @@ Adds or removes named subnet types (e.g., "Fiduciary", "Storage"). These types p *(Args: `variant { Add: text; Remove: text; }`, Returns: `variant { Ok: (); Err: text }`)* -### `change_subnet_type_assignment` (Governance Only) +### `change_subnet_type_assignment` (Governance only) Assigns or unassigns specific subnets to a named subnet type. @@ -464,7 +463,7 @@ This is primarily used for debugging, reproducibility, and audits. Fields may be -## `http_request` (Metrics Endpoint) +## `http_request` (Metrics endpoint) Standard HTTP interface for fetching canister metrics in Prometheus format. From 5386421536598cbd7dfe5c02f4436f29b1a61701 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Tue, 13 May 2025 14:19:41 +0200 Subject: [PATCH 20/24] Update docs/references/cmcspec.mdx Co-authored-by: Lara Schmid <73884086+LaraAS@users.noreply.github.com> --- docs/references/cmcspec.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 127092253b..9f496d9ec9 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -4,7 +4,7 @@ import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow"; -The Cycles Minting Canister (CMC) is a system canister on the Internet Computer responsible for converting ICP tokens into **cycles**, tracking the total cycles minted, and providing configuration and exchange rate data. It supports: +The Cycles Minting Canister (CMC) is a protocol canister on the Internet Computer responsible for converting ICP tokens into **cycles**, tracking the total cycles minted, and providing configuration and exchange rate data. It supports: - Topping up existing canisters. - Creating new canisters. From f666563a4243d8c40759c1c5f59848c15bd9ac81 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Tue, 13 May 2025 14:20:27 +0200 Subject: [PATCH 21/24] Update docs/references/cmcspec.mdx Co-authored-by: Lara Schmid <73884086+LaraAS@users.noreply.github.com> --- docs/references/cmcspec.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 9f496d9ec9..edf9bc0f6d 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -6,7 +6,7 @@ import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow"; The Cycles Minting Canister (CMC) is a protocol canister on the Internet Computer responsible for converting ICP tokens into **cycles**, tracking the total cycles minted, and providing configuration and exchange rate data. It supports: -- Topping up existing canisters. +- Topping up existing canisters with cycles. - Creating new canisters. - Minting and depositing minted cycles into cycle ledger-managed accounts. - Fetching exchange rates and subnet configuration. From 93ac15bb9281981a430eb72a4f7f40a2de936519 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Tue, 13 May 2025 14:24:25 +0200 Subject: [PATCH 22/24] Update docs/references/cmcspec.mdx Co-authored-by: Lara Schmid <73884086+LaraAS@users.noreply.github.com> --- docs/references/cmcspec.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index edf9bc0f6d..bd2178f25f 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -78,7 +78,7 @@ variant { Ok: principal; Err: NotifyError } ## `notify_top_up` -Tops up an existing canister by minting cycles based on an ICP payment recorded in the ICP Ledger. +Tops up an existing canister with additional cycles by minting cycles based on an ICP payment recorded in the ICP Ledger. The CMC expects the payment to follow a strict structure that encodes both the **intent** and the **target canister**: From c287dad33b72f1f66326205047ad9a3c0b8e4458 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Tue, 13 May 2025 14:30:47 +0200 Subject: [PATCH 23/24] Update docs/references/cmcspec.mdx --- docs/references/cmcspec.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index bd2178f25f..1e6411127a 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -411,7 +411,7 @@ These are usually `"application"` type subnets and serve as a fallback option. ## `total_cycles_minted` -Returns the total number of cycles ever minted by the CMC since its genesis. This provides a measure of the overall cycles created via the CMC mechanism. +Returns the total number of cycles ever minted by the CMC since its genesis. #### Parameters From 2b22664196c54dd282f134f0862b1288f65d6787 Mon Sep 17 00:00:00 2001 From: bogwar <51327868+bogwar@users.noreply.github.com> Date: Tue, 13 May 2025 14:33:17 +0200 Subject: [PATCH 24/24] Update docs/references/cmcspec.mdx Co-authored-by: Lara Schmid <73884086+LaraAS@users.noreply.github.com> --- docs/references/cmcspec.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/cmcspec.mdx b/docs/references/cmcspec.mdx index 1e6411127a..03ff5e1c6a 100644 --- a/docs/references/cmcspec.mdx +++ b/docs/references/cmcspec.mdx @@ -517,7 +517,7 @@ record { Unspecified fields are interpreted as: - `controllers`: Defaults to the caller of the method (`notify_create_canister`'s controller or `create_canister`'s caller). -- Other fields: Use system-wide default configuration values set by the IC replica. +- Other fields: Use system-wide default configuration values set by the ICP replica. ### Encoding a principal into a subaccount