Migrating from ICRC1 to ICRC3

Hi Everyone,

I have some source code for an ICRC1 token.

I am trying to integrate the ng index canister with my token that has been live for quite some time.

I have taken a look at the source code for the ng index cansiter and i “think” the only method i need to add is the icrc3_get_blocks method.

I have taken a look at doing a migration using this icrc3-mo mops package. and it looks a lot more complex than what i need.

I have taken a look at the source code for this mops package and have noticed a block looks like this:
blocks : [{ id : Nat; block : Value }];

public type Value = {
#Int : Int;
#Map : [(Text, Value)];
#Nat : Nat;
#Blob : Blob;
#Text : Text;
#Array : [Value];
};

I have no idea what Value is / where it comes from. Could anyone possibly explain?

Also if anyone has motoko source code or an easier way of adding this get_blocks method that would be great. Or just any easy ways of integrating with the ng index canister

Also do i need to upgrade to icrc2 before upgrading to icrc3

A Value is similar to a Json object. Have a look at different ledgers’ icrc3_get_blocks outputs. You’ll see pretty quickly how a Value looks like.

ICRC-2 support is optional. ICRC-2 requires ICRC-1 to be supported, but no such requirement exists for ICRC-3

1 Like

Do you know if there any examples on how to transform data from icrc1 transactions to icrc3 blocks?

ideally an example in motoko

I think i can work out most of it but im not sure what these are::

  record { "fee_col_block"; variant { Nat = 39_493 : nat } };
            record {
              "phash";
              variant {
                Blob = blob "\49\6e\fe\83\e1\cc\ed\84\08\20\be\88\3c\b3\c0\2f\4d\14\46\fc\64\4d\30\0d\f4\95\34\ad\70\c2\63\fb"
              };
            };

If you already have the old get_blocks set you you may want to use that indexing canister. There are two versions of the indexing canister. One for ‘old style’ SNS tokens and one for ‘ICRC3’.

If you need to add icrc3 in motoko you can just setup and include the icrc3-mo library and copy the add_records sections from icrc1/2 libraries if you don’t want to replace your icrc1 code.

ICRC3 : Mops • Motoko Package Manager
Example implementation: icrc3.mo/example/main.mo at df223cdd9b3802e7504299edca166c72e6b2bc47 · PanIndustrial-Org/icrc3.mo · GitHub

Code to build the records from icrc1-mo:

Build the tx and top part of the value: icrc1.mo/src/ICRC1/lib.mo at a5ea2faf15faebe760e2fb9727e0b52f91d0088d · PanIndustrial-Org/icrc1.mo · GitHub

add to the ledger:

and

In this case you pass your icrc3 object into the icrc1-mo object…you likely won’t need to be that fancy and can just call the icrc3.add_record(tx, ?top) function (tx and top are values…the tx gets inserted into the top as a “tx”, #Map(x) entry…they are separated out for deduping.

1 Like

Hi i’m not sure this solution works for me as I am not using the icrc1-mo package from mops my token/ledger data is stored in a StableBuffer.

Also this would have no affect on my archive canister right?

hence having the callback: callback : shared query GetBlocksArgs → async GetBlocksResult;

Isn’t possible if my current callback on my archive canister is get_transactions

If you are trying to write ICRC3 logs then you can use the icrc3-mo package without needing icrc1-mo. If you’re just looking at converting existing blocks into icrc3 blocks, the the icrc1-mo should have some examples that directly do that when the transactions are recorded…you can just pull that code out and it should get you most of the way there.

I’m trying to remember the exact format of the get_transactions, but I think that is what @tomijaga used for his format in the original icrc1 tokens…I used that as a patter when writing these, so they should line up nicely.

Specifically this part:

/// `transfer_req_to_value`
    ///
    /// Converts a transaction request into a `Value` type that can be processed by an ICRC-3 transaction log.
    ///
    /// Parameters:
    /// - `request`: The transaction request to convert.
    ///
    /// Returns:
    /// - `Value`: The transaction request converted to a `Value` type suitable for logs.
    public func transfer_req_to_value(request: TransactionRequest) : Value {
      let trx = Vec.new<(Text, Value)>();

      Vec.add(trx, ("amt",#Nat(request.amount)));

      switch(request.kind){
        case(#mint) {
          Vec.add(trx, ("op",#Text("mint")));
          Vec.add(trx, ("to", Utils.accountToValue(request.to)));
        };
        case(#burn){
          Vec.add(trx, ("op",#Text("burn")));
          Vec.add(trx, ("from", Utils.accountToValue(request.from)));
        };
        case(#transfer){
          Vec.add(trx, ("op",#Text("xfer")));
          Vec.add(trx, ("to", Utils.accountToValue(request.to)));
          Vec.add(trx, ("from", Utils.accountToValue(request.from)));
        };
      };

      switch(request.fee){
        case(null){
        };
        case(?val){
          Vec.add(trx, ("fee", #Nat(val)));
        };
      };

      switch(request.created_at_time){
        case(null){
        };
        case(?val){
          Vec.add(trx, ("ts", #Nat(Nat64.toNat(val))));
        };
      };

      switch(request.memo){
        case(null){
        };
        case(?val){
          Vec.add(trx, ("memo", #Blob(val)));
        };
      };

      let vTrx = #Map(Vec.toArray(trx));

      return vTrx
    };

Okay that sounds a bit more like what I am looking for. I am trying to convert an array of Transactions:

    public type Transaction = {
        kind : Text;
        mint : ?Mint;
        burn : ?Burn;
        transfer : ?Transfer;
        index : TxIndex;
        timestamp : Timestamp;
    };

Into something that can be used in a GetBlocksResult

  public type GetBlocksResult = {
    log_length : Nat;
    blocks : [{ id : Nat; block : Value }];
    archived_blocks : [
      {
        args : GetBlocksArgs;
        callback : shared query GetBlocksArgs -> async GetBlocksResult;
      }
    ];
  };

Are you aware of anything closer to this? or should i just try editing the code you provided

It shouldn’t take much…just unwarp that transfer : ?Transfer to get to the to/from/amount/etc.