use crate::constants::shared::{IC_TRANSACTION_FEE_ICP, MEMO_CANISTER_TOP_UP};
use crate::env::CMC;
use crate::errors::{
JUNO_ERROR_CMC_CALL_CREATE_CANISTER_FAILED, JUNO_ERROR_CMC_CALL_LEDGER_FAILED,
JUNO_ERROR_CMC_CREATE_CANISTER_FAILED, JUNO_ERROR_CMC_INSTALL_CODE_FAILED,
JUNO_ERROR_CMC_LEDGER_TRANSFER_FAILED,
};
use crate::ic::DecodeCandid;
use crate::ledger::icp::transfer_payment;
use crate::mgmt::ic::install_code;
use crate::mgmt::settings::{create_canister_cycles, create_canister_settings};
use crate::mgmt::types::cmc::{
CreateCanister, CreateCanisterResult, Cycles, NotifyError, SubnetId, SubnetSelection,
TopUpCanisterArgs,
};
use crate::mgmt::types::ic::{CreateCanisterInitSettingsArg, WasmArg};
use candid::Principal;
use ic_cdk::call::Call;
use ic_cdk::management_canister::{CanisterId, CanisterInstallMode};
use ic_ledger_types::{Subaccount, Tokens};
pub async fn top_up_canister(canister_id: &CanisterId, amount: &Tokens) -> Result<(), String> {
let send_amount = Tokens::from_e8s(amount.e8s() - (2 * IC_TRANSACTION_FEE_ICP.e8s()));
let cmc = Principal::from_text(CMC).unwrap();
let to_sub_account: Subaccount = convert_principal_to_sub_account(canister_id.as_slice());
let block_index = transfer_payment(
&cmc,
&to_sub_account,
MEMO_CANISTER_TOP_UP,
send_amount,
IC_TRANSACTION_FEE_ICP,
)
.await
.map_err(|e| format!("{JUNO_ERROR_CMC_CALL_LEDGER_FAILED} ({e:?})"))?
.map_err(|e| format!("{JUNO_ERROR_CMC_LEDGER_TRANSFER_FAILED} ({e:?})"))?;
let args = TopUpCanisterArgs {
block_index,
canister_id: *canister_id,
};
let _ = Call::unbounded_wait(cmc, "notify_top_up")
.with_arg(args)
.await
.decode_candid::<Result<Cycles, NotifyError>>()?;
Ok(())
}
fn convert_principal_to_sub_account(principal_id: &[u8]) -> Subaccount {
let mut bytes = [0u8; 32];
bytes[0] = principal_id.len().try_into().unwrap();
bytes[1..1 + principal_id.len()].copy_from_slice(principal_id);
Subaccount(bytes)
}
pub async fn create_and_install_canister_with_cmc(
create_settings_arg: &CreateCanisterInitSettingsArg,
wasm_arg: &WasmArg,
cycles: u128,
subnet_id: &SubnetId,
) -> Result<Principal, String> {
let canister_id = create_canister_with_cmc(create_settings_arg, cycles, subnet_id).await?;
install_code(canister_id, wasm_arg, CanisterInstallMode::Install)
.await
.map_err(|_| JUNO_ERROR_CMC_INSTALL_CODE_FAILED.to_string())?;
Ok(canister_id)
}
pub async fn create_canister_with_cmc(
create_settings_arg: &CreateCanisterInitSettingsArg,
cycles: u128,
subnet_id: &SubnetId,
) -> Result<Principal, String> {
let cmc = Principal::from_text(CMC).unwrap();
let create_canister_arg = CreateCanister {
subnet_type: None,
subnet_selection: Some(SubnetSelection::Subnet { subnet: *subnet_id }),
settings: create_canister_settings(create_settings_arg),
};
let result = Call::unbounded_wait(cmc, "create_canister")
.with_arg(create_canister_arg)
.with_cycles(create_canister_cycles(cycles))
.await
.decode_candid::<CreateCanisterResult>();
result
.map_err(|error| {
format!(
"{} ({})",
JUNO_ERROR_CMC_CALL_CREATE_CANISTER_FAILED, &error
)
})?
.map_err(|err| format!("{JUNO_ERROR_CMC_CREATE_CANISTER_FAILED} ({err})"))
}