Calling/importing different actors in Motoko relationship

Hey,

I’m bit confused about the relationship between calling/import other actors in Motoko, here are some cases:

  1. Calling a constructor of actor and invoke function calls on them:
  • I have an actor A which imports another actor B File with import "ActorB"; and invokes a constructor of B, in this case a new canister with actor B is created in a new subnet, right? Further calls to B from A can be simple function calls, because the previous constructor invocation returned a new actor B object with the correct canister-address right?
  • How can I destroy this new created canister with actor B in code of canister A?
  1. Calling a different actor in a different canister (both canisters are in a single project)
  • I have a dfx.json with two canisters (canister 1 with actor A and canister 2 with actor B)
  • I can import actor B in actor A with import ActorB "canister:ActorB";
  • now I can directly call actor B from actor A because the build process somehow inject the canister-address of actor B into the code of actor A, right?
  1. Calling a different actor in a different canister (both canisters are in different projects)
  • we have two canisters in different projects, so they are not part of the same dfx.json
  • canister 1 with actor A and canister 2 with actor B
  • now I can’t simply import ActorB "canister:ActorB";, because these actors are in different projects right?
  • how can I import now actor B in actor A in Motoko (with types of actor B visible in code of actor A), so I can call actor B from actor A and how is the address of actor B is injected into actor A in this case?
  1. Is it possible to deploy the same actor with the same code to different canisters, so that each of them store different state or possibly also the same state?

Thanks

1 Like

Great questions.

  1. Not sure if it’s necessarily a new subnet, or the same. Yes, method calls look like ordinary ones, even though they involve cross-canister messaging on the IC, under the hood.

  2. Yeah, the build process knows about the canisters ID’s on either the local replica, or the IC (I believe). In either case (I believe), it uses these IDs in place of the human-readable canister names. I say “I believe” twice because I haven’t done this on the IC itself, but many times in a local replica. I believe it’s the same code in both cases.

  3. Right, the importing in this case is not as clear. But you can hard code or otherwise import the canister IDs, once assigned, into each of the two sibling canisters of the pair. In Motoko, you may cast a Text containing the canister ID into an actor of the actor type that you supply, explicitly.

3 Likes

Yeah, in many ways.

The easiest using dfx.json consists of pointing your main file to a Motoko file that you just reuse for multiple canister names, each listed in that file.

Here’s an example where I am listing a Motoko source file for an actor that is not even in the same repo as the dfx.json file that refers to it. It uses vessel to resolve that github URL, and assist the build.

2 Likes

Perhaps others can respond to this question

I mentioned this, but did not give a code example.

Since I just did this myself today, I wanted to include an example now, while it’s still fresh.

1 Like

For stopping and deleting the actor you can use the IC Management Canister, adapting some of the code from this sample:

You’ll need to obtain the Principal of the actor in question (using library function Principal.fromActor : () -> Principal), wrap that principal in a record and pass it to the management canister’s stop and delete functions.

2 Likes

Something like this should work (untested though):

import Principal "mo:base/Principal";

module {  
  // Use an actor reference to access the well-known, virtual
  // IC management canister with specified Principal "aaaaa-aa",
  // asserting its interface type
  // NB: this is a smaller supertype of the full interface at
  //     https://sdk.dfinity.org/docs/interface-spec/index.html#ic-management-canister
  let IC =
    actor "aaaaa-aa" : actor {

      stop_canister : { canister_id : Principal } -> async ();

      delete_canister : { canister_id : Principal } -> async ();
    };

  // stop and delete and actor
  public func discard(a : actor) : async () {
    let cid = { canister_id = Principal.fromActor(a) };
    let status = await IC.canister_status(cid);
    await IC.stop_canister(cid);
    await IC.delete_canister(cid);
 };
2 Likes

(delete the line starting:

let status = ...

it’s unnecessary but the forum won’t let me edit the post)

1 Like

Great, thank you both!