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