Dynamically create a canister from wasm module

how to dynamically create a canister from compiled wasm module with another canister using rust?

The cycles wallet is a good reference for that

4 Likes

This forum is blocked me, I just don’t know why

It blocks pasted code pretty often, probably trying to avoid code execution attacks

1 Like

Bro it is 1200 lines of code, people really need minimal examples for each case.
Can you point out to the place where I can find dynamically create a canister

Creating a canister is here, wasm gets installed here

1 Like

I agree with @AliSci that this warrants docs

1 Like

what is the difference between wallet_create_canister128 and wallet_create_canister?

and how should I run the command?
dfx canister call wallet create_canister "??";

No real difference. The one without 128 is a leftover from when cycles were u64. Internally it simply turns the amount of cycles into an u128 and then calls wallet_create_canister128.

That depends on the interface of the wallet canister. Assuming you’re using the default wallet canister, then the interface is here. You could use e.g. `‘(record { cycles = 1000000000000; settings = record { controller = ?principal “<your principal>”}})’

But it’s probably easier to just use dfx canister create

1 Like
use std::cell::RefCell;

use candid::Principal;
use ic_cdk::api;
use ic_cdk::api::call::CallResult;
use ic_cdk::api::management_canister::main::CanisterSettings;
use ic_cdk::export::candid::{
    candid_method, CandidType, check_prog, Deserialize, export_service, IDLProg, TypeEnv,
};

#[derive(Default)]
struct WalletWASMBytes(Option<serde_bytes::ByteBuf>);

/// The wallet (this canister's) name.
#[derive(Default)]
struct WalletName(pub(crate) Option<String>);


thread_local! {
    static WALLET_NAME: RefCell<WalletName> = Default::default();
    static WALLET_WASM_BYTES: RefCell<WalletWASMBytes> = Default::default();
}


#[candid_method(query)]
#[ic_cdk::query]
fn my_canister_id() -> String {
    format!("{}", ic_cdk::id())
}


#[derive(CandidType, Deserialize)]
struct CreateResult {
    canister_id: Principal,
}

#[derive(CandidType, Deserialize)]
struct CanisterInstall {
    mode: InstallMode,
    canister_id: Principal,
    #[serde(with = "serde_bytes")]
    wasm_module: Vec<u8>,
    arg: Vec<u8>,
}


#[derive(CandidType, Deserialize)]
enum InstallMode {
    #[serde(rename = "install")]
    Install,
    #[serde(rename = "reinstall")]
    Reinstall,
    #[serde(rename = "upgrade")]
    Upgrade,
}


#[candid_method(update)]
#[ic_cdk::update]
async fn create_canister() -> String {
    #[derive(CandidType)]
    struct In {
        settings: Option<CanisterSettings>,
    }
    let settings = CanisterSettings {
        controllers: Some(vec![ic_cdk::api::id()]),
        compute_allocation: None,
        memory_allocation: None,
        freezing_threshold: None,
    };
    let in_arg = In {
        settings: Some(settings),
    };

    let (create_result, ): (CreateResult, ) = match api::call::call_with_payment128(
        Principal::management_canister(),
        "create_canister",
        (in_arg, ),
        400_000_000_000,
    )
        .await
    {
        Ok(x) => x,
        Err((code, msg)) => {
            return format!(
                "An error happened during the call: {}: {}",
                code as u8, msg
            );
        }
    };

    // wasm install

    let wasm_module = WALLET_WASM_BYTES.with(|wallet_bytes| match &wallet_bytes.borrow().0 {
        Some(o) => o.clone().into_vec(),
        None => {
            ic_cdk::trap("No wasm module stored.");
        }
    });

    let install_config = CanisterInstall {
        mode: InstallMode::Install,
        canister_id: ic_cdk::api::id(),
        wasm_module: wasm_module.clone(),
        arg: b" ".to_vec(),
    };

    let (install_res, ): (CreateResult, ) = match api::call::call(
        Principal::management_canister(),
        "install_code",
        (install_config, ),
    )
        .await
    {
        Ok(x) => x,
        Err((code, msg)) => {
            return format!(
                "An error happened during the call: {}: {}",
                code as u8, msg
            );
        }
    };
    format!("{}", create_result.canister_id)
}


I build this now, but I don’t know how this wasm thing should work when I call the create_canister I got

Call was rejected:
Request ID: ccacd6fd84d19be86952a40d1d810b5798f3709934db2ecf244cb222ca849695
Reject code: 5
Reject text: Canister bw4dl-smaaa-aaaaa-qaacq-cai trapped explicitly: No wasm module stored.
1 Like

You have to have a wasm module that you can install. You can either pass it in the function argument or you can upload it ahead of time (like your example is set up for), but you have to have something available to install

1 Like

Now I am getting this error from the candid UI.

("An error happened during the call: 3: IC0302: Canister br5f7-7uaaa-aaaaa-qaaca-cai has no update method 'install_code'")

the should I make and update or a query named install_code?

It’s on you to decide what you want. If you want an install_code function in your canister then go ahead and make it. It probably will have to be an update call since you want to modify state. Queries’ changes get discarded after execution

Or do you want to call the install_code method on the management canister?

I don’t know why would I want install_code or not I don’t fully understand the process here. I am building this minimal project to see how it work, and to see what are my options.

@Severin Is it true that using this method the wasm size can be 10MB? I understand that ingress is no longer the limiting factor here, but where exactly does this limitation come from?

Also, can the wasm_module be gzipped here?

If the canister you’re installing to via the proxy method AND the two canisters live on the same subnet, then yes, you should be able to go up to 10MB WASM. The 10MB is for unzipped wasm, but you can install it gzipped without any problems

Actually we recently made a change that allows the Wasm module to be 30MB uncompressed as long as the code section is still under 10MB: Merge branch 'abk/run-647-larger-wasms' into 'master' · dfinity/ic@a044776 · GitHub

3 Likes