AssemblyScript CDK on Dfinity

I’ve been experimenting with writing AssemblyScript on DFINITY and ran into some questions.

First I grabbed the reverse project from dfinity examples, compiled it and wasm2wat the results which gave me these definitions:

  (import "ic0" "msg_arg_data_size" (func $dfn_ads (type $t0)))
  (import "ic0" "msg_arg_data_copy" (func $dfn_adc (type $t1)))
  (import "ic0" "debug_print" (func $dfn_print (type $t2)))
  (import "ic0" "msg_reply_data_append" (func $dfn_reply_append (type $t2)))
  (import "ic0" "msg_reply" (func $dfn_reply (type $t3)))
  (func $canister_update_go (export "canister_update go") (type $t3)

So I created this AssemblyScript Interface: (ic0.ts)

export declare function debug_print(msg : string, length: i32) : void;
export declare function msg_arg_data_size() : i32;
export declare function msg_arg_data_copy(x : i32, y: i32, z: i32) : void;
export declare function msg_reply_data_append(x : string, y: i32) : void;
export declare function msg_reply() : void;

index.ts

function init() : void {
  var text: string = "hello dfinity from assemblyscript";
  IC.debug_print(text,String.UTF16.byteLength(text));
}

export {
  init as canister_init,
}

Results:
[Canister rwlgt-iiaaa-aaaaa-aaaaa-cai] hello dfinity from assemblyscript

Now I wanted to query a function with my canister being called tshello
dfx canister call tshello sayhello

so I added the function:

function sayhello() : void {
  var text: string = "sayhello";
  IC.debug_print(text,String.UTF16.byteLength(text));
  var results: ArrayBuffer = String.UTF8.encode(`DIDL\00\00`);
  IC.msg_reply_data_append(results, results.byteLength)
  IC.msg_reply();
}

export {
  init as canister_init,
  sayhello as canister_update_sayhello,
}

Which gives me the WAT
(export "canister_update_sayhello" (func $assembly/index/sayhello))

Which gives me the results:
Replica error (code 3): IC0302: Canister rwlgt-iiaaa-aaaaa-aaaaa-cai has no update method 'sayhello'

I realized there needs to be a space between canister_update and sayhello, so if I go into the .wat file and add a space.

(export "canister_update sayhello" (func $assembly/index/sayhello))

Results are good with --output raw

[Canister rwlgt-iiaaa-aaaaa-aaaaa-cai] sayhello
4449444c5c30305c3030

Without Raw
"Unsupported op_code 48 in type table",

Questions

  • Is the space required with canister_update and the function name?
  • Anyone familiar enough with AssemblyScript to properly define an export with a space in its name?
  • Whats the correct way to use an empty reply? DIDL\00\00

I’m going to keep experimenting with this, but there are the initial results.

btw I had a lot of problems submitting this post, something about the code.

Thanks,
Rick

3 Likes
  • Is the space required with canister_update and the function name?

Yes

  • Whats the correct way to use an empty reply? DIDL\00\00

Yes

Without Raw
"Unsupported op_code 48 in type table",

sayhello returns a string not an empty reply. So we need to encode the string in Candid.

var results: ArrayBuffer = String.UTF8.encode(`DIDL\00\01\71` + LEB128.encode(text.length) + UTF8.encode(text));
4 Likes

I don’t know AssemblyScript, but I think your exported name is wrong.

You want “canister_update sayhello” not “canister_update_sayhello”.

Just some tips to help you with this.
While the documentation isn’t great, you’ll get a lot of clues on how to implement this stuff in these places:
The macro obscures the meaning of the code a bit, so cargo expand should make it a bit more intelligible.


And here’s some uses of this

If you run cargo expand on this you’ll see how we actually bind endpoints:

Finally, when you’re getting started there’s absolutely no reason to use candid as a serialization format. My advice is just to talk to the canisters using JSON because it’s much easier to see where you’ve gone wrong and there’s probably already a mature library for you to lean on.

3 Likes

I would strongly recommend checking out this post:


And taking a look at this code:


Which makes use of the system API at:
2 Likes

Thanks [email protected],[email protected], [email protected]

I was able to produce the following that seems to work

import * as MSG from "./message";

var counter: number = 0;

function init(): void {
  var text: string = "hello DFINITY from AssemblyScript";
  MSG.printLine(text);
}

function printOnly(): void {
  var text: string = "This just prints only :)";
  MSG.printLine(text);
  MSG.respondEmpty();
}

function echo(): void {
  var param:string = MSG.recieveString();
  MSG.printLine(param);
  MSG.respondString(param);
}

function inc(): void {
  MSG.recieveEmpty();
  counter += 1;
  MSG.respondString(counter.toString());
}

function dec(): void {
  MSG.recieveEmpty();
  counter -= 1;
  MSG.respondString(counter.toString());
}

export {
  init as canister_init,
  echo as canister_update_echo,
  dec as canister_update_dec,
  inc as canister_update_inc, 
}

I wrote a NodeJS script that goes through the WAT and removes the underscore between update and function name and then I run wat2wasm.

Enzos C Counter example helped a lot as well as the Rust CDK.

Its interesting how counter keeps its value, even after a DFX restart, I assumed that was a MOTOKO only feature. I think with decorators, the functions can be slimmed down and not require a ‘receive’ or ‘respond’. Really exciting to see how far DFINITY has come.

Thank you,
Rick

3 Likes

For anyone else curious, I moved my code into a repo.

4 Likes