How to Pass Zero Token Values to Hedera Contracts | Hedera Hedera Network Services Token Service Mint and configure tokens and accounts. Consensus Service Verifiable timestamps and ordering of events. Smart Contracts Run Solidity smart contracts. HBAR The Hedera network's native cryptocurrency. Insights How It Works Learn about Hedera from end to end. Explorers View live and historical data on Hedera. Dashboards Analyze network activity and metrics. Network Nodes Understand networks and node types. Devs Start Building Get Started Learn core concepts and build the future. Documentation Review the API and build using your favorite language. Developer Resources Integrations Plugins and microservices for Hedera. Fee Estimator Understand and estimate transaction costs. Open Source Hedera is committed to open, transparent code. Learning Center Learn about web3 and blockchain technologies. Grants Grants & accelerators for your project. Bounties Find bugs. Submit a report. Earn rewards. Ecosystem ECOSYSTEM Hedera Ecosystem Applications, developer tools, network explorers, and more. NFT Ecosystem Metrics Analyze on-chain and market NFT ecosystem metrics. CATEGORIES Web3 Applications Connect into the innovative startups decentralizing the web on Hedera. Enterprise Applications Learn about the Fortune 500 companies decentralizing the web on Hedera. Wallets & Custodians Create a Hedera account to manage HBAR, fungible tokens, and NFTs. Network Explorers Hedera mainnet and testnet graphical network explorers. Developer Tooling Third-party APIs, integrations, and plugins to build apps on Hedera. Grants & Accelerators Boost your project with support from the Hedera ecosystem. Partner Program Explore our partners to bring your vision into reality. Hedera Council Over 30 highly diversified organizations govern Hedera. Use Cases Hedera Solutions Asset Tokenization Studio Open source toolkit for tokenizing assets securely. Stablecoin Studio All-in-one toolkit for stablecoin solutions. Hedera Guardian Auditable carbon markets and traceability. Functional Use Cases Data Integrity & AI Reliable, secure, and ethically governed insights. Sustainability Enabling fair carbon markets with trust. Real-World Asset Tokenization Seamless tokenization of real-world assets and digital at scale. Consumer Engagement & Loyalty Mint, distribute, and redeem loyalty rewards. Decentralized Identity Maintain the lifecycle of credentials. Decentralized Logs Scalable, real-time timestamped events. DeFi Dapps built for the next-generation of finance. NFTs Low, fixed fees. Immutable royalties. Payments Scalable, real-time, and affordable crypto-payments. HBAR Overview Learn about Hedera's token, HBAR. Treasury Management Hedera’s report of the HBAR supply. Governance Decentralized Governance Hedera Council See the world's leading organizations that own Hedera. About Meet Hedera's Board of Directors and team. Journey Watch Hedera's journey to build an empowered digital future for all. Transparent Governance Public Policy Hedera's mission is to inform policy and regulation that impact the industry. Meeting Minutes Immutably recorded on Hedera. Roadmap Follow Hedera's roadmap in its journey to build the future. Resources Company What's New Partners Papers Careers Media Blog Technical Press Podcast Community Events Meetups Store Brand Navigation QUICKSTART How to Pass Zero Token Values to Hedera Contracts technical Oct 28, 2022 by Ed Marquez Head of Developer Relations In use cases like DeFi, contracts often need to mint, burn, or transfer 0 units of a token due to integer rounding. The implementation of HIP-564 in release 0.31 of Hedera Services enables smart contract developers to specify a zero value for contract functions performing token operations. The benefit is that transactions with 0 tokens go through successfully without having to implement if statements or qualification logic to check for 0 values. This also facilitates migrating existing applications to Hedera even more as the behavior when passing 0 tokens to contract functions is the same as that of other chains. Previously, passing 0 units of a token to a contract function was not possible. Those transactions would fail while reporting an invalid token amount. In this article, you will learn how to pass zero token values to smart contract operations on Hedera. Try It Yourself Get a Hedera testnet account This portal acts like a faucet, giving you 10,000 test HBAR every 24 hours If you’re new to Hedera, check out these steps to setup your development environment Use this Codesandbox to try the example Fork the sandbox Remember to provide testnet account credentials in the .env file Open a new terminal to execute index.js Get the example code from GitHub You Will Use These Tools Hedera JavaScript SDK (Documentation) Solidity and Libraries for Hedera Native Tokens (Documentation) Mirror node REST API (Learn More) Mirror node explorer (HashScan) Example: Transfer, Mint, and Wipe a Total of 0 Tokens This example guides you through the following steps: Creating an additional Hedera account (Alice), a contract, and a fungible HTS token Executing a contract function to transfer 0 tokens from the contract to Alice Executing a contract function to mint (or burn) 0 tokens Executing a contract function to wipe 0 tokens After completing all steps, your console should look something like this: 1. Create Accounts, Contract, and Fungible Token There are four entities in this scenario: Operator, Alice, a contract, and a fungible HTS token. Your testnet credentials from the Hedera portal should be used for the operator variables, which are used to initialize the Hedera client that submits transactions to the network and gets confirmations. Create a new account for Alice. Start by specifying the initial balance of the new account (initBalance) to be 10 HBAR Generate and record the private key for the account. Hedera supports ED25519 and ECDSA keys Use the helper function accountCreateFcn to create the new account The function returns the status of the transaction (aliceSt) and the new account ID (aliceId) The inputs are the newly generated private key (aliceKey), initBalance, and the client object Output to the console a link to the mirror node explorer, HashScan, showing information about the new accounts Deploy the contract ZeroTokenOperations – see the Solidity code in the second tab. Set the gas limit (gasLim) to be 4,000,000 and define the contract bytecode Use ContractFunctionParameters() from the SDK to specify the parameters for the contract constructor. Pass the Operator’s and Alice’s account IDs in Solidity format Deploy the contract using the helper function contracts.deployContractFcn. The function returns the contractId in Hedera format and contractAddress in Solidity format The inputs are the bytecode, constructorParams, gasLim, and client Output to the console contractId and contractAddress Create a fungible HTS token using the function contracts.executeContractPayableFcn The function outputs the record for the token creation operation (tokenCreateRec) The inputs are the contractId, the name of the contract function to execute (createHtsToken), gasLim, and client Query a mirror node using the function queries.mirrorTxQueryFcn to get information from the network about the token creation transaction and a URL to check the transaction in HashScan The query takes in the ID of the transaction, tokenCreateRec.transactionId Output to the console the token ID and the HashScan URL for the transaction Index.js Step 1 Index.js Step 1 ZeroTokenOperations.sol // STEP 1 =================================== console.log(`\nSTEP 1 ===================================\n`); console.log(`- Creating Hedera accounts, contract, and HTS token...\n`); // Accounts const initBalance = new Hbar(10); const aliceKey = PrivateKey.generateECDSA(); const [aliceSt, aliceId] = await accountCreateFcn(aliceKey, initBalance, client); console.log(`- Alice's account: https://hashscan.io/#/testnet/account/${aliceId}`); // Contract let gasLim = 4000000; const bytecode = contract.object; const constructorParams = new ContractFunctionParameters().addAddress(operatorId.toSolidityAddress()).addAddress(aliceId.toSolidityAddress()); const [contractId, contractAddress] = await contracts.deployContractFcn(bytecode, constructorParams, gasLim, client); console.log(`\n- Contract ID: ${contractId}`); console.log(`- Contract ID in Solidity address format: ${contractAddress}`); // Token const tokenCreateRec = await contracts.executeContractPayableFcn(contractId, "createHtsToken", gasLim, client); console.log(`\n- Contract call for token creation: ${tokenCreateRec.receipt.status}`); const [tokenCreateInfo, tokenCreateExpUrl] = await queries.mirrorTxQueryFcn(tokenCreateRec.transactionId); console.log(`- Token ID: ${tokenCreateInfo.data.transactions[1].entity_id}`); console.log(`- See details: ${tokenCreateExpUrl}`); // SPDX-License-Identifier: Apache-2.0 pragma solidity >=0.5.0 <0.9.0; pragma experimental ABIEncoderV2; import "../hts-precompiles/ExpiryHelper.sol"; // To alter the behavior of the SolidityPrecompileExample, re-compile this solidity file // (you will also need the other files in this directory) // and copy the outputted json file to ./PrecompileExample.json contract ZeroTokenOperations is ExpiryHelper { address payable owner; address payable aliceAccount; address fungibleToken; address nftToken; constructor(address payable _owner, address payable _aliceAccount) { owner = _owner; aliceAccount = _aliceAccount; } // In order for some functions (such as createFungibleToken) to work, the contract must possess the funds for // the function call. We are using ContractExecuteTransaction.setPayableAmount() to transfer some Hbar // to the contract's account at each step (which means this function must be payable), and then transferring // the excess Hbar back to the owner at the end of each step. function createHtsToken() external payable returns (int responseCode) { require(msg.sender == owner); IHederaTokenService.TokenKey[] memory keys = new IHederaTokenService.TokenKey[](1); // Set the admin key, supply key, pause key, and freeze key to the key of the account that executed function (INHERIT_ACCOUNT_KEY). keys[0] = createSingleKey( ADMIN_KEY_TYPE | SUPPLY_KEY_TYPE | PAUSE_KEY_TYPE | FREEZE_KEY_TYPE | WIPE_KEY_TYPE, INHERIT_ACCOUNT_KEY, bytes("") ); (responseCode, fungibleToken) = createFungibleToken( IHederaTokenService.HederaToken( "Example Fungible token", // name "E", // symbol address(this), // treasury "memo", true, // supply type, false -> INFINITE, true -> FINITE 1000, // max supply false, // freeze default (setting to false means that this token will not be initially frozen on creation) keys, // the keys for the new token // auto-renew fee paid by aliceAccount every 7,000,000 seconds (approx. 81 days). // This is the minimum auto renew period. createAutoRenewExpiry(aliceAccount, 7000000) ), 100, // initial supply 0 // decimals ); // send any excess Hbar back to the owner owner.transfer(address(this).balance); } function associateHtsToken() external returns (int responseCode) { require(msg.sender == owner); responseCode = associateToken(aliceAccount, fungibleToken); } function transferHtsToken() external returns (int responseCode) { require(msg.sender == owner); responseCode = transferToken( fungibleToken, address(this), // sender aliceAccount, // receiver 0 // amount to transfer ); } function mintHtsToken() external returns (int responseCode) { require(msg.sender == owner); uint64 newTotalSupply; int64[] memory mintedSerials; // applicable to NFT tokens only (responseCode, newTotalSupply, mintedSerials) = mintToken( fungibleToken, 0, // amount (applicable to fungible tokens only) new bytes[](0) // metadatas (applicable to NFT tokens only) ); require(newTotalSupply == 100 + 0); } function burnHtsToken() external returns (int responseCode) { require(msg.sender == owner); uint64 newTotalSupply; int64[] memory mintedSerials; // applicable to NFT tokens only (responseCode, newTotalSupply) = burnToken( fungibleToken, 0, // amount (applicable to fungible tokens only) mintedSerials // metadatas (applicable to NFT tokens only) ); require(newTotalSupply == 100 + 0); } function wipeHtsToken() external returns (int responseCode) { require(msg.sender == owner); responseCode = wipeTokenAccount( fungibleToken, aliceAccount, // owner of tokens to wipe from 0 // amount to transfer ); } } Helper Functions Using accountCreateFcn simplifies the account creation process and is reusable in case you need to create more accounts in the future. This function uses the AccountCreateTransaction() class of the SDK. We’ll use this modular approach throughout the article. Keep in mind that on Hedera an account and a token must be associated with each other before that account can transact the token. Notice that during creation, the account is set to have 10 automatic token associations. This way, no manual association is needed for the first 10 tokens the account wishes to transact. Visit this documentation page to learn more about token associations. The function contracts.deployContractFcn uses ContractCreateFlow() to store the bytecode and deploy the contract on Hedera. This single call handles for you the operations FileCreateTransaction(), FileAppendTransaction(), and ContractCreateTransaction(). The function contracts.executeContractPayableFcn uses ContractExecuteTransaction() in the SDK to call the specified contract function. Note that specifying .setPayableAmount() to 20 HBAR provides sufficient funds to the contract to complete some operations like creating a fungible HTS token. Lastly, the helper function queries.mirrorTxQueryFcn gets transaction information for the mirror nodes. The function introduces a delay of 10 seconds to allow for the propagation of information to the mirror nodes. It then formats the transaction ID and performs string operations to return a mirror REST API query and a mirror node explorer URL. accountCreateFcn accountCreateFcn contracts.deployContractFcn contracts.executeContractPayableFcn queries.mirrorTxQueryFcn async function accountCreateFcn(pvKey, iBal, client) { const response = await new AccountCreateTransaction() .setInitialBalance(iBal) .setKey(pvKey.publicKey) .setAlias(pvKey.publicKey.toEvmAddress()) .setMaxAutomaticTokenAssociations(10) .execute(client); const receipt = await response.getReceipt(client); return [receipt.status, receipt.accountId]; } export async function deployContractFcn(bytecode, params, gasLim, client) { const contractCreateTx = new ContractCreateFlow().setBytecode(bytecode).setGas(gasLim).setConstructorParameters(params); const contractCreateSubmit = await contractCreateTx.execute(client); const contractCreateRx = await contractCreateSubmit.getReceipt(client); const contractId = contractCreateRx.contractId; const contractAddress = contractId.toSolidityAddress(); return [contractId, contractAddress]; } export async function executeContractPayableFcn(cId, fcnName, gasLim, client) { const contractExecuteTx = new ContractExecuteTransaction().setContractId(cId).setGas(gasLim).setFunction(fcnName).setPayableAmount(new Hbar(20)); const contractExecuteSubmit = await contractExecuteTx.execute(client); const contractExecuteRec = await contractExecuteSubmit.getRecord(client); return contractExecuteRec; } export async function mirrorTxQueryFcn(txIdRaw) { // Query a mirror node for information about the transaction const delay = (ms) => new Promise((res) => setTimeout(res, ms)); await delay(10000); // Wait for 10 seconds before querying a mirror node const txIdPretty = prettify(txIdRaw.toString()); const mirrorNodeExplorerUrl = `https://hashscan.io/testnet/transaction/${txIdPretty}`; const mirrorNodeRestApi = `https://testnet.mirrornode.hedera.com/api/v1/transactions/${txIdPretty}`; const mQuery = await axios.get(mirrorNodeRestApi); return [mQuery, mirrorNodeExplorerUrl]; } function prettify(txIdRaw) { const a = txIdRaw.split("@"); const b = a[1].split("."); return `${a[0]}-${b[0]}-${b[1]}`; } Console Output: STEP 1 =================================== - Creating Hedera accounts, contract, and HTS token... - Alice's account: https://hashscan.io/#/testnet/account/0.0.48760462 - Contract ID: 0.0.48760464 - Contract ID in Solidity address format: 0000000000000000000000000000000002e80690 - Contract call for token creation: SUCCESS - Token ID: 0.0.48760465 - See details: https://hashscan.io/testnet/transaction/0.0.2520793-1666975193-540118792 2. Execute a Contract Function to Transfer 0 Tokens From the Solidity code in the previous step, the fungible token created has an initial supply of 100 units and the contract is the treasury for the token. Use the helper function contracts.executeContractFcn to execute the contract function transferHtsToken In the Solidity code, the contract function transferHtsToken transfers 0 units of the token from the contract to Alice and the transaction still goes through successfully as expected The helper function returns the record object of the transaction (transferFtRec), which is used to obtain the status and ID of the transaction The inputs are the contract ID (contractId), the contract function to execute, gasLim, and client Output to the console: The status of the contract call The mirror node explorer URL with more details about the transaction (after using the helper function queries.mirrorTxQueryFcn) // STEP 2 =================================== console.log(`\nSTEP 2 ===================================\n`); console.log(`- Transferring zero tokens from contract to Alice...\n`); const transferFtRec = await contracts.executeContractFcn(contractId, "transferHtsToken", gasLim, client); console.log(`- Contract call for token transfer: ${transferFtRec.receipt.status} \n`); const [transferFtInfo, transferExpUrl] = await queries.mirrorTxQueryFcn(transferFtRec.transactionId); console.log(`- See details: ${transferExpUrl}`); Copy Helper Functions The function contracts.executeContractFcn also uses ContractExecuteTransaction() in the SDK to call the specified contract function. However, this helper function does not set a payable amount of HBAR. export async function executeContractFcn(cId, fcnName, gasLim, client) { const contractExecuteTx = new ContractExecuteTransaction().setContractId(cId).setGas(gasLim).setFunction(fcnName); const contractExecuteSubmit = await contractExecuteTx.execute(client); const contractExecuteRec = await contractExecuteSubmit.getRecord(client); return contractExecuteRec; } Copy Console Output: STEP 2 =================================== - Transferring zero tokens from contract to Alice... - Contract call for token transfer: SUCCESS - See details: https://hashscan.io/testnet/transaction/0.0.2520793-1666975205-711024658 3. Execute a Contract Function to Mint or Burn 0 Tokens In this step: Use the helper function contracts.executeContractFcn again to execute the contract function mintHtsToken (or burnHtsToken) As seen in the Solidity code in the first step, this contract function adds (or removes) 0 units to the existing token supply and the transaction completes successfully as expected The helper function returns the record object of the transaction (mintFtRec or burnFtRec) Output to the console: The status of the contract call The mirror node explorer URL with more details about the transaction (after using the helper function queries.mirrorTxQueryFcn) // STEP 3 =================================== console.log(`\nSTEP 3 ===================================\n`); console.log(`- Minting (or burning) zero tokens...\n`); const mintFtRec = await contracts.executeContractFcn(contractId, "mintHtsToken", gasLim, client); console.log(`- Contract call for minting zero tokens: ${mintFtRec.receipt.status} \n`); const [mintFtInfo, mintExpUrl] = await queries.mirrorTxQueryFcn(mintFtRec.transactionId); console.log(`- See details: ${mintExpUrl}`); // const burnFtRec = await contracts.executeContractFcn(contractId, "burnHtsToken", gasLim, client); // console.log(`\n - Contract call for burning zero tokens: ${burnFtRec.receipt.status} \n`); // const [burnFtInfo, burnExpUrl] = await queries.mirrorTxQueryFcn(burnFtRec.transactionId); // console.log(`- See details: ${burnExpUrl}`); Copy Console Output: STEP 3 =================================== - Minting (or burning) zero tokens... - Contract call for minting zero tokens: SUCCESS - See details: https://hashscan.io/testnet/transaction/0.0.2520793-1666975218-508125238 4. Execute a Contract Function to Wipe 0 Tokens Finally, in this step: Use the helper function contracts.executeContractFcn one more time to execute the contract function wipeHtsToken As shown in the Solidity code, this contract function wipes 0 units of the fungible token from Alice’s account and the transaction completes successfully as expected The helper function returns the record object of the transaction (wipeFtRec) Output to the console: The status of the contract call The mirror node explorer URL with more details about the transaction (after using the helper function queries.mirrorTxQueryFcn) The last step is to join the Hedera Developer Discord! // STEP 4 =================================== console.log(`\nSTEP 4 ===================================\n`); console.log(`- Wiping zero tokens...\n`); const wipeFtRec = await contracts.executeContractFcn(contractId, "wipeHtsToken", gasLim, client); console.log(`- Contract call for wiping zero tokens: ${wipeFtRec.receipt.status} \n`); const [wipeFtInfo, wipeExpUrl] = await queries.mirrorTxQueryFcn(wipeFtRec.transactionId); console.log(`- See details: ${wipeExpUrl}`); console.log(` ==================================================== THE END - NOW JOIN: https://hedera.com/discord ====================================================\n`); Copy Console Output: STEP 4 =================================== - Wiping zero tokens... - Contract call for wiping zero tokens: SUCCESS - See details: https://hashscan.io/testnet/transaction/0.0.2520793-1666975228-509195333 ==================================================== 🎉🎉 THE END - NOW JOIN: https://hedera.com/discord ==================================================== Summary Now you know how to perform contract actions with zero token units on Hedera using the JavaScript SDK and Solidity. You can try this example with the other officially supported SDKs for Java, Go, and Swift. Keep in mind that zero token operations can also be performed with the SDK. For instance, a token transfer as follows completes successfully: //Transfer token const tokenTransferTx = new TransferTransaction() .addTokenTransfer(tokenId, operatorId, 0) // deduct 0 tokens .addTokenTransfer(tokenId, aliceId, 0) // increase balance by 0 .freezeWith(client); const tokenTransferSign = await tokenTransferTx.sign(operatorKey); const tokenTransferSubmit = await tokenTransferSign.execute(client); //Verify the transaction reached consensus const tokenTransferRec = await tokenTransferSubmit.getRecord(client); Copy Continue Learning Open a Testnet Account Try Examples and Tutorials Join the Developer Discord Read the Learning Center Share This Back to blog What is gRPC, gRPC-Web, and Proxies? Ed Marquez Pragmatic Blockchain Design Patterns – Integrating Blockchain into Business Processes Michiel Mulders Zero Cost EthereumTransaction on Success: Hedera's New Fee Model for Relay Operators Oliver Thorn Hedera Adopts Chainlink Standard for Cross-Chain Interoperability To Accelerate Ecosystem Adoption Hedera Team Hedera Developer Highlights March 2025 Michiel Mulders Hedera Release Cycle Overview Ed Marquez View All Posts Sign up for the newsletter CONNECT WITH US Transparency Open Source Audits & Standards Sustainability Commitment Carbon Offsets Governance Hedera Council Public Policy Treasury Management Meeting Minutes LLC Agreement Node Requirements Community Events Meetups HBAR Telegram Developer Discord Twitter Community Support FAQ Network Status Developer Discord StackOverflow Brand Brand Guidelines Built on Hedera Logo Hedera Store About Team Partners Journey Roadmap Careers Contact General Inquiry Public Relations © 2018-2025 Hedera Hashgraph, LLC. All trademarks and company names are the property of their respective owners. All rights in the Deutsche Telekom mark are protected by Deutsche Telekom AG. All rights reserved. Hedera uses the third party marks with permission. Terms of Use  |  Privacy Policy