Skip to main content

Messages

In this section we describe the processing of the tokenfactory messages and the corresponding updates to the state.

Messages

CreateDenom

Creates a denom of factory/{creator address}/{subdenom} given the denom creator address, subdenom and associated metadata (name, symbol, decimals). Subdenoms can contain [a-zA-Z0-9./]. allow_admin_burn can be set to true to allow the admin to burn tokens from other addresses.
State Modifications:
  • Fund community pool with the denom creation fee from the creator address, set in Params.
  • Set DenomMetaData via bank keeper.
  • Set AuthorityMetadata for the given denom to store the admin for the created denom factory/{creator address}/{subdenom}. Admin is automatically set as the Msg sender.
  • Add denom to the CreatorPrefixStore, where a state of denoms created per creator is kept.

Mint

Mint authorization depends on whether the denom has a permissions namespace.
  • No namespace: only the current admin can mint. The admin defaults to the creator of the denom.
  • With a namespace: the admin check is skipped entirely and the sender must hold the MINT permission. Being the denom admin is not sufficient on its own.
If receiver is empty, tokens are minted to the sender. State Modifications:
  • Safety check the following
    • Check that bank metadata exists for the denom, otherwise ErrDenomDoesNotExist
    • If the denom has no permissions namespace, check that the sender is the admin of the denom
    • If the denom has a permissions namespace, check that the sender holds the MINT permission
  • Mint designated amount of tokens for the denom via bank module and send them to the receiver
When minting is triggered from the EVM through a paired ERC20, the sender of this message is the ERC20 contract address, not the EVM caller. The module’s admin check therefore runs against the contract address, while the contract’s own code decides which EVM callers may trigger it. Holding the denom admin key does not authorize a mint through the paired contract: the contract address itself must be the admin, or hold the MINT permission. See ERC20 Module for the configuration this requires.

Burn

Burn authorization is stricter than mint authorization and depends on two independent things: whether the sender is burning from its own balance, and whether the denom has a permissions namespace.
If burnFromAddress is empty it defaults to the sender, which makes the message a self-burn. State Modifications:
  • Safety check the following
    • Resolve burnFromAddress, defaulting to the sender
    • Apply the authorization matrix above
  • Burn designated amount of tokens from the burn-from address via bank module
allow_admin_burn is checked first and unconditionally on every burn-from. It can only be set at CreateDenom and is a latch: once it is false or has been disabled, it can never be re-enabled. Attempting to do so fails with cannot enable AdminBurnAllowed if it was previously disabled.This matters for paired ERC20 tokens. An ERC20 burn() reaches this message with the contract as sender and the holder as the burn-from address, so it is always a burn-from, never a self-burn. A denom created without --allow-admin-burn can never burn through its paired ERC20. See ERC20 Module.

ChangeAdmin

Change the admin of a denom. Note, this is only allowed to be called by the current admin of the denom. After the admin address is set to zero address, token holders can still execute MsgBurn for tokens they possess.
Changing the denom admin has no effect on a paired ERC20 contract. If the pair deployed a MintBurnBankERC20, its Solidity owner() is unchanged by this message, and transferring ERC20 ownership does not change the denom admin either. The two authorities are independent.

SetDenomMetadata

Setting of metadata for a specific denom is only allowed for the admin of the denom, or for governance once the admin has been set to the empty address. It allows the overwriting of the denom metadata in the bank module. The admin can also disable the admin burn capability, if enabled.
Decimals are effectively immutable once set. If existing bank metadata already records a non-zero decimals, any attempt to change it fails with cannot update denom metadata decimals. Only a denom still sitting at decimals: 0 can have the value set. Confirm the intended precision before the first metadata write, including in governance proposals for peggy0x... and ibc/... denoms.
State Modifications:
  • Check that sender of the message is the admin of denom
  • Modify AuthorityMetadata state entry to change the admin of the denom and to potentially disable admin burn capability.

Expectations from the chain

The chain’s bech32 prefix for addresses can be at most 16 characters long. This comes from denoms having a 128 byte maximum length, enforced from the SDK, and us setting longest_subdenom to be 44 bytes. A token factory token’s denom is: factory/{creator address}/{subdenom} Splitting up into components, this has:
  • len(factory) = 7
  • 2 * len("/") = 2
  • len(longest_subdenom)
  • len(creator_address) = len(bech32(longest_addr_length, chain_addr_prefix)).
Longest addr length at the moment is 32 bytes. Due to SDK error correction settings, this means len(bech32(32, chain_addr_prefix)) = len(chain_addr_prefix) + 1 + 58. Adding this all, we have a total length constraint of 128 = 7 + 2 + len(longest_subdenom) + len(longest_chain_addr_prefix) + 1 + 58. Therefore len(longest_subdenom) + len(longest_chain_addr_prefix) = 128 - (7 + 2 + 1 + 58) = 60. The choice between how we standardized the split these 60 bytes between maxes from longest_subdenom and longest_chain_addr_prefix is somewhat arbitrary. Considerations going into this:
  • Per BIP-0173 the technically longest HRP for a 32 byte address (‘data field’) is 31 bytes. (Comes from encode(data) = 59 bytes, and max length = 90 bytes)
  • subdenom should be at least 32 bytes so hashes can go into it
  • longer subdenoms are very helpful for creating human readable denoms
  • chain addresses should prefer being smaller. The longest HRP in cosmos to date is 11 bytes. (persistence)
For explicitness, its currently set to len(longest_subdenom) = 44 and len(longest_chain_addr_prefix) = 16. Please note, if the SDK increases the maximum length of a denom from 128 bytes, these caps should increase. So please don’t make code rely on these max lengths for parsing.
Last modified on October 26, 2026