Digital Asset Standards
ICP implements digital assets using the ICRC standard family. The specifications use the term “token” throughout: a fungible digital asset is a “fungible token” in ICRC-1, and an NFT is a “non-fungible token” in ICRC-7. This page covers the digital asset standards (ICRC-1 through ICRC-37). For the full list of ICRC standards including wallet signer protocols, see ICRC Standards.
Standards overview
Section titled “Standards overview”| Standard | Purpose | Extends | Status |
|---|---|---|---|
| ICRC-1 | Fungible token base standard | none | Adopted |
| ICRC-2 | Approve and transfer-from for fungible tokens | ICRC-1 | Adopted |
| ICRC-3 | Transaction log and block archive | ICRC-1 | Adopted |
| ICRC-7 | Non-fungible token (NFT) base standard | none | Adopted |
| ICRC-37 | Approve and transfer-from for NFTs | ICRC-7 | Adopted |
ICRC-1: Fungible tokens
Section titled “ICRC-1: Fungible tokens”ICRC-1 is the base standard for fungible tokens on ICP. It defines transfer, balance, and metadata interfaces. The standard intentionally excludes certain features (transaction notifications, block structure, and pre-signed transactions) which are provided by extension standards (ICRC-2, ICRC-3).
A ledger can report which extensions it supports through the icrc1_supported_standards endpoint.
Account model
Section titled “Account model”An ICRC-1 account consists of two parts:
owner: aPrincipalidentifying the account holdersubaccount: an optional 32-byteBlobthat defaults to all zeros when omitted
This means a single principal can control up to 2^256 distinct accounts by varying the subaccount.
type Account = record { owner : principal; subaccount : opt blob;};Core methods
Section titled “Core methods”| Method | Signature | Description |
|---|---|---|
icrc1_transfer | (TransferArg) -> (variant { Ok : nat; Err : TransferError }) | Transfer tokens between accounts |
icrc1_balance_of | (Account) -> (nat) query | Return the balance of an account |
icrc1_total_supply | () -> (nat) query | Return the total token supply |
icrc1_metadata | () -> (vec record { text; Value }) query | Return token metadata entries |
icrc1_name | () -> (text) query | Return the token name |
icrc1_symbol | () -> (text) query | Return the token symbol |
icrc1_decimals | () -> (nat8) query | Return the number of decimals |
icrc1_fee | () -> (nat) query | Return the default transfer fee |
icrc1_minting_account | () -> (opt Account) query | Return the minting account |
icrc1_supported_standards | () -> (vec record { name : text; url : text }) query | Return supported standard extensions |
Transfer arguments
Section titled “Transfer arguments”type TransferArg = record { from_subaccount : opt blob; to : Account; amount : nat; fee : opt nat; memo : opt blob; created_at_time : opt nat64;};Setting created_at_time enables deduplication. The ledger rejects duplicate transfers submitted within a 24-hour window. Without it, identical transfers both execute.
Transfer errors
Section titled “Transfer errors”type TransferError = variant { BadFee : record { expected_fee : nat }; BadBurn : record { min_burn_amount : nat }; InsufficientFunds : record { balance : nat }; TooOld; CreatedInFuture : record { ledger_time : nat64 }; Duplicate : record { duplicate_of : nat }; TemporarilyUnavailable; GenericError : record { error_code : nat; message : text };};Metadata entries
Section titled “Metadata entries”| Key | Type | Example |
|---|---|---|
icrc1:symbol | Text | "ICP" |
icrc1:name | Text | "Internet Computer" |
icrc1:decimals | Nat | 8 |
icrc1:fee | Nat | 10000 |
For a few well-known ledger canister IDs and index canisters, see Ledgers. For a broader overview of tokens on ICP, see the ICP Dashboard token list.
ICRC-2: Approve and transfer-from
Section titled “ICRC-2: Approve and transfer-from”ICRC-2 extends ICRC-1 with an approve/transfer-from workflow, similar to ERC-20 allowances on Ethereum. An account owner delegates spending authority to a third party, who can then transfer tokens on the owner’s behalf.
The workflow has two steps:
- The account owner calls
icrc2_approveto authorize a spender for up to X tokens. - The spender calls
icrc2_transfer_fromto move tokens from the owner’s account. Multiple transfers are allowed as long as the total does not exceed the approved amount.
Methods
Section titled “Methods”| Method | Signature | Description |
|---|---|---|
icrc2_approve | (ApproveArg) -> (variant { Ok : nat; Err : ApproveError }) | Authorize a spender for a token amount |
icrc2_transfer_from | (TransferFromArg) -> (variant { Ok : nat; Err : TransferFromError }) | Transfer tokens on behalf of the owner |
icrc2_allowance | (AllowanceArg) -> (Allowance) query | Query the remaining allowance for a spender |
Approve arguments
Section titled “Approve arguments”type ApproveArg = record { from_subaccount : opt blob; spender : Account; amount : nat; expected_allowance : opt nat; expires_at : opt nat64; fee : opt nat; memo : opt blob; created_at_time : opt nat64;};The expected_allowance field provides protection against race conditions. The call fails if the current allowance does not match the expected value. The expires_at field sets a deadline (in nanoseconds since the Unix epoch) after which the approval is no longer valid.
Approve errors
Section titled “Approve errors”type ApproveError = variant { BadFee : record { expected_fee : nat }; InsufficientFunds : record { balance : nat }; AllowanceChanged : record { current_allowance : nat }; Expired : record { ledger_time : nat64 }; TooOld; CreatedInFuture : record { ledger_time : nat64 }; Duplicate : record { duplicate_of : nat }; TemporarilyUnavailable; GenericError : record { error_code : nat; message : text };};Transfer-from arguments
Section titled “Transfer-from arguments”type TransferFromArg = record { spender_subaccount : opt blob; from : Account; to : Account; amount : nat; fee : opt nat; memo : opt blob; created_at_time : opt nat64;};Transfer-from errors
Section titled “Transfer-from errors”type TransferFromError = variant { BadFee : record { expected_fee : nat }; BadBurn : record { min_burn_amount : nat }; InsufficientFunds : record { balance : nat }; InsufficientAllowance : record { allowance : nat }; TooOld; CreatedInFuture : record { ledger_time : nat64 }; Duplicate : record { duplicate_of : nat }; TemporarilyUnavailable; GenericError : record { error_code : nat; message : text };};Allowance query
Section titled “Allowance query”type AllowanceArg = record { account : Account; spender : Account;};
type Allowance = record { allowance : nat; expires_at : opt nat64;};Common use cases
Section titled “Common use cases”- DEX integrations: a DEX canister is approved to pull tokens from a user’s account during a swap.
- Subscription payments: a service canister is approved for recurring token withdrawals.
- Escrow: an intermediary canister holds approval to release tokens when conditions are met.
ICP, ckBTC, and ckETH all implement ICRC-2.
ICRC-3: Transaction log
Section titled “ICRC-3: Transaction log”ICRC-3 extends ICRC-1 with a standardized transaction log interface. It defines how ledgers expose their block history, enabling clients and index canisters to retrieve and verify transaction records.
Archive model
Section titled “Archive model”Ledgers store recent blocks directly and move older blocks to archive canisters to manage memory. When fetching blocks, icrc3_get_blocks returns blocks the ledger holds directly plus callbacks to fetch archived blocks from the appropriate archive canister. Use icrc3_get_archives to discover all archive canisters and the block ranges they hold.
Methods
Section titled “Methods”| Method | Signature | Description |
|---|---|---|
icrc3_get_blocks | (vec record { start : nat; length : nat }) -> (GetBlocksResult) query | Retrieve blocks by index range; returns callbacks for archived blocks |
icrc3_get_archives | (GetArchivesArgs) -> (vec record { canister_id; start; end }) query | List archive canisters and the block ranges they hold |
icrc3_get_tip_certificate | () -> (opt DataCertificate) query | Return a certificate for the last block hash and index |
icrc3_supported_block_types | () -> (vec record { block_type : text; url : text }) query | List the block types the ledger produces |
Block schema
Section titled “Block schema”ICRC-3 blocks use a generic Value representation that preserves all data for verification. Each block contains:
phash: hash of the previous block (absent for the genesis block)btype: block type string (e.g.,"1xfer"for ICRC-1 transfers,"2approve"for ICRC-2 approvals)ts: timestamp in nanoseconds- Transaction-specific fields: vary by block type (e.g.,
from,to,amtfor transfers)
Adopted block types
Section titled “Adopted block types”Block type identifiers follow the naming convention <icrc-number><operation> (e.g., 1xfer for ICRC-1 transfer). Anyone can define new block types for custom standards following this convention.
| Block type | Standard | Description |
|---|---|---|
1xfer | ICRC-1 | Transfer |
1burn | ICRC-1 | Burn |
1mint | ICRC-1 | Mint |
2approve | ICRC-2 | Approval |
2xfer | ICRC-2 | Transfer-from |
Proposed block types
Section titled “Proposed block types”The following block types are currently in the ICRC proposal process and not yet adopted:
| Block type(s) | Proposal | Status |
|---|---|---|
| ICRC-122 | Ledger notification blocks | Proposed |
| ICRC-123 | Batch call blocks | Proposed |
| ICRC-124 | Canister management blocks | Proposed |
| ICRC-152 | RWA compliance blocks | Proposed |
| ICRC-153 | RWA regulatory blocks | Proposed |
| ICRC-154 | RWA lifecycle blocks | Proposed |
ICRC-7: Non-fungible tokens
Section titled “ICRC-7: Non-fungible tokens”ICRC-7 defines the base standard for non-fungible tokens (NFTs) on ICP. It can be used to create and manage NFT collections. Like ICRC-1 for fungible tokens, ICRC-7 is intentionally minimal and excludes transaction notifications, block structure, and pre-signed transactions: these can be added through extensions.
ICRC-7 uses the same account model as ICRC-1 (principal + optional 32-byte subaccount).
Core methods
Section titled “Core methods”| Method | Signature | Description |
|---|---|---|
icrc7_collection_metadata | () -> (vec record { text; Value }) query | Return collection metadata |
icrc7_name | () -> (text) query | Return collection name |
icrc7_symbol | () -> (text) query | Return collection symbol |
icrc7_total_supply | () -> (nat) query | Return total number of tokens |
icrc7_supply_cap | () -> (opt nat) query | Return maximum supply (if set) |
icrc7_token_metadata | (vec nat) -> (vec opt vec record { text; Value }) query | Return metadata for specific token IDs |
icrc7_owner_of | (vec nat) -> (vec opt Account) query | Return the owner of specific token IDs |
icrc7_balance_of | (vec Account) -> (vec nat) query | Return the number of tokens owned by each account |
icrc7_tokens | (opt nat, opt nat) -> (vec nat) query | List token IDs with pagination |
icrc7_tokens_of | (Account, opt nat, opt nat) -> (vec nat) query | List token IDs owned by an account |
icrc7_transfer | (vec TransferArg) -> (vec opt TransferResult) | Batch transfer tokens |
Collection metadata
Section titled “Collection metadata”| Key | Type | Description |
|---|---|---|
icrc7:symbol | Text | Token symbol |
icrc7:name | Text | Token name |
icrc7:description | Text | Collection description |
icrc7:logo | Text | URL of the collection logo |
icrc7:total_supply | Nat | Current number of tokens |
icrc7:supply_cap | Nat | Maximum number of tokens (optional) |
ICRC-37: NFT approvals
Section titled “ICRC-37: NFT approvals”ICRC-37 extends ICRC-7 with an approval workflow for NFTs, analogous to how ICRC-2 extends ICRC-1 for fungible tokens. It adds support for creating approvals, revoking approvals, querying approval state, and executing transfers based on approvals.
A ledger that implements ICRC-37 must also implement all ICRC-7 methods. Support for ICRC-37 is optional for ICRC-7 ledgers.
Methods
Section titled “Methods”| Method | Signature | Description |
|---|---|---|
icrc37_approve_tokens | (vec ApproveTokenArg) -> (vec opt ApproveTokenResult) | Approve a spender for specific token IDs |
icrc37_approve_collection | (vec ApproveCollectionArg) -> (vec opt ApproveCollectionResult) | Approve a spender for all tokens in the collection |
icrc37_revoke_token_approvals | (vec RevokeTokenApprovalArg) -> (vec opt RevokeTokenApprovalResult) | Revoke approvals for specific tokens |
icrc37_revoke_collection_approvals | (vec RevokeCollectionApprovalArg) -> (vec opt RevokeCollectionApprovalResult) | Revoke collection-level approvals |
icrc37_is_approved | (vec IsApprovedArg) -> (vec bool) query | Check if a spender is approved for specific tokens |
icrc37_get_token_approvals | (nat, opt nat, opt nat) -> (vec TokenApproval) query | List approvals for a token ID |
icrc37_get_collection_approvals | (Account, opt nat, opt nat) -> (vec CollectionApproval) query | List collection-level approvals |
icrc37_transfer_from | (vec TransferFromArg) -> (vec opt TransferFromResult) | Transfer tokens using an approval |
Additional metadata
Section titled “Additional metadata”| Key | Type | Description |
|---|---|---|
icrc37:max_approvals_per_token_or_collection | Nat | Maximum active approvals allowed per principal or token |
icrc37:max_revoke_approvals | Nat | Maximum approvals that can be revoked in one call (optional) |
Read the full ICRC-37 standard
Next steps
Section titled “Next steps”- ICRC Standards: full index of all ICRC standards including wallet signer protocols
- Ledgers guide: deploy and interact with ICRC-1/ICRC-2 ledger canisters
- Chain-key tokens guide: work with ckBTC, ckETH, and other chain-key tokens
- Wallet integration guide: implement wallet signer standards (ICRC-21/25/27/29/49) in your app
- ICRC-1 standard specification: full specification on GitHub
- ICRC-7 standard specification: full NFT specification on GitHub