How to Manage Hedera Tokens using Smart Contracts
Screen Shot 2022 01 27 at 9 52 08 PM
Jul 14, 2022
by Francesco Coacci
Developer Evangelist

In this article, you will learn how to use a keyless Solidity smart contract to manage your Hedera tokens. Specifically, you will create a token that has a contract as the treasury account and auto-renew account.

Having a keyless contract ensures a transparent and trustless workflow, where the contract itself is responsible for managing various aspects of the token.

Try It Yourself

  • Get a Hedera testnet account
  • Use this Codesandbox to try the example using SDK.
  • Use this Codesandbox to try the example using Solidity
    • Fork the sandbox
    • Remember to provide testnet account credentials in the .env file
    • Open a new terminal to execute index.js
  • Get the example using SDK from Github
  • Get the example using Solidity from Github

Set Up a Contract as Treasury using the SDKs

First, to simplify account creation, you can introduce this helper function:

Code Snippet Background
async function accountCreator(privateKey, initialBalance, client) {
   const response = await new AccountCreateTransaction()
       .setInitialBalance(new Hbar(initialBalance))
       .setKey(privateKey.publicKey)
       .execute(client);
   const receipt = await response.getReceipt(client);
   return receipt.accountId;
}

So now to generate an admin account just call the accountCreator function like this:

Code Snippet Background
// Admin account creation
const adminKey = PrivateKey.generateED25519();
const adminId = await accountCreator(adminKey, 50, client);

The contract you will set as a token treasury in this example will be a simple empty contract as you won’t need any particular functionality.

Solidity Contract:

Code Snippet Background
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
 
contract TreasuryContract {
 
   // EMPTY CONTRACT
 
}

You can get your contract bytecode directly from this Codesandbox or by compiling your contract on Remix IDE or using solc.

Now you can create the contract that will be the treasury of your fungible token using the ContractCreateFlow() method and specifying your adminKey. For now, you need to switch the operator using client.setOperator() to your admin account as you need to sign the transaction using the adminKey.

Code Snippet Background
const bytecode = fs.readFileSync("./TreasuryContract_sol_TreasuryContract.bin");
 
// Switch operator to admin to sign transactions
client.setOperator(adminId, adminKey);
 
// Create contract with adminKey
const contractCreate = new ContractCreateFlow()
    .setGas(100000)
    .setBytecode(bytecode)
    .setAdminKey(adminKey);
const contractCreateTx = await contractCreate.execute(client);
const contractCreateRx = await contractCreateTx.getReceipt(client);
const contractId = contractCreateRx.contractId;
 
console.log("Contract created with ID: " + contractId);

Console Output:

Manage tokens sdk 1

It’s time to create your fungible token, if you are not familiar with token creation you can check out this article.

Code Snippet Background
// Create fungible token using contract as treasury
const tokenCreate = new TokenCreateTransaction()
    .setTokenName("USD Bar")
    .setTokenSymbol("USDB")
    .setTreasuryAccountId(AccountId.fromString(contractId))
    .setInitialSupply(10000)
    .setDecimals(2)
    .setAutoRenewAccountId(contractId)
    .setAutoRenewPeriod(7000000)
    .setMaxTransactionFee(new Hbar(30))
    .freezeWith(client);
const signedTokenCreate = await tokenCreate.sign(adminKey);
const tokenCreateTx = await signedTokenCreate.execute(client);
const tokenCreateRx = await tokenCreateTx.getReceipt(client);
const tokenId = tokenCreateRx.tokenId;
 
console.log("Token created with ID: " + tokenId);

Console Output:

Manage tokens sdk 2

After creating your token, you need to remove the admin key from your contract. To proceed you need to execute a ContractUpdateTransaction() and set as new admin key an empty KeyList().

Code Snippet Background
const contractUpdate = await new ContractUpdateTransaction()
    .setContractId(contractId)
    .setAdminKey(new KeyList())
    .execute(client);
const contractUpdateRx = await contractUpdate.getReceipt(client);
 
console.log("Contract update status: " + contractUpdateRx.status.toString());

Console Output:

Manage tokens sdk 3

Done! Now your keyless contract is successfully managing the HTS token.

Setup a Contract as Treasury Using Solidity

Setting the contract as token treasury is straightforward when creating a token using a smart contract function.

Let’s start by creating a contract with a createFungible method responsible for token creation. Notice how inside this function, the token treasury and token auto-renew account is the contract itself ( address(this) ).

Make sure to include these files in your working directory by downloading them from the contracts folder here:

    • IHederaTokenService.sol
    • HederaTokenService.sol
    • HederaResponseCodes.sol
    • ExpiryHelper.sol
    • FeeHelper.sol
    • KeyHelper.sol

Solidity Contract:

Code Snippet Background
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
 
import './HederaResponseCodes.sol';
import './IHederaTokenService.sol';
import './HederaTokenService.sol';
import './ExpiryHelper.sol';
 
contract TreasuryContract is ExpiryHelper{
 
   function createFungible(
       string memory name,
       string memory symbol,
       uint initialSupply,
       uint decimals,
       uint32 autoRenewPeriod
   ) external payable returns (address createdTokenAddress) {
 
       IHederaTokenService.HederaToken memory token;
       token.name = name;
       token.symbol = symbol;
       token.treasury = address(this);
 
       // create the expiry schedule for the token using ExpiryHelper
       token.expiry = getAutoRenewExpiry(address(this), autoRenewPeriod);
 
       // call HTS precompiled contract, passing initial supply and decimals
       (int responseCode, address tokenAddress) =
                   HederaTokenService.createFungibleToken(token, initialSupply, decimals);
 
       if (responseCode != HederaResponseCodes.SUCCESS) {
           revert ();
       }
 
       createdTokenAddress = tokenAddress;
   }
 
}

If you want to compile your contract you can either use Remix IDE or solc. You can also get contract bytecode through this Codesandbox.

This time to create your contract you won’t need to set any admin key:

Code Snippet Background
const bytecode = fs.readFileSync("./binaries/TreasuryContract_sol_TreasuryContract.bin");
 
const contractCreate = new ContractCreateFlow()
    .setGas(100000)
    .setBytecode(bytecode);
const contractCreateTx = await contractCreate.execute(client);
const contractCreateRx = await contractCreateTx.getReceipt(client);
const contractId = contractCreateRx.contractId;
 
console.log("Contract created with ID: " + contractId);

Console Output:

Manage tokens sol 1

And to create your token with your contract as treasury you just need to execute your contract function.

Code Snippet Background
// Create FT using precompile function
const createToken = new ContractExecuteTransaction()
    .setContractId(contractId)
    .setGas(300000) // Increase if revert
    .setPayableAmount(20) // Increase if revert
    .setFunction("createFungible",
        new ContractFunctionParameters()
        .addString("USD Bar") // FT name
        .addString("USDB") // FT symbol
        .addUint256(10000) // FT initial supply
        .addUint256(2) // FT decimals
        .addUint32(7000000)); // auto renew period
 
const createTokenTx = await createToken.execute(client);
const createTokenRx = await createTokenTx.getRecord(client);
const tokenIdSolidityAddr = createTokenRx.contractFunctionResult.getAddress(0);
const tokenId = AccountId.fromSolidityAddress(tokenIdSolidityAddr);
 
console.log("Token created with ID: " + tokenId);

Console Output:

Manage tokens sol 2

Done! You just learned a completely trustless way to manage Hedera tokens using a smart contract.

For further explanation on this article, please contact us on Hedera Discord Server.

Check out the Code

Check out the example using SDK on Github

Check out the example using Solidity on Github

Continue Learning

Hedera Service Solidity Libraries (Hedera Documentation)

Hedera Token Service (Hedera Documentation)

What are Smart Contracts? (Hedera Learning Center)