Skip to content

Latest commit

 

History

History
377 lines (289 loc) · 8.93 KB

File metadata and controls

377 lines (289 loc) · 8.93 KB

Tokens and NFTs

Mintlayer supports on-chain fungible tokens and NFTs. The wallet daemon manages the full lifecycle. The mintlayer.wasm module provides low-level encoding for manual transaction flows.


Fungible tokens (wallet daemon)

Supply policies

When issuing a token you choose one of three supply policies (TokenSupply dataclass, wire field type):

Policy TokenSupply.type Description
Lockable "Lockable" Unlimited minting until lock_token_supply is called, after which the supply is frozen permanently
Unlimited "Unlimited" Minting is always allowed with no cap
Fixed "Fixed" A cap is set at issuance; supply cannot exceed it

For a Fixed cap, set the content field to an Amount:

TokenSupply(type="Fixed", content=Amount(atoms="1000000"))  # hard cap

content is omitted from the wire for the other two policies.

Issuing a token

from mintlayer.wallet import (
    Amount,
    Client,
    IssueTokenParams,
    TokenMetadata,
    TokenSupply,
)

wc = Client("http://127.0.0.1:3034")

authority_addr = wc.new_address(0)

result = wc.issue_token(
    IssueTokenParams(
        account=0,
        destination_address=authority_addr,
        metadata=TokenMetadata(
            token_ticker="MYTOKEN",
            number_of_decimals=2,
            metadata_uri="https://example.com/token-metadata.json",
            token_supply=TokenSupply(type="Lockable"),
            is_freezable=True,
        ),
    )
)
print(f"token id: {result.token_id}\ntx id:    {result.tx_id}")

The destination_address becomes the authority address: the key that controls future token operations (minting, freezing, authority transfer). Keep it secure.

Minting

Wait for the issuance transaction to confirm before minting.

from mintlayer.wallet import MintParams

mint_result = wc.mint_tokens(
    MintParams(
        account=0,
        token_id=result.token_id,
        address=recipient_addr,
        amount=Amount(atoms="100000"),  # in smallest token units
    )
)
print(f"minted tx id: {mint_result.tx_id}")

Unminting

Returns tokens to an unminted state (removes them from circulation without burning).

from mintlayer.wallet import UnmintParams

unmint_result = wc.unmint_tokens(
    UnmintParams(
        account=0,
        token_id="ttml1...",
        amount=Amount(atoms="50000"),
    )
)

Locking supply

After locking, the supply policy becomes Fixed at the current circulating supply. This operation is irreversible.

from mintlayer.wallet import LockSupplyParams

lock_result = wc.lock_token_supply(
    LockSupplyParams(
        account_index=0,
        token_id="ttml1...",
    )
)

Quirk: the field is account_index, not account — the daemon route expects the wire key account_index (every other token method uses account).

Freezing and unfreezing

Freezing prevents all transfers. If is_unfreezable is True, the authority can unfreeze later.

from mintlayer.wallet import FreezeParams, UnfreezeParams

freeze_result = wc.freeze_token(
    FreezeParams(
        account=0,
        token_id="ttml1...",
        is_unfreezable=True,
    )
)

unfreeze_result = wc.unfreeze_token(
    UnfreezeParams(
        account=0,
        token_id="ttml1...",
    )
)

Transferring authority

from mintlayer.wallet import ChangeAuthorityParams

change_result = wc.change_token_authority(
    ChangeAuthorityParams(
        account=0,
        token_id="ttml1...",
        address=new_authority_addr,
    )
)

Sending tokens

from mintlayer.wallet import TokenSendParams

send_result = wc.send_token(
    TokenSendParams(
        account=0,
        token_id="ttml1...",
        address=recipient_addr,
        amount=Amount(atoms="10000"),
    )
)

send_token is an alias of token_send on the transactions mixin — both call the daemon's token_send route.


NFTs (wallet daemon)

NFTs are non-fungible tokens. Each has unique on-chain metadata.

Issuing an NFT

from mintlayer.wallet import IssueNFTParams, NFTMetadata

owner_addr = wc.new_address(0)

result = wc.issue_nft(
    IssueNFTParams(
        account=0,
        destination_address=owner_addr,
        metadata=NFTMetadata(
            name="My NFT",
            description="A unique digital collectible",
            ticker="MYNFT",
            media_hash="sha256hexhash...",
            media_uri="https://example.com/media.png",
            icon_uri="https://example.com/icon.png",
            # creator and additional_metadata_uri default to None
        ),
    )
)
print(f"nft id: {result.token_id}")

NFTs cannot be minted after issuance: each issuance transaction creates exactly one NFT.


Reading token state from the indexer

from mintlayer.indexer import Client, PageOpts

idx = Client("http://127.0.0.1:3000")

# Find by ticker
ids = idx.find_tokens_by_ticker("MYTOKEN", PageOpts(items=10))

# Full token info
token = idx.get_token("ttml1...")
print(f"ticker:    {token.token_ticker}")
print(f"supply:    {token.circulating_supply.decimal}")
print(f"locked:    {token.is_locked}")
print(f"frozen:    {token.frozen}")

# Transaction history
txs = idx.get_token_transactions("ttml1...", PageOpts(items=20))

# NFT
nft = idx.get_nft("nftid1...")
print(f"owner: {nft.owner}")
print(f"name:  {nft.metadata.name}")

# Tokens where address is authority
token_ids = idx.get_token_authority("mtc1qauthority...")

Building token transactions manually

Use the mintlayer.wasm module for full control over token transactions.

Issue a token

from mintlayer.wasm import Amount, Client, FreezableToken, Network, TotalSupply

c = Client()
try:
    tip = idx.get_tip()

    issuance_fee = c.fungible_token_issuance_fee(tip.block_height, Network.MAINNET)

    # Predict the token ID
    token_id_str = c.get_token_id(encoded_inputs, tip.block_height, Network.MAINNET)

    issue_output = c.encode_output_issue_fungible_token(
        authority_addr,
        "MYTOKEN",
        "https://example.com/metadata.json",
        2,  # decimals
        TotalSupply.LOCKABLE,
        None,  # supply_amount: only required for TotalSupply.FIXED
        FreezableToken.YES,
        tip.block_height,
        Network.MAINNET,
    )
finally:
    c.close()

For a capped supply, pass the cap as the supply_amount:

issue_output = c.encode_output_issue_fungible_token(
    authority_addr,
    "MYTOKEN",
    "https://example.com/metadata.json",
    2,
    TotalSupply.FIXED,
    Amount.from_atoms("1000000"),  # required exactly for TotalSupply.FIXED
    FreezableToken.NO,
    tip.block_height,
    Network.MAINNET,
)

Include the issuance fee as a separate output or subtract it from an input UTXO.

Issue an NFT

issue_nft_output = c.encode_output_issue_nft(
    token_id=token_id_str,  # derived with get_token_id (same scheme as FTs)
    authority=owner_addr,
    name="My NFT",
    ticker="MYNFT",
    description="A unique digital collectible",
    media_hash=bytes.fromhex("..."),  # 32-byte sha256 of the media
    creator=bytes.fromhex("..."),  # or None
    media_uri="https://example.com/media.png",
    icon_uri=None,
    additional_metadata_uri=None,
    current_block_height=tip.block_height,
    network=Network.MAINNET,
)

Mint tokens

# Get the current nonce from the indexer
token_info = idx.get_token(token_id_str)

mint_input = c.encode_input_for_mint_tokens(
    token_id_str,
    Amount.from_atoms("100000"),
    token_info.next_nonce,
    Network.MAINNET,
)

mint_output = c.encode_output_token_transfer(
    Amount.from_atoms("100000"),
    recipient_addr,
    token_id_str,
    Network.MAINNET,
)

Freeze a token

from mintlayer.wasm import TokenUnfreezable

freeze_input = c.encode_input_for_freeze_token(
    token_id_str,
    TokenUnfreezable.YES,  # can be unfrozen later
    token_info.next_nonce,
    Network.MAINNET,
)

Assemble, sign, and submit manual token transactions exactly as in transactions.md.


Token fees

Protocol fees apply to many token operations. Query them from the WASM client before building transactions:

tip = idx.get_tip()

issuance_fee = c.fungible_token_issuance_fee(tip.block_height, Network.MAINNET)
nft_fee = c.nft_issuance_fee(tip.block_height, Network.MAINNET)
mint_fee = c.token_supply_change_fee(tip.block_height, Network.MAINNET)
freeze_fee = c.token_freeze_fee(tip.block_height, Network.MAINNET)
authority_fee = c.token_change_authority_fee(tip.block_height, Network.MAINNET)

These fees must be included as coin inputs in the transaction (or deducted from change). The data_deposit_fee applies to DataDeposit outputs.


Related