HIP-206: Hedera Token Service Precompiled Contract for Hedera SmartContract Service
📢 This HIP is in the last call for review stage. The authors wish to finalize the HIP and appreciate feedback.

HIP-206: Hedera Token Service Precompiled Contract for Hedera SmartContract Service Source

AuthorDanno Ferrin
Discussions-Tohttps://github.com/hashgraph/hedera-improvement-proposal/discussions/208
StatusLast Call
Needs Council ApprovalYes
Review period ends2021-11-26 07:00:00 +0000
TypeStandards Track
CategoryService
Created2021-11-04
Updated2021-11-11

Abstract

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.

Motivation

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.

Rationale

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.

User stories

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.

Specification

EVM Precompile

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 multi-dimensional arrays. Only the tokenTransfer method is not compatible with ABI version 1.

Solidity Function Signatures

The Solidity file for development contains the function signatures that the precompile will respond to. It is included in this HIP by reference.

The tokenTransfer message will be supported by five distinct functions. The principal function tokenTransfer use of the ABIv2 multi-dimensional array encoding and supports the full atomic transfer of multiple token types in one transaction.

To accommodate ABIv1 use four functions with a more focused application are provided. Two party single token transfers are supported by tokenTransferSingle for fungible tokens and tokenTransferNFT for NFTs. Two AccountAmount or a single NFTTransfer structs will be created for these calls.

To do bulk transfers in a single token type the tokenTransferBulk and 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 AccountAmount or NFTTransfer object.

The four remaining functions tokenMint, tokenBurn, associateTokens, and dissociateTokens serve as direct mappings to their respective gRPC calls.

The ABI signature and hashes for each call are as follows

function hash signature
cryptoTransfer 189a554c ((address,(address,int64)[],(address,address,int64)[])[])
transerTokens 82bba493 (address,address[],int64[])
transferToken eca36917 (address,address,address,int64)
transferNFTs 2c4ba191 (address,address[],address[],int64[])
transferNFT 7c502795 (address,address,address,int64)
mintToken 36dcedf0 (address,uint64,bytes)
burnToken acb9cff9 (address,uint64,int64[])
associateTokens 2e63879b (address,address[])
associateToken 49146bde (address,address[])
dissociateTokens 78b63918 (address,address[])
dissociateToken 099794e8 (address,address[])

Precompile Gas Costs

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 cryptoTransfer, transferToken, transferTokens, transferNFT, and transferNFTs functions. A transfer is considered an item in either of the NFT or token transfer list. transferToken and transferNFT have 2 transfers implicit in the function call.

Mint and Burn are the mintToken and burnToken functions.

Associate and Dissociate are the associateToken, associateTokens, dissociateToken, and dissociateTokens functions.

Contract Key

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 CALL operation in the EVM.

Delegatable Contract Key

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 delegatableContractKey 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 DELEGATECALLs to) can CALL the precompile contract and have the authorization granted as though it was the origin of the current delegate call chain.

Using a 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.

Signature Verification

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 SignaturePair message. 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.

Backwards Compatibility

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.

Security Implications

Admin Key Call Depth

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.

Delegatable Contract Keys and DELEGAGECALL

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 contracts.

How to Teach This

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.

Reference Implementation

Targeting 0.20.0 at the moment.

Rejected Ideas

Making Token Accounts behave like ERC-20 and ERC-721 contracts has been moved outside the scope of this hip.

Open Issues

  • Gas Charged needs to be finalized.

References

Copyright/license

This document is licensed under the Apache License, Version 2.0 – see LICENSE or (https://www.apache.org/licenses/LICENSE-2.0)

Citation

Please cite this document as: