ICRC-1 Account Human Readable Format

Hi folks,

While working on the ICRC-1 fungible token standard we realised that it would be nice to have a string format for addresses that is human readable and compatible with most systems. This format is orthogonal to the standard but is a nice to have that could be incorporated with the standard itself.

We can discuss about it in the working group but I feel like this is a good discussion for the forum.

DFINITY proposal is to use URIs for account representation with the format:

ic:rc1:<principal>(.<subaccount>)?

where

  1. ic:rc1: is fixed
  2. <principal> is the string representation of principal, e.g aaaaa-aa
  3. <subaccount> is a string version of subaccount. The format could be decimal and/or hexadecimal. E.g. decimal subaccounts are just numbers, e.g. 10, and hexadecimal subaccounts start with 0x, e.g. 0xa.
  4. the default subaccounts can be omitted to shrink the format for the default account to just ic:rc1:<principal>

For instance, ic:rc1:aaaaa-aa.10 and ic:rc1:aaaaa-aa.0xa would be valid representations of the Account record { owner = principal "aaaaa-aa"; subaccount = opt vec {0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;1;0} }.

An alternative we could consider is to use bech32 from BIP-0173. In this case the address could be something like ic1<bech32(principal (. subaccount)?)>. The reason why I’m not proposing this as first option is that we know some people don’t like to encode accountid in the ICP Ledger because they find it confusing and bench32 could be considered the same.

There are also other options (Principal encoding or base58 either adaptive or not) that @infu put to vote in a poll ICRC-1 Token Standard Ledger - #109 by infu .

What do you think we should use?

10 Likes

I like that you mentioned URIs. - Uniform Resource Identifier

A Uniform Resource Identifier (URI ) is a unique sequence of characters that identifies a logical or physical resource used by web technologies. URIs may be used to identify anything, including real-world objects, such as people and places, concepts, or information resources such as web pages and books. Some URIs provide a means of locating and retrieving information resources on a network (either on the Internet or on another private network, such as a computer filesystem or an Intranet); these are Uniform Resource Locators (URLs). A URL provides the location of the resource. A URI identifies the resource by name at the specified location or URL. Other URIs provide only a unique name, without a means of locating or retrieving the resource or information about it, these are Uniform Resource Names (URNs).

I would like also to add the locating feature so that when you have an address you know where it’s coming from. Makes sense in web3.
When that’s needed:
Imagine the following scenario. We are at app 1 and we want to prove to it that we have proof of humanity NFT (or another soulbound token rewarded to us) in another dapp.
The user takes their address from app 2 and pastes it in app 1. Now app 1 can send frontend-to-frontend request to app 2 and prompt the user. If there is no location inside the address, then the user has to copy/paste the location too.

Also this can be optional, but part of the spec, like https://address.com and //address.com and http://username:password@example.com/ are part of the same format. Same spec, optional parameters.

So you end up having something like

(<domain>/)?ic:rc1:<principal>(.<subaccount>)?

Added benefit - when someone pastes that address in a browser, it can be made by the dapp devs to work like a blockchain explorer.
kontribute.app/ic:rc1:pcotu-nluuc-vzney-2ugxz-y2uis-duoyw-unfos-x6uyo-lnvgj-ohijr-sae.0xa
Another added benefit - users won’t be “losing” their address origins. It looks like everyone is making wallets and a lot of dapps will also want to have their own wallets. They may use third-party “identities” but for backup/PoH, not for storing assets. So users will have a lot of different addresses. If they write down the address without location and they forget what the location is, they will lose access to their assets.

1 Like

This would be URN-style, not a URL (the last of the three links in your quote).

Account-id of ICP is a better solution. Reason.

  1. Easier for users to use.
  2. Consistent with ICP and developer friendly.
  3. Support for privacy protection.

I never understood why this was used.

type Account = record { owner: principal; subaccount: opt Subaccount; }

2 Likes

Could you explain why it is easier to use?

As a normal user, the address format in the wallet now has Principal and AccountId, which is already confusing.
Now that Token has introduced “Principal+Subaccount”, won’t the user be even more confused?
An account identifier is usually a relatively regular string of characters, so if “Principal+Subaccount” is used, how does the user send his account to another person? Using a format like "ic:rc1:pcotu-nluuc-vzney-2ugxz-y2uis-duoyw-unfos-x6uyo-lnvgj-ohijr-sae.0x0000000000000000000000000000000000000000000000000000000000000001”? It will be difficult for users to accept.

That was precisely the idea behind the proposal, to remove the fact that there are two identifiers and make it just one (the principal). So you could tell another person to send you tokens at ic:rc1:pcotu-nluuc-vzney-2ugxz-y2uis-duoyw-unfos-x6uyo-lnvgj-ohijr-sae. That’s enough for the default account which is equal to subaccount 0. If you want a different subaccount it would be ic:rc1:pcotu-nluuc-vzney-2ugxz-y2uis-duoyw-unfos-x6uyo-lnvgj-ohijr-sae.1 in your example.

I think the use case where someone uses a long hex string as subaccount (for example a hash value) are rather special.

Fair criticism. If you want a relatively regular string then that would be achieved with the second proposal from the original post (the one based on bech32).

Hey @bitbruce, thanks for the feedback. Let me try to address it step-by-step.

Let’s start with AccountId. AccountId will eventually be deprecated in favour of the ICRC-1 Accounts. The reason is that a part of the community was really strong about how unclear and opaque AccountIds are in the ICP Ledger. We heard the community and decided to replace AccountId with Account. Once ICRC-1 will be in place we will add more documentation for users. Note that eventually also ICP will support ICRC-1.
Personally I think that AccountId were a mistake of DFINITY and it’s good the community raised the issue. Kudos to the working group in particular for this.

Now let’s talk about Principals. Principals have limitations, e.g. a Canister can have only one principal. Usually you want a user or smart contract to have multiple addresses. Hence Principals should not be used as addresses.
Let me also add here, before somebody jumps into the discussion, that changing the replica to solve a problem that can be easily solved at application level is not a good idea and would have deep security impact on the IC.

Given the two points above, the natural solution was to

  1. make address equal to Principal+Subaccount, aka Account
  2. provide a string representation for Account that would be the only thing seen by the user

Point 1. doesn’t really affect users. I would expect users to see always the string format of Account in their wallet.
For point 2. we have different options which I enumerated above. II think that sending an account for a transfer will be easy as long as the account is a string that can easily be copied. This string can be generated by your wallet (btw that’s always the case) and you would just copy it and send it. Any format would work.
The question is which format would be nicer. Should principal and account be transparent, but contain characters that are not alphanum, so that the user can double check? Should do the same but remove all the :, ., -? Should we instead use bech32 and have a less transparent string but compatible with existing libraries?

Finally a quick nitpick: with the transparent format, the address above would look more like ic:rc1:pcotu-nluuc-vzney-2ugxz-y2uis-duoyw-unfos-x6uyo-lnvgj-ohijr-sae.1 which imho is easy to parse by both humans and machines :slight_smile:.

1 Like

I think this change will bring great harm to the ecology.

  1. some users are already used to Principal and AccountId. they are already used to managing ICP assets with AccountId. If the ICP account format is suddenly changed, many people will think they have lost their assets. Be aware that they are not technical people. I have not seen a public chain project change its AccountId in this way after the main network has been launched.

  2. Any Canister code that has been written using the original ICP interface needs to be upgraded. You have to balance compatibility and composability of the blockchain. The impact of this disruption on developers is very significant.

  3. AccountId is not a bug, the idea is similar to BIP32 and allows for multiple account addresses as well as enabling anonymity.

  4. If the “Principal+Subaccount” format is used, anonymity is lost and the format is difficult for the average user to understand (why would there be two fields for an account). Of course, this is not a problem for technical people, but it is different from the common sense of blockchain systems.

  5. To eliminate the confusion between Principal and AccountId, it is recommended that Dapp reduce the use of Principal, which is only used for account management with matching private keys.

My personal take is that the 32-byte-length has some practical benefit, because it translates to U256. I am not aware of any major chain that has an address not fitting into 32 bytes.

2 Likes

I think I didn’t explain myself clearly. The ICP Ledger will always be compatible with AccountId. Changing that would break everything. Accounts and the ICRC-1 interface are in addition to the existing ICP interface. A new format will be added like it happens in many other networks where you have multiple type of addresses. Nothing will break, I promise :slight_smile: .

I can see the equivalence with BIP32 and similar formats. Anonymity on the other hand is not a valid arguments if we publish the subnets blocks, which has been required by the community. Specifically it would be always possible to link the “from” of a transfer with its principal.

For the user though there is little difference between the representation of AccountId and of Account proposed above. Both are strings you can use from a wallet.

I think it’s easier to understand because it makes the existence of multiple accounts way more transparent. Each principal has many accounts that are identified by a subaccount. Simple and clean.

I would suggest to switch to ICRC-1 Account as soon as the standard is in place. I think it’s better than AccountId because you can extract Principal and Subaccount from it and it’s also battle tested.

4 Likes

@PaulLiu @bitbruce That ship has sailed. This thread is not about the standard, but how we merge both pieces into one on a frontend level. The exact text users will see and nothing else.
How to convert the (Principal 29bytes and Subaccount 32bytes) into text so users can copy-paste it between apps.
If we don’t agree on one format, every dapp will use its own and users won’t be able to copy addresses from one site and paste them into a transfer dialog on another, even if they both use the same standard.

Here is the thread related to the standard ICRC-1 Token Standard final draft - #36 by timo

1 Like

If you use AccountId, this thread will not appear.
This is artificially creating trouble.

A reality that has to be acknowledged.
The IC ecology is now a bit far from the crypto ecology, and there are many disconnects. It is the users of the crypto ecosystem and Cex who are now the mainstream user base, and the habits they have formed over a decade of experience are not easy to change. If account IDs are incomprehensible to them, many will be turned off and exchanges will find it difficult to list IC-related assets.

I hope you will do more to understand the habits of crypto users, especially those of the ETH ecosystem.

I understand the concerns, believe me. The Foundation has discussed about this with our users and gathered a ton of feedback from the forum and other channels. The ICRC-1 Account is the result of that feedback and getting together with the community to agree on a standard. ICRC-1 is the first standard on the IC that is a joint effort between the Foundation and many entities on the IC.

AccountId is not incomprehensible. It’s just not transparent because it’s the hash of Principal+Subaccount and many users complained about that. I think it’s fair but the point usually is that we should not hash the pair, not that the two components of AccountId are incomprehensible.

There is room for improvements and we will do our best to listen and understand the habits of our users. I’m sorry that we don’t meet your expectations and I assure you we will try to improve. We are always happy to get feedback from our users. Consider that many changes have been done because of inputs from the community. The existence of the working groups themselves are the result of feedback from the users. Same for the ICRC-1 standards.
I also want to mention that we actively look for feedback in the working group. Next working group session, for instance, will also have a retrospective on how to improve the process.
Feel free to provide any feedback in the working group, here in the forum or even privately if you prefer.

I took the following address screenshots from IC, ETH, Cosmos, and Solana (at the end of the post).

  1. Almost never a full address is displayed
  2. It always starts with the first 4+ characters and ends with 4+ characters

For crypto/anti-hacking purposes, it doesn’t matter which characters the user is checking, but it’s probably easier to read the first 4 and last 4 than reading 8 from the beginning or 8 from the end.

So we can’t shorten it to ic:rc1:....0xa (because there are no pub key chars in that, anyone can make such an address) So it’s best if we use the first few public key chars, then the last few pub key chars and then some coming from the subaccount. But then the problem is, that the full address won’t look anything like the short address, which will confuse users.
The short version has to be something like PPPPPPP...SSSS
Where P is Principal char and S is a subaccount char. P-s are providing security while S is not in most cases.
or PPPPPP...PPPP if the Subaccount is null.
But what happens if a canister is for example making permissionless donation campaigns and giving each user a different Subaccount, but from one Principal - the canister? Then P won’t be bringing a lot of security, anyone can create an account there. Only S will bring security and the difference can be one character. Probably nobody really types these by hand, but if you make a mistake, there will be no crc32 to error and you could be sending tokens to a bad address.
So we could do something like PPPPP…SSCC Where C is crc32
But then you won’t be able to clearly see your subaccount.

Another problem is Principals right now look like serial keys.
I guess I am more inclined to use base58 (which has more security inside the ~10 chars we show)
AnwN...UCaY
Which won’t look too bad with a condensed font.
image
image

Let’s see how to location idea will work with this. You see the short version, hit copy and you get mywallet.org/AnwNBG7a4rVJizGXQWTSQc69pFCUTCQJTC951zbLUCaY
or mywallet.org/ic:rc1:AnwNBG7a4rVJizGXQWTSQc69pFCUTCQJTC951zbLUCaY
(Not sure if the location idea is good like that. Maybe it should be a nat64 → location registry and always be inside the address, something like what cosmos is doing)
Food for thought

Other:

image


image
image
image

image
image
image





image
image

5 Likes

Great post, thank you @infu ! Should we consider bech32 (BIP0173) instead of base58? I think the motivations given to use bech32 instead of base58 are very interesting and we are in the position where we can pick the better option.

Of course, the working group can have its own decision. I just wish ICRC1 was better.

My preference would be for the Account to be a 32-byte hash value with a check digit, which is the format that users are used to.
There are a lot of things that can happen when users copy addresses, and they can easily lose assets. For example, if the address has a “.” etc., by double-clicking to select them, some of the characters may be lost.

2 Likes

This issue can be avoided by encoding Account with base58 or bech32. It’s a problem only with plain strings. Would you find using base58 or bech32 a good solution @bitbruce ?

I agree. Encoding can improve the user experience.

As mentioned above, ICP will be compatible with Account-id. In a dapp, for example a wallet, three address formats will appear in the UI for better compatibility: Principal, AccountId (It is being used by CEX), ICRC1Address.

The question is, is this an acceptable outcome?

History is a burden. Don’t change it so easily unless you have no choice but to do so.

1 Like

One of the benefits of base32 from that link is The mixed case in base58 makes it inconvenient to reliably write down, type on mobile keyboards, or read out loud.
So it’s basically removing the uppercase characters. This will also make addresses look better in UIs

Bech32 consists of (custom text) 1 (data) - where 1 is the separator and the custom text can be anything. For example: “bc”- main net, “tb”- test net, “avax”. Cosmos addresses are Bech32 too and each network has a different prefix.

In our case, we have one network, but the wallet is different. Maybe not a good idea to put the location there as I originally thought.

We could always let users copy a special unrelated to the address pairing code to connect both frontends or just enter the domain without the address. Then one of the sites opens the other in a window or Iframe and uses postMessage for secure communication (The same one used in extension wallets).

We end up needing an open protocol for site-to-site (you can also say wallet-to-wallet) communication. Right now every wallet has a different protocol and they ask you to insert their js library to interface with their end. What we can do next is IC site-to-site frontend protocol so it’s standardized and permissionless.

At some point when users acquire soulbound tokens like certificates, fungible scores, awards, badges, and proof of humanity. Then if site X won’t integrate wallet Y directly at signup, there will be a need for communication so that the user can sign/make non-transferring transactions with the soulbound tokens to prove to site X they own them.

Edit: It’s actually a very common request right now “I have collected 1000 Plug addresses for my airdrop” I am getting and my dapps will only ever use Internet Identity or my own identity. So I need the user to prove they are the Plug address owner, while linking it securely to their new address. Anyway, that’s for another thread