junobuild_shared/mgmt/
ic.rs

1use crate::errors::{
2    JUNO_ERROR_CANISTER_CREATE_FAILED, JUNO_ERROR_CANISTER_INSTALL_CODE_FAILED,
3    JUNO_ERROR_CYCLES_DEPOSIT_BALANCE_LOW, JUNO_ERROR_CYCLES_DEPOSIT_FAILED,
4    JUNO_ERROR_SEGMENT_DELETE_FAILED, JUNO_ERROR_SEGMENT_STOP_FAILED,
5};
6use crate::mgmt::settings::{create_canister_cycles, create_canister_settings};
7use crate::mgmt::types::ic::{CreateCanisterInitSettingsArg, WasmArg};
8use crate::types::interface::DepositCyclesArgs;
9use candid::Principal;
10use ic_cdk::api::canister_cycle_balance;
11use ic_cdk::call::CallResult;
12use ic_cdk::management_canister::{
13    create_canister_with_extra_cycles, delete_canister, deposit_cycles as ic_deposit_cycles,
14    install_code as ic_install_code, stop_canister, update_settings, CanisterId,
15    CanisterInstallMode, CanisterSettings, CreateCanisterArgs, DeleteCanisterArgs,
16    DepositCyclesArgs as MgmtDepositCyclesArgs, InstallCodeArgs, StopCanisterArgs,
17    UpdateSettingsArgs,
18};
19
20/// Asynchronously creates a new canister and installs provided Wasm code with additional cycles.
21///
22/// # Arguments
23/// - `create_settings_arg`: The custom settings to apply to spinup the canister.
24/// - `wasm_arg`: Wasm binary and arguments to install in the new canister (`WasmArg` struct).
25/// - `cycles`: Additional cycles to deposit during canister creation on top of `CREATE_CANISTER_CYCLES`.
26///
27/// # Returns
28/// - `Ok(Principal)`: On success, returns the `Principal` ID of the newly created canister.
29/// - `Err(String)`: On failure, returns an error message.
30pub async fn create_canister_install_code(
31    create_settings_arg: &CreateCanisterInitSettingsArg,
32    wasm_arg: &WasmArg,
33    cycles: u128,
34) -> Result<Principal, String> {
35    let record = create_canister_with_extra_cycles(
36        &CreateCanisterArgs {
37            settings: create_canister_settings(create_settings_arg),
38        },
39        create_canister_cycles(cycles),
40    )
41    .await;
42
43    match record {
44        Err(err) => Err(format!(
45            "{} ({})",
46            JUNO_ERROR_CANISTER_CREATE_FAILED,
47            &err.to_string()
48        )),
49        Ok(record) => {
50            let canister_id = record.canister_id;
51
52            let install = install_code(canister_id, wasm_arg, CanisterInstallMode::Install).await;
53
54            match install {
55                Err(_) => Err(JUNO_ERROR_CANISTER_INSTALL_CODE_FAILED.to_string()),
56                Ok(_) => Ok(canister_id),
57            }
58        }
59    }
60}
61
62/// Asynchronously installs code on a specified canister.
63///
64/// # Arguments
65/// - `canister_id`: `Principal` ID of the target canister.
66/// - `wasm_arg`: Contains the Wasm module and installation arguments.
67/// - `mode`: Installation mode defined by `CanisterInstallMode`.
68///
69/// # Returns
70/// - A `CallResult<()>` indicating success or failure.
71pub async fn install_code(
72    canister_id: Principal,
73    WasmArg { wasm, install_arg }: &WasmArg,
74    mode: CanisterInstallMode,
75) -> CallResult<()> {
76    let arg = InstallCodeArgs {
77        mode,
78        canister_id,
79        wasm_module: wasm.clone(),
80        arg: install_arg.clone(),
81    };
82
83    ic_install_code(&arg).await
84}
85
86/// Asynchronously updates the controller list of a specified canister.
87///
88/// # Arguments
89/// - `canister_id`: `Principal` ID of the target canister.
90/// - `controllers`: New list of `Principal` IDs to set as controllers.
91///
92/// # Returns
93/// - A `CallResult<()>` indicating success or failure.
94pub async fn update_canister_controllers(
95    canister_id: Principal,
96    controllers: Vec<Principal>,
97) -> CallResult<()> {
98    // Not including a setting in the settings record means not changing that field.
99    // In other words, setting wasm_memory_limit to None here means keeping the actual value of wasm_memory_limit.
100    let arg = UpdateSettingsArgs {
101        canister_id,
102        settings: CanisterSettings {
103            controllers: Some(controllers),
104            compute_allocation: None,
105            memory_allocation: None,
106            freezing_threshold: None,
107            reserved_cycles_limit: None,
108            log_visibility: None,
109            wasm_memory_limit: None,
110            wasm_memory_threshold: None,
111        },
112    };
113
114    update_settings(&arg).await
115}
116
117/// Deposits cycles into a specified canister from the calling canister's balance.
118///
119/// # Arguments
120/// - `args`: `DepositCyclesArgs` struct containing the destination canister ID and cycle amount.
121///
122/// # Returns
123/// - `Ok(())`: On successful deposit.
124/// - `Err(String)`: If the balance is insufficient or on failure to deposit.
125pub async fn deposit_cycles(
126    DepositCyclesArgs {
127        destination_id,
128        cycles,
129    }: DepositCyclesArgs,
130) -> Result<(), String> {
131    let balance = canister_cycle_balance();
132
133    if balance < cycles {
134        return Err(format!(
135            "{JUNO_ERROR_CYCLES_DEPOSIT_BALANCE_LOW} (balance {balance}, {cycles} to deposit)"
136        ));
137    }
138
139    let result = ic_deposit_cycles(
140        &MgmtDepositCyclesArgs {
141            canister_id: destination_id,
142        },
143        cycles,
144    )
145    .await;
146
147    match result {
148        Err(err) => Err(format!(
149            "{} ({})",
150            JUNO_ERROR_CYCLES_DEPOSIT_FAILED,
151            &err.to_string()
152        )),
153        Ok(_) => Ok(()),
154    }
155}
156
157/// Stops the execution of a specified segment (canister).
158///
159/// # Arguments
160/// - `canister_id`: The `CanisterId` of the canister to stop.
161///
162/// # Returns
163/// - `Ok(())`: If the canister is successfully stopped.
164/// - `Err(String)`: On failure, returns an error message.
165pub async fn stop_segment(canister_id: CanisterId) -> Result<(), String> {
166    let result = stop_canister(&StopCanisterArgs { canister_id }).await;
167
168    match result {
169        Err(err) => Err(format!(
170            "{} ({})",
171            JUNO_ERROR_SEGMENT_STOP_FAILED,
172            &err.to_string()
173        )),
174        Ok(_) => Ok(()),
175    }
176}
177
178/// Deletes a specified segment (canister).
179///
180/// # Arguments
181/// - `canister_id`: The `CanisterId` of the canister to delete.
182///
183/// # Returns
184/// - `Ok(())`: If the canister is successfully deleted.
185/// - `Err(String)`: On failure, returns an error message.
186pub async fn delete_segment(canister_id: CanisterId) -> Result<(), String> {
187    let result = delete_canister(&DeleteCanisterArgs { canister_id }).await;
188
189    match result {
190        Err(err) => Err(format!(
191            "{} ({})",
192            JUNO_ERROR_SEGMENT_DELETE_FAILED,
193            &err.to_string()
194        )),
195        Ok(_) => Ok(()),
196    }
197}