Skip to content

Latest commit

 

History

History
234 lines (161 loc) · 9.7 KB

README.md

File metadata and controls

234 lines (161 loc) · 9.7 KB

ICRC-1 Token Standard

Status
Accepted

The ICRC-1 is a standard for Fungible Tokens on the Internet Computer.

Data

account

A principal can have multiple accounts. Each account of a principal is identified by a 32-byte string called subaccount. Therefore an account corresponds to a pair (principal, subaccount).

The account identified by the subaccount with all bytes set to 0 is the default account of the principal.

type Subaccount = blob;
type Account = record { owner : principal; subaccount : opt Subaccount; };

Methods

icrc1_name

Returns the name of the token (e.g., MyToken).

icrc1_name : () -> (text) query;

icrc1_symbol

Returns the symbol of the token (e.g., ICP).

icrc1_symbol : () -> (text) query;

icrc1_decimals

Returns the number of decimals the token uses (e.g., 8 means to divide the token amount by 100000000 to get its user representation).

icrc1_decimals : () -> (nat8) query;

icrc1_fee

Returns the default transfer fee.

icrc1_fee : () -> (nat) query;

icrc1_metadata

Returns the list of metadata entries for this ledger. See the "Metadata" section below.

type Value = variant { Nat : nat; Int : int; Text : text; Blob : blob };
icrc1_metadata : () -> (vec record { text; Value }) query;

icrc1_total_supply

Returns the total number of tokens on all accounts except for the minting account.

icrc1_total_supply : () -> (nat) query;

icrc1_minting_account

Returns the minting account if this ledger supports minting and burning tokens.

icrc1_minting_account : () -> (opt Account) query;

icrc1_balance_of

Returns the balance of the account given as an argument.

icrc1_balance_of : (Account) -> (nat) query;

icrc1_transfer

Transfers amount of tokens from account record { of = caller; subaccount = from_subaccount } to the to account. The caller pays fee tokens for the transfer.

type TransferArgs = record {
    from_subaccount : opt Subaccount;
    to : Account;
    amount : nat;
    fee : opt nat;
    memo : opt blob;
    created_at_time : opt nat64;
};

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 };
};
icrc1_transfer : (TransferArgs) -> (variant { Ok: nat; Err: TransferError; });

The caller pays the fee. If the caller does not set the fee argument, the ledger applies the default transfer fee. If the fee argument does not agree with the ledger fee, the ledger MUST return variant { BadFee = record { expected_fee = ... } } error.

The memo parameter is an arbitrary blob that has no meaning to the ledger. The ledger SHOULD allow memos of at least 32 bytes in length. The ledger SHOULD use the memo argument for transaction deduplication.

The created_at_time parameter indicates the time (as nanoseconds since the UNIX epoch in the UTC timezone) at which the client constructed the transaction. The ledger SHOULD reject transactions that have created_at_time argument too far in the past or the future, returning variant { TooOld } and variant { CreatedInFuture = record { ledger_time = ... } } errors correspondingly.

The result is either the transaction index of the transfer or an error.

icrc1_supported_standards

Returns the list of standards this ledger implements. See the "Extensions" section below.

icrc1_supported_standards : () -> (vec record { name : text; url : text }) query;

The result of the call should always have at least one entry,

record { name = "ICRC-1"; url = "https://github.com/dfinity/ICRC-1" }

Extensions

The base standard intentionally excludes some ledger functions essential for building a rich DeFi ecosystem, for example:

  • Reliable transaction notifications for smart contracts.
  • The block structure and the interface for fetching blocks.
  • Pre-signed transactions.

The standard defines the icrc1_supported_standards endpoint to accommodate these and other future extensions. This endpoint returns names of all specifications (e.g., "ICRC-42" or "DIP-20") implemented by the ledger.

Metadata

A ledger can expose metadata to simplify integration with wallets and improve user experience. The client can use the icrc1_metadata method to fetch the metadata entries. All the metadata entries are optional.

Key format

The metadata keys are arbitrary Unicode strings and must follow the pattern <namespace>:<key>, where <namespace> is a string not containing colons. Namespace icrc1 is reserved for keys defined in this standard.

Standard metadata entries

Key Semantics Example value
icrc1:symbol The token currency code (see ISO-4217). When present, should be the same as the result of the icrc1_symbol query call. variant { Text = "XTKN" }
icrc1:name The name of the token. When present, should be the same as the result of the icrc1_name query call. variant { Text = "Test Token" }
icrc1:decimals The number of decimals the token uses. For example, 8 means to divide the token amount by 108 to get its user representation. When present, should be the same as the result of the icrc1_decimals query call. variant { Nat = 8 }
icrc1:fee The default transfer fee. When present, should be the same as the result of the icrc1_fee query call. variant { Nat = 10_000 }
icrc1:logo The URL of the token logo. The value can contain the actual image if it's a Data URL. variant { Text = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InJlZCIvPjwvc3ZnPg==" }

Transaction deduplication

Consider the following scenario:

  1. An agent sends a transaction to an ICRC-1 ledger hosted on the IC.
  2. The ledger accepts the transaction.
  3. The agent loses the network connection for several minutes and cannot learn about the outcome of the transaction.

An ICRC-1 ledger SHOULD implement transfer deduplication to simplify the error recovery for agents. The deduplication covers all transactions submitted within a pre-configured time window TX_WINDOW (for example, last 24 hours). The ledger MAY extend the deduplication window into the future by the PERMITTED_DRIFT parameter (for example, 2 minutes) to account for the time drift between the client and the Internet Computer.

The client can control the deduplication algorithm using the created_at_time and memo fields of the transfer call argument:

  • The created_at_time field sets the transaction construction time as the number of nanoseconds from the UNIX epoch in the UTC timezone.
  • The memo field does not have any meaning to the ledger, except that the ledger will not deduplicate transfers with different values of the memo field.

The ledger SHOULD use the following algorithm for transaction deduplication if the client set the created_at_time field:

  • If created_at_time is set and is before time() - TX_WINDOW - PERMITTED_DRIFT as observed by the ledger, the ledger should return variant { TooOld } error.
  • If created_at_time is set and is after time() + PERMITTED_DRIFT as observed by the ledger, the ledger should return variant { CreatedInFuture = record { ledger_time = ... } } error.
  • If the ledger observed a structurally equal transfer payload (i.e., all the transfer argument fields and the caller have the same values) at transaction with index i, it should return variant { Duplicate = record { duplicate_of = i } }.
  • Otherwise, the transfer is a new transaction.

If the client did not set the created_at_time field, the ledger SHOULD NOT deduplicate the transaction.

Minting account

The minting account is a unique account that can create new tokens and acts as the receiver of burnt tokens.

Transfers from the minting account act as mint transactions depositing fresh tokens on the destination account. Mint transactions have no fee.

Transfers to the minting account act as burn transactions, removing tokens from the token supply. Burn transactions have no fee but might have minimal burn amount requirements. If the client tries to burn an amount that is too small, the ledger SHOULD reply with

variant { Err = variant { BadBurn = record { min_burn_amount = ... } } }

The minting account is also the receiver of the fees burnt in regular transfers.