Completed: ICDevs.org - Bounty #26 - ICRC-1 Motoko - up to $10k

@roman-kashitsyn This looks like some legacy stuff from back when canisters couldn’t hold ICP.

Actually…now that I look at it, this may be an old implementation from back before the new textual encoding was being discussed.

I think the actual encoding is more like:

//note: does not consider crc check on the blob bytes
account_icrc1_text = Principal.toText(Principal.fromActor(this)) # ":" # Conversion.valueToText(#Bytes(#frozen(hash)));

Decoding should be pretty straight forward if you just split on “:” and use something like GitHub - aviate-labs/encoding.mo: Encoding Package for Motoko to decode the second part.

See ICRC-1 Account Human Readable Format - #42 by lastmjs
and Announcing "Token Standard" as topic of the first meeting of the Ledger & Tokenization Working Group - #53 by Maxfinity

We do really need to tie down the final format. I think we’re very close and if you go with principal:bytesinhexwithtruncated0s I think you’ll be ok. If it is intercanister you can ignore the CRC check anyway as no one will be copy/pasting it.

Do you mean the ICRC-1 standard is not finalised yet? The textual encoding at least?

I’ll tell you what I’m trying to do:

I need my backend canister to give each authenticated user a unique string for them to send ICRCs to, so that the user can do that from any wallet, and so that when ICRCs arrive there, the canister owns them and knows which user they belong to.

The approach I was taking is

let user_account : Account = {
      owner = Principal.fromActor(this);
      subaccount = ?Principal.toBlob(callerId);
    };

and then

let text_user_account = Account.toText(user_account);

That’s when I hit that line 22 error.

If I understand correctly I need a Motoko function to get that Principal.toBlob(callerId) into a subbacount form that is compatible with Account.toText() from ICRC-1/Account.mo at main · dfinity/ICRC-1 · GitHub

Though this doesn’t seem to include any referece to the subaccount. What is a textual representation for the case where the subaccount is present, and I want that subaccount to uniquely represent a user’s principal, so that the text end result is unique to the user.

We’ll you’ve got the right idea. Check out A Christmas Hack: Minimum viable approvals. When we get this finalized the principal will be the canister and the sub-account will be the hash of something that you can use to uniquely assign that sub-account to one of your users(or in the example an approver/transferer pair).

I don’t think many wallets have ICRC-1 integrated yet, so your best way to do this to initiate the transaction from your dapp and have your back-end canister take {owner; subaccount;}. Your UI can take care of displaying to the user(show textual encoding if desired). Your dapp can call something like getApprovalAccount in the example to get the proper subaccount to use.

Basically: You shouldn’t really ever have to have an update call take a text string…let the wallet/dapp handle that for you.

Yes, I’m definitely not using the string for the backend, but I want to give the user a single string where funds can be send from anywhere in the IC and everything will work out as expected.

What I tried just now, given the error due to blob size, is turning the user’s principal blob into an account identifier with this function here: examples/Account.mo at 433dd4afc1b1bba2c190993eaee44db33a1a237b · dfinity/examples · GitHub

And then use that as the subaccount here:


let user_account : Account = {
      owner = Principal.fromActor(this);
      subaccount = ACCOUNT_IDENTIFIER;
    };

and passing

let user_account_provisory : Account = {
      owner = Principal.fromActor(this);
      subaccount = ?Principal.toBlob(callerId);
    };

to accountIdentifier()

where

ACCOUNT_IDENTIFIER = accountIdentifier(user_account_provisory)

Then I do get back a string that I think and hope is both unique and functional. If you see problems with this approach please let me know.

Well…I just think that that particular code you are using is wrong now. We actually have a meeting tomorrow to hopefully finalize the human-readable encoding: Announcing "Token Standard" as topic of the first meeting of the Ledger & Tokenization Working Group - #83 by dieter.sommer

Alright. Yes, we need to get this done. Textual encoding is very important partly because it allows third parties to send tokens to an account id or controlled by apps they have not had direct contact with / have had no business in producing.

I may turn up tomorrow if time allows.

1 Like

We did not get to this agenda item in yesterday’s meeting and will now discuss in the meeting of January 24th. New contributors are of course warmly welcomed! :slight_smile:

2 Likes

If anyone happens to have tips or advice regarding deploying this Motoko ICRC repo as a canister that’s not in the readme do share.

I’ll probably start tinkering tomorrow.

There was a recent issue on the GitHub page regarding the custom Result type used by the ICRC-1 reference implementation which capitalizes the first letter of the #ok and #err variants.

I’m curious why the Token Standard uses this type instead of the one in the base library.

I’ve updated the icrc1 lib to use the TransferResult type from the standard (@7af28bb) and published a release version to github and mops

2 Likes

I was looking last night and I thought that there was a rosetta implementation, but I didn’t see any http_request in the Token.mo. Have you done this yet or is it just the function that exists and it is up to the user to build the http_request pipeline? I may have been looking in the wrong place!

1 Like

Hey @skilesare, sorry about the confusion, but this library does not contain an implementation of the rosetta API. It only implements the get_transactions() function needed by the ic-rosetta-server.
I was under the impression that exchanges/users could run the server, connect it to an ICRC-1 token and make requests through it using the rosetta API standard instead of interacting with the coin directly via HTTP requests.
If this is the case, I don’t think it would be necessary to add the http_request() function in Token.mo or for token creators to implement it themselves.

I think you are right. DFINITY indicated to me that they will likely rewrite the rosetta node to support ICRC-1 in the coming month. This makes it harder to use an ICRC-1 token to go to a CEX, but we will hopefully get there eventually.

1 Like

Hi @tomijaga, I tried to use this library as an interface to interact with the SNS-1 container and found that the subaccount type is Blob unlike the SNS-1 canister, which has [Nat8] type. Also ICP ledger canister uses [Nat8] for subaccounts.

ICP ledger candid
SNS-1 candid

1 Like

Nat8 and Blob are aliases for each other in candid. They should be interchangeable.

1 Like

The inconvenience starts when you want to use the same subaccount data types as for the ICP ledger

type error [M0096], expression of type
  {owner : Principal; subaccount : ?Subaccount}
cannot produce expected type
  {owner : Principal; subaccount : ?Subaccount__1}

The official type is Blob which puts the SNS out of compliance with the standard. ICRC-1/README.md at main · dfinity/ICRC-1 · GitHub

This is odd. @roman-kashitsyn any idea why this is the case?

Blob.toArray(x) blob.fromArray(x) should get rid of the errors.

2 Likes

It appears to be a Motoko-specific problem, or rather the problem with generating Motoko bindings for .did files. Both the ICP ledger and the SNS-1 implementation are implemented in Rust and use the same Account type. The interface files for these ledgers specify that the subaccount is a blob:

I submitted a pull request today updating this library to use aync* which really helps with batch transfers.

1 Like