dfx-core 0.3.0

dfx core library
Documentation
use crate::{
    error::{
        canister::{CanisterBuilderError, CanisterInstallError},
        cli::UserConsent,
    },
    identity::CallSender,
};
use candid::Principal;
use ic_agent::Agent;
use ic_utils::{
    Argument,
    interfaces::{
        ManagementCanister, WalletCanister,
        management_canister::builders::{CanisterInstallMode, InstallCodeArgs},
    },
};

pub async fn build_wallet_canister(
    id: Principal,
    agent: &Agent,
) -> Result<WalletCanister<'_>, CanisterBuilderError> {
    WalletCanister::from_canister(
        ic_utils::Canister::builder()
            .with_agent(agent)
            .with_canister_id(id)
            .build()
            .unwrap(),
    )
    .await
    .map_err(CanisterBuilderError::WalletCanisterCaller)
}

pub fn install_mode_to_present_tense(mode: &CanisterInstallMode) -> &'static str {
    match mode {
        CanisterInstallMode::Install => "Installing",
        CanisterInstallMode::Reinstall => "Reinstalling",
        CanisterInstallMode::Upgrade { .. } => "Upgrading",
    }
}

pub fn install_mode_to_past_tense(mode: &CanisterInstallMode) -> &'static str {
    match mode {
        CanisterInstallMode::Install => "Installed",
        CanisterInstallMode::Reinstall => "Reinstalled",
        CanisterInstallMode::Upgrade { .. } => "Upgraded",
    }
}

pub async fn install_canister_wasm(
    agent: &Agent,
    canister_id: Principal,
    canister_name: Option<&str>,
    args: &[u8],
    mode: CanisterInstallMode,
    call_sender: &CallSender,
    wasm_module: Vec<u8>,
    ask_for_consent: impl FnOnce(&str) -> Result<(), UserConsent> + Send,
) -> Result<(), CanisterInstallError> {
    let mgr = ManagementCanister::create(agent);
    if mode == CanisterInstallMode::Reinstall {
        let msg = if let Some(name) = canister_name {
            format!("You are about to reinstall the {name} canister")
        } else {
            format!("You are about to reinstall the canister {canister_id}")
        } + r#"
This will OVERWRITE all the data and code in the canister.

YOU WILL LOSE ALL DATA IN THE CANISTER.

"#;
        ask_for_consent(&msg).map_err(CanisterInstallError::UserConsent)?;
    }

    match call_sender {
        CallSender::SelectedId => {
            let install_builder = mgr
                .install(&canister_id, &wasm_module)
                .with_raw_arg(args.to_vec())
                .with_mode(mode);
            install_builder
                .await
                .map_err(CanisterInstallError::InstallWasmError)
        }
        CallSender::Impersonate(_) => {
            unreachable!("Impersonating sender when installing canisters is not supported.")
        }
        CallSender::Wallet(wallet_id) => {
            let wallet = build_wallet_canister(*wallet_id, agent).await?;
            let install_args = InstallCodeArgs {
                mode,
                canister_id,
                wasm_module,
                arg: args.to_vec(),
                sender_canister_version: None,
            };
            wallet
                .call(
                    *mgr.canister_id_(),
                    "install_code",
                    Argument::from_candid((install_args,)),
                    0,
                )
                .await
                .map_err(CanisterInstallError::InstallWasmError)
        }
    }
}