karim
March 13, 2023, 1:13pm
1
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
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
1 Like
v1ctor
March 13, 2023, 2:06pm
3
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
karim
March 17, 2023, 9:18am
5
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?