Hedera released the Hedera Token Service on February 9th, 2021. This service offers configuration and issuance of native fungible and non-fungible tokens in a fast, secure, and sustainable way with low, predictable fees.
At Hedera, we’re often asked how Hedera Token Service (HTS) tokens map to the most ubiquitous fungible and non-fungible token models: ERC20, ERC721 and ERC1155. Let’s dive deeper into the Hedera Token Service and highlight how Hedera token models and ERC token models overlap.
In this blog post, we’ll take a look at how Hedera token models extend beyond ERC models through built-in configurable compliance and programmability, such as custom token fees and scheduled transactions.
While there may have been evolutions of the ERC models over time, or developer-own customizations, we have used the following OpenZeppelin implementation as a reference:
ERC20: Fungible Token Standard (link)
ERC721: Non-Fungible Token Standard (link)
ERC1155: Multi Token Standard (link)
Note: Only functions and getters that are external in the contract definitions, along with events are documented here. Likewise, interfaces have not been included.
Tokens on Hedera are identified by a Token Id, made of three numerals separated by a dot; for example, 0.0.123344. The Token Id is allocated by the network when a token is first issued and cannot change. The Id is the equivalent to a smart contract address for an ERC token.
Just like ERC tokens, there is no enforcement of the uniqueness of a token’s name or symbol. Two tokens with the same name and/or symbol may exist on the Hedera network but they will have different token Ids.
The ERC20 token specification is the simplest standard of the three. It maps to the Hedera Token Service as follows:
A tokenCreate transaction constructs a new token on Hedera, returning the token’s unique identifier known as Token Id (e.g. 0.0.123433).
The getInfo query for a particular Token Id will return the properties of the token. This includes its name, symbol, decimals, and total supply.
Querying the balance of an account for a given Account Id will return a list of tokens which are held by the account and the balance of each token.
The cryptoTransfer Hedera transaction enables a token to be transferred between accounts. Moreover, the same transaction supports complex atomic swaps including one or more tokens and optionally some hbar in the same transaction. Should any of the specified transfers fail due to insufficient balances or incorrect signatures, the entire transaction is cancelled.
An example atomic transfer may be Alice sending 5 blue tokens to Bob and Bob sending 2 yellow tokens to Carol while Carol sends 20 hbar to Alice.
Subject to the token having a supplyKey, additional tokens may be minted in a quantity expressed in the decimal value of the token via the mintToken transaction. Note that if the token has a finite supply, it is not possible to mint more than the maxSupply specified when the token was issued (see ERC20Capped below).
Subject to the token having a supplyKey, tokens may be burnt in a quantity expressed in the decimal value of the token via the burnToken transaction.
The allowance features of an ERC20 token comprise approve, transferFrom, increaseAllowance and decreaseAllowance. There is no direct equivalent of these methods in the Token Service API, however through the use of the native multi-sig capabilities of Hedera Accounts, it is possible to effect an allowance as explained below.
First, create a new account with at least two keys (could be more to enable allowance towards multiple people at once), these keys would be subject to a threshold such as 1 of M and one of the keys would be known to the token holder wishing to allow others to spend on his/her behalf.
Second, transfer the desired amount of tokens to be approved to this new account from the token holding account. From this point, any of the key holders in the threshold list is able to transfer tokens from this new account to any other. This satisfies the approve (transfer to the new account) and transferFrom (a key from the keylist can transfer from the new account) methods in the ERC20 contract.
Similarly, by transferring additional tokens to this new account, or inversely, transferring from the new account, the allowance can be increased or decreased, thus satisfying the increaseAllowance and decreaseAllowance features of the ERC20 contract.
Finally, querying the balance of the new account would return the remaining allowance which is equivalent to the allowance getter in the ERC20 contract.
The equivalent of an event is a transaction recorded on a mirror node, by querying a mirror node for CRYPTOTRANSFER transactions for a given account, the token transfers included in the transaction can be identified.
Similarly to the transfer event, this would require querying a mirror node for CRYPTOTRANSFER transactions involving the approving account and token.
This contract implements additional burn-related methods.
Subject to the token being issued with a wipeKey, a quantity of tokens may be removed from an account which will also burn the same quantity from the token itself.
When issuing a new token, it’s possible to specify the token’s supplyType which may be FINITE or INFINITE.
In the event the supplyType is FINITE, a maxSupply amount has to be specified which essentially determines the supply cap on the token.
getInfo on a token will return the maxSupply and supplyType for the token. If the maxSupply is 0, the token is considered to have infinite supply (in fact limited to 2^63-1).
There is no equivalent implementation of this, except that if the token has a freezeKey, the appropriate key holder(s) could iteratively freeze all accounts that hold a balance of the token, then later unfreeze those accounts if necessary.
The owner of the token can be identified by querying the token via a getInfo and inspecting the treasuryAccountId for the token.
This is native to the Token Service whereby operations on the token can only occur if signed by the appropriate private key(s).
If the token has an adminKey, it is possible to update the treasuryAccountId for the token, thus transferring ownership of the token.
This can be determined by querying a mirror node for a TOKENUPDATE operation involving the token and seeing a change in treasuryAccountId.
When issuing a token, it is possible to define an initialSupply in the decimal denomination of the token.
The table below summarizes the mappings described above. To review documentation for the Hedera Token Service equivalent API call, please visit the documentation.
Solidity Contract
Method/function/event
HTS Equivalent
ERC20
constructor
tokenCreate
name, symbol, decimals and totalSupply getters
tokenGetInfo
balanceOf and allowance getters
cryptoGetAccountBalance
transfer method
cryptoTransfer
mint method
tokenMint
burn method
tokenBurn
approve, transferFrom, increaseAllowance and decreaseAllowance methods
Use multisig feature of Hedera accounts.
transfer event
Use mirror node
approve event
ERC20Burnable
burn
burnFrom
tokenWipe
ERC20Capped
cap getter
ECR20Pausable
all
tokenFreeze and tokenUnfreeze on all accounts holding the token
Ownable
owner getter
onlyOwner function
Only approved keys can operate the token natively
transferOwnership method
tokenUpdate
ownershipTransferred method
ERC20PresetFixedSupply
ECDSA
Not required
SafeMath
Not necessary, the Hedera Token Service takes care of the necessary calculations
TokenTimeLock
Not necessary due to the atomicity of transfer transactions which can involve multiple tokens at once
ERC20FlashMint
ERC20Snapshot
ERC20Votes
ERC20VotesComp
ERC20Wrapper
ERC20PresetMinterPauser
No equivalent at the time of writing this blog.
The ERC721 token specification introduces the ability to support non fungible tokens. It maps to the Hedera Token Service as follows:
There is no direct equivalent to this function
A TokenGetAccountNftInfosQuery will return the list of non fungible tokens held by a given Account Id.
A TokenGetNftInfoQuery will return the AccountId which owns a given NFT token.
A getInfo on the Token Id will return its name and symbol.
A tokenGetNFTInfoQuery on a given NFT Id will return a metadata value which may contain the URI of the NFT or any other arbitrary binary data up to 100 bytes.
There are no direct equivalent of these methods in the Token Service API, however through the use of the native multi-sig capabilities of Hedera Accounts, it is possible to effect an approval as explained below.
First, create a new account with at least two keys (could be more to enable approval towards multiple people at once), these keys would be subject to a threshold such as 1 of M and one of the keys would be known to the token holder wishing to allow others to spend on his/her behalf.
Second, transfer the desired tokens to be approved to this new account from the token holding account. From this point, any of the key holders in the threshold list is able to transfer tokens from this new account to any other. This satisfies the approve (transfer to the new account) and transferFrom (a key from the keylist can transfer from the new account) methods in the ERC20 contract.
A TokenGetNftInfoQuery will return the AccountId which owns a given NFT token. If this Account Id has multiple keys associated with it, it’s likely the NFT has been approved previously, a query against mirror node using the public keys would enable anyone to identify the approved accounts.
A token is associated with a treasury Account Id which holds all newly minted NFTs. Using Hedera’s native multisig capabilities, the keys on the treasury account can be updated to include as many other approved keys as required in order to allow these keys to transfer tokens from treasury to other accounts.
Querying the keys associated with the token’s treasury Account Id (cryptoGetInfo) would yield one or more keys. If more than one key is returned, the token is approved for transfer by more than one key. Querying a mirror node using the public keys would identify which Account Ids are approved.
An approve for all operation would be performed by updating the keys on the token’s treasury Account Id, this transaction can be witnessed on a mirror node.
Subject to the token having a supplyKey, NFTs may be burnt individually or in bulk by supplying a list of NFTs to burn via a tokenBurn transaction.
There is no equivalent in the Hedera Token Service.
A TokenGetAccountNftInfosQuery query will return an array of NFTs owned by a given Account Id. By specifying the start and end indices of the range of NFTs to return, it’s possible to get the owned NFTs by index.
A tokenGetInfo will return the total supply for a given token.
A TokenGetNftInfosQuery query will return an array of NFTs for a given Token Id. The range of returned NFTs can be specified with a start and end index.
ERC721Storage contract
This constructor takes a baseTokenURI which in HTS could either be stored in the token’s memo or symbol although this value isn’t used by subsequently minted NFTs.
The tokenMint transaction enables one or more NFTs to be minted. Indeed an array of metadata may be supplied which will determine the number of NFTs to mint and will associate each metadata value to its corresponding NFT. The receipt for the transaction will include an array of serial numbers uniquely identifying the newly minted NFTs.
Note that this transaction mints the NFTs to the treasury, subsequent cryptoTransfer transactions are necessary to transfer the minted NFTs to their owner unless they are to be kept in treasury.
Minting can only take place if the token has a supplyKey.
ERC721
supportsInterface
No equivalent
balanceOf
tokenGetAccountNftInfosQuery
ownerOf
tokenGetNftInfoQuery
name and symbol
tokenURI
tokenGetNFTInfoQuery
approve, transferFrom and safeTransferFrom
Use multisig feature of Hedera accounts and cryptoTransfer transaction to make the transfers
getApproved
setApprovalForAll
isApprovedForAll
Query keys on token’s treasury account
Transfer, approval and approvalForAll
ERC721Burnable
ERC721Enumerable
tokenOfOwnerByIndex
totalSupply
tokenByIndex
tokenGetNftInfosQuery
ERC721Storage
ERC721PresetMinterPauserAutoId
mint
pause and unpause
The ERC1155 token specification introduces the ability to support fungible and non fungible tokens within the same contract definition. It maps to the Hedera Token Service as follows:
There is no direct equivalent in the Hedera Token Service. Once could consider that HTS implements ERC1155 natively, allowing any and all token types to be created without instantiating a contract in the first place.
There is no equivalent in the Hedera Token Service for this.
There is no equivalent in the Hedera Token Service for this, although NFT tokens may be minted with empty metadata and the corresponding URI be derived from the token type’s metadata + the id of the resulting NFT.
Querying the balance (cryptoGetBalance) of an Account Id will return the balance of all Fungible Tokens owned by this account.
Querying TokenGetAccountNftInfosQuery for a given Account Id will return the NFTs the account owns.
There is no equivalent in the Hedera Token Service for this other than querying each account in turn.
All token exchanges are performed atomically through a cryptoTransfer transaction listing the senders, tokens, amounts and recipients. Subject to the necessary signatures being present on the transaction to validate it, transfers occur atomically as a batch (or single token).
A tokenBurn transaction will burn a number of tokens or a list of NFTs from the treasury account for a given Token Id.
A tokenBurn transaction will burn a number of tokens or a list of NFTs from the treasury account for a given Token Id. If multiple token types need to be burnt from, a separate tokenBurn transaction must be issued for each token type.
A tokenGetInfo query will return the token’s total supply amongst other token details.
A tokenGetInfo query for a Token Id will return its details, indicating it exists. If a non-existent Token Id is supplied, the query would return an error.
No Hedera Token Service equivalent.
Subject to having a supplyKey, any token may be minted with a tokenMint transaction. If the token is fungible, a quantity to mint is required. If the token is non fungible, an array of metadata for each minted NFT determines the quantity to mint.
Note: The mint operation mints to the token’s treasury Account Id, subsequent transfers are required to send the minted tokens to other accounts than treasury.
A tokenMint transaction mints to a specific Token Id, if multiple tokens need to be minted, a transaction needs to be issued for each Token Id.
There is no equivalent implementation of this, except that if the token has a freezeKey, the appropriate key holder(s) could iteratively freeze all accounts that hold a balance of the token, then later unfreeze those accounts if necessary. If multiple tokens need to be paused, then transactions have to be issued for each Token Id.
ERC1155
uri
Mint tokens with empty metadata. Use token type’s metadata + NFTid for uri
cryptoGetBalance and tokenGetAccountNftInfosQuery
repeat cryptoGetBalance and tokenGetAccountNftInfosQuery
safeTransferFrom and safeBatchTransferFrom
ERC1155Burnable
burnBatch
Repeat tokenBurn for each token type
ERC1155Supply
exists
tokenGetInfo (fails if Token Id doesn’t exist)
ERC1155PresetMinterPauser
mintBatch
tokenMint (optionally multiple times)
tokenFreeze and tokenUnfreeze