Create a Token

Introduction to Creating a Token

This tutorial will walk you through a TokenCreateTransaction and create a fungible Hedera Token Service (HTS) token. You will learn how to configure essential token properties, set up necessary keys and permissions, and submit your transaction to the Hedera network.

What you will accomplish

By the end of this tutorial, you will be able to:

  • Create a new fungible token using HTS.

  • Query the transaction via Mirror Node API.

  • View your transaction on a Mirror Node Explorer.


Prerequisites

Before you begin, you should have completed the following tutorials:


Step 1: Navigate to the hts example in the project directory

From the root directory of the hedera-future-world project CD (change directories) to the token create transaction example.

cd hts

If you completed a previous example in the series you can use to go back to the root directory and cd into this example.

cd ../hts

If you want to get back to the root directory, you can CD out from any directory with this command.

cd ../

You can follow along through the code walkthrough or skip ahead to execute the program here.


Step 2: Guided Code Walkthrough

Open the HTS token script (/hts/script-hts-ft...) in a code editor like VS Code, IntelliJ, or a Gitpod instance. The imports at the top include modules for interacting with the Hedera network via the SDK. The @hashgraph/sdk enables account management and transactions like creating a token while the dotenv package loads environment variables from the .env file, such as the operator account ID, private key, and name variables.

script-hts-ft.js
import {
    Client,
    PrivateKey,
    AccountId,
    TokenCreateTransaction,
    TokenType,
} from '@hashgraph/sdk';
import dotenv from 'dotenv';
import {
    createLogger,
} from '../util/util.js';

const logger = await createLogger({
    scriptId: 'htsFt',
    scriptCategory: 'task',
});
let client;

async function scriptHtsFungibleToken() {
    logger.logStart('Hello Future World - HTS Fungible Token - start');

    // Read in environment variables from `.env` file in parent directory
    dotenv.config({ path: '../.env' });
    logger.log('Read .env file');

    // Initialize the operator account
    const operatorIdStr = process.env.OPERATOR_ACCOUNT_ID;
    const operatorKeyStr = process.env.OPERATOR_ACCOUNT_PRIVATE_KEY;
    if (!operatorIdStr || !operatorKeyStr) {
        throw new Error('Must set OPERATOR_ACCOUNT_ID and OPERATOR_ACCOUNT_PRIVATE_KEY environment variables');
    }
    const operatorId = AccountId.fromString(operatorIdStr);
    const operatorKey = PrivateKey.fromStringECDSA(operatorKeyStr);
    client = Client.forTestnet().setOperator(operatorId, operatorKey);
    logger.log('Using account:', operatorIdStr);
}

Create a Hedera Testnet Client

To set up your Hedera Testnet client, create the client and configure the operator using your Testnet account ID and private key. The operator account covers transaction and query fees in HBAR, with all transactions requiring a signature from the operator's private key for authorization.

script-hts-ft.js
// The client operator ID and key is the account that will be automatically set to pay for the transaction fees for each transaction
client = Client.forTestnet().setOperator(operatorId, operatorKey);

//Set the default maximum transaction fee (in Hbar)
client.setDefaultMaxTransactionFee(new Hbar(100));

//Set the maximum payment for queries (in Hbar)
client.setDefaultMaxQueryPayment(new Hbar(50));

To avoid encountering the INSUFFICIENT_TX_FEE error while executing transactions, you can also specify the maximum transaction fee limit through the .setDefaultMaxTransactionFee() method and the maximum query payment through the .setDefaultMaxQueryPayment() method to control costs, ensuring your client operates within your desired financial limits on the Hedera Testnet.

🚨 How to resolve the INSUFFICIENT_TX_FEE error

To resolve this error, you must adjust the max transaction fee to a higher value suitable for your needs.

Here is a simple example addition to your code:

Copy

const maxTransactionFee = new Hbar(XX); // replace XX with desired fee in Hbar

In this example, you can set maxTransactionFee to any value greater than 5 HBAR (or 500,000,000 tinybars) to avoid the "INSUFFICIENT_TX_FEE" error for transactions greater than 5 HBAR. Please replace XX with the desired value.

To implement this new max transaction fee, you use the setDefaultMaxTransactionFee() method as shown below:

Copy

client.setDefaultMaxTransactionFee(maxTransactionFee);

Create a Token Create Transaction

To create a fungible token using the HTS, start by instantiating a TokenCreateTransaction. Set the token type to TokenType.FungibleCommon, which functions similarly to ERC-20 tokens on Ethereum, meaning all token units are interchangeable.

Configure the token with the required properties:

  • Token Name: The name of the token.

  • Token Symbol: A publicly visible symbol for the token (e.g., hBARK).

  • Treasury Account ID: The account holding the initial token supply.

Other optional fields

Default values will apply if left unspecified:

  • No admin key makes the token immutable (unchangeable).

  • No supply key fixes the token's supply (no minting or burning).

  • No token type defaults to fungible.

Unlike NFTs, fungible tokens don’t require decimals or an initial supply of zero. For example, an initial supply of 10,000 units is represented as 1000000 in code to account for two decimals.

script-hts-ft.js
// Create the token create transaction
const tokenCreateTx = await new TokenCreateTransaction()
    //Set the transaction memo
    .setTransactionMemo(`Hello Future World token - ${logger.version}`)
    // HTS `TokenType.FungibleCommon` behaves similarly to ERC20
    .setTokenType(TokenType.FungibleCommon)
    // Configure token options: name, symbol, decimals, initial supply
    .setTokenName(`${yourName} coin`)
    //Set the token symbol
    .setTokenSymbol(logger.scriptId)
    //Set the token decimals to 2
    .setDecimals(2)
    //Set the initial supply of the token to 10,000
    .setInitialSupply(1000000)
    // Configure token access permissions: treasury account, admin, freezing
    .setTreasuryAccountId(operatorId)
    //Set the admin key of the the token to the operator account
    .setAdminKey(operatorKey) 
    //Set the freeze default value to false
    .setFreezeDefault(false)
    // Freeze the transaction to prepare for signing
    .freezeWith(client);
    
// Get the transaction ID of the transaction. 
// The SDK automatically generates and assigns a transaction ID when the transaction is created
const tokenCreateTxId = tokenCreateTx.transactionId;
logger.log('The token create transaction ID: ', tokenCreateTxId.toString());
Key terminology for HTS token create transaction
  • Token Type: Fungible tokens, declared using TokenType.FungibleCommon, may be thought of as analogous to ERC20 tokens. Note that HTS also supports another token type, TokenType.NonFungibleUnique, which may be thought of as analogous to ERC721 tokens.

  • Token Name: This is the full name of the token. For example, "Singapore Dollar".

  • Token Symbol: This is the abbreviation of the token's name. For example, "SGD".

  • Decimals: This is the number of decimal places the currency uses. For example, 2 mimics "cents", where the smallest unit of the token is 0.01 (1/100) of a single token.

  • Initial Supply: This is the number of units of the token to "mint" when first creating the token. Note that this is specified in the smallest units, so 1_000_000 initial supply when decimals is 2, results in 10_000 full units of the token being minted. It might be easier to think about it as "one million cents equals ten thousand dollars".

  • Treasury Account ID: This is the account for which the initial supply of the token is credited. For example, using operatorId in this examplewould mean that your specified testnet account receives all the tokens when they are minted.

  • Admin Key: This is the key that is authorized to administrate this token. For example, using operatorKey would mean that your testnet account key would authorize (required to sign related transactions) to perform actions such as minting additional supply.

Sign and Submit the Token Create Transaction

The transaction must be signed using the operator's private key. This step ensures that the transaction is authenticated and authorized by the operator. Once signed, the transaction is submitted to the Hedera Testnet, where it will be processed and validated.

After submission, get the transaction receipt. The receipt contains important information about the transaction, such as its status and any resulting data. The receipt is used to confirm that the transaction was successfully processed and to get the new token ID. The token ID uniquely identifies the newly created token on the Hedera network and is required for any subsequent operations with the token.

script-hts-ft.js
// Sign the transaction with the operator's private key
const tokenCreateTxSigned = await tokenCreateTx.sign(operatorKey);

// Submit the signed transaction to the Hedera network
const tokenCreateTxSubmitted = await tokenCreateTxSigned.execute(client);

// Get the transaction receipt
const tokenCreateTxReceipt = await tokenCreateTxSubmitted.getReceipt(client);

// Get and log the newly created token ID to the console
const tokenId = tokenCreateTxReceipt.tokenId;
logger.log('tokenId:', tokenId.toString());

Query the Account Token Balance Mirror Node API

Mirror nodes store the history of transactions that took place on the network. To query the token balance of your account, use the Mirror Node API with the path /api/v1/tokens/{tokenId}. This API endpoint allows you to get the balance information about a specific token by replacing {tokenId} with your actual token ID. Since the treasury account was configured as your own account, it will hold the entire initial supply of the token.

  • Specify tokenId within the URL path

The constructed tokenVerifyMirrorNodeApiUrl string should look like this:

script-hts-ft.js
const tokenVerifyMirrorNodeApiUrl =
    `https://testnet.mirrornode.hedera.com/api/v1/tokens/${tokenId.toString()}`;
Learn more about Mirror Node APIs

You can explore the Mirror Node APIs interactively via its Swagger page: Hedera Testnet Mirror Node REST API.

You can perform the same Mirror Node API query as tokenVerifyMirrorNodeApiUrl above. This is what the relevant part of the Swagger page would look like when doing so:

➡ You can learn more about the Mirror Nodes via its documentation: REST API.


Step 3: Run the Token Create Transaction Script

In the terminal, cd into the ./hts directory and run the token create transaction script:

node script-hts-ft.js

Sample output:

🏁 Hello Future World - HTS Fungible Token - start  …
Read .env file
Using account: 0.0.1455

🟣 Creating new HTS token  …
↪️ file:///workspace/hello-future-world-x/hts/script-hts-ft...
The token create transaction ID:  0.0.46495@1722971043.397070956
tokenId: 0.0.5878530 

🟣 View the token on HashScan  …
↪️ file:///workspace/hello-future-world-x/hts/script-hts-ft...
Paste URL in browser:
 https://hashscan.io/testnet/token/0.0.46599 

🟣 Get token data from the Hedera Mirror Node  …
↪️ file:///workspace/hello-future-world-x/hts/script-hts-ft...
The token Hedera Mirror Node API URL:
 https://testnet.mirrornode.hedera.com/api/v1/tokens/0.0.46599
The name of this token: bguiz coin
The total supply of this token: 1000000 

🎉 Hello Future World - HTS Fungible Token - complete  …

Copy and paste the HashScan URL in your browser to view the token creation transaction details and verify that:

HTS transaction in Hashscan, with annotated items to check.
  • The token should exist, and its "token ID" should match tokenId. (1)

  • The "name" and "symbol" should be shown as the same values derived from your name (or nickname) that you chose earlier. (2)

  • The "treasury account" should match operatorId. (3)

  • Both the "total supply" and "initial supply" should be 10,000. (4)

Note: "total supply" and "initial supply" are not displayed as 1,000,000 because of the two decimal places configured. Instead, these are displayed as 10,000.00.


Code Check ✅


Complete

Congratulations, you have completed the Create a Token tutorial in the Getting Started series for the Web2 Developers learning path! 🎉🎉🎉!

You have learned how to:


Next Steps

Continue building on Hedera with another tutorial in the series to explore more Hedera services.

Create a Topic

Learn how to create topics and publish messages using the Hedera Consensus Service (HCS).

Transfer HBAR

Learn how to transfer HBAR, Hedera's native cryptocurrency, between accounts.

Have questions? Join the Hedera Discord and post them in the developer-general channel or ask on Stack Overflow.

Last updated

#2871: HIP-423 long term scheduled transactions

Change request updated