Skip to main content
The HTS system contract at 0x167 lets a Solidity contract create native HTS tokens. The resulting token is a real HTS token: same association rules, same mirror node REST responses, same HashScan view as an SDK-created one. You can operate on it through the HTS interface or through ERC-20 / ERC-721 redirects.

At a glance

Why use this instead of a plain ERC-20?

Two practical reasons. The first is compliance features. HTS tokens get native support for KYC keys, freeze, pause, wipe, supply caps, and royalty fees, and the network enforces them. If you build the same controls into an ERC-20 contract, you write them yourself and pay gas every time they fire. The second is pricing. HTS operations are priced in USD and paid in HBAR by the network. ERC-20 operations are priced in EVM gas, which scales with whatever you put in your contract. If you don’t need any of that, a plain ERC-20 is simpler and works fine. The choice is per-token; nothing stops you from using both in one app.

Required imports

The helper library is HederaTokenService.sol, published with its companion contracts in the @hiero-ledger/hiero-contracts npm package. Install it in your project:
Import the contracts from the package root: Keep the package layout. HederaTokenService.sol imports ../common/HederaResponseCodes.sol, so copying the files into one flat folder breaks the build.

Example: fungible token

Example: non-fungible token

Paying for it

Token creation costs HBAR. The caller has to forward enough msg.value to cover the TokenCreate transaction fee, which buys the token its initial auto-renew period (90 days, the defaultAutoRenewPeriod of 7,776,000 seconds in HederaTokenService.sol). The current base fee is around $1 USD worth of HBAR for both fungible and non-fungible tokens. Three important things to know about how Hedera handles the money:
  • msg.value must cover the create price, in tinybars. HederaTokenService.createFungibleToken and createNonFungibleToken forward msg.value to the HTS system contract. It requires at least the HAPI TokenCreate price plus a 20% surcharge, minus the fixed gas portion it already charged. Inside the EVM, msg.value is in tinybars (8 decimals), so don’t scale it by 10**10.
  • Gas is charged on consumption. Per HIP-1249, Hedera refunds 100% of unused gas, and the per-transaction limit is 15M (HIP-185). Set a generous gas limit; you only pay for what you actually use.
  • Excess msg.value is not refunded. Anything you forward beyond what the system contract consumes for the TokenCreate fee stays in the calling contract’s balance. There is no automatic refund to the EOA.
The practical pattern: compute the tinybars via the Exchange Rate system contract just before the call, or build a refund step into your contract that returns leftover HBAR to msg.sender after the create call.
A token created with custom fees costs more: the fee schedule lists TokenCreate with custom fees at $2.00, so with the 20% surcharge forward about $2.40 worth of tinybars (for example 24 * 10**9 tinycents) instead of the $1.50 above.
If msg.value doesn’t cover the fee, the create call returns INSUFFICIENT_TX_FEE and a zero token address instead of reverting, so always check the response code (the examples above require SUCCESS). If you can’t compute the exact amount, pass extra and refund the leftover from inside your contract. Don’t expect Hedera to do it for you.
Calling createFungible from a client through the JSON-RPC relay (ethers, viem, a wallet) is different: there, value is in 18-decimal weibars, so pass the tinybar amount times 10**10 (in ethers, parseEther("2") sends 2 HBAR). The network converts it to tinybars before the contract sees it. See HBAR decimal places.

Token keys

The HederaToken.tokenKeys array decides who can do what to the token. Each entry is a (keyType, keyValue) pair: Build each entry with KeyHelper.getSingleKey(KeyType.X, KeyValueType.Y, address). Common KeyValueType values: CONTRACT_ID (the contract signs implicitly), INHERIT_ACCOUNT_KEY (use the caller’s key), ED25519 / SECP256K1 (provide a raw public key). token-service/KeyHelper.sol has no METADATA key type, so getSingleKey can’t build the metadata key from HIP-657.

See also

HTS System Contract Overview

Full function reference for the HTS precompile, including transfer, mint, burn, freeze, pause, and KYC operations.