MultiToken Pallet
The utility pallets of the Enjin Blockchain.
Use console.enjin.io to use the user interface referenced in this document.
What is the MultiToken Pallet?
Enjin has developing a token standard called MultiTokens. The standard will be compatible with matrixchains, parachains, parathreads and smart contracts, so it’s interoperable with the entire Enjin, Polkadot and Kusama ecosystem.
Multi-Unit Token are stackable, have a quantity and optional decimal places. An example of a fungible token is a twenty dollar bill - each bill is worth the same amount as another twenty dollar bill.
Non-fungible tokens are not interchangeable; each token has its own unique identifier. Examples of NFTs are original art, gaming characters and pets, numbered collectibles, and more.
Grouped NFTs are non-fungible tokens that are a part of a family, and share the same Base ID.
Terminology
- Collection - A group of tokens. Also holds data for those tokens and the policies that govern their behavior.
- Token - A unique asset with a balance
- Policy - Governs behavior for tokens in a collection
- Attribute - Metadata for a collection or a token
- Operator - An account that operates on behalf of another account (transferFrom)
- Approval - Required for an operator to use an account
- Freeze/Thaw - If a collection, token, or account is frozen, it cannot transfer tokens
- Descriptor - Used to create something. For example, a CollectionDescriptor creates a Collection.
Collections
A collection must be created before tokens may be minted. A collection is somewhat akin to an ERC-721 smart contract - its creator has certain privileges, such as minting new tokens or setting metadata for the collection and its tokens.
The first 2000 Collection IDs are reserved for future system collections. Collections created on-chain through this Extrinsic start from ID 2001 and are sequentially created.
A deposit of 6.25 ENJ is required to create a collection. The deposit can be recovered by the creator if all tokens are burned and the collection is destroyed.
Policies
Policies allow controlling behavior of various aspects of tokens in a collection. All policies are contained within the collection, and some of them store data and are configurable. This paradigm was chosen because it allows other blockchains that include pallet-multi-tokens
to easily customize its behavior. It also helps the end-user by separating token behavior into separate groups.
There are 5 types of polices:
- Mint Policy - Handles minting.
- Burn Policy - Validates burning. Has no configurable parameters.
- Transfer Policy - Handles transfers, including freezing and allowing transfer_from. Stores the collection freeze state.
- Attribute Policy - Handles metadata. Does not store data and has no configurable parameters.
- Market Policy - Handles interfacing with the marketplace. Stores collection royalty.
Tokens
Each token must belong to a Collection, and is created using the mint extrinsic.
There is no distinction between Multi-Unit Token and non-fungible tokens. A non-fungible token is simply a token with a total supply of one. Additional constraints, like a cap, can be applied to a token at the time of minting to make sure the total supply never increases.
How to Create a Collection
A collection is created by using the create_collection
extrinsic. The only required parameter is a descriptor which allows customizing the collection's behavior. The descriptor contains all of the policies and some additional settings. Some settings can be changed later using the mutate_collection
extrinsic, but the policies currently cannot be changed, so think carefully before choosing the policies.
By default, the policies are flexible, but you can make them more strict. For example, if you want a collection to only contain non-fungible tokens, you can set max_token_supply
to Some(1) and force_single_mint
to true on the mint policy.
When the collection is created, it will emit a CollectionCreated
event. This event contains the collection id that is used to access the collection.
How to create a Token
Each token must contain a minimum backing of ENJ to be minted, called Unit Price.
Tokens are created using the mint
extrinsic. It only contains two parameters, the recipient
and mint params
. The mint params is an enum with a few variants. The only important variants are:
CreateToken
This must be used the first time a token is created. The provided token id must not already exist. Some additional settings can be chosen when creating a token, such as setting a cap on the balance or giving it a royalty for the marketplace. If the token is to be an NFT, set the cap to Some(TokenCap::SingleMint)
and set initial_supply
to 1
.
Some of these settings can be changed later by using the mutate_token extrinsic
.
Mint
This is used when minting additional balance to an already existing token. The unit price can be supplied if you want to increase it, but it cannot be decreased. Otherwise, set the unit price to None
.
Transfering tokens and NFTs
To perform a transfer, use the transfer
extrinsic. There are two types of transfers:
Simple Transfer
A simple transfer is when the origin
of the extrinsic is also the sender.
Operator Transfer
An operator transfer allows one account to make transfers on behalf of another account. This is also known as transfer_from
.
Approvals can be set for entire collections, or they can be set for specific tokens. They can have expiration times, and specific balances can be set for token approvals. The following extrinsics are used for approvals:
- approve_collection - Approves all tokens in a collection.
- approve_token - Approves a specific token in a collection for a specific amount. For security reasons, you must specify the exact amount of the previous approval (or zero if there is none) in the
current_amount
parameter for the extrinsic to succeed. - unapprove_collection - Removes a collection approval.
- unapprove_token - Removes a token approval.
Burning Tokens
Tokens can be burned by calling burn
. This works exactly like a transfer, but it will also decrease the total supply of the token.
There is an additional field on the BurnParam
called remove_token_storage
. If this is set to true, the token will be removed from the storage when it's total supply reaches zero. This can only be done by the token's owner.
If the token is removed from storage, the deposit will be returned to the owner, and it will be as if the token never existed, so it can be recreated in the future.
Setting/Removing Attributes
To add or update metadata for a token or a collection, use the set_attribute
extrinsic. Providing token_id
adds the attribute to the token, otherwise add it directly to the collection. It's only callable by the collection's owner.
To remove an attribute
, use remove_attribute
extrinsic. It's only callable by the collection owner. If the token_id
is provided, the attribute will be removed from the token. Otherwise, it will be removed from the collection.
Setting Collection Attributes
This is done by clicking the Manage Attributes
button under the expanded collection. Use toggle to switch between setting and removing attributes.
Setting Token Attributes
This is done by clicking the Manage Attributes
button on the token page. Use toggle to switch between setting and removing attributes.
Freezing
Accounts, collections, and tokens can be frozen to prevent transfers. This is done using the freeze
extrinsic, which expects freezing info
to be provided. The info
specifies whether the collection, token, collection account or token account should be frozen.
Freeze a collection or a collection account
This is done by clicking on Freeze button under expanded collection.
Freeze a token or a token account
This is done by clicking on Freeze button on the token page.
Thawing
To unfreeze either collection, token, collection account or token account, use the thaw extrinsic. It expects the same info
as the freeze
extrinsic.
Thaw a collection or a collection account
This is done by clicking on the Thaw
button under expanded collection.
Thaw a token or a token account
This is done by clicking on the Thaw
button on the token page.
Batch operations
It is also possible to perform certain operations in batch. The following operations are supported:
Batch Transfer
Using batch_transfer
you can transfer the specific amount
of tokens of a collection
to list of recipients
. If continue_on_failure
is false, a single transfer failure will fail all of them. If it is true, execution will continue when a failure is encountered.
Batch Mint
Using batch_mint
you can mint the tokens of a collection to a list of recipients
consisting of an AccountId
and MintParams
. If continue_on_failure
is false, a single mint failure will fail all operations. If it is true, execution will continue when a failure is encountered.
Batch Set Attribute
Using batch_set_attribute
you can set the list of attributes to a collection or a token. If token_id
is None, the attribute is added to the collection. If it is Some
, the attribute is added to the token.
Remove All Attributes
Removes all attributes from the given collection_id
or token_id
.
Unit Price
The unit price of a token represents the backing value of each unit in ENJ. This is needed to pay storage deposits for accounts that are created to hold the token's balances. The unit price determines two things:
- The amount of ENJ that will be reserved when minting units
- The minimum balance for each account
ENJ Deposit
The ENJ deposit is taken from the issuer account as units are minted, and it is returned proportionally as units are burned. The minimum deposit is Config::TokenAccountDeposit
, which has a value of 10^16 (0.01 ENJ).
The formula for the ENJ deposit is:
unit_price _ total_supply
You must set a unit price that will cause the deposit to be equal to or above the minimum value. You can calculate the minimum like this:
Config::TokenAccountDeposit / total_supply
Minimum Balance
Accounts that go below the 0.01 ENJ minimum balance are deleted. This can be prevented during transfers by setting keep_alive to true in TransferParams.
The formula for the minimum balance is:
Config::TokenAccountDeposit / unit_price
When the minimum unit price is used, the minimum balance will be the entire total supply. This means only one account can hold units of the token. This is recommended for NFTs. For Multi-Unit Tokens, the unit price should be set according to what you want the minimum balance to be.
As the unit_price increases, the minimum balance decreases, but the ENJ deposit goes up.
The unit price can be increased, but it can never be decreased.
Updated about 1 month ago