Deploy a Contract Using the Hedera Token Service

Summary

In this example, you will learn how to create a Solidity contract that interacts with the Hedera Token Service (HTS). The initial release of this feature supports token mint, burn, associate, dissociate, and transfer transactions.

The example does not cover the environment setup or creating certain variables that may be seen in the code blocks. The full coding example can be found at the end of the page.

Smart contract entity auto renewal and expiry will be enabled in a future release. Please check out HIP-16 for more information.

Prerequisites

We recommend you complete the following introduction to get a basic understanding of Hedera transactions. This example does not build upon the previous examples.

1. Create Your "HTS" Smart Contract

In this example, you will associate a token to an account and transfer tokens to the associated account by interacting with the HTS contract deployed to Hedera. The HTS contract has three functions that allow you to associate, transfer, and dissociate tokens from a Hedera account.

  • tokenAssociate

  • tokenTransfer

  • tokenDissociate

The HTS.sol will serve as a reference to the contract that was compiled. The HTS.json file contains the data.bytecode.object field that will be used to store the contract bytecode in a file on the Hedera network.

To write a contract using HTS, you will need to add the HTS Solidity support libraries to your project and import them into your contract. Please see the HTS.sol example for reference. The IHederaTokenService.sol will need to be in the same directory as the other two files. An explanation of the functions can be found here.

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.6.12;

import "./HederaTokenService.sol";
import "./HederaResponseCodes.sol";


contract HTS is HederaTokenService {

    function tokenAssociate(address sender, address tokenAddress) external {
        int response = HederaTokenService.associateToken(sender, tokenAddress);

        if (response != HederaResponseCodes.SUCCESS) {
            revert ("Associate Failed");
        }
    }

    function tokenTransfer(address tokenId, address fromAccountId , address toAccountId , int64 tokenAmount) external {
        int response = HederaTokenService.transferToken(tokenId, fromAccountId, toAccountId, tokenAmount);

        if (response != HederaResponseCodes.SUCCESS) {
            revert ("Transfer Failed");
        }
    }

    function tokenDissociate(address sender, address tokenAddress) external {
        int response = HederaTokenService.dissociateToken(sender, tokenAddress);

        if (response != HederaResponseCodes.SUCCESS) {
            revert ("Dissociate Failed");
        }
    }
}

2. Store the Smart Contract Bytecode on Hedera

Create a file using the FileCreateTransaction() API to store the hex-encoded byte code of the "HTS" contract. Once the file is created, you can obtain the file ID from the receipt of the transaction.

Note: The bytecode is required to be hex-encoded. It should not be the actual data the hex represents.

//Import the HTS.json file from the resources folder
ClassLoader cl = HTS.class.getClassLoader();

Gson gson = new Gson();
JsonObject jsonObject;

//Get the json file
InputStream jsonStream = cl.getResourceAsStream("HTS.json");
jsonObject = gson.fromJson(new InputStreamReader(jsonStream, StandardCharsets.UTF_8), JsonObject.class);


//Store the "object" field from the HTS.json file as hex-encoded bytecode
String object = jsonObject.getAsJsonObject("data").getAsJsonObject("bytecode").get("object").getAsString();
byte[] bytecode = object.getBytes(StandardCharsets.UTF_8);

//Create a file on Hedera and store the hex-encoded bytecode
FileCreateTransaction fileCreateTx = new FileCreateTransaction()
        .setKeys(privateKeyTest)
        .setContents(bytecode);

//Submit the file to the Hedera test network
TransactionResponse submitTx = fileCreateTx.execute(client);

//Get the receipt of the file create transaction
TransactionReceipt fileReceipt = submitTx.getReceipt(client);

//Get the file ID
FileId newFileId = fileReceipt.fileId;

//Log the file ID
System.out.println("The smart contract byte code file ID is " + newFileId);

//v2.6.0 Hedera Java SDK

3. Deploy a Hedera Smart Contract

Create the contract and set the file ID to the file that contains the hex-encoded bytecode from the previous step. You will need to set the gas high enough to deploy the contract. The gas should be estimated to be within 25% of the actual gas cost to avoid paying extra gas. You can read more about gas and fees here.

Note: You will need to set the gas value high enough to deploy the contract. If you don't have enough gas, you will receive an INSUFFICIENT_GAS response. If you set the value too high you will be refunded a maximum of 20% of the amount that was set for the transaction.

//Deploy the contract
ContractCreateTransaction contractTx = new ContractCreateTransaction()
        //The contract bytecode file
        .setBytecodeFileId(newFileId)
        //The max gas to reserve for this transaction
        .setGas(2_000_000);

//Submit the transaction to the Hedera test network
TransactionResponse contractResponse = contractTx.execute(client);

//Get the receipt of the file create transaction
TransactionReceipt contractReceipt = contractResponse.getReceipt(client);

//Get the smart contract ID
ContractId newContractId = contractReceipt.contractId;

//Log the smart contract ID
System.out.println("The smart contract ID is " + newContractId);

//v2.6.0 Hedera Java SDK

4. Call the tokenAssociate Contract Function

The tokenAssociate function in the contract will associate a token that was created using the native Hedera Token Service. You can create a new token using HTS or using an existing token in this example. Use the ContractExecuteTransaction() API to call the contract's tokenAssociate function. You will pass the token ID and account ID in Solidity address format to the contract function. The contract function parameters must be provided in the order expected by the function to execute successfully.

//Associate the token to an account using the HTS contract
ContractExecuteTransaction associateToken = new ContractExecuteTransaction()
     //The contract to call
     .setContractId(newContractId)
     //The gas for the transaction
     .setGas(2_000_000)
     //The contract function to call and parameters to pass
     .setFunction("tokenAssociate", new ContractFunctionParameters()
              //The account ID to associate the token to
              .addAddress(accountIdTest.toSolidityAddress())
              //The token ID to associate to the account
              .addAddress(tokenId.toSolidityAddress()));

//Sign with the account key to associate and submit to the Hedera network
TransactionResponse associateTokenResponse = associateToken.freezeWith(client).sign(privateKeyTest).execute(client);

System.out.println("The transaction status: " +associateTokenResponse.getReceipt(client).status);

5. Get the tokenAssociate Transaction Record

The contract execute transaction that triggered a subsequent token associate transaction in the contract is an example of a nested transaction. The contract execute transaction is the parent transaction and the token associate transaction is referred to as the child transaction. Both parent and child transactions share the same payer account ID and transaction valid duration with the exception of the child transaction having a nonce value at the end. The nonce value increments for each subsequent child transaction.

  • Parent Transaction ID: 0.0.2252@1640119571.329880313

  • Child Transaction ID: 0.0.2252@1640119571.329880313/1

The parent transaction record, receipt, or response will only return the parent transaction information. If you would like to get the child transaction record, receipt, or response you will need to use the TransactionRecordQuery() or TransactionReceiptQuery() and set children equal to true. The child transaction record will also have a parentConsensusTimestamp field populated with the consensus timestamp of the parent transaction.

To confirm the account was associated with the token, request the balance of the account. The account balance should show the ID of the token that was associated with a zero balance.

//Get the child token associate transaction record
TransactionRecord childRecords = new TransactionRecordQuery()
        //Set the bool flag equal to true
        .setIncludeChildren(true)
        //The transaction ID of th parent contract execute transaction
        .setTransactionId(associateTokenResponse.transactionId)
        .execute(client);

System.out.println("The transaction record for the associate transaction" +childRecords.children.get(0));

//The balance of the account
AccountBalance accountBalance3 = new AccountBalanceQuery()
        .setAccountId(accountIdTest)
        .execute(client);

System.out.println("The " + tokenId + " should now be associated to my account: " + accountBalance3.tokens);

6. Call the tokenTransfer Contract Function

Transfer 100 units of the token to the account that was associated with the token. You will use the ContractExecuteTransaction() API and set the contract function to tokenTransfer. The contract function parameters must be provided in the order of the function expects to receive them.

The transaction must be signed by the account that is sending the tokens. In this case, it is the treasury account.

You can verify the transfer was successful by checking the account token balance!

//Transfer the new token to the account
//Contract function params need to be in the order of the paramters provided in the tokenTransfer contract function
ContractExecuteTransaction tokenTransfer = new ContractExecuteTransaction()
     .setContractId(newContractId)
     .setGas(2_000_000)
     .setFunction("tokenTransfer", new ContractFunctionParameters()
          //The ID of the token
          .addAddress(tokenId.toSolidityAddress())
          //The account to transfer the tokens from
          .addAddress(treasuryAccountId.toSolidityAddress())
          //The account to transfer the tokens to
          .addAddress(accountIdTest.toSolidityAddress())
          //The number of tokens to transfer
         .addInt64(100));

//Sign the token transfer transaction with the treasury account to authorize the transfer and submit
ContractExecuteTransaction signTokenTransfer = tokenTransfer.freezeWith(client).sign(treasuryKey);

//Submit transfer transaction
TransactionResponse submitTransfer = signTokenTransfer.execute(client);

//Get transaction status
Status txStatus = submitTransfer.getReceipt(client).status;

//Verify your account received the 100 tokens
 AccountBalance newAccountBalance = new AccountBalanceQuery()
      .setAccountId(accountIdTest)
      .execute(client);

System.out.println("My new account balance is " +newAccountBalance.tokens);

Note: Check out our smart contract mirror node rest APIs that return information about a contract like contract results and logs!

Congratulations ๐ŸŽ‰! You have learned how to deploy a contract using the Hedera Token Service and completed the following:

  • Created a smart contract that calls HTS transactions

  • Associated an HTS token by using the deployed contract

  • Requested a transaction record for a nested transaction

  • Transferred tokens using the deployed contract

Code Check โœ…

Java
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.hedera.hashgraph.sdk.*;

import io.github.cdimascio.dotenv.Dotenv;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;


public class HTS {

    public static void main(String[] args) throws TimeoutException, PrecheckStatusException, ReceiptStatusException, InterruptedException, IOException {

        AccountId accountIdTest = AccountId.fromString(Dotenv.load().get("PREVIEWNET_ACCOUNT_ID"));
        PrivateKey privateKeyTest = PrivateKey.fromString(Dotenv.load().get("PREVIEWNET_PRIVATE_KEY"));

        Client client = Client.forPreviewnet();
        client.setOperator(accountIdTest, privateKeyTest);

        //Import the HTS.json file from the resources folder
        ClassLoader cl = HTS.class.getClassLoader();

        Gson gson = new Gson();
        JsonObject jsonObject;

        //Get the json file
        InputStream jsonStream = cl.getResourceAsStream("HTS.json");
        jsonObject = gson.fromJson(new InputStreamReader(jsonStream, StandardCharsets.UTF_8), JsonObject.class);


        //Store the "object" field from the HTS.json file as hex-encoded bytecode
        String object = jsonObject.getAsJsonObject("data").getAsJsonObject("bytecode").get("object").getAsString();
        byte[] bytecode = object.getBytes(StandardCharsets.UTF_8);

        //Create a treasury Key
        PrivateKey treasuryKey = PrivateKey.generateED25519();

        //Create a treasury account
        AccountCreateTransaction treasuryAccount = new AccountCreateTransaction()
                .setKey(treasuryKey)
                .setInitialBalance(new Hbar(100))
                .setAccountMemo("treasury account");

        //Submit the account create transaction
        TransactionResponse submitAccountCreateTx = treasuryAccount.execute(client);

        //Get the receipt of the transaction
        TransactionReceipt newAccountReceipt = submitAccountCreateTx.getReceipt(client);

        //Get the treasury account ID
        AccountId treasuryAccountId = newAccountReceipt.accountId;
        System.out.println("The new account ID is " +treasuryAccountId);

        //Create a token to interact with
        TokenCreateTransaction createToken = new TokenCreateTransaction()
                .setTokenName("HSCS demo")
                .setTokenSymbol("H")
                .setTokenType(TokenType.FUNGIBLE_COMMON)
                .setTreasuryAccountId(treasuryAccountId)
                .setInitialSupply(500);

        //Submit the token create transaction
        TransactionResponse submitTokenTx = createToken.freezeWith(client).sign(treasuryKey).execute(client);

        //Get the token ID
        TokenId tokenId = submitTokenTx.getReceipt(client).tokenId;
        System.out.println("The new token ID is " +tokenId);


        //Create a file on Hedera and store the hex-encoded bytecode
        FileCreateTransaction fileCreateTx = new FileCreateTransaction()
                .setKeys(privateKeyTest)
                .setContents(bytecode);

        //Submit the file to the Hedera test network
        TransactionResponse submitTx = fileCreateTx.execute(client);

        //Get the receipt of the file create transaction
        TransactionReceipt fileReceipt = submitTx.getReceipt(client);

        //Get the file ID
        FileId newFileId = fileReceipt.fileId;

        //Log the file ID
        System.out.println("The smart contract byte code file ID is " + newFileId);


        //Deploy the contract
        ContractCreateTransaction contractTx = new ContractCreateTransaction()
                //The contract bytecode file
                .setBytecodeFileId(newFileId)
                //The max gas to reserve for this transaction
                .setGas(2_000_000);

        //Submit the transaction to the Hedera test network
        TransactionResponse contractResponse = contractTx.execute(client);

        //Get the receipt of the file create transaction
        TransactionReceipt contractReceipt = contractResponse.getReceipt(client);

        //Get the smart contract ID
        ContractId newContractId = contractReceipt.contractId;

        //Log the smart contract ID
        System.out.println("The smart contract ID is " + newContractId);

        //Associate the token to an account using the HTS contract
        ContractExecuteTransaction associateToken = new ContractExecuteTransaction()
                //The contract to call
                .setContractId(newContractId)
                //The gas for the transaction
                .setGas(2_000_000)
                //The contract function to call and parameters to pass
                .setFunction("tokenAssociate", new ContractFunctionParameters()
                        //The account ID to associate the token to
                        .addAddress(accountIdTest.toSolidityAddress())
                        //The token ID to associate to the account
                        .addAddress(tokenId.toSolidityAddress()));

        //Sign with the account key to associate and submit to the Hedera network
        TransactionResponse associateTokenResponse = associateToken.freezeWith(client).sign(privateKeyTest).execute(client);

        System.out.println("The transaction status: " +associateTokenResponse.getReceipt(client).status);

        //Get the child token associate transaction record
        TransactionRecord childRecords = new TransactionRecordQuery()
                //Set the bool flag equal to true
                .setIncludeChildren(true)
                //The transaction ID of th parent contract execute transaction
                .setTransactionId(associateTokenResponse.transactionId)
                .execute(client);

        System.out.println("The transaction record for the associate transaction" +childRecords.children.get(0));

        //The balance of the account
        AccountBalance accountBalance3 = new AccountBalanceQuery()
                .setAccountId(accountIdTest)
                .execute(client);

        System.out.println("The " + tokenId + " should now be associated to my account: " + accountBalance3.tokens);

        //Transfer the new token to the account
        //Contract function params need to be in the order of the paramters provided in the tokenTransfer contract function
        ContractExecuteTransaction tokenTransfer = new ContractExecuteTransaction()
                .setContractId(newContractId)
                .setGas(2_000_000)
                .setFunction("tokenTransfer", new ContractFunctionParameters()
                        //The ID of the token
                        .addAddress(tokenId.toSolidityAddress())
                        //The account to transfer the tokens from
                        .addAddress(treasuryAccountId.toSolidityAddress())
                        //The account to transfer the tokens to
                        .addAddress(accountIdTest.toSolidityAddress())
                        //The number of tokens to transfer
                        .addInt64(100));

        //Sign the token transfer transaction with the treasury account to authorize the transfer and submit
        ContractExecuteTransaction signTokenTransfer = tokenTransfer.freezeWith(client).sign(treasuryKey);

        //Submit transfer transaction
        TransactionResponse submitTransfer = signTokenTransfer.execute(client);

        //Get transaction status
        Status txStatus = submitTransfer.getReceipt(client).status;

        //Get the transaction status
        System.out.println("The transfer transaction status " +txStatus);

        //Verify your account received the 100 tokens
        AccountBalance newAccountBalance = new AccountBalanceQuery()
                .setAccountId(accountIdTest)
                .execute(client);

        System.out.println("My new account balance is " +newAccountBalance.tokens);
    }
}
JavaScript
require("dotenv").config();

const {
    Client,
    PrivateKey,
    ContractCreateTransaction,
    ContractExecuteTransaction,
    FileCreateTransaction,
    ContractFunctionParameters,
    Hbar,
    AccountId,
    AccountCreateTransaction,
    TokenCreateTransaction,
    TokenType,
    TransactionRecordQuery,
    AccountBalanceQuery
} = require("@hashgraph/sdk");

// Import the compiled contract
const htsContract = require("./HTS.json");

async function htsContract() {
    //Grab your Hedera testnet account ID and private key from your .env file
    const accountIdTest = AccountId.fromString(process.env.PREVIEWNET_ACCOUNT_ID);
    const accountKeyTest = PrivateKey.fromStringED25519(process.env.PREVIEWNET_PRIVATE_KEY);

   
    // If we weren't able to grab it, we should throw a new error
    if (accountIdTest == null ||
        accountKeyTest == null ) {
        throw new Error("Environment variables myAccountId and myPrivateKey must be present");
    }
 

    const client = Client.forPreviewnet();
    client.setOperator(accountIdTest, accountKeyTest);

   //Get the contract bytecode
    const bytecode = htsContract.data.bytecode.object;

    //Treasury Key
    const treasuryKey = PrivateKey.generateED25519();

    //Create token treasury account
    const treasuryAccount = new AccountCreateTransaction()
            .setKey(treasuryKey)
            .setInitialBalance(new Hbar(100))
            .setAccountMemo("treasury account");
    
    //Submit the transaction to a Hedera network
    const submitAccountCreateTx = await treasuryAccount.execute(client);
    
    //Get the receipt of the transaction
    const newAccountReceipt = await submitAccountCreateTx.getReceipt(client);

    //Get the account ID from the receipt
    const treasuryAccountId = newAccountReceipt.accountId;

    console.log("The new account ID is " +treasuryAccountId);

    //Create a token to interact with
    const createToken =  new TokenCreateTransaction()
            .setTokenName("HTS demo")
            .setTokenSymbol("H")
            .setTokenType(TokenType.FungibleCommon)
            .setTreasuryAccountId(treasuryAccountId)
            .setInitialSupply(500);
            
    //Sign with the treasury key
    const signTokenTx = await createToken.freezeWith(client).sign(treasuryKey)
    
    //Submit the transaction to a Hedera network
    const submitTokenTx = await signTokenTx.execute(client);

    //Get the token ID from the receipt
    const tokenId = await (await submitTokenTx.getReceipt(client)).tokenId;

    //Log the token ID
    console.log("The new token ID is " +tokenId);

    //Create a file on Hedera and store the hex-encoded bytecode
    const fileCreateTx = new FileCreateTransaction()
        .setContents(bytecode);

    //Submit the file to the Hedera test network signing with the transaction fee payer key specified with the client
    const submitTx = await fileCreateTx.execute(client);

    //Get the receipt of the file create transaction
    const fileReceipt = await submitTx.getReceipt(client);

    //Get the file ID from the receipt
    const bytecodeFileId = fileReceipt.fileId;

    //Log the file ID
    console.log("The smart contract byte code file ID is " +bytecodeFileId)

    //Deploy the contract instance
    const contractTx = await new ContractCreateTransaction()
        //The bytecode file ID
        .setBytecodeFileId(bytecodeFileId)
        //The max gas to reserve
        .setGas(2000000);

    //Submit the transaction to the Hedera test network
    const contractResponse = await contractTx.execute(client);

    //Get the receipt of the file create transaction
    const contractReceipt = await contractResponse.getReceipt(client);

    //Get the smart contract ID
    const newContractId = contractReceipt.contractId;

    //Log the smart contract ID
    console.log("The smart contract ID is " + newContractId);

    //Associate the token to an account using the HTS contract
    const associateToken = new ContractExecuteTransaction()
        //The contract to call
        .setContractId(newContractId)
        //The gas for the transaction
        .setGas(2000000)
        //The contract function to call and parameters to pass
        .setFunction("tokenAssociate", new ContractFunctionParameters()
             //The account ID to associate the token to
             .addAddress(accountIdTest.toSolidityAddress())
             //The token ID to associate to the account
             .addAddress(tokenId.toSolidityAddress()));

    //Sign with the account key and submit to the Hedera network
    const signTx = await associateToken.freezeWith(client).sign(accountKeyTest);
    
    //Submit the transaction
    const submitAssociateTx = await signTx.execute(client);

    //Get the receipt
    const txReceipt = await submitAssociateTx.getReceipt(client);

    //Get transaction status
    const txStatus = txReceipt.status

    console.log("The associate transaction was " + txStatus.toString())

    //Get the token associate transaction record
    const childRecords = new TransactionRecordQuery()
        //Set children equal to true for child records
        .setIncludeChildren(true)
        //The parent transaction ID
        .setTransactionId(submitAssociateTx.transactionId)
        .setQueryPayment(new Hbar(10))
        .execute(client);
    

    console.log("The transaction record for the associate transaction" +JSON.stringify((await childRecords).children));

    //The balance of the account
    const accountBalance = new AccountBalanceQuery()
        .setAccountId(accountIdTest)
        .execute(client);

    console.log("The " + tokenId + " should now be associated to my account: " + (await accountBalance).tokens.toString());

     //Transfer the new token to the account
    //Contract function params need to be in the order of the parameters provided in the tokenTransfer contract function
    const tokenTransfer = new ContractExecuteTransaction()
            .setContractId(newContractId)
            .setGas(2000000)
            .setFunction("tokenTransfer", new ContractFunctionParameters()
                    //The ID of the token
                    .addAddress(tokenId.toSolidityAddress())
                    //The account to transfer the tokens from
                    .addAddress(treasuryAccountId.toSolidityAddress())
                    //The account to transfer the tokens to
                    .addAddress(accountIdTest.toSolidityAddress())
                    //The number of tokens to transfer
                    .addInt64(100));

    //Sign the token transfer transaction with the treasury account to authorize the transfer and submit
    const signTokenTransfer = await tokenTransfer.freezeWith(client).sign(treasuryKey);

    //Submit transfer transaction
    const submitTransfer = await signTokenTransfer.execute(client);

    //Get transaction status
    const transferTxStatus = await (await submitTransfer.getReceipt(client)).status;

    //Get the transaction status
    console.log("The transfer transaction status " +transferTxStatus.toString());

    //Verify your account received the 100 tokens
    const newAccountBalance = new AccountBalanceQuery()
            .setAccountId(accountIdTest)
            .execute(client);

    console.log("My new account balance is " +(await newAccountBalance).tokens.toString());

}

void htsContract();
Go
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/hashgraph/hedera-sdk-go/v2"
	"github.com/joho/godotenv"
)

type contract struct {
	// ignore the link references since it is empty
	Object    string `json:"object"`
	OpCodes   string `json:"opcodes"`
	SourceMap string `json:"sourceMap"`
}

func main() {

	//Loads the .env file and throws an error if it cannot load the variables from that file corectly
	err := godotenv.Load(".env")
	if err != nil {
		panic(fmt.Errorf("Unable to load enviroment variables from .env file. Error:\n%v\n", err))
	}

	//Grab your testnet account ID and private key from the .env file
	accountIdTest, err := hedera.AccountIDFromString(os.Getenv("PREVIEWNET_ACCOUNT_ID"))
	if err != nil {
		panic(err)
	}

	privateKeyTest, err := hedera.PrivateKeyFromString(os.Getenv("PREVIEWNET_PRIVATE_KEY"))
	if err != nil {
		panic(err)
	}

	//Create your testnet client
	client := hedera.ClientForPreviewnet()
	client.SetOperator(accountIdTest, privateKeyTest)

	//Treasury Key
	treasuryKey, err := hedera.PrivateKeyGenerateEd25519()

	//Create token treasury account
	treasuryAccount := hedera.NewAccountCreateTransaction().
		SetKey(treasuryKey).
		SetInitialBalance(hedera.NewHbar(100)).
		SetAccountMemo("treasury account")

	//Submit the transaction to a Hedera network
	submitAccountCreateTx, err := treasuryAccount.Execute(client)

	//Get the receipt of the transaction
	newAccountReceipt, err := submitAccountCreateTx.GetReceipt(client)

	//Get the account ID from the receipt
	treasuryAccountId := *newAccountReceipt.AccountID

	fmt.Printf("The treasury account ID: %v\n", treasuryAccountId)

	//Create a token to interact with
	createToken := hedera.NewTokenCreateTransaction().
		SetTokenName("HTS demo").
		SetTokenSymbol("H").
		SetTokenType(hedera.TokenTypeFungibleCommon).
		SetTreasuryAccountID(treasuryAccountId).
		SetInitialSupply(500)

	//Freeze the transaction for signing
	freezeTokenTx, err := createToken.FreezeWith(client)

	//Sign with the treasury key to authorize the transaction
	signTokenTx := freezeTokenTx.Sign(treasuryKey)

	//Submit the transaction
	submitTokenTx, err := signTokenTx.Execute(client)

	//Get the receipt of the transaction
	getTokenReceipt, err := submitTokenTx.GetReceipt(client)

	//Get the token ID
	tokenId := *getTokenReceipt.TokenID

	//Log the token ID
	fmt.Printf("The token ID: %v\n", tokenId)

	//Get the HTS contract bytecode
	rawSmartContract, err := ioutil.ReadFile("./hts.json")
	if err != nil {
		println(err.Error(), ": error reading hts.json")
		return
	}

	var contract contract = contract{}

	err = json.Unmarshal([]byte(rawSmartContract), &contract)
	if err != nil {
		println(err.Error(), ": error unmarshaling the json file")
		return
	}

	smartContractByteCode := []byte(contract.Object)

	// Upload a file containing the byte code
	fileCreateTx, err := hedera.NewFileCreateTransaction().
		SetContents([]byte(smartContractByteCode)).
		Execute(client)

	if err != nil {
		println(err.Error(), ": error creating file")
		return
	}

	//Get the receipt of the transaction
	fileTxReceipt, err := fileCreateTx.GetReceipt(client)
	if err != nil {
		println(err.Error(), ": error getting file create transaction receipt")
		return
	}

	//Get the bytecode file ID
	byteCodeFileID := *fileTxReceipt.FileID

	fmt.Printf("The contract bytecode file ID: %v\n", byteCodeFileID)

	// Deploy the contract instance
	contractTransactionID, err := hedera.NewContractCreateTransaction().
		//The max gas for the transaction
		SetGas(2000000).
		//The contract bytecode file ID
		SetBytecodeFileID(byteCodeFileID).
		Execute(client)

	if err != nil {
		println(err.Error(), ": error creating contract")
		return
	}

	//Get the contract receipt
	contractReceipt, err := contractTransactionID.GetReceipt(client)

	//Get the contract contract ID
	contractId := *contractReceipt.ContractID

	//Log the contract ID
	fmt.Printf("The contract ID %v\n", contractId)

	//Construct a Solidity address from token ID
	tokenIdToSol := tokenId.ToSolidityAddress()

	//Construct a Solidity address from account ID
	accountIdToSol := accountIdTest.ToSolidityAddress()

	//Add the function parameters in order for tokenAssociate
	contractParamsAccount, err := hedera.NewContractFunctionParameters().AddAddress(accountIdToSol)

	contractParamsToken, err := contractParamsAccount.AddAddress(tokenIdToSol)

	//Associate an account with a token
	associateTx, err := hedera.NewContractExecuteTransaction().
		//The contract ID
		SetContractID(contractId).
		//The max gas
		SetGas(2000000).
		//The contract function to call and parameters
		SetFunction("tokenAssociate", contractParamsToken).
		Execute(client)

	if err != nil {
		println(err.Error(), ": error executing contract")
		return
	}

	//Get the receipt
	associateTxReceipt, err := associateTx.GetReceipt(client)

	//Get transaction status
	txStatus := associateTxReceipt.Status

	fmt.Printf("The associate transaction status %v\n", txStatus)

	//Get the child transaction record
	childRecord, err := hedera.NewTransactionRecordQuery().
		SetIncludeChildren(true).
		SetTransactionID(associateTx.TransactionID).
		Execute(client)
	if err != nil {
		println(err.Error(), ": error getting record")
		return
	}

	//Log the child record
	fmt.Printf("The associate child transaction record %v\n", childRecord.Children)

	//The balance of the account
	accountBalance, err := hedera.NewAccountBalanceQuery().
		SetAccountID(accountIdTest).
		Execute(client)

	if err != nil {
		println(err.Error(), ": error getting balance")
		return
	}

	//Log the account balance
	fmt.Printf("The account token balance %v\n", accountBalance.Tokens)
	
	//Transfer the token
	transferTx := hedera.NewContractExecuteTransaction().
		//The contract ID
		SetContractID(contractId).
		//The max gas
		SetGas(2000000).
		//The contract function to call and parameters
		SetFunction("tokenTransfer", contractParamsAmount)

	//Sign with treasury key to authorize the transfer from the treasury account
	signTx, err := transferTx.Sign(treasuryKey).Execute(client)

	if err != nil {
		println(err.Error(), ": error executing contract")
		return
	}
	//Get the receipt
	transferTxReceipt, err := signTx.GetReceipt(client)

	if err != nil {
		println(err.Error(), ": error getting receipt")
		return
	}

	//Get transaction status
	transferTxStatus := transferTxReceipt.Status

	fmt.Printf("The transfer transaction status %v\n", transferTxStatus)

	//Verify the transfer by checking the balance
	transferAccountBalance, err := hedera.NewAccountBalanceQuery().
		SetAccountID(accountIdTest).
		Execute(client)

	if err != nil {
		println(err.Error(), ": error getting balance")
		return
	}

	//Log the account token balance
	fmt.Printf("The account token balance %v\n", transferAccountBalance.Tokens)

}

Happy Building! Feel free to reach out if you have any questions:

Have a question? Ask it on StackOverflow

Last updated