Learn how to launch a simple fungible token on Hedera testnet. A fungible token is a divisible digital asset (think loyalty points, stablecoins, or stocks) created by the Hedera Token Service (HTS).
Prerequisites
A Hedera testnet operator account ID and DER-encoded private key (from the Quickstart)
A few testnet HBAR (ℏ) to cover the ≈ $1 token-creation fee
We will use the operator account as the token’s treasury (the account that initially holds the supply).
Note
You can always check the " ✅ Code Check" section at the bottom of each page to view the entire code if you run into issues. You can also post your issue to the respective SDK channel in our Discord community here.
Project Setup and SDK Installation
Open your terminal and create a directory hedera-examples directory. Then change into the newly created directory:
mkdirhedera-examples&&cdhedera-examples
Initialize a node.js project in this new directory:
npminit-y
Ensure you have Node.jsv18 or later installed on your machine. Then, install the JavaScript SDK.
npminstall--save@hashgraph/sdk
Update your package.json file to enable ES6 modules and configure the project:
In your project's root directory, initialize modules and pull in the Go SDK:
go mod init create_token_demo
go get github.com/hiero-ledger/hiero-sdk-go/v2@latest
go mod tidy
Environment Variables
Set your operator credentials as environment variables. Your OPERATOR_ID is your testnet account ID. Your OPERATOR_KEY is your testnet account's corresponding ECDSA private key.
Load your operator credentials from environment variables and initialize your Hedera testnet client. This client will connect to the Hedera test network and use your operator account to sign transactions and pay transaction fees.
// Load your operator credentials
const operatorId = process.env.OPERATOR_ID;
const operatorKey = process.env.OPERATOR_KEY;
// Initialize your testnet client and set operator
const client = Client.forTestnet()
.setOperator(operatorId, operatorKey);
// Load your operator credentials
AccountId operatorId = AccountId.fromString(System.getenv("OPERATOR_ID"));
PrivateKey operatorKey = PrivateKey.fromString(System.getenv("OPERATOR_KEY"));
// Initialize your testnet client and set operator
Client client = Client.forTestnet().setOperator(operatorId, operatorKey);
// Load your operator credentials
operatorId, _ := hedera.AccountIDFromString(os.Getenv("OPERATOR_ID"))
operatorKey, _ := hedera.PrivateKeyFromString(os.Getenv("OPERATOR_KEY"))
// Initialize the client for testnet
client := hedera.ClientForTestnet()
client.SetOperator(operatorId, operatorKey)
Step 2: Generate Token Keys
Generate an ECDSA key and use it for both admin and supply operations.
Why keys?
adminKey lets you update or delete the token; supplyKey authorizes mint and burn operations. We use the same key for both roles to keep this tutorial simple.
// Generate keys that control your token
const supplyKey = PrivateKey.generateECDSA(); // can mint/burn
const adminKey = supplyKey; // can update and delete (reuse for simplicity)
// Generate keys that control your token
PrivateKey supplyKey = PrivateKey.generateECDSA(); // can mint/burn
PrivateKey adminKey = supplyKey; // can update/delete (reuse for simplicity)
// Generate keys that control your token
supplyKey, _ := hedera.PrivateKeyGenerateEcdsa() // can mint/burn
adminKey := supplyKey // can update/delete (reuse for simplicity)
‼️ Security reminder: Keep your private keys secure - anyone with access can control your token.
Step 3: Create Your First Token on Hedera
Build a TokenCreateTransaction with your token properties like name, symbol, and initial supply. Sign the transaction with your admin key, submit it to the network, and Hedera returns a unique token ID that identifies your token.
// Build the transaction
const transaction = new TokenCreateTransaction()
.setTokenName("Demo Token") // readable name
.setTokenSymbol("DEMO")
.setDecimals(2) // 100 = 1.00 token
.setInitialSupply(100_000) // 1 000.00 DEMO in treasury
.setSupplyType(TokenSupplyType.Finite)
.setMaxSupply(100_000) // cap equals initial supply
.setTreasuryAccountId(operatorId)
.setAdminKey(adminKey.publicKey) // optional
.setSupplyKey(supplyKey.publicKey) // optional for fungible tokens
.setTokenMemo("Created via tutorial")
.freezeWith(client);
// Sign with the admin key, execute with the operator, and get receipt
const signedTx = await transaction.sign(adminKey);
const txResponse = await signedTx.execute(client);
const receipt = await txResponse.getReceipt(client);
const tokenId = receipt.tokenId;
console.log(`\nFungible token created: ${tokenId.toString()}`)
// Build the transaction
TokenCreateTransaction transaction = new TokenCreateTransaction()
.setTokenName("Demo Token")
.setTokenSymbol("DEMO")
.setDecimals(2)
.setInitialSupply(100_000)
.setSupplyType(TokenSupplyType.FINITE)
.setMaxSupply(100_000)
.setTreasuryAccountId(operatorId)
.setAdminKey(adminKey.getPublicKey())
.setSupplyKey(supplyKey.getPublicKey())
.setTokenMemo("Created via tutorial")
.freezeWith(client);
// Sign with the admin key, execute with the operator, and get receipt
TokenCreateTransaction signedTx = transaction.sign(adminKey);
TransactionResponse txResponse = signedTx.execute(client);
TransactionReceipt receipt = txResponse.getReceipt(client);
TokenId tokenId = receipt.tokenId;
System.out.println("\nFungible token created: " + tokenId);
// Build the transaction
transaction, _ := hedera.NewTokenCreateTransaction().
SetTokenName("Demo Token").
SetTokenSymbol("DEMO").
SetDecimals(2).
SetInitialSupply(100_000).
SetSupplyType(hedera.TokenSupplyTypeFinite).
SetMaxSupply(100_000).
SetTreasuryAccountID(operatorId).
SetAdminKey(adminKey.PublicKey()).
SetSupplyKey(supplyKey.PublicKey()).
SetTokenMemo("Created via tutorial").
FreezeWith(client)
// Sign with the admin key, execute with the operator, and get receipt
signedTx := transaction.Sign(adminKey)
txResponse, _ := signedTx.Execute(client)
receipt, _ := txResponse.GetReceipt(client)
tokenId := *receipt.TokenID
fmt.Printf("Token created: %s\n", tokenId.String())
Step 4: Query the Treasury Balance Using Mirror Node API
Use the Mirror Node REST API to check your treasury account's token balance. Mirror nodes provide free access to network data without transaction fees.
{accountId} - Your treasury account (operator account)
{tokenId} - Token ID from the creation transaction
Why this endpoint?
This endpoint queries the account's token balances, filtered by the specific token ID. It returns detailed information including the balance and token metadata, making it ideal for verifying the treasury account holds the expected initial supply.
🎉 Great work! You've successfully created your first fungible token on Hedera and verified its balance using the Mirror Node API. Guard your private keys and happy building!