|Needs Council Approval||Yes|
|Review period ends||Fri, 26 Nov 2021 07:00:00 +0000|
|Updated||2022-01-10, 2021-11-11, 2021-11-30|
Table of Contents
- User stories
- Backwards Compatibility
- Security Implications
- How to Teach This
- Reference Implementation
- Rejected Ideas
- Open Issues
Describe the integration of Hedera Token Service (HTS) with the Hedera Smart Contract Service (HSCS), allowing contracts to transfer, mint, burn, associate, and dissociate tokens programmatically.
The fusion of the high performance token system in Hedera with programmatic contracts is a frequently requested feature. Specifically, the ability to manage tokens outside the smart contract service as well as via smart contracts.
To bridge the EVM and Hedera services it will be necessary to use the EVM’s precompiled contract facility. Because of the integration with Solidity it is also valuable to be able to treat the precompile as a system contract. Hence, a precompile whose input data follows the solidity calling conventions addresses both needs.
There are also 3 perspectives on the HTS System. The first perspective is with the same level of specificity the gRPC calls provide to the mapped service message. A second perspective is of Smart Contract authors trying to make gas-efficient calls. The third perspective is Smart Contract Engineers who want to treat HTS tokens as much as ERC-20 tokens as possible.
There are 5 main calls: transfer, mint, burn, associate, and dissociate. A layer of convince calls with explicit mapping to those main 5 calls will be provided for the second perspective. This third perspective is out of scope for this HIP and will be addressed in a later HIP.
As a smart contract developer, I want to be able to transfer, mint, burn, associate, and dissociate smart contract tokens through solidity contract calls.
As a smart contract developer, I want my smart contract to be able to transfer, mint, burn, associate, and dissociate tokens where authorization is granted because my smart contract initiated the call and without any other signatures.
The precompile address will be
0x167, decimal 359 (U+0167 is lower case t-bar
‘ŧ’). The precompile input data will follow the calling conventions of
Solidity ABI version 2. Version 2 is required to use multidimensional arrays.
tokenTransfer method is not compatible with ABI version 1.
The Solidity file for development contains the function signatures that the precompile will respond to. It is included in this HIP by reference.
tokenTransfer message will be supported by five distinct functions. The
tokenTransfer use of the ABIv2 multidimensional array
encoding and supports the full atomic transfer of multiple token types in one
To accommodate ABIv1 use four functions with a more focused application are
provided. Two party single token transfers are supported
tokenTransferSingle for fungible tokens and
tokenTransferNFT for NFTs.
AccountAmount or a single
NFTTransfer structs will be created for these
To do bulk transfers in a single token type the
tokenTransferBulkNFT support fungible and non-fungible transfers between
multiple parties. The size of each array for both calls needs to be the same,
and each array index represents a separate
The four remaining functions
dissociateTokens serve as direct mappings to their respective gRPC calls.
All functions return the
responseCode that would be generated by calling their
counterparts via gRPC. In addition to the response codes The
tokenBurn methods return the new total supply after their operation
tokenMint returns the new NFT serial numbers if new NFTs are generated.
The ABI signature and hashes for each call are as follows
The gas cost of the precompile will be variable based on the specific function called, but will not be less than 1 gas per byte of data in the input data. Gas will be priced so that it will be cheaper to do calls directly to HTS rather than through HSCS where such calls are possible.
|Function||Base Cost||Incremental Cost|
|Transfers||xx Gas||xx Gas / Transfer|
|Mint and Burn||xx Gas||0 Gas|
|Associate and Dissociate||xx Gas||xx Gas / Token|
Transfers are the
transferNFTs functions. A transfer is considered an item in
either of the NFT or token transfer list.
2 transfers implicit in the function call.
Mint and Burn are the
Associate and Dissociate are the
To support contracts controlling features like minting and burning the defined
but up until now unused field
contractID in the
Key protobuf message will be
utilised. When a token has a contract key as the admin key or one of the admin
keys then that key is considered validated if the
CALLER of the relevant
method is the contract identified in the call. This is the effect of a
operation in the EVM.
Some smart contracts may want to delegate control to a library contract. In
order to allow this and preserve the default security of only permitting direct
contract calls to the EVM a second contract key field
will be added to the
Key protobuf message. The requirement for authorization
will be that only the
CALLER of the call be the authorized contract ID. This
will allow contracts to do a
DELEGATECALL to the library and then the
library (or any of the other sub-libraries it then
CALL the precompile contract and have the authorization granted as though
it was the origin of the current delegate call chain.
contractKey is the safer options and is recommended as the default
contract level security.
delegatableContractKey should only be used when the
library is fully known and audited ahead of time.
For operations that require signature verification the signatures of the top level contract call are considered, in addition to the immediate caller of the operation. This may result in a contract call that may need to be signed by more than the initial caller of the smart contract.
In order for a signature to be used for verification, the entire public key
needs to be stored in the
pubKeyPrefix field of the
Any partial prefixes provided in the signature map will be ignored and will not
properly authorize the contract, even if the signature is valid. This is the
current practice of the Hedera supplied SDKs.
Whenever a precompile performs an action that is equivalent to a top level transaction a transaction record must be produced in the record stream corresponding to that transaction. This transaction record also needs to be traceable between parent and child. To facilitate this a few fields will be added to the standard protobuf definitions.
nonce field of will be added to the
message. Each child transaction will use the next sequential value of
that is unused.
In addition to use for precompiled contracts this nonce field will be used by scheduled transactions and other future transactions that may have child as part of their design.
There will be an upper limit of 500 child transactions per transaction. If that limit is reached then the remaining precompile calls must fail.
Each record that is a child transaction will need to populate
parent_consensus_timestamp that has the timestamp of the parent
transaction. Transactions that have no parent will not populate this field
Support for querying child transactions will be added to
TransactionGetRecordQuery messages via
include_child_records boolean field in each
query. When that field is set to true the repeated
child_transaciton_records fields in the response message will contain the
relevant child transactions for the requested transaction.
Whenever a precompiled contract succeeds but is later reverted within the smart
contract (for example, a
REVERT operation was executed or the frame ran out of
gas) the return status will be set to
While other fields will be set to non-success values (such as listing no crypto
ContractCreateResult field appropriate
to the transaciton will need to remain with the values used in the EVM. This is
needed to ensure that the smart contract call can be replayed in external
There is no previous implementation of HTS in the HSCS. There is also no conflict with existing ERC-20s that are EVM only. Migration of existing contracts is out of scope for this HIP.
All the new protobuf fields are optional. The zero/empty value has the appropriate meaning for transactions without child transactions and transactions that are not themselves a child transaction.
Controllers of admin keys will need to be careful signing smart contract calls. As their authorization extends to the entire call stack the signers need to make sure that any HTS calls made as a consequence of the initial function call are intended. These calls could be multiple levels deep and crafty contracts can manipulate the calls based on stored state.
Mitigations include relying on contract keys exclusively, using a threshold signature that includes a contract key, only signing contract calls for known and audited sources, and only signing direct HTS calls while relying on a threshold scheme to allow contracts to also perform actions per smart contract rules.
Care needs to be taken writing contracts that are expected to call the HTS
precompile contracts via
DELEGATECALL into libraries. It is possible to write
libraries that can arbitrarily call external code via after-the-fact
configuration. Before enabling this key administrators should ensure that only
known contracts are called. In many cases these will be contracts authored by
the same team, but extra special scrutiny should be given to calling third party
SDK tutorials should include examples of how to use the precompile contracts.
Documentation talking about admin keys will need to be updated to address the implication of contract keys, including threshold signatures including contract keys.
- Protobuf Changes - hashgraph/hedera-protobufs PR#120
- Services Implementation - hashgraph/hedera-services hts-contracts
Current target is 0.21.0.
Making Token Accounts behave like ERC-20 and ERC-721 contracts has been moved outside the scope of this hip.
- Gas Charged needs to be finalized.
- Hedera Token Service Developer Docs
- Solidity Support Code
This document is licensed under the Apache License, Version 2.0 – see LICENSE or (https://www.apache.org/licenses/LICENSE-2.0)
Please cite this document as: