junobuild_shared/mgmt/
cmc.rs1use crate::constants_shared::{IC_TRANSACTION_FEE_ICP, MEMO_CANISTER_TOP_UP};
2use crate::env::CMC;
3use crate::ledger::icp::transfer_payment;
4use crate::mgmt::ic::install_code;
5use crate::mgmt::settings::{create_canister_cycles, create_canister_settings};
6use crate::mgmt::types::cmc::{
7 CreateCanister, CreateCanisterResult, Cycles, NotifyError, SubnetId, SubnetSelection,
8 TopUpCanisterArgs,
9};
10use crate::mgmt::types::ic::WasmArg;
11use candid::Principal;
12use ic_cdk::api::call::{call_with_payment128, CallResult};
13use ic_cdk::api::management_canister::main::{CanisterId, CanisterInstallMode};
14use ic_cdk::call;
15use ic_ledger_types::{Subaccount, Tokens};
16
17pub async fn top_up_canister(canister_id: &CanisterId, amount: &Tokens) -> Result<(), String> {
18 let send_amount = Tokens::from_e8s(amount.e8s() - (2 * IC_TRANSACTION_FEE_ICP.e8s()));
20
21 let cmc = Principal::from_text(CMC).unwrap();
22
23 let to_sub_account: Subaccount = convert_principal_to_sub_account(canister_id.as_slice());
24
25 let block_index = transfer_payment(
26 &cmc,
27 &to_sub_account,
28 MEMO_CANISTER_TOP_UP,
29 send_amount,
30 IC_TRANSACTION_FEE_ICP,
31 )
32 .await
33 .map_err(|e| format!("failed to call ledger: {:?}", e))?
34 .map_err(|e| format!("ledger transfer error {:?}", e))?;
35
36 let args = TopUpCanisterArgs {
37 block_index,
38 canister_id: *canister_id,
39 };
40
41 let result: CallResult<(Result<Cycles, NotifyError>,)> =
42 call(cmc, "notify_top_up", (args,)).await;
43
44 match result {
45 Err((_, message)) => {
46 Err(["Top-up failed.", &message].join(" - "))
49 }
50 Ok(_) => Ok(()),
51 }
52}
53
54fn convert_principal_to_sub_account(principal_id: &[u8]) -> Subaccount {
55 let mut bytes = [0u8; 32];
56 bytes[0] = principal_id.len().try_into().unwrap();
57 bytes[1..1 + principal_id.len()].copy_from_slice(principal_id);
58 Subaccount(bytes)
59}
60
61pub async fn cmc_create_canister_install_code(
73 controllers: Vec<Principal>,
74 wasm_arg: &WasmArg,
75 cycles: u128,
76 subnet_id: &SubnetId,
77) -> Result<Principal, String> {
78 let cmc = Principal::from_text(CMC).unwrap();
79
80 let create_canister_arg = CreateCanister {
81 subnet_type: None,
82 subnet_selection: Some(SubnetSelection::Subnet { subnet: *subnet_id }),
83 settings: create_canister_settings(controllers),
84 };
85
86 let result: CallResult<(CreateCanisterResult,)> = call_with_payment128(
87 cmc,
88 "create_canister",
89 (create_canister_arg,),
90 create_canister_cycles(cycles),
91 )
92 .await;
93
94 match result {
95 Err((_, message)) => Err(["Failed to call CMC to create canister.", &message].join(" - ")),
96 Ok((result,)) => match result {
97 Err(err) => Err(format!("Failed to create canister with CMC - {}", err)),
98 Ok(canister_id) => {
99 let install =
100 install_code(canister_id, wasm_arg, CanisterInstallMode::Install).await;
101
102 match install {
103 Err(_) => {
104 Err("Failed to install code in canister created with CMC.".to_string())
105 }
106 Ok(_) => Ok(canister_id),
107 }
108 }
109 },
110 }
111}