Prefix all the methods of the ICRC-1 Token Standard with icrc1_

Hi everyone,

I’d like to propose two changes to the ICRC-1 Token Standard:

  1. add the icrc1_ prefix to all the methods
  2. use snake case everywhere for consistency, e.g. balance_of instead of balanceOf

1 was already proposed by @skilesare here and by @bitbruce here but I’d like to bump the discussion again. It would allow us to have canisters supporting different standards and also having a clear separation between standard and non-standard methods. The interface would look like this:

service : {
    icrc1_name: () -> (text) query;
    icrc1_symbol: () -> (text) query;
    icrc1_decimals: () -> (nat32) query;
    icrc1_total_supply: () -> (nat32) query;
    icrc1_balance_of: (record { of: principal; subaccount: opt SubAccount; }) -> (nat64) query;
    icrc1_transfer: (TransferArgs) -> (variant { Ok: nat64; Err: TransferError; });
}

A good real world use case for this are the various ICP Ledger, DIP-20 Ledgers, IS-20 Ledgers, etc… that could add the ICRC-1 methods seamlessly without having conflicts, e.g. in the transfer method.

2 is more for consistency. Mixing snake_case, that are used for arg names and probably prefix-name separation, and camelCase is weird and serves no purpose. I would suggest snake_case because I find it more readable :slight_smile: .

What do you think?

Related discussions are:

  1. Proposal to Adopt the Namespaced Interfaces Pattern as a Best Practice for IC Developers
  2. Discussion on the compatibility of different token standards
3 Likes

I think a prefix is a good idea for compatibility reasons, but I’m not that happy about the length of it: icrc1_ is quite a lot to type without fuzzy autocomplete. And if the format becomes common, it’ll be icrc1835_ at some point. Looking at the standard keyboard layout and the letters in ICRC-1 my first idea would be ir1_.

snake_case is consistent with Rust formatting, Motoko is a mix between snake_case camelCase from what I’ve seen so far. I agree with making snake_case official.

1 Like

From a typing, reading and convince standpoint I prefer a post fix since you get to the distinguishing context first and you mostly deal with one standard at a time, but I won’t be that picky. A prefix is fine.

If I’m looking for the approve function my brain is going to find approve_icrc1 before it finds icrc1_approve.

5 Likes

Historically languages put the namespace before the name, not after. I prefer the prefix just for this reason. It’s also easier to group methods of the same standard in this way.

3 Likes

For our Portal Interfaces (PI) we started out using a prefix for the reason you mention (its a namespace) but quickly switched to postfix exactly for the reason @skilesare mentioned (delayed context recognition).

Also, the snake_case vs camelCase has been interesting to me. Motoko guidelines recommend camelCase but then the system functions use snake_case, due to the influences of Rust (I think I read somewhere?).

For our codebase we continue to use camelCase even though I’ve seen a lot of examples of developers using snake_case.

Here’s screenshot of our .did for a video channel (was going to link to ic.rocks but its not running anymore?):

I’d say it’s hard to read this interface because sorting messes up grouping.
For example, if I want to look at all the things I can do with canisters (PICanister), I have to scan the whole interface linearly and parse the method names.

The following grouping is much easier for me to scan and understand:

service {
  PIAdmin_addAdmin ...
  PIAdmin_getAdmin ...
  PICanister_create ...
  PICanister_delete ...
  PICanister_getAll ...
  PIKey_authorize ...
  PIKey_checkAccess ...
  PIKey_getAccessType ...
  ...
}

The main purpose of namespacing is to enable canisters to implement multiple interfaces at once (for example, ICRC1 and DIP20). I think visually grouping by standard makes more sense: when you read the interface, you are probably want methods coming from the same standard to be grouped together.
There are, of course, other ways to achieve that with the postfix namespacing, such as carefully crafting .did files by hand for readability.

1 Like

Yes, the prefix is definitely the traditional method, and we started out that way. Our issue was developer experience, having to type our the interface before typing the method name vs just the method name which could then be autocompleted. Also, the cognitive difference of scanning the code for a method name was slower when each method was prefixed, I’m not sure why. So, changing to postfix allowed faster development and better experience.

If it was truly a module or namespace that could be autocompleted and then accessed with dot notation that would be the dream.

We could make it just rc1_. The ic characters may be redundant information if you use an interface in the context of the IC.

2 Likes

The importance here is probably uniqueness across a universe of future services. Doesn’t matter if it is a prefix or post fix. rc1_ probably gets you there and since this is so foundational, probably warranted. Type ahead is going to hate the pre fix though.

Type ahead probably isn’t relevant for an account label, is it? You will probably never hardcode an account label into any code that you write with an editor. Where else would it apply? In the shell when editing a dfx command line?

I was referring to the prefix in namespacing because I was confused on the topic . Please disregard.