Announcing ICRC-1 support in the ICP Ledger and a javascript library for ICRC Ledgers

Hi community,

I’m happy to announce that the ICP Ledger has been upgraded to support the ICRC-1 Fungible Token Standard! The upgrade went live today. You can use the ICP Ledger canister UI to see the new methods.

This is a major step forward for the ecosystem because it means that developers can interface with the ICP Ledger, all the SNSes Ledgers, the ckBTC Ledger and many more Ledgers using a single standard interface defined by the Ledger&Tokenization Working Group. Next we will work on the implementation of the ICRC-2 standard for all our Ledgers.

At the same time I’m happy to announce that our frontend team has published the first version of a javascript library that supports ICRC Ledgers. You can find it on npm as @dfinity/ledger.

Let me use this opportunity to thank every contributor to the Ledger&Tokenization Working Group and all the people that contributed in the discussion on the token standard.



Does this mean that a block explorer can now track transactions? Its a key point for DEFI kick-off on ICP so please promote it. BTW tokens released on a ICRC1 standard will me automaticaly upgraded to ICRC2 once released. I mean… I don’t see thepoint on releasing a token on ICRC1 standard if it is going to get deprecated once ICRC2 is released.
You should guys communicate better on this matter.

ICRC-2 is an extension of ICRC-1 (source). No need to deprecate the ICRC-1 version.


I think he means applications (eg DEXs) might be built on the assumption that ICRC-2 is implemented, not supporting ICRC-1-only tokens for practical reasons. Hence a non-upgradable ICRC-1 token launched today could be excluded from the ecosystem in practice.

A bit of a basic question but does this (only) mean that the protocols are now compatible, or that ICRC-1 token canisters’ transaction history will automatically or with some configurable setting be stored by the ICP Ledger without the need for ad-hoc implementation of archives and transaction storage systems and resources to sustain them on a token-by-token basis?

Either way, great feature.

Very cool! :exploding_head: Thanks for the update Mario

1 Like

Block explorers can track ICP transactions but this was true since before ICRC. The ICP Ledger always exposed transactions so that you can verify what’s going on. You can check our dashboard for example. DFINITY provides an application called rosetta node which creates a database with all the transactions in the ICP Ledger and can be used to provide something like a block explorer.

ICRC-2 is an extension of ICRC-1. In other words, ICRC-1 is a requirement to implement ICRC-2. ICRC-1 won’t be deprecated. For instance, icrc1_metadata, icrc1_balance_of and icrc1_supported_standard are all relevant regardless of which transfer primitives one decides to use.

The support for ICRC-1 in the ICP Ledger is important because it means that developers don’t have to write custom code for the ICP Ledger anymore :slight_smile:. If your app works against ICRC Ledgers then it will work for the ICP Ledger, all the SNS Ledgers, the ckBTC Ledger… It’s a major simplification and it’s an important one because ICP is an important token to support.


All out tokens can be upgraded and we will do our best to support all the core extensions that the working group deems necessary.

I’m not sure I fully understand the question. I can try to answer but feel free to elaborate if you think my answer is not correct or satisfying.

ICRC-1 is focused on ledger primitives and does not concerns transaction history. The ICP Ledger transaction history is not affected by this change and will continue to work as before.

Yes, all good at the standards level. What I think he/she meant is at the individual token level, meaning, if today a community project deploys a token that only adheres to the ICRC-1 standard and not to the ICRC-2 or other standards, and you make that deployed canister unapgradable for security or other reasons (many defi tokens in crypto have “ownership renounced” or are “non-upgradable” for security reasons), then that token would run into problems down the line if it turns out that for practical reasons, the defi ecosystem in the IC does requires ICRC-2 endpoints in their apps (eg because they don’t want to make a DEX without a icrc2_approve() function because it just turns too complicated or insecure to develop internal DEX logic without it).

In that sense, and I don’t know but that’s how I understood the above comment by gatsby, people could be in trouble if they deploy an (only) ICRC-1, (non-ICRC-2, 3, 4) token today.

Thanks. I probably have too may category confusions around it because I’ve not looked into ICP Ledger at all, or into transaction archives: I only have some experience with ICRCs and more precisely this Motoko implementation: GitHub - NatLabs/icrc1: A full implementation of the ICRC-1 fungible token standard

To rephrase the question in a less technical way: today, if I deploy an ICRC-1 token with eg this example code, icrc1/ at main · NatLabs/icrc1 · GitHub

Do I as a deployer / maintainer / holder of such a token need to worry about creating archival logic, spinning out archive canisters to store transactions, feed them cycles, etc, or is all that handles automatically / only requires feeding the base token canister cycles?

It would seem from lines like these icrc1/ at main · NatLabs/icrc1 · GitHub

that that’s all taken care of (except keeping canisters’s cycles topped up?)?

And in what way does ICP Ledger come into the picture from the perspective of an ICRC-token deployer / maintainer / user? Many dots to join for me still.

Reading my own reply I see it pretty much turned into a question about that other repo, so if you’re familiar with it perhaps just reduce it to the last question.

1 Like

when icrc2 methods? TransferFrom is pretty important for defi

1 Like

That’s going to be the next big upgrade of the Ledgers. We are working on it right now.

1 Like

I can speak only for the ICP Ledger and ICRC-1 Ledger. Both Ledgers automatically create archive nodes but leave the cycles management to you. I suggest to have a look at services that can top up your canisters automatically or write a simple script that does it for you.

The ICP Ledger doesn’t really affect ICRC tokens maintainers. The new upgrade of the ICP Ledger mainly affects developers of services that need to integrate with Ledgers because now they can integrate the ICP Ledger using the same code they use to integrate with the ICRC Ledgers.

1 Like

Thanks, I get it now.


@roman-kashitsyn This file still says that subaccount is a blob: ICRC-1/ at main · dfinity/ICRC-1 · GitHub

But the ledger did file is [Nat8](at least the candid on ICScan is

Just checking to make sure I’m not going crazy and that it is in fact, [nat8].

In candid blob is merely a shorthand for vec nat8.

I thought this was the case for candid…It can make programming motoko maddening though when .did file swap them out randomly.

Since the did file reports Nat8 for the ICP ledger, shouldn’t we update the icrc1 docs to match?

As a random aside, did we ever get a canonical toText motoko function that the working group agreed on?

There shouldn’t be any problem because blob and vec nat8 are equivalent. You can write either in the did file. It doesn’t effect the Motoko side. Motoko can deserialize either to [Nat8] or Blob.

But maybe I don’t understand what you mean because you wrote Nat8 not nat8. In a did file there can only be the latter.

Sorry…I was just implying that if in motoko you have account as { owner : Principal; subaccount : ?[Nat8]} and, say your to text function takes { owner : Principal; subaccount : ?Bob} the cast is annoying.

I wrote a helper function:

public func fromBytesAccount(item: BytesAccount) : Account{
      item with
      subaccount = switch(item.subaccount){
        case(null) null;
        case(?val) ?Blob.fromArray(val);
  }; another module....

return {
        principal = host;
        account_id_text = Hex.encode(to);
        account_id = Blob.fromArray(to);
        icrc1_text = ICRC_Text.toText(
        account = account