Skip to main content

Create a token

Intermediate
Tutorial

Overview

Custom tokens can be created and deployed on ICP using the ICRC-1 or ICRC-2 token standards.

The ICRC-1 standard is considered the 'base' standard, as it defines a set of general functionalities that all ledgers must adhere to. Any tokens and their corresponding ledgers must fulfill all requirements of the ICRC-1 standard. Read about token standards to learn more about what token standards are and the functionalities each one supports.

This guide will provide a quickstart on how you can create and deploy your own custom token using the ICRC-1 standard. 

Setup a local ICRC-1 ledger

To create a new ICRC-1 token, you will need to first download and configure a local project for the ICRC-1 ledger canister. This is because each token will need its own ledger canister to record the token's balances and transactions. Follow the ICRC-1 ledger documentation for instructions.

Export token settings

The initialization arguments of the ICRC-1 ledger are not specified in the standard. Thus, the arguments defined in this section are dependent on the reference implementation of the ICRC-1 ledger. If you build your own ICRC-1 ledger, you may use different initialization arguments.

To create a token, you will need to export several environmental variables. The full list can be found below:

PRE_MINTED_TOKENS

Amount of tokens that are minted during deployment for a specific account. In this tutorial, it will be the DEFAULT account.

export PRE_MINTED_TOKENS=10_000_000_000
dfx identity use default
export DEFAULT=$(dfx identity get-principal)

TRANSFER_FEE

Fee that users of the ledger will have to pay anytime they want to make a transfer.

export TRANSFER_FEE=10_000

ARCHIVE_CONTROLLER

The controller principal of the archive canisters.

dfx identity new archive_controller
dfx identity use archive_controller
export ARCHIVE_CONTROLLER=$(dfx identity get-principal)

TRIGGER_THRESHOLD

The number of blocks to archive when the trigger threshold is exceeded.

export TRIGGER_THRESHOLD=2000

CYCLE_FOR_ARCHIVE_CREATION

The number of cycles that will be sent to the archive canister when it is created.

export CYCLE_FOR_ARCHIVE_CREATION=10000000000000

NUM_OF_BLOCK_TO_ARCHIVE

The number of blocks that will be archived.

export NUM_OF_BLOCK_TO_ARCHIVE=1000

TOKEN_NAME

The name of your token.

export TOKEN_NAME="My Token"

TOKEN_SYMBOL

The ticker symbol of your new token.

export TOKEN_SYMBOL="XMTK"

MINTER

The account of the principal responsible for minting and burning tokens (see the ICRC-1 specification). Transfers from the minting account will generate Mint transactions, resulting in the creation of new tokens. To mint tokens, the minting account must call the icrc1_transfer function, specifying the recipient in the 'to' value.

Transfers sent to the minting account will generate Burn transactions, destroying tokens. An icrc1_transfer call from any principal with the 'to' value set as the minting account will burn tokens.

dfx identity new minter
dfx identity use minter
export MINTER=$(dfx identity get-principal)

FEATURE_FLAGS

A flag for enabling or disabling certain extension standards to the ICRC-1 standard. In this case, the reference implementation can also support ICRC-2 transactions.

If you only want to support the ICRC-1 standard, then you can set the flag to false. If you want to also support the ICRC-2 standard, set it to true.

export FEATURE_FLAGS=false

Deploy the ledger locally

Deploying to the playground is ICP's equivalent of deploying to a testnet network.

In this workflow, you cannot deploy this canister to the playground (using the flag dfx deploy --playground) because it does not accept gzipped Wasm files.

Deploy the ICRC-1 ledger canister locally and pass in the arguments to create your token.

dfx start --clean --background
dfx deploy icrc1_ledger_canister --specified-id mxzaz-hqaaa-aaaar-qaada-cai --argument "(variant {Init =
record {
     token_symbol = \"${TOKEN_SYMBOL}\";
     token_name = \"${TOKEN_NAME}\";
     minting_account = record { owner = principal \"${MINTER}\" };
     transfer_fee = ${TRANSFER_FEE};
     metadata = vec {};
     feature_flags = opt record{icrc2 = ${FEATURE_FLAGS}};
     initial_balances = vec { record { record { owner = principal \"${DEFAULT}\"; }; ${PRE_MINTED_TOKENS}; }; };
     archive_options = record {
         num_blocks_to_archive = ${NUM_OF_BLOCK_TO_ARCHIVE};
         trigger_threshold = ${TRIGGER_THRESHOLD};
         controller_id = principal \"${ARCHIVE_CONTROLLER}\";
         cycles_for_archive_creation = opt ${CYCLE_FOR_ARCHIVE_CREATION};
     };
 }
})"

For additional details, you can learn more in the ICRC-1 ledger documentation.

Your token has been created locally. To interact with your token, learn how to interact with the ICRC-1 ledger.

Deploying your token to the mainnet

If you want to deploy your ICRC-1 token on the mainnet, follow these steps:

  • Remove the --specified-id flag. Your ledger canister will receive a unique canister ID when deployed on the mainnet.

  • Specify the initially minted token value by setting initial_values = vec {<INITIAL_VALUES>}. View the ledger.did file for the details of the argument.

  • Add the --network ic flag to the command to deploy to the mainnet.

  • Confirm that the archive_options field is set. If archiving is disabled, your ledger's memory capacity will be limited to that of a single canister.

  • Be sure that your ledger canister has cycles attached to the create_canister messages, using the cycles_for_archive_creation.

Next steps

Learn how to interact with the ICRC-1 ledger

Resources