Loading...
Loading...
Manage cycles and canister lifecycle. Covers cycle balance checks, top-ups, freezing thresholds, canister creation, and ICP-to-cycles conversion via the CMC. Use when working with cycles, canister funding, freezing threshold, frozen canister, out of cycles, top-up, canister creation, or cycle balance. Do NOT use for wallet-to-dApp integration or ICRC signer flows — use wallet-integration instead.
npx skill4agent add dfinity/icskills cycles-managementum5iw-rqaaa-aaaaq-qaaba-caiicp cycles balanceicp cycles minticp canister top-upmopscore = "2.0.0"ic-cdk >= 0.19| Service | Canister ID | Purpose |
|---|---|---|
| Cycles Minting Canister (CMC) | | Converts ICP to cycles, creates canisters |
| Cycles Ledger | | Tracks cycle balances for all principals |
| Management Canister | | Canister lifecycle (create, install, stop, delete, status) |
aaaaa-aaicp canister update-settings --add-controller PRINCIPALCyclesimport ExperimentalCycles "mo:base/ExperimentalCycles"import Cycles "mo:core/Cycles"import Cycles "mo:core/Cycles";
import Principal "mo:core/Principal";
import Runtime "mo:core/Runtime";
persistent actor {
// Check this canister's cycle balance
public query func getBalance() : async Nat {
Cycles.balance()
};
// Accept cycles sent with a call (for "tip jar" or payment patterns)
public func deposit() : async Nat {
let available = Cycles.available();
if (available == 0) {
Runtime.trap("No cycles sent with this call")
};
let accepted = Cycles.accept<system>(available);
accepted
};
// Send cycles to another canister via inter-canister call
public func topUpCanister(target : Principal) : async () {
let targetActor = actor (Principal.toText(target)) : actor {
deposit_cycles : shared () -> async ();
};
// Attach 1T cycles to the call
await (with cycles = 1_000_000_000_000) targetActor.deposit_cycles();
};
}import Principal "mo:core/Principal";
persistent actor Self {
type CanisterId = { canister_id : Principal };
type CreateCanisterSettings = {
controllers : ?[Principal];
compute_allocation : ?Nat;
memory_allocation : ?Nat;
freezing_threshold : ?Nat;
};
// Management canister interface
let ic = actor ("aaaaa-aa") : actor {
create_canister : shared { settings : ?CreateCanisterSettings } ->
async CanisterId;
canister_status : shared { canister_id : Principal } ->
async {
status : { #running; #stopping; #stopped };
memory_size : Nat;
cycles : Nat;
settings : CreateCanisterSettings;
module_hash : ?Blob;
};
deposit_cycles : shared { canister_id : Principal } -> async ();
stop_canister : shared { canister_id : Principal } -> async ();
delete_canister : shared { canister_id : Principal } -> async ();
};
// Create a new canister with 1T cycles
public func createNewCanister() : async Principal {
let result = await (with cycles = 1_000_000_000_000) ic.create_canister({
settings = ?{
controllers = ?[Principal.fromActor(Self)];
compute_allocation = null;
memory_allocation = null;
freezing_threshold = ?2_592_000; // 30 days in seconds
};
});
result.canister_id
};
// Check a canister's status and cycle balance
public func checkStatus(canisterId : Principal) : async Nat {
let status = await ic.canister_status({ canister_id = canisterId });
status.cycles
};
// Top up another canister
public func topUp(canisterId : Principal, amount : Nat) : async () {
await (with cycles = amount) ic.deposit_cycles({ canister_id = canisterId });
};
}use ic_cdk::{query, update};
use candid::Nat;
#[query]
fn get_balance() -> Nat {
Nat::from(ic_cdk::api::canister_cycle_balance())
}
#[update]
fn deposit() -> Nat {
let available = ic_cdk::api::msg_cycles_available();
if available == 0 {
ic_cdk::trap("No cycles sent with this call");
}
let accepted = ic_cdk::api::msg_cycles_accept(available);
Nat::from(accepted)
}use candid::{Nat, Principal};
use ic_cdk::update;
use ic_cdk::management_canister::{
create_canister_with_extra_cycles, canister_status, deposit_cycles, stop_canister, delete_canister,
CreateCanisterArgs, CanisterStatusArgs, DepositCyclesArgs, StopCanisterArgs, DeleteCanisterArgs,
CanisterSettings, CanisterStatusResult,
};
#[update]
async fn create_new_canister() -> Principal {
let caller = ic_cdk::api::canister_self(); // capture canister's own principal
let user = ic_cdk::api::msg_caller(); // capture caller before await
let settings = CanisterSettings {
controllers: Some(vec![caller, user]),
compute_allocation: None,
memory_allocation: None,
freezing_threshold: Some(Nat::from(2_592_000u64)), // 30 days
reserved_cycles_limit: None,
log_visibility: None,
wasm_memory_limit: None,
wasm_memory_threshold: None,
environment_variables: None,
};
let arg = CreateCanisterArgs {
settings: Some(settings),
};
// Send 1T cycles with the create call
let result = create_canister_with_extra_cycles(&arg, 1_000_000_000_000u128)
.await
.expect("Failed to create canister");
result.canister_id
}
#[update]
async fn check_status(canister_id: Principal) -> CanisterStatusResult {
canister_status(&CanisterStatusArgs { canister_id })
.await
.expect("Failed to get canister status")
}
#[update]
async fn top_up(canister_id: Principal, amount: u128) {
deposit_cycles(&DepositCyclesArgs { canister_id }, amount)
.await
.expect("Failed to deposit cycles");
}
#[update]
async fn stop_and_delete(canister_id: Principal) {
stop_canister(&StopCanisterArgs { canister_id })
.await
.expect("Failed to stop canister");
delete_canister(&DeleteCanisterArgs { canister_id })
.await
.expect("Failed to delete canister");
}# Check your canister's cycle balance
icp canister status backend
# Look for "Balance:" line in output
# Check balance on mainnet
icp canister status backend -e ic
# Check any canister by ID
icp canister status ryjl3-tyaaa-aaaaa-aaaba-cai -e ic# Top up with cycles from the cycles ledger (local)
icp canister top-up backend --amount 1000000000000
# Adds 1T cycles to the backend canister
# Top up on mainnet
icp canister top-up backend --amount 1000000000000 -e ic
# Convert ICP to cycles and top up in one step (mainnet)
icp cycles mint --icp 1.0 -e ic
icp canister top-up backend --amount 1000000000000 -e ic# Create an empty canister (local)
icp canister create my_canister
# Create on mainnet with specific cycles
icp canister create my_canister -e ic --cycles 2000000000000
# Add a backup controller
icp canister update-settings my_canister --add-controller BACKUP_PRINCIPAL_HERE# Set freezing threshold to 90 days (in seconds: 90 * 24 * 60 * 60 = 7776000)
icp canister update-settings backend --freezing-threshold 7776000
# Mainnet
icp canister update-settings backend --freezing-threshold 7776000 -e ic# Deploy and check status
icp network start -d
icp deploy backend
icp canister status backend
# Expected output includes:
# Status: Running
# Balance: 3_100_000_000_000 Cycles (local default, varies)
# Freezing threshold: 2_592_000
# Check balance programmatically (if you added getBalance)
icp canister call backend getBalance '()'
# Expected: a large nat value, e.g. (3_100_000_000_000 : nat)
# On mainnet: verify cycles balance is not zero
icp canister status backend -e ic
# If Balance shows 0, the canister will freeze. Top up immediately.