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            environment_variables: None,
112        },
113    };
114
115    update_settings(&arg).await
116}
117
118/// Deposits cycles into a specified canister from the calling canister's balance.
119///
120/// # Arguments
121/// - `args`: `DepositCyclesArgs` struct containing the destination canister ID and cycle amount.
122///
123/// # Returns
124/// - `Ok(())`: On successful deposit.
125/// - `Err(String)`: If the balance is insufficient or on failure to deposit.
126pub async fn deposit_cycles(
127    DepositCyclesArgs {
128        destination_id,
129        cycles,
130    }: DepositCyclesArgs,
131) -> Result<(), String> {
132    let balance = canister_cycle_balance();
133
134    if balance < cycles {
135        return Err(format!(
136            "{JUNO_ERROR_CYCLES_DEPOSIT_BALANCE_LOW} (balance {balance}, {cycles} to deposit)"
137        ));
138    }
139
140    let result = ic_deposit_cycles(
141        &MgmtDepositCyclesArgs {
142            canister_id: destination_id,
143        },
144        cycles,
145    )
146    .await;
147
148    match result {
149        Err(err) => Err(format!(
150            "{} ({})",
151            JUNO_ERROR_CYCLES_DEPOSIT_FAILED,
152            &err.to_string()
153        )),
154        Ok(_) => Ok(()),
155    }
156}
157
158/// Stops the execution of a specified segment (canister).
159///
160/// # Arguments
161/// - `canister_id`: The `CanisterId` of the canister to stop.
162///
163/// # Returns
164/// - `Ok(())`: If the canister is successfully stopped.
165/// - `Err(String)`: On failure, returns an error message.
166pub async fn stop_segment(canister_id: CanisterId) -> Result<(), String> {
167    let result = stop_canister(&StopCanisterArgs { canister_id }).await;
168
169    match result {
170        Err(err) => Err(format!(
171            "{} ({})",
172            JUNO_ERROR_SEGMENT_STOP_FAILED,
173            &err.to_string()
174        )),
175        Ok(_) => Ok(()),
176    }
177}
178
179/// Deletes a specified segment (canister).
180///
181/// # Arguments
182/// - `canister_id`: The `CanisterId` of the canister to delete.
183///
184/// # Returns
185/// - `Ok(())`: If the canister is successfully deleted.
186/// - `Err(String)`: On failure, returns an error message.
187pub async fn delete_segment(canister_id: CanisterId) -> Result<(), String> {
188    let result = delete_canister(&DeleteCanisterArgs { canister_id }).await;
189
190    match result {
191        Err(err) => Err(format!(
192            "{} ({})",
193            JUNO_ERROR_SEGMENT_DELETE_FAILED,
194            &err.to_string()
195        )),
196        Ok(_) => Ok(()),
197    }
198}