canic_core/interface/ic/
mod.rs

1//! IC Interfaces
2//! Thin wrappers around the management canister and network-specific helpers.
3
4pub mod canister;
5pub mod cycles;
6pub mod helper;
7pub mod icp;
8pub mod network;
9pub mod sns;
10
11pub use helper::*;
12
13use crate::{
14    Error,
15    cdk::{
16        call::Call,
17        mgmt::{
18            self, CanisterInstallMode, CanisterStatusArgs, CanisterStatusResult,
19            DeleteCanisterArgs, DepositCyclesArgs, InstallCodeArgs, UninstallCodeArgs, WasmModule,
20        },
21    },
22    env::nns::NNS_REGISTRY_CANISTER,
23    interface::prelude::*,
24    log,
25    log::Topic,
26    spec::nns::{GetSubnetForCanisterRequest, GetSubnetForCanisterResponse},
27};
28use candid::{CandidType, Principal, decode_one, encode_args, utils::ArgumentEncoder};
29
30//
31// ────────────────────────────── CANISTER STATUS ──────────────────────────────
32//
33
34/// Query the management canister for a canister's status.
35pub async fn canister_status(canister_pid: Principal) -> Result<CanisterStatusResult, Error> {
36    let args = CanisterStatusArgs {
37        canister_id: canister_pid,
38    };
39
40    mgmt::canister_status(&args).await.map_err(Error::from)
41}
42
43//
44// ──────────────────────────────── CYCLES API ─────────────────────────────────
45//
46
47/// Returns the local canister's cycle balance (cheap).
48#[must_use]
49pub fn canister_cycle_balance() -> Cycles {
50    crate::cdk::api::canister_cycle_balance().into()
51}
52
53/// Deposits cycles into a canister.
54pub async fn deposit_cycles(canister_pid: Principal, cycles: u128) -> Result<(), Error> {
55    let args = DepositCyclesArgs {
56        canister_id: canister_pid,
57    };
58    mgmt::deposit_cycles(&args, cycles)
59        .await
60        .map_err(Error::from)
61}
62
63/// Gets a canister's cycle balance (expensive: calls mgmt canister).
64pub async fn get_cycles(canister_pid: Principal) -> Result<Cycles, Error> {
65    let status = canister_status(canister_pid).await?;
66
67    Ok(status.cycles.into())
68}
69
70//
71// ────────────────────────────── TOPOLOGY LOOKUPS ─────────────────────────────
72//
73
74/// Queries the NNS registry for the subnet that this canister belongs to.
75pub async fn get_current_subnet_pid() -> Result<Option<Principal>, Error> {
76    let request = GetSubnetForCanisterRequest::new(canister_self());
77
78    let subnet_id_opt = Call::unbounded_wait(*NNS_REGISTRY_CANISTER, "get_subnet_for_canister")
79        .with_arg(request)
80        .await?
81        .candid::<GetSubnetForCanisterResponse>()?
82        .map_err(Error::CallFailed)?
83        .subnet_id;
84
85    if let Some(subnet_id) = subnet_id_opt {
86        log!(Topic::Topology, Info, "get_current_subnet_pid: {subnet_id}");
87    } else {
88        log!(Topic::Topology, Warn, "get_current_subnet_pid: not found");
89    }
90
91    Ok(subnet_id_opt)
92}
93
94//
95// ────────────────────────────── INSTALL / UNINSTALL ──────────────────────────
96//
97
98/// Installs or upgrades a canister with the given wasm + args.
99pub async fn install_code<T: ArgumentEncoder>(
100    mode: CanisterInstallMode,
101    canister_pid: Principal,
102    wasm: &[u8],
103    args: T,
104) -> Result<(), Error> {
105    let arg = encode_args(args)?;
106    let install_args = InstallCodeArgs {
107        mode,
108        canister_id: canister_pid,
109        wasm_module: WasmModule::from(wasm),
110        arg,
111    };
112
113    mgmt::install_code(&install_args).await.map_err(Error::from)
114}
115
116/// Uninstalls code from a canister.
117pub async fn uninstall_code(canister_pid: Principal) -> Result<(), Error> {
118    let args = UninstallCodeArgs {
119        canister_id: canister_pid,
120    };
121
122    mgmt::uninstall_code(&args).await.map_err(Error::from)
123}
124
125/// Deletes a canister (code + controllers) via the management canister.
126pub async fn delete_canister(canister_pid: Principal) -> Result<(), Error> {
127    let args = DeleteCanisterArgs {
128        canister_id: canister_pid,
129    };
130
131    mgmt::delete_canister(&args).await.map_err(Error::from)
132}
133
134//
135// ──────────────────────────────── GENERIC HELPERS ────────────────────────────
136//
137
138/// Calls a method on a canister and candid-decodes the response into `T`.
139pub async fn call_and_decode<T: CandidType + for<'de> candid::Deserialize<'de>>(
140    pid: Principal,
141    method: &str,
142    arg: impl CandidType,
143) -> Result<T, Error> {
144    let response = Call::unbounded_wait(pid, method)
145        .with_arg(arg)
146        .await
147        .map_err(Error::from)?;
148
149    decode_one(&response).map_err(Error::from)
150}