Create a token
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 theledger.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 thecycles_for_archive_creation
.
Next steps
Learn how to interact with the ICRC-1 ledger