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,
7};
8use crate::ic::DecodeCandid;
9use crate::ledger::icp::transfer_payment;
10use crate::mgmt::ic::install_code;
11use crate::mgmt::settings::{create_canister_cycles, create_canister_settings};
12use crate::mgmt::types::cmc::{
13 CreateCanister, CreateCanisterResult, Cycles, NotifyError, SubnetId, SubnetSelection,
14 TopUpCanisterArgs,
15};
16use crate::mgmt::types::ic::{CreateCanisterInitSettingsArg, WasmArg};
17use candid::Principal;
18use ic_cdk::call::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> {
43 let send_amount = Tokens::from_e8s(amount.e8s() - (2 * IC_TRANSACTION_FEE_ICP.e8s()));
45
46 let cmc = Principal::from_text(CMC).unwrap();
47
48 let to_sub_account: Subaccount = convert_principal_to_sub_account(canister_id.as_slice());
49
50 let block_index = transfer_payment(
51 &cmc,
52 &to_sub_account,
53 MEMO_CANISTER_TOP_UP,
54 send_amount,
55 IC_TRANSACTION_FEE_ICP,
56 )
57 .await
58 .map_err(|e| format!("{JUNO_ERROR_CMC_CALL_LEDGER_FAILED} ({e:?})"))?
59 .map_err(|e| format!("{JUNO_ERROR_CMC_LEDGER_TRANSFER_FAILED} ({e:?})"))?;
60
61 let args = TopUpCanisterArgs {
62 block_index,
63 canister_id: *canister_id,
64 };
65
66 let _ = Call::unbounded_wait(cmc, "notify_top_up")
69 .with_arg(args)
70 .await
71 .decode_candid::<Result<Cycles, NotifyError>>()?;
72
73 Ok(())
74}
75
76fn convert_principal_to_sub_account(principal_id: &[u8]) -> Subaccount {
77 let mut bytes = [0u8; 32];
78 bytes[0] = principal_id.len().try_into().unwrap();
79 bytes[1..1 + principal_id.len()].copy_from_slice(principal_id);
80 Subaccount(bytes)
81}
82
83pub async fn create_and_install_canister_with_cmc(
95 create_settings_arg: &CreateCanisterInitSettingsArg,
96 wasm_arg: &WasmArg,
97 cycles: u128,
98 subnet_id: &SubnetId,
99) -> Result<Principal, String> {
100 let canister_id = create_canister_with_cmc(create_settings_arg, cycles, subnet_id).await?;
101
102 install_code(canister_id, wasm_arg, CanisterInstallMode::Install)
103 .await
104 .map_err(|_| JUNO_ERROR_CMC_INSTALL_CODE_FAILED.to_string())?;
105
106 Ok(canister_id)
107}
108
109pub async fn create_canister_with_cmc(
124 create_settings_arg: &CreateCanisterInitSettingsArg,
125 cycles: u128,
126 subnet_id: &SubnetId,
127) -> Result<Principal, String> {
128 let cmc = Principal::from_text(CMC).unwrap();
129
130 let create_canister_arg = CreateCanister {
131 subnet_type: None,
132 subnet_selection: Some(SubnetSelection::Subnet { subnet: *subnet_id }),
133 settings: create_canister_settings(create_settings_arg),
134 };
135
136 let result = Call::unbounded_wait(cmc, "create_canister")
137 .with_arg(create_canister_arg)
138 .with_cycles(create_canister_cycles(cycles))
139 .await
140 .decode_candid::<CreateCanisterResult>();
141
142 result
143 .map_err(|error| {
144 format!(
145 "{} ({})",
146 JUNO_ERROR_CMC_CALL_CREATE_CANISTER_FAILED, &error
147 )
148 })?
149 .map_err(|err| format!("{JUNO_ERROR_CMC_CREATE_CANISTER_FAILED} ({err})"))
150}