I recently achieved this by calling the CMC create_canister
use candid::{CandidType, Nat, Principal};
use ic_cdk::{api::management_canister::main::CanisterSettings, update};
use ic_ledger_types::MAINNET_CYCLES_MINTING_CANISTER_ID;
use serde::Deserialize;
#[derive(CandidType, Deserialize)]
struct CreateCanisterArg {
settings: Option<CanisterSettings>,
subnet_type: Option<String>,
subnet_selection: Option<SubnetSelection>,
}
#[derive(CandidType, Deserialize)]
enum SubnetSelection {
Subnet { subnet: Principal },
Filter { filter: SubnetFilter },
}
#[derive(CandidType, Deserialize)]
struct SubnetFilter {
subnet_type: Option<String>,
}
#[derive(CandidType, Deserialize)]
enum CreateCanisterResult {
Ok(Principal),
Err(CreateCanisterError),
}
#[derive(CandidType, Deserialize)]
enum CreateCanisterError {
Refunded {
refund_amount: Nat,
create_error: String,
},
RefundFailed {
create_error: String,
refund_error: String,
},
}
#[update]
async fn create_canister(subnet: String) -> Principal {
let settings = CanisterSettings {
controllers: Some(vec![
// dev wallet
Principal::from_text("uc5c3-viaaa-aaaal-qaaja-cai").unwrap(),
// dev id
Principal::from_text("hopg6-qb5bk-fywb2-44xhe-2ovp4-stuku-oitob-vkpim-zrip3-7f375-rae")
.unwrap(),
]),
compute_allocation: None,
memory_allocation: None,
freezing_threshold: None,
reserved_cycles_limit: None,
};
let create_canister_result: CreateCanisterResult =
ic_cdk::api::call::call_with_payment::<(CreateCanisterArg,), (CreateCanisterResult,)>(
MAINNET_CYCLES_MINTING_CANISTER_ID,
"create_canister",
(CreateCanisterArg {
settings: Some(settings),
subnet_type: None,
subnet_selection: Some(SubnetSelection::Subnet {
subnet: Principal::from_text(subnet).unwrap(),
}),
},),
// 5T cycles
5_000_000_000_000,
)
.await
.expect("Error calling create_canister")
.0;
match create_canister_result {
CreateCanisterResult::Ok(canister_id) => return canister_id,
CreateCanisterResult::Err(create_canister_error) => match create_canister_error {
CreateCanisterError::Refunded {
refund_amount,
create_error,
} => {
ic_cdk::trap(&format!(
"Refunded {} cycles due to error: {}",
refund_amount, create_error
));
}
CreateCanisterError::RefundFailed {
create_error,
refund_error,
} => {
ic_cdk::trap(&format!(
"Error creating canister: {}. Error refunding cycles: {}",
create_error, refund_error
));
}
},
}
}