Dynamically creating new canisters: how to not hardcode WASM code?

Hello everyone,

Problem Statement: In my application, I need to dynamically create new canisters based on certain conditions. To achieve this, I’m using the ic.create_canister and ic.install_code functions provided by the DFINITY Internet Computer SDK. However, I’m encountering difficulties in efficiently sending the Wasm code to install the new canisters. The current approach involves hardcoding the Wasm code, which is neither scalable nor efficient.

Current Approach: Here’s a simplified version of the relevant part of my code:

motokoCopy code

let nums8 : [Nat8] = Array.map<Nat, Nat8>(Wasm.token_array, Nat8.fromNat);

await ic.install_code({
  arg = to_candid(tokenId);
  wasm_module = Blob.fromArray(nums8);
  mode = #install;
  canister_id;
});

Challenges Faced:

  • The Wasm code is long, and passing it directly in the code leads to readability issues and potential errors.
  • Generating the Wasm code dynamically and passing it in the Candid file byte by byte is time-consuming and not practical.
  • Hardcoding the entire Wasm code is not a scalable solution, especially when dealing with multiple canisters.

Request for Assistance: I’m seeking advice on a more efficient approach to handle the installation of dynamically generated canisters. Specifically, I’m looking for suggestions on how to dynamically generate and install the Wasm code without resorting to hardcoding or time-consuming byte-by-byte operations.

Conclusion: If anyone has encountered a similar challenge or has expertise in efficiently handling Wasm code installation for dynamically generated canisters, I would greatly appreciate your insights and suggestions.

Thank you in advance for your assistance!

1 Like

If you have your dynamic canisters as actor classes, then you can reference them in another actor to deploy them.

Then you can just call the type with an await and the args and it will create the actor

Then if you need to upgrade, you can do a system call

Here is an example:

import MyDynamicActor "MyDynamicActor";

actor {
  ...
  public func deploy() : async (){
    let canisterCreationCost = 100_000_000_000;
    let initialBalance = 100_000_000_000;
    Cycles.add<system>(canisterCreationCost + initialBalance);
    myDynamicActor = await MyDynamicActor.MyDynamicActor(
      // actor class args
    );
    // store actor reference/id
  }

  public func upgrade() : async (){
    // use the actor reference/id to upgrade the actor
     let _ = await (system MyDynamicActor.MyDynamicActor)(#upgrade(myDynamicActor))(
        // actor class args
    );
  }
 }

I’ve started building a canister factory for 221Bravo - which was supported by code from Juno. 221BravoApp_v3/src/canister_factory at main · SaorsaLabs/221BravoApp_v3 · GitHub

It only has methods for deploying canisters at the moment, not upgrade and re-installs. Might help?

I took a lot of inspiration from Juno.build github repo - defo also worth a look.