Agent index (llms.txt) — fetch this first, then the page it names.

Token factory

x/token lets anyone create a token without writing a contract. A token is a bank denom named factory/<creator address>/<subdenom> and its balances live in the normal bank module.

Creating a token

CreateToken takes:

  • +subdenom, 2 to 44 characters
  • +name (up to 64), symbol (up to 16), description (up to 256)
  • +capability flags: mint, freeze, permanent_delegate, transfer_fee_bps (0 to 10,000), non_transferable, pause
  • +transfer_hook, the bech32 address of an existing CosmWasm contract (--transfer-hook on the command line), or empty for none

It burns a 10 ORAMA creation fee and locks a metadata deposit of 68,359 norama per byte of the text fields (about 0.07 ORAMA per KiB). Deleting the token returns 99% of the deposit to earnings and burns 1%.

What the creator can do

MessageRule
MintThe creator, while it still holds the mint capability
BurnBurns from the signer's own bank balance
TransferA bank send with the pause, non-transferable, freeze and transfer-fee checks. from must be the signer unless the signer is the permanent delegate
SetFrozen, SetPausedThe creator, while it holds those capabilities
RenounceGives a capability up for good. The permanent delegate alone renounces its own
SetShieldableA one-way flag. Refused while freeze, permanent delegate or pause is held. The shielded pool moves ORAMA only, so this does not shield tokens
DeleteTokenOnly when supply is zero

Capabilities can only be dropped, never added or reassigned. There is no module-level admin key.

What it cannot do

  • +A token cannot mint norama. Only x/emission can, and a mint of a norama-named denom here is refused.
  • +A contract can create, mint and burn tokens through the token binding, again never norama. See Contracts.

The transfer hook

A token can name a contract as its transfer hook. The address is checked when the token is created (it must already be a contract), is fixed from then on, and can only be renounced. On every Transfer of that token the chain calls the contract's sudo entry with {"transfer_hook":{"denom","from","to","amount"}} under a gas cap of 100,000.

  • +If the contract returns an error, the transfer is refused.
  • +If it needs more than the cap, the transfer fails with "transfer hook exceeded gas cap".
  • +Either way nothing moves, and whatever the contract wrote is dropped. Its writes are kept only when the transfer succeeds.
  • +A build without the contract VM (Contracts) refuses to create a token with a hook.

Because the hook and the transfer fee run only inside Transfer, a bank send of such a token (MsgSend, MsgMultiSend, a contract's BankMsg) is refused. Use Transfer.

Queries

Params, Token and Frozen are public. The invariants tie each token's issued to the bank supply and each deposit to its token/{denom} entry. There is no list query: the explorer lists tokens from the bank's denoms (TotalSupply) and their holders from DenomOwners.