Import "canister: canistername" error

Try to import canister similar as linkedup demo, but seems it doesn’t work.

By using " import AAA “canister:aaa”; "
then run "dfx build "

it shows:

import error [M0153], file ~/.dfx/local/canisters/idl/r7inp-6aaaa-aaaaa-aaabq-cai.did uses Candid types without corresponding Motoko type

r7inp-6aaaa-aaaaa-aaabq-cai.did:13.11-13.46: import error [M0163], cannot import a Candid service constructor

I had this the other day. Is your canister a class? You can’t import it with canister:. You need to create an actor type that conforms to the actor interface and seed it with a hardcoded principal.

I’m not sure if this corresponds to what you are trying to do, but a motoko actor can directly import and actor class as a library too.

See here for an example

Doc

Code

1 Like

that is exactly the problem! thx a lot~! :smiley:

:+1: :+1: :+1: :+1:

this works also !

Will this restriction on import Foo "canister:Foo only working with actors without constructor arguments ever be lifted?

I saw this draft PR, but it seems to have been abandoned.

2 Likes

Hi Claudio. Could u give a example how to import the actor without calling the contractor?

NVM. I just got it right after making the post. :sweat_smile:

let’s use the this example: Actor classes | Internet Computer Home

import Nat "mo:base/Nat";
import Map "mo:base/RBTree";

actor class Bucket(n : Nat, i : Nat) {

  type Key = Nat;
  type Value = Text;

  let map = Map.RBTree<Key, Value>(Nat.compare);

  public func get(k : Key) : async ?Value {
    assert((k % n) == i);
    map.get(k);
  };

  public func put(k : Key, v : Value) : async () {
    assert((k % n) == i);
    map.put(k,v);
  };

};

Support u have deployed this actor on mainet and the canister ID is r7inp-6aaaa-aaaaa-aaabq-cai, and now you want deploy another canister and define actor Map to interact with Bucket canister. Just do this:

import Buckets "Buckets";

actor Map {
     type Bucket = Buckets.Bucket;
     let Bucket_actor = actor "r7inp-6aaaa-aaaaa-aaabq-cai" :Bucket;
    ....
}

Now u r ready to go. Cheers!

Resurfacing this issue because hard-coding the canister principal is not scalable and requires manually changing the principal id every time you want to deploy to a new environment (mainnet or local). I’ve tried a few ways of getting around this, including instantiating the class using a principal that comes from the encapsulating function’s arguments, and even importing the class canister like this: import ElasticSearch “canister:elastic_search”;

but both solutions are futile. Is there any other way to get around this without an extremely hacky solution? Seems like a very important feature that should be available to developers in the absence of environment variables.

2 Likes

How about something like this?

If the work-around involves hard-coding the canister id can I then not just as well use the line:

import AAA "canister:<principal-id>" instead of import AAA "canister:<canister-name>" ?

1 Like

Is this still the best / only way to import a class canister?

Not unless you want to spawn that canister. If you just need to call it, just make an actor type with the same signature. Otherwise, the entire library gets compiled and it makes your wasm huge.(You don’t want to have to include the ICP ledger code in your canister just to call it).

I was wondering why dfx deploy was suddenly taking ages.

How do I make an actor type “with the same signature”?

Typically you can find the signature at the end of a motoko types file. One easy way is to go to icscan.io and put in the canister. Click on ‘view did file’ an go to the motoko file. It will be at the end and named “Service” or “Self”:

Copy that one type to your Types file and then you can


let myactor :Types.CopiedService = actor(remotePrincipal);

Then you can call those functions.
1 Like

Although actor class was designed to take initialization arguments, its intended use case of dynamically creating unamed actors is at odds with its typical use case of creating and initializing a named canister only once at deployment time.

I suggest that we actually allow this failed case to pass:

import error [M0153], file ~/.dfx/local/canisters/idl/r7inp-6aaaa-aaaaa-aaabq-cai.did uses Candid types without corresponding Motoko type

r7inp-6aaaa-aaaaa-aaabq-cai.did:13.11-13.46: import error [M0163], cannot import a Candid service constructor

I don’t think allowing it to go through does any harm. For example, the Bucket actor class compiles to the following candid interface:

type Value = text;
type Key = nat;
type Bucket =
 service {
   get: (Key) -> (opt Value);
   put: (Key, Value) -> ();
 };
service : (nat, nat) -> Bucket

Can we not look for the Bucket type when the import statement is import XYZ "canister:Bucket"? That is to use the canister name as the type name, and only when that fails, we then throw the above error.

What do you think, @claudio ?

Or alternatively introduce a new syntax import XYZ "canister:Bucket" as Bucket, assuming the type Bucket is already in scope.

We could implement that impedance matching, stripping the argument when trying to import the service class, but, IIRC, @rossberg objected to that idea when we raised it in the past, since it would break if we ever extend candid to support service types as first class types (which, however, I think is unlikely to happen soon).

Personally, I think the fact that the import is specifying canister (not, say, canister class), is probably enough to resolve the ambiguity sensibly and we should just strip the argument as you suggest.

The alternative if for dfx to obtain the (instantiated) service type by looking at the icp custom section embedded in the imported canister’s wasm: it already has the argument stripped.

Tracking issue FR: allow canister import of service constructor by stripping argument · Issue #3990 · dfinity/motoko · GitHub