HIP-206: Hedera Token Service Precompiled Contract for Hedera SmartContract Service
Author | Danno Ferrin |
---|---|
Discussions-To | https://github.com/hashgraph/hedera-improvement-proposal/discussions/208 |
Status | Last Call |
Needs Council Approval | Yes |
Review period ends | 2021-11-26 07:00:00 +0000 |
Type | Standards Track |
Category | Service |
Created | 2021-11-04 |
Updated | 2021-11-11 |
Table of Contents
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 DELEGATECALL
s 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: