ICP Index canister created_at_time timestamp

In the new ICP index canister, what does the created_at_time timestamp represent?

type Transaction = record {
  memo : nat64;
  icrc1_memo : opt vec nat8;
  operation : Operation;
  created_at_time : opt TimeStamp; // <------ ????

Is that the effective timestamp of a transaction - e.g. when was a transfer executed on the ledger - or the timestamp of when was the transaction collected in the Index canister?

If the second, where do I find the timestamp of a transaction of type Transfer? There is no such field in the struct.

Transfer : record { to : text; fee : Tokens; from : text; amount : Tokens };

Last but not, least, why is this created_at_time optional?

created_at_time represents the time when the Transaction was created. The timestamp ts in the Block instead represents when the Block containing the Transaction was added to the Ledger.

created_at_time is set by the submitter. The timestamp ts is set by Ledger itself.

An example can help understanding the difference. Let’s say that you want to send some tokens to a friend. You create a transaction and set the created_at_time to now. Then you try to submit the transaction but it fails because e.g. you cannot reach the IC. While you are retrying, your transaction doesn’t change including the created_at_time. Eventually you manage to send the transaction to the Ledger which inserts it in a Block and then records the Block. If you query that Block you’ll see that its timestamp is way more recent than the created_at_time.

created_at_time serves also an important role as it flags the transaction for de-duplication when it is set. The Ledger will check if another transaction with the same structure, created_at_time included, has been seen before (up to a certain point). This is very useful for applications outside the IC trying to make transaction.

The reason why the field is optional is because of transaction sent by canisters. Canister don’t have the duplication issue because of the guarantees given by the IC.

The rule is the following:

  • if you are sending a transaction from outside the IC then you MUST set the created_at_time and use it for deduplication
  • if you are sending a transaction from a canister on the IC then you don’t have to set the created_at_time

Thanks for the explanation @mariop, it’s much more clear. If the timestamp ts represent the timestamp of the block, why is it not exposed by the ICP index canister? Or at least not exposed in the did file?

I mean when calling the get_account_identifier_transactions.

This thread is kind of going into the direction im seeking the answer for from this question NNS Update: August 28, 2023 - #4 by RMCS.

The main thing is that I want to be able to show the transaction timestamp within the UI without needing to fetch seperate blocks for each transaction, like it is possible with ICRC1 transactions.

I assumed this is what the created_at_time was for

The reason is that we are not exposing Blocks per account yet in the Index canister. For instance, another field that is not available is the parent hash (phash). We will add new endpoints that return the Blocks themselves and those will contain everything needed.

Blocks require a special encoding in order to be retrieved and I want to be sure that the encoding is correct before we add the endpoint. I’ve been working on this in the past days. Stay tuned.