-
Notifications
You must be signed in to change notification settings - Fork 37
ICRC-106: standard for discovering the location of the index canister from the ledger canister #196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,242 @@ | ||
| | Status | | ||
| |:------:| | ||
| |Draft| | ||
|
|
||
| # ICRC-106: Standard for Associating Index Canisters with ICRC-1 Tokens | ||
|
|
||
| ## 1. Introduction | ||
|
|
||
| Wallet applications and token management tools often need to retrieve both token metadata and transaction history for a given principal. Index canisters provide such data, but identifying an associated index canister for ICRC-1 tokens is currently not standardized, leading to inconsistencies in wallet integrations. | ||
|
|
||
| Standard **ICRC-106** : | ||
| 1. Introduces a standard approach for indicating the presence of an index canister for ICRC-1 tokens through ledger metadata. | ||
| 2. Defines a minimal interface for the associated index canister to facilitate querying transaction history in a consistent manner. | ||
|
|
||
| This draft standard aims to improve interoperability, simplify wallet integrations, and enable token-related applications to reliably access transaction histories. It acts as a placeholder and documentation source for the API of the index canister, until a comprehensive standard for index canisters will be developed. | ||
|
|
||
|
|
||
| ## 2. Metadata | ||
|
|
||
| A ledger implementing ICRC-106 MUST include the following entry in the output of the `icrc1_supported_standards` method: | ||
|
|
||
| ```candid | ||
| record { name = "ICRC-106"; url = "https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-106" } | ||
| ``` | ||
|
|
||
| Additionally, the ledger MUST provide the following metadata entry retrievable via the `icrc1_metadata` method: | ||
|
|
||
| - `icrc106:index_principal` (text): The textual representation of the principal of the associated index canister. | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| These metadata entries allow clients to discover and interact with the index canister associated with a ledger and can be retrieved using method `icrc1_metadata` defined by the ICRC-1 standard. | ||
|
|
||
|
|
||
| Compliant ledgers MUST also implement the following endpoint for programmatically retrieving the index principal: | ||
|
|
||
| ```candid | ||
|
|
||
| icrc106_get_index_principal: () -> (GetIndexPrincipalResult) query; | ||
|
|
||
| type GetIndexPrincipalResult = variant { | ||
| Ok : principal; | ||
| Err : GetIndexPrincipalError; | ||
| }; | ||
|
|
||
| type GetIndexPrincipalError = variant { | ||
| IndexPrincipalNotSet; | ||
|
|
||
| // Any error not covered by the above variants. | ||
| GenericError: record { | ||
| error_code: nat; | ||
| description: text; | ||
| }; | ||
| }; | ||
|
|
||
| ``` | ||
|
|
||
| The metadata entry `icrc106:index_principal` and the `icrc106_get_index_principal` method MUST provide consistent information. Specifically: | ||
|
|
||
| - The `icrc106:index_principal` metadata entry MUST represent the textual form of the principal returned by the `icrc106_get_index_principal` method. | ||
| - The `icrc106_get_index_principal` method MUST return the principal corresponding to the index canister associated with the ledger, as specified in the `icrc106:index_principal` metadata entry. | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - If the entry `icrc106:index_principal` does not exist or is not appropriately set, the endpoint should return `IndexPrincipalNotSet`. | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| This requirement ensures that both mechanisms reliably point to the same index canister. | ||
|
|
||
|
|
||
| ## 3. Index Canister Interface | ||
|
|
||
| The index canister associated with the ledger SHOULD implement the following minimal Candid interface: | ||
|
|
||
| ```candid | ||
| type Tokens = nat; | ||
|
|
||
| type BlockIndex = nat; | ||
|
|
||
| type SubAccount = blob; | ||
|
|
||
| type Account = record { | ||
| owner: principal; | ||
| subaccount: opt SubAccount; | ||
| }; | ||
|
|
||
| type GetAccountTransactionsArgs = record { | ||
| account: Account; | ||
| start: opt BlockIndex; // The block index of the last transaction seen by the client. | ||
| max_results: nat; // Maximum number of transactions to fetch. | ||
| }; | ||
|
|
||
| type Burn = record { | ||
| from : Account; | ||
| memo : opt blob; | ||
| created_at_time : opt nat64; | ||
| amount : Tokens; | ||
| spender : opt Account; | ||
| }; | ||
|
|
||
| type Mint = record { | ||
| to : Account; | ||
| memo : opt blob; | ||
| created_at_time : opt nat64; | ||
| amount : Tokens; | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| type Transfer = record { | ||
| to : Account; | ||
| fee : opt Tokens; | ||
| from : Account; | ||
| memo : opt blob; | ||
| created_at_time : opt nat64; | ||
| amount : Tokens; | ||
| spender : opt Account; | ||
| }; | ||
|
|
||
| type Approve = record { | ||
| fee : opt Tokens; | ||
| from : Account; | ||
| memo : opt blob; | ||
| created_at_time : opt nat64; | ||
| amount : Tokens; | ||
| expected_allowance : opt nat; | ||
| expires_at : opt nat64; | ||
| spender : Account; | ||
| }; | ||
|
|
||
| type Transaction = record { | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| burn : opt Burn; | ||
| kind : text; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the textual represenation of the variant (option) included? If so, a variant type would be more consistent. |
||
| mint : opt Mint; | ||
| approve : opt Approve; | ||
| timestamp : nat64; | ||
| transfer : opt Transfer; | ||
| }; | ||
|
|
||
| type TransactionWithId = record { | ||
| id : BlockIndex; | ||
| transaction : Transaction; | ||
| }; | ||
|
|
||
| type GetTransactions = record { | ||
| balance : Tokens; | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| transactions : vec TransactionWithId; | ||
| // The txid of the oldest transaction the account has | ||
| oldest_tx_id : opt BlockIndex; | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| type GetTransactionsErr = record { | ||
| message : text; | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| type GetAccountTransactionsResult = variant { | ||
| Ok : GetTransactions; | ||
| Err : GetTransactionsErr; | ||
| }; | ||
|
|
||
| type ListSubaccountsArgs = record { | ||
| owner: principal; | ||
| start: opt SubAccount; | ||
mraszyk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| type Status = record { | ||
| num_blocks_synced : BlockIndex; | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| service : { | ||
| get_account_transactions: (GetAccountTransactionsArgs) -> (GetAccountTransactionsResult) query; | ||
| ledger_id: () -> (principal) query; | ||
| list_subaccounts : (ListSubaccountsArgs) -> (vec SubAccount) query; | ||
| status : () -> (Status) query; | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| ``` | ||
|
|
||
|
|
||
|
|
||
| # Methods Provided by the Index Canister | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this section be placed before the candid interface? (So that the description comes first and the reader is not confused by hitting an undocumented candid declarations first.) |
||
|
|
||
| The index canister provides methods to facilitate querying of transaction history and metadata associated with accounts. Below is a description of the relevant methods specified in this standard, including their purpose, input, output, and typical use case. | ||
|
|
||
| --- | ||
|
|
||
| ## get_account_transactions | ||
| - **Purpose**: Retrieves transactions associated with a specific account, starting from a specified block index and returning up to a maximum number of results. Transactions are returned in **reverse chronological order** (newest to oldest). | ||
| - **Input**: | ||
| - `account`: The account (principal and optional subaccount) for which transactions are to be fetched. | ||
| - `start`: *(Optional)* The block index of the most recent transaction the client has already seen. If provided, only transactions with block indices **less than** this value will be returned. | ||
| - `max_results`: The maximum number of transactions to return. | ||
| - **Output**: | ||
| - **`Ok`**: Returns a record containing: | ||
| - `balance`: The current token balance of the account. | ||
| - `transactions`: A vector of `TransactionWithId`, each containing: | ||
| - `id`: The block index of the transaction. | ||
| - `transaction`: Details of the transaction (burn, transfer, approval, and timestamp). | ||
| - `oldest_tx_id`: *(Optional)* The block index of the oldest transaction for the account, or `None` if no transactions exist. | ||
| - **Typical Use Case**: This method is often used by wallets to display transaction history and update account balances. It also supports paginated transaction retrieval for efficient history browsing. | ||
|
|
||
| --- | ||
|
|
||
| ## list_subaccounts | ||
| - **Purpose**: Lists all subaccounts associated with a specified principal. | ||
| - **Input**: | ||
| - `owner`: The principal for which to list subaccounts. | ||
| - `start`: *(Optional)* Specifies the subaccount to start listing from. Only subaccounts lexicographically greater than `start` will be included. If start is omitted, the method will return all subaccounts from the beginning of the list, ordered lexicographically. | ||
| - **Output**: A vector of `SubAccount`, each representing a subaccount under the specified principal. The list will be empty if the principal has not used subaccounts, or if there are no subaccounts lexicographically higher than `start`. | ||
| - **Typical Use Case**: Useful for wallets or tools that need to enumerate *all* subaccounts associated with a user. To get all subaccounts, start with no start parameter and repeatedly call the method, updating start with the last subaccount from each response, until the returned list is empty. | ||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## ledger_id | ||
| - **Purpose**: Retrieves the principal of the ledger canister that the index is linked to. | ||
| - **Input**: None. | ||
| - **Output**: The `principal` of the ledger canister. | ||
| - **Typical Use Case**: This method is primarily used for validating the relationship between the index and the ledger, ensuring they are correctly linked, and facilitating integrations requiring the ledger’s identity. | ||
|
|
||
|
|
||
| --- | ||
|
|
||
| ## status | ||
| - **Purpose**: Retrieves the synchronization and operational status of the index canister. | ||
| - **Input**: None. | ||
| - **Output**: A `Status` record containing: | ||
| - `num_blocks_synced`: The total number of blocks that have been successfully synchronized by the index canister. | ||
| - **Typical Use Case**: Used for monitoring the health and synchronization status of the index, this method is helpful for determining whether the index has fully caught up with the ledger and is operational. | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| ## Optional Methods | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How useful is this section given that it doesn't really provide much details on the method listed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the point of view of documenting the existing index it says that "there's these other bunch of methods implemented by the index" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, but if the input/output types and their description is omitted, then how is a reader suppose to use this information?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They could look the at the .did file of the index canister. But I think this discussion points out to simply omitting this part and not even popularising those methods anyway |
||
|
|
||
| While the methods defined in this standard are sufficient for compliance with ICRC-106, certain implementations of the index canister may include additional methods to extend functionality. These methods are not required by ICRC-106 but may be present for advanced use cases: | ||
|
|
||
| - **`get_blocks`**: Fetches raw block data for a specified range of indices. This is useful for applications requiring detailed historical data. | ||
bogwar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| - **`get_fee_collectors_ranges`**: Provides detailed information about fee collection, including accounts and associated block ranges. | ||
| - **`icrc1_balance_of`**: Queries the token balance of specific accounts. This method is commonly used for token management in wallets and tools. | ||
|
|
||
| These methods, while potentially helpful, are outside the scope of ICRC-106 and are not guaranteed to be present in all index canisters. Developers should refer to the documentation of the specific implementation they are working with for details on these optional methods. | ||
|
|
||
|
|
||
|
|
||
|
|
||
| ## 4. Implementation Considerations | ||
|
|
||
| Implementers should ensure that: | ||
| - The `icrc106:index_principal` metadata entry accurately reflects the principal of the associated index canister. | ||
| - Any changes to the index canister interface should maintain backward compatibility. | ||
|
|
||
| By adhering to ICRC-106, ledger canisters provide a standardized mechanism for clients to discover and interact with their associated index canisters, improving integration and user experience within the Internet Computer ecosystem. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the link https://github.com/dfinity/ICRC/blob/main/ICRCs/ICRC-106 doesn't work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the location of where the standard once the PR is merged. In the meantime there was some reorganisation of the working gorups (and of repositories) so there will be a new location. I'll leave this open so that I remember to update the link accordingly.