Create Fungible Token with Golang

This document gives instructions and examples on how to use our pkg/client package to create and manage fungible token.

Complete code

Complete code with go.mod file you can find hereopen in new window

P.S. If you have issues with go mod tidy command, just copy go.mod file from the example above.

Go code skeleton

Imports and main function

Create standard main.go file containing this skeleton importing pkg/client:

package main

import (
	"github.com/CoreumFoundation/coreum/v2/pkg/client"
	"github.com/CoreumFoundation/coreum/v2/pkg/config/constant"
)

const (
	senderMnemonic = "" // put mnemonic here

	chainID          = constant.ChainIDTest
	addressPrefix    = constant.AddressPrefixTest
	denom            = constant.DenomTest
	recipientAddress = "testcore1534s8rz2e36lwycr6gkm9vpfe5yf67wkuca7zs"
	nodeAddress      = "full-node.testnet-1.coreum.dev:9090"
)

func main() {

}

Preparing test account

Before you may broadcast transactions, you need to have access to a funded account. Normally you would create a private key stored securely in local keystore. Here, for simplicity, we will use private key generated by our faucet. Never ever use mnemonic directly in code and never ever use key generated by faucet in production. It might cause complete funds loss! Please reference keyring documentation to learn on using keyring: https://docs.cosmos.network/v0.45/run-node/keyring.html and https://pkg.go.dev/github.com/cosmos/cosmos-sdk/crypto/keyring.

To get funded account, go to our faucet website: https://docs.coreum.dev/tools-ecosystem/faucet and click on "Generate Funded Wallet" button in "Testnet" section.

In response, you get your wallet address on our testnet chain and mnemonic used to generate the private key. Assign mnemonic to the constant senderMnemonic in the code snippet above.

Setting Cosmos SDK configuration

First we need to configure Cosmos SDK:

config := sdk.GetConfig()
config.SetBech32PrefixForAccount(addressPrefix, addressPrefix+"pub")
config.SetCoinType(constant.CoinType)
config.Seal()

Preparing client context and tx factory

Before we are able to broadcast transaction, we must create and configure client context and tx factory:

modules := module.NewBasicManager(
    auth.AppModuleBasic{},
)

// If you don't use TLS then replace `grpc.WithTransportCredentials()` with `grpc.WithInsecure()`
grpcClient, err := grpc.Dial(nodeAddress, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
if err != nil {
    panic(err)
}

clientCtx := client.NewContext(client.DefaultContextConfig(), modules).
    WithChainID(string(chainID)).
    WithGRPCClient(grpcClient).
    WithKeyring(keyring.NewInMemory()).
    WithBroadcastMode(flags.BroadcastBlock)

txFactory := client.Factory{}.
    WithKeybase(clientCtx.Keyring()).
    WithChainID(clientCtx.ChainID()).
    WithTxConfig(clientCtx.TxConfig()).
    WithSimulateAndExecute(true)

Generate private key

To sign a transaction, private key generated from mnemonic stored in senderMnemonic is required. We store that key in the temporary keystore. In production you should use any keyring other than memory or test. Good choice might be os or file. For more details, refer keyring documentation: https://docs.cosmos.network/v0.45/run-node/keyring.html and https://pkg.go.dev/github.com/cosmos/cosmos-sdk/crypto/keyring.

senderInfo, err := clientCtx.Keyring().NewAccount(
    "key-name",
    senderMnemonic,
    "",
    sdk.GetConfig().GetFullBIP44Path(),
    hd.Secp256k1,
)
if err != nil {
    panic(err)
}

Creating fungible token

Here is the example of creating new token uabc with initial supply of 100 000 000:

const subunit = "uabc"
msgIssue := &assetfttypes.MsgIssue{
    Issuer:        senderInfo.GetAddress().String(),
    Symbol:        "ABC",
    Subunit:       subunit,
    Precision:     6,
    InitialAmount: sdk.NewInt(100_000_000),
    Description:   "ABC coin",
    Features:      []assetfttypes.Feature{assetfttypes.Feature_freezing},
}

ctx := context.Background()
_, err = client.BroadcastTx(
    ctx,
    clientCtx.WithFromAddress(senderInfo.GetAddress()),
    txFactory,
    msgIssue,
)
if err != nil {
    panic(err)
}

Querying the initial supply

After creating the token, initial supply is available on the issuer's account:

denom := subunit + "-" + senderInfo.GetAddress().String()
bankClient := banktypes.NewQueryClient(clientCtx)
resp, err := bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{
    Address: senderInfo.GetAddress().String(),
    Denom:   denom,
})
if err != nil {
    panic(err)
}
fmt.Printf("Issuer's balance: %s\n", resp.Balance)

Sending tokens

Now issuer may send those tokens to someone:

recipientInfo, _, err := clientCtx.Keyring().NewMnemonic(
    "recipient",
    keyring.English,
    sdk.GetConfig().GetFullBIP44Path(),
    "",
    hd.Secp256k1,
)
if err != nil {
    panic(err)
}

msgSend := &banktypes.MsgSend{
    FromAddress: senderInfo.GetAddress().String(),
    ToAddress:   recipientInfo.GetAddress().String(),
    Amount:      sdk.NewCoins(sdk.NewInt64Coin(denom, 1_000_000)),
}

_, err = client.BroadcastTx(
    ctx,
    clientCtx.WithFromAddress(senderInfo.GetAddress()),
    txFactory,
    msgSend,
)
if err != nil {
    panic(err)
}

resp, err = bankClient.Balance(ctx, &banktypes.QueryBalanceRequest{
    Address: recipientInfo.GetAddress().String(),
    Denom:   denom,
})
if err != nil {
    panic(err)
}
fmt.Printf("Recipient's balance: %s\n", resp.Balance)

Freezing

Because issuer enabled freezing feature during token issuance, he/she might freeze the portion of the recipient's balance:

msgFreeze := &assetfttypes.MsgFreeze{
    Sender:  senderInfo.GetAddress().String(),
    Account: recipientInfo.GetAddress().String(),
    Coin:    sdk.NewInt64Coin(denom, 500_000),
}

_, err = client.BroadcastTx(
    ctx,
    clientCtx.WithFromAddress(senderInfo.GetAddress()),
    txFactory,
    msgFreeze,
)
if err != nil {
    panic(err)
}

After doing this, recipient is not allowed to transfer 500_000uabc from its account.

All the other features may be used in a similar fashion. More info is available in the documentationopen in new window

Last Updated: