There are two canisters, and when using a function in canister A to call a function in canister B, need to pass in the canister id <Principal> of canister A as a parameter, so is there any way to get the canister id in the motoko code? like address(this) in Ethereum solidity
One way that is not so good, is adding the below code to canister B, and canister A call this function in canister B to get its canister id:
// Return the principal of the message caller/user identity
public shared(msg) func callerPrincipal() : async Principal {
return msg.caller;
};
While that solution works, it’s quite slow since it requires an expensive await.
A more direct solution is to declare the optional this parameter of the actor or actor class and return its principal using the Principal.fromActor base library function. Something like:
import Principal "mo:base/Principal";
shared (install) actor class WhoAmI(someone : Principal) =
this { // Bind the optional `this` argument (any name will do)
...
// Return the principal identifier of this canister via the optional `this` binding.
// This is much quicker than `id()` above, since it avoids the latency of `await whoami()`.
public func idQuick() : async Principal {
return Principal.fromActor(this);
};
};
I’ll post a PR to the examples repository to include this faster alternative.
The call Principal.fromActor(this) is basically free compared to 18k~ cycles burned by the await call
cf.
Main.mo
// Return the principal identifier of this canister.
public func id() : async (Principal, Nat, Nat) {
let before = Cycles.balance();
let result = await whoami();
let after = Cycles.balance();
return (result, before, after);
};
// Return the principal identifier of this canister via the optional `this` binding.
// This is much quicker than `id()` above, since it avoids the latency of `await whoami()`.
public func idQuick() : async (Principal, Nat, Nat) {
let before = Cycles.balance();
let result = Principal.fromActor(this);
let after = Cycles.balance();
return (result, before, after);
};
Yes, it’s essentially free. The only cost here is the enclosing method itself, but if you were using it to pass on the principal of actor A to actor B as an explicit argument, just obtaining the principal of A can be dead cheap.
For an actor declaration, you can just pass the name of the actor (the name, if provided, is the “this” reference).
actor Test {
...
public func idQuick() : async Principal {
return Principal.fromActor(Test);
};
}
(Classes need additional syntax to reference “this” because the name of the class names the constructor (a function), not the instance being constructed (an object)).
I’m note sure I understand the question, but does the example above not answer it?
this names the instance returned by the constructor, Principal.fromActor(this) give the raw principal of that instance.
The class itself has no identity per se, but I may be misunderstanding your question. I’m off to bed, but can answer tomorrow if you provide more context.
Binding this to the actor class to obtain its principal id, however, calling it from next js file an error Error: Impossible to convert undefined to Principal. is thrown while trying to pass some inputs from a form.
Thanks for your response!
I just realized the problem, I was trying to use self to declare a variable in my global state rather than inside a function. Makes sense now I suppose.
Cheers!
@claudio, is there a particular reason we can’t expose a system-level function in Prim or experimental at least, that pulls this from the system API? Having to wait until the actor is instantiated to be able to determine what canister id our code is being deployed on seems to be an artifact of traditional coding environments. Sometimes it is really convenient to know this during actor class initialization and I have to set up a fake timer, or instant self-call to initialize a bunch of stuff in almost every motoko actor I make.
Couldn’t this be part of msg? Right now we only have msg.caller, but what about msg.target?