Encode Principal to Candid arguments

I have got a manager canister that creates user canisters. These user are actor that takes a principal as argument.

actor class UserBucket(owner: Principal) = this {
   ...

So far, so good.

Now, through the manager I am trying to implement a function that can update the code of these user canisters.

public func installCode(canisterId: Principal, owner: Principal, wasmModule: Blob): async() {
            await IC.install_code({
                arg = Blob.toArray(Principal.toBlob(owner));
                wasm_module = wasmModule;
                mode = #upgrade;
                canister_id = canisterId;
            });
        };

After double checking that all parameters are effectively correctly set I am facing following error when I try to update the code:

Canister a3ukg-3yaaa-aaaai-aa6ta-cai trapped explicitly: IDL error: missing magic bytes

If I get it correctly, it means that my arg, the owner principal currently converted to [Nat8], is not correctly encoded as valid Candid arguments.

Do I get it right?

How do I encode the principal as valid candid arguments?

1 Like

You can import actor class directly, see examples/Map.mo at master · dfinity/examples · GitHub

Mmmh not sure to understand how is it related? The actor class works fine, I try to upgrade the WASM code of existing canisters that have been generated on the fly. I try to use the install_code feature (as in the motoko playground).

Ah, so the wasm module is not generated from the actor class? In that case, maybe you can pass arg as an argument as well, and use didc to encode the principal to a candid blob.

yeah I think it’s what I need but I don’t know how?

how to “encode a principal to a candid blob”?

From CLI, you can run didc encode '(principal "aaaaa-aa")' -f blob

1 Like

I am writing a script in JavaScript (sorry should have had mentioned that earlier) and the backend is in Motoko.

Found something in the playground and candid, so trying the following:

import { IDL } from "@dfinity/candid";

const arg = IDL.encode([IDL.Principal], owner);

but I get an error

Cannot mix BigInt and other types, use explicit conversions

owner is a principal

console.log(JSON.stringify(owner))

// {"_arr":{"0":154,"1":1 .... "28":2},"_isPrincipal":true}
1 Like

The second argument is an array as well, [owner]

1 Like

Nice :+1:

const arg = IDL.encode([IDL.Principal], [owner]);
// -> Uint8Array(38) [68,  73,  68,  76....65,   2]

Now I should check how I can send it to my canister. I used a Blob but it does not seem happy with it.

Shit, I think it may have worked out!!!

My manager access point

public shared({ caller }) func installCode(canisterId: Principal, owner: Blob, wasmModule: Blob): async() {
        await canisterUtils.installCode(canisterId, owner, wasmModule);
    };

My utils

public func installCode(canisterId: Principal, owner: Blob, wasmModule: Blob): async() {
            await ic.install_code({
                arg = owner;
                wasm_module = wasmModule;
                mode = #upgrade;
                canister_id = canisterId;
            });
        };

The js call

const arg = IDL.encode([IDL.Principal], [owner]);
await actor.installCode(bucketId, [...arg], [...new Uint8Array(buffer)]);
3 Likes

Gosh it indeed works out!

I just added a “hello world” function in the WASM and used above function to update the user canister code :point_right: ic.rocks now displays the new function :partying_face:

Thanks for the help @chenyan

2 Likes