This is the message type used to mint an item of a non-fungible token.

Parameters

The message type contains the following parameters:

Name Type Required Description
itemID string true Item ID, which must be unique
symbol string true Token symbol, which must be unique
owner string true Token owner
to string true Item owner
properties string true Properties of item
metadata string true Metadata of item

Example

{
    "type": "nonFungible/mintNonFungibleItem",
    "value": {
        "itemID": "ITEM-123",
        "symbol": "TNFT",
        "owner": "mxw1x5cf8y99ntjc8cjm00z603yfqwzxw2mawemf73",
        "to": "mxw1md4u2zxz2ne5vsf9t4uun7q2k0nc3ly5g22dne",
        "properties": "item-properties",
        "metadata": "item-metadata"
    }
}

Handler

The role of the handler is to define what action(s) needs to be taken when this MsgMintNonFungibleItem message is received.

In the file (./x/token/nonfungible/handler.go) start with the following code:

func NewHandler(keeper *Keeper) sdkTypes.Handler {
    return func(ctx sdkTypes.Context, msg sdkTypes.Msg) sdkTypes.Result {
        switch msg := msg.(type) {
        case MsgCreateNonFungibleToken:
            return handleMsgCreateNonFungibleToken(ctx, keeper, msg)
        case MsgSetNonFungibleTokenStatus:
            return handleMsgSetNonFungibleTokenStatus(ctx, keeper, msg)
        'case MsgMintNonFungibleItem:
            return handleMsgMintNonFungibleItem(ctx, keeper, msg)'
        case MsgTransferNonFungibleItem:
            return handleMsgTransferNonFungibleItem(ctx, keeper, msg)
        case MsgBurnNonFungibleItem:
            return handleMsgBurnNonFungibleItem(ctx, keeper, msg)
        case MsgSetNonFungibleItemStatus:
            return handleMsgSetNonFungibleItemStatus(ctx, keeper, msg)
        case MsgTransferNonFungibleTokenOwnership:
            return handleMsgTransferNonFungibleTokenOwnership(ctx, keeper, msg)
        case MsgAcceptNonFungibleTokenOwnership:
            return handleMsgAcceptTokenOwnership(ctx, keeper, msg)
        case MsgEndorsement:
            return handleMsgEndorsement(ctx, keeper, msg)
        case MsgUpdateItemMetadata:
            return handleMsgUpdateItemMetadata(ctx, keeper, msg)
        case MsgUpdateNFTMetadata:
            return handleMsgUpdateNFTMetadata(ctx, keeper, msg)
    case MsgUpdateEndorserList:
            return handleMsgUpdateEndorserList(ctx, keeper, msg)
        default:
            errMsg := fmt.Sprintf("Unrecognized fungible token Msg type: %v", msg.Type())
            return sdkTypes.ErrUnknownRequest(errMsg).Result()
        }
    }

}

NewHandler is essentially a sub-router that directs messages coming into this module to the proper handler. Now, you need to define the actual logic for handling the MsgMintNonFungibleItem message in handleMsgMintNonFungibleItem:

func (k *Keeper) MintNonFungibleItem(ctx sdkTypes.Context, symbol string, from sdkTypes.AccAddress, to sdkTypes.AccAddress, itemID, properties, metadata string) sdkTypes.Result {

    nonFungibleToken := new(Token)

    if exists := k.GetTokenDataInfo(ctx, symbol, nonFungibleToken); !exists {
        return types.ErrInvalidTokenSymbol(symbol).Result()
    }

    // get minter account.
    // if token is public that means minter can be anyone.
    minterAccount := k.accountKeeper.GetAccount(ctx, from)
    if minterAccount == nil {
        return types.ErrInvalidTokenAccount().Result()
    }

    if !nonFungibleToken.Flags.HasFlag(PubFlag) {
        if !nonFungibleToken.Owner.Equals(from) {
            return types.ErrInvalidTokenMinter().Result()
        }
    } else {
        if !from.Equals(to) {
            return sdkTypes.ErrInternal("Public token can only be minted to oneself.").Result()
        }
    }

    if !nonFungibleToken.Flags.HasFlag(MintFlag) {
        return types.ErrInvalidTokenAction().Result()
    }

    if !nonFungibleToken.Flags.HasFlag(ApprovedFlag) {
        return types.ErrTokenInvalid().Result()
    }

    if nonFungibleToken.Flags.HasFlag(FrozenFlag) {
        return types.ErrTokenFrozen().Result()
    }

    if !k.IsItemIDUnique(ctx, nonFungibleToken.Symbol, itemID) {
        return types.ErrTokenItemIDInUsed().Result()
    }

    amt := sdkTypes.NewUint(1)
    nonFungibleToken.TotalSupply = nonFungibleToken.TotalSupply.Add(amt)

    // check mint limit, if token mint limit !=0
    if !nonFungibleToken.MintLimit.IsZero() {

        if k.IsMintLimitExceeded(ctx, nonFungibleToken.Symbol, to) {
            return sdkTypes.ErrInternal("Holding limit existed.").Result()
        }
        k.increaseMintItemLimit(ctx, symbol, to)
    }

    k.storeToken(ctx, symbol, nonFungibleToken)

    item := k.createNonFungibleItem(ctx, nonFungibleToken.Symbol, to, itemID, properties, metadata)

    eventParam := []string{symbol, string(item.ID), from.String(), to.String()}
    eventSignature := "MintedNonFungibleItem(string,string,string,string)"

    accountSequence := minterAccount.GetSequence()
    resultLog := types.NewResultLog(accountSequence, ctx.TxBytes())

    return sdkTypes.Result{
        Events: types.MakeMxwEvents(eventSignature, from.String(), eventParam),
        Log:    resultLog.String(),
    }
}

In this function, requirements need to be met before emitted by the network.

  • A valid Token.
  • Token must be approved, and not yet be freeze.
  • Token which Public Flag equals to true can only be minted to same user.
  • Token which Mint-limit Flag set to ZERO, any items can be minted without the limitation. Otherwise, will base on the threshold of this setting.
  • A valid Item ID which must be unique.
  • Action of Re-mint is not allowed.

Events

This tutorial describes how to create maxonrow events for scanner on this after emitted by a network.

eventParam := []string{symbol, string(item.ID), from.String(), to.String()}
eventSignature := "MintedNonFungibleItem(string,string,string,string)"

accountSequence := minterAccount.GetSequence()
resultLog := types.NewResultLog(accountSequence, ctx.TxBytes())

return sdkTypes.Result{
    Events: types.MakeMxwEvents(eventSignature, from.String(), eventParam),
    Log:    resultLog.String(),
}

Usage

This MakeMxwEvents create maxonrow events, by accepting :

  • eventSignature : Custom Event Signature that using MintedNonFungibleItem(string,string,string,string)
  • from : Token owner
  • eventParam : Event Parameters as below
Name Type Description
symbol string Token symbol, which must be unique
itemID string Item ID
from string Token owner
to string Item owner