Install_code() - Easiest way to convert a .wasm module to [Nat8]?

My goal is to have a canister create a new canister and install a pre-built wasm module. Now, the install_code() function of the management canister expects type [Nat8] as an argument for the wasm module. I’ve read some related threads, but wasn’t able to get a solution from it unfortunately. If I’m not mistaken, all I need to do is to somehow convert the wasm module to a [Nat8] array and hard-code the argument for the install_code() function. My problem is that I don’t know how to convert the wasm module to an appropriate format so that I could store it in the (parent-)canister like so:

let wasmBlob : [Nat8] = [];

public shared(msg) func uploadWasm(wasm : [Nat8]) : async () {
    wasmBlob := wasm;
};

As always, any help would be much appreciated :slight_smile:

Got that in Papyrs in Motoko+NodeJS if it can help?

Canister:

 private stable var storageWasm : [Nat8] = [];

public shared ({caller}) func storageLoadWasm(blob : [Nat8]) : async ({total : Nat; chunks : Nat}) {
    if (not Utils.isAdmin(caller)) {
      throw Error.reject("Unauthorized access. Caller is not an admin. " # Principal.toText(caller));
    };

    // Issue: https://forum.dfinity.org/t/array-to-buffer-in-motoko/15880/15
    // let buffer: Buffer.Buffer<Nat8> = Buffer.fromArray<Nat8>(storageWasm);
    // let chunks: Buffer.Buffer<Nat8> = Buffer.fromArray<Nat8>(blob);
    // buffer.append(chunks);
    // storageWasm := buffer.toArray();

    storageWasm := Array.append<Nat8>(storageWasm, blob);

    // return total wasm sizes
    return {
      total = storageWasm.size();
      chunks = blob.size();
    };
  };

JS script load the wasm to array as follow:

export const loadWasm = async (type) => {
  const buffer = await readFile(`${process.cwd()}/.dfx/local/canisters/${type}/${type}.wasm`);
  return [...new Uint8Array(buffer)];
};

and the the scripts chunks and upload it:

const installWasm = async ({actor, type, wasmModule}) => {
  console.log(`Installing ${type} wasm code in manager.`);

  const chunkSize = 700000;

  const upload = async (chunks) => {
    const result = await actor.storageLoadWasm(chunks); // <---- here call of above Motoko endpoint
    console.log('Chunks:', result);
  };

  for (let start = 0; start < wasmModule.length; start += chunkSize) {
    const chunks = wasmModule.slice(start, start + chunkSize);
    await upload(chunks);
  }

  console.log(`Installation ${type} done.`);
};

Source in this repo: https://github.com/papyrs/ic

2 Likes

Depending on the size of your Wasm module, the Motoko compiler won’t be able to process it if it’s an array. See: https://github.com/dfinity/motoko/issues/3557

So you must convert your wasm module to hexadecimal first (there are a many options online and some Unix tools to do that offline) and convert the result to be compatible with Motoko’s Text, like:

let wasmModule: Blob = "\de\ad\be\ef";

Then it’s simple to install the module:

Cycles.add(300_000_000_000);
let id = await IC.create_canister({
     settings = ?{
        controllers = ?[Principal.fromActor(this)];
        compute_allocation = null;
        memory_allocation = null;
        freezing_threshold = null;
    }
});
  await IC.install_code({
      mode = #install;
      canister_id = id.canister_id;
      wasm_module = wasmModule;
      arg = to_candid({
          //your canister's arguments, ex: foo = 1234;
      });
  });
3 Likes

I will try this out victor. Thank you and regards

1 Like

Sorry for my late response, took me some time. I tried Victor’s approach and it works perfectly. Thank you both for your answers!

2 Likes

Karim sir,
How did you converted wasm to blob, which tool did you used?

Hi all, I have a question here:
can we dynamically generate candid method, read wasm code, and install wasm code on chain?