Skip to main content

Issue a Token

This guide covers the full MLS-01 token lifecycle in Go: issuing, minting, transferring, and managing supply and authority with the Go SDK. The same workflow in JavaScript lives in the JavaScript guides; the wallet-cli version, which explains the underlying concepts (token id, authority address, reserve vs circulating supply), is Issue a new token.

Prerequisites: a funded wallet and a running wallet-rpc-daemon plus indexer (see Developer Setup for ports and authentication).

Issuing

import "github.com/mintlayer/go-sdk/wallet"

wc := wallet.New("http://127.0.0.1:3034")

authorityAddr, err := wc.NewAddress(ctx, 0)

result, err := wc.IssueToken(ctx, wallet.IssueTokenParams{
Account: 0,
DestinationAddress: authorityAddr,
Metadata: wallet.TokenMetadata{
TokenTicker: "MYTOKEN",
NumberOfDecimals: 8,
MetadataURI: "https://example.com/mytoken.json",
TokenSupply: wallet.TokenSupply{Type: "Lockable"},
IsFreezable: true,
},
})
fmt.Printf("token id: %s tx: %s\n", result.TokenID, result.TxID)

For a fixed supply, cap it at issuance:

TokenSupply: wallet.TokenSupply{
Type: "Fixed",
Content: &wallet.Amount{Atoms: "1000000"}, // hard cap
},

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

Metadata

Publish metadata at the metadata URI following the Token Metadata Standards (MLS-01 schema) so wallets and explorers can render your token. The URI can be updated later by the authority.

Minting and unminting

Wait for the issuance transaction to confirm first. Minting moves tokens from the reserve into circulation; unminting reverses it.

mintResult, err := wc.MintTokens(ctx, wallet.MintParams{
Account: 0, TokenID: result.TokenID,
Address: recipientAddr,
Amount: wallet.Amount{Atoms: "100000"}, // smallest units
})

_, err = wc.UnmintTokens(ctx, wallet.UnmintParams{
Account: 0, TokenID: result.TokenID,
Amount: wallet.Amount{Atoms: "50000"},
})

Transferring and burning

_, err = wc.SendToken(ctx, wallet.TokenSendParams{
Account: 0, TokenID: result.TokenID,
Address: recipientAddr,
Amount: wallet.Amount{Atoms: "10000"},
})

Authority, freeze, and supply lock

All management operations require the token authority key:

_, err = wc.LockTokenSupply(ctx, wallet.LockSupplyParams{AccountIndex: 0, TokenID: result.TokenID}) // irreversible

_, err = wc.FreezeToken(ctx, wallet.FreezeParams{Account: 0, TokenID: result.TokenID, IsUnfreezable: true})
_, err = wc.UnfreezeToken(ctx, wallet.UnfreezeParams{Account: 0, TokenID: result.TokenID})
_, err = wc.ChangeTokenAuthority(ctx, wallet.ChangeAuthorityParams{Account: 0, TokenID: result.TokenID, Address: newAuthorityAddr})

Reading token state

import "github.com/mintlayer/go-sdk/indexer"

idx := indexer.New("http://127.0.0.1:3000")

token, err := idx.GetToken(ctx, result.TokenID)
fmt.Printf("ticker: %s circulating: %s locked: %v frozen: %v\n",
token.TokenTicker, token.CirculatingSupply.Decimal, token.IsLocked, token.Frozen)

Manual transaction building

For full custody without the wallet daemon, the wasm package encodes issuance, mint, and freeze inputs/outputs directly, and exposes the protocol fee functions (FungibleTokenIssuanceFee, TokenSupplyChangeFee, TokenFreezeFee, TokenChangeAuthorityFee). See Go SDK: Tokens and Forging custom transactions.