Skip to content
Open
Changes from 9 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
93332e5
initial cmc spec file
bogwar Mar 24, 2025
5fcf45a
API overview
bogwar Mar 24, 2025
05e0039
notify_create_canister
bogwar Mar 24, 2025
8b357f7
notify_create_canister
bogwar Mar 24, 2025
c615079
notify_topup_canister
bogwar Mar 24, 2025
4a3502e
fixed memo for topup
bogwar Mar 25, 2025
37ff838
create_canister
bogwar Mar 25, 2025
e95b8c6
added shared logic section for settings and encoding of principal as …
bogwar Mar 25, 2025
61680ad
added legacy memo for notify_mint
bogwar Mar 25, 2025
a16f639
Apply Jesse's changes
bogwar Mar 26, 2025
cfe15e4
Apply Severin's changes
bogwar Mar 26, 2025
4c033e7
memo fix
bogwar Mar 26, 2025
90ba3e8
Merge branch 'bw/cmcspec' of github.com:dfinity/portal into bw/cmcspec
bogwar Mar 26, 2025
cc22a9f
added some clarifications
bogwar Apr 4, 2025
fa16af2
removed mnemonics for memo fields, added remaining methods
bogwar Apr 9, 2025
6b841fa
new section + shared behaviour for notify-* methods
bogwar Apr 9, 2025
d0289cc
pass
bogwar Apr 15, 2025
f6a79cf
added more methods
bogwar Apr 15, 2025
0c5976e
spourious lines
bogwar Apr 15, 2025
da41311
pointers to encoding of principals into subaccounts
bogwar Apr 15, 2025
5386421
Update docs/references/cmcspec.mdx
bogwar May 13, 2025
f666563
Update docs/references/cmcspec.mdx
bogwar May 13, 2025
93ac15b
Update docs/references/cmcspec.mdx
bogwar May 13, 2025
c287dad
Update docs/references/cmcspec.mdx
bogwar May 13, 2025
2b22664
Update docs/references/cmcspec.mdx
bogwar May 13, 2025
fee8fb1
Merge branch 'master' into bw/cmcspec
marc0olo Jul 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 241 additions & 0 deletions docs/references/cmcspec.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import { MarkdownChipRow } from "/src/components/Chip/MarkdownChipRow";

# Cycles Minting Canister (CMC) API

<MarkdownChipRow labels={["Reference"]} />

## Overview
Comment thread
bogwar marked this conversation as resolved.
Outdated

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
Comment thread
bogwar marked this conversation as resolved.
Outdated

The CMC is governed entirely by the **NNS (Network Nervous System)** and receives configuration updates through proposals.
Comment thread
bogwar marked this conversation as resolved.
Outdated

## Configuration Parameters
Comment thread
bogwar marked this conversation as resolved.
Outdated

- **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.
Comment thread
bogwar marked this conversation as resolved.
Outdated

- **Governance Control**: All operations of the CMC — including upgrades, configuration, and authorized callers — are managed through NNS proposals.
Comment thread
bogwar marked this conversation as resolved.
Outdated


## API Endpoints
Comment thread
bogwar marked this conversation as resolved.
Outdated

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 a cycles ledger account associated with a subaccount.
Comment thread
bogwar marked this conversation as resolved.
Outdated
- `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.
Comment thread
bogwar marked this conversation as resolved.
Outdated
- `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.
Comment thread
bogwar marked this conversation as resolved.
Outdated
- `get_build_metadata`: Displays internal version and build information for the CMC.


## API Reference
Comment thread
bogwar marked this conversation as resolved.
Outdated

### `notify_create_canister`
Comment thread
bogwar marked this conversation as resolved.
Outdated

Creates a new canister after verifying an ICP payment recorded in the ICP Ledger.
Comment thread
bogwar marked this conversation as resolved.
Outdated
If the `subnet_selection field is omitted, the CMC will automatically choose a suitable subnet at random from the available options.
Comment thread
bogwar marked this conversation as resolved.
Outdated
Comment thread
bogwar marked this conversation as resolved.
Outdated

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:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
```candid
record {
block_index: nat64; // The ledger block containing the ICP payment
controller: principal; // The principal who will control the new canister
Comment thread
bogwar marked this conversation as resolved.
Outdated
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:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
```candid
variant { Ok: principal; Err: NotifyError }
```

- **Notes:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
- The call is idempotent with respect to `block_index`; repeating it returns the same result.
Comment thread
bogwar marked this conversation as resolved.
Outdated
- The controller must match the one encoded in the subaccount used for the payment.
Comment thread
bogwar marked this conversation as resolved.
Outdated
- Only transactions that follow the exact memo format will be processed successfully.


### `notify_top_up`
Comment thread
bogwar marked this conversation as resolved.
Outdated

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 `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:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
```candid
record {
block_index: nat64; // Block index of the ICP ledger payment
canister_id: principal; // Canister to be topped up
}
```

- **Returns:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
```candid
variant { Ok: nat; Err: NotifyError } // Number of cycles minted and sent
Comment thread
bogwar marked this conversation as resolved.
Outdated
```

- **Notes:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
- 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).
Comment thread
bogwar marked this conversation as resolved.
Outdated
- This method is idempotent by `block_index`: retrying the same request will return the same result if it already succeeded.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same here, probably worth noting cache expiry time and that it can only be called on recent-enough blocks

- Only transfers matching the expected structure and memo will be accepted.

## `notify_mint_cycles`
Comment thread
bogwar marked this conversation as resolved.
Outdated

Mints cycles into a **cycles ledger account** based on an ICP payment recorded in the ICP Ledger.
Comment thread
bogwar marked this conversation as resolved.
Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
Mints cycles into a **cycles ledger account** based on an ICP payment recorded in the ICP Ledger.
Mints cycles into the caller's **cycles ledger account** 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.
Comment thread
bogwar marked this conversation as resolved.
Outdated

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.
Comment thread
bogwar marked this conversation as resolved.
Outdated
- The **memo field** must explicitly indicate the intent to mint cycles. This can be:
Comment thread
bogwar marked this conversation as resolved.
Outdated
- 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:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
```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
Comment thread
bogwar marked this conversation as resolved.
Outdated
}
```

- **Returns:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
```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:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
- The caller must be authorized to invoke this method.
- The subaccount in the payment must match `to_subaccount` exactly.
Comment thread
bogwar marked this conversation as resolved.
Outdated
- This method is idempotent by `block_index`.
Comment thread
bogwar marked this conversation as resolved.
Outdated
- Both legacy and ICRC-1 memo formats are accepted if they correctly encode `"MINT"`.


### `create_canister`
Comment thread
bogwar marked this conversation as resolved.
Outdated

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.
Comment thread
bogwar marked this conversation as resolved.
Outdated
- If `settings` is provided, it will override default configuration for the new canister.
Comment thread
bogwar marked this conversation as resolved.
Outdated
- If no `settings` are given, the calling principal becomes the sole controller.
Comment thread
bogwar marked this conversation as resolved.
Outdated

- **Parameters:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
```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:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
```candid
variant { Ok: principal; Err: CreateCanisterError }
```

- **Notes:**
Comment thread
bogwar marked this conversation as resolved.
Outdated
- 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
Comment thread
bogwar marked this conversation as resolved.
Outdated

### Canister Settings
Comment thread
bogwar marked this conversation as resolved.
Outdated

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.
Comment thread
bogwar marked this conversation as resolved.
Outdated

```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
Comment thread
bogwar marked this conversation as resolved.
Outdated

---
Comment thread
bogwar marked this conversation as resolved.
Outdated

### Encoding a Principal into a Subaccount
Comment thread
bogwar marked this conversation as resolved.
Outdated

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
Comment thread
bogwar marked this conversation as resolved.
Outdated

This convention ensures the CMC can determine which principal or canister an incoming payment was intended for.