GitHub Logo HIP-376: Support Approve/Allowance/transferFrom standard calls from ERC20 and ERC721

Author Stoyan Panayotov
Discussions-To https://github.com/hashgraph/hedera-improvement-proposal/discussions/378
Status Final
Needs Council Approval Yes
Review period ends Wed, 06 Apr 2022 07:00:00 +0000
Type Standards Track
Category Service
Created 2022-03-02
Updated 2022-11-14
Requires 206, 218, 336
Release v0.26.0

Abstract

Describe the added support for industry standart ERC20 and ERC721 functions related to token approvals and allowances in Hedera’s Smart Contract Service.

Motivation

The goal is to expose HIP-336: Approval and Allowance API for Tokens functionality in industry standard ERC-20 and ERC-721 calls for smart contract developers on Hedera.

User stories

As a hedera smart contract services user, I want to enable another user or contract the ability to spend x fungible tokens from my account so that they can trade on my behalf without requiring that I expose my private key to them.

As a hedera smart contract services user, I want to query tokens for the current token allowances that have been granted to a user (spender) for a specific owner account.

As a hedera smart contract services user, I want to modify or remove an allowance I have granted to another user or contract so that I can manage that user/contract’s ability to spend my tokens over time.

As a hedera smart contract services user that has been granted an allowance, I want to be able to transfer tokens owned by the owner account as if they were my tokens so that I can issue transfers on their behalf without them having to expose their private key.

As a hedera smart contract services user, I want to enable another user the ability to spend specific non-fungible tokens from my account so that they can trade on my behalf without requiring that I expose my private key to them.

As a hedera smart contract services user, I want to enable or disable another user the ability to spend all instances of a particular non-fungible token that I currently or may in the future hold in my account so that they can trade on my behalf without requiring that I expose my private key to them.

As a hedera smart contract services user, I want to modify or remove an allowance I have granted to another user so that I can manage that user’s ability to spend my tokens over time.

As a hedera smart contract services user, I want to query a token for the information whether an account has approve for all nfts permission.

As a hedera smart contract services user that has been granted an allowance, I want to be able to transfer tokens owned by the owner account as if they were my tokens so that I can issue transfers on their behalf without them having to expose their private key.

Specification

EVM Precompile

Extend the functionality from HIP-218: Smart Contract interactions with Hedera Token Accounts with support for approve, allowance and transferFrom functions.

Supported ERC-20 operations

Tokens of type FUNGIBLE_COMMON support standard ERC-20 calls.

The following ERC-20 operations will be supported. Standard ERC-20 Events will be emitted as appropriate.

  • function allowance(address owner, address spender) external view returns (uint256);

Returns the remaining number of tokens that spender will be allowed to spend on behalf of owner through transferFrom. This is zero by default. This value changes when approve or transferFrom are called.

Works by loading the owner FUNGIBLE_TOKEN_ALLOWANCES from the accounts ledger and returning the allowance approved for spender.

  • function approve(address spender, uint256 amount) external returns (bool);

Sets amount as the allowance of spender over the caller’s tokens.

Works by creating a synthetic CryptoApproveAllowanceTransaction with payer - the account that called the precompile (the message sender property of the message frame in the EVM). Fires an approval event with the following signature when executed:

event Approval(address indexed owner, address indexed spender, uint256 value);

  • function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

Moves amount tokens from from to to using the allowance mechanism. amount is then deducted from the caller’s allowance.

Works by creating a synthetic CryptoTransferTransaction with fungible token transfers with the is_approval property set to true.

Supported ERC-721 operations

Tokens of type NON_FUNGIBLE_UNIQUE will support standard ERC-721 calls.

The following ERC-721 operations will be supported. Standard ERC-721 Events will be emitted as appropriate.

  • function approve(address to, uint256 tokenId) external;

Gives permission to to to transfer tokenId token to another account. The approval is cleared when the token is transferred.

Works by creating a synthetic CryptoApproveAllowanceTransaction with payer - the account that called the precompile (the message sender property of the message frame in the EVM).

If the spender address is 0, this creates a CryptoDeleteAllowanceTransaction instead and removes any allowances previously approved on the token.

Fires an approval event with the following signature when executed:

event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

  • function setApprovalForAll(address operator, bool approved) external

Approve or remove operator as an operator for the caller. Operators can call transferFrom for any token owned by the caller.

Works by creating a synthetic CryptoApproveAllowanceTransaction with payer - the account that called the precompile (the message sender property of the message frame in the EVM).

  • function getApproved(uint256 tokenId) external view returns (address)

Returns the account approved for tokenId token.

Works by loading the SPENDER property of the token from the NFTs ledger.

  • function isApprovedForAll(address owner, address operator) external view returns (bool)

Returns if the operator is allowed to manage all of the assets of owner.

Works by loading the APPROVE_FOR_ALL_NFTS_ALLOWANCES property of the owner account and verifying if the list of approved for all accounts contains the account id of the operator.

  • function transferFrom(address from, address to, uint256 tokenId) external payable

Transfers tokenId token from from to to.

Works by creating a synthetic CryptoTransferTransaction with nft token transfers with the is_approval property set to true.

HAPI payer, allowance and signatures for transferFrom

HAPI cryptoTransfer functions using allowance work with the transaction payer - this is the account that should have allowance to spend the tokens. ERC20/721 transferFrom functions work with msg.sender and that is the account that will be verified in the context of a precompile. Additionaly, in HAPI TX context, the spender account must have signed the transaction for the transfer to be valid. We have already introduced an exception for signatures verification in the case when the owner and the initiator of a transfer through the precompile are the same account. This exception will now be extended to also include the spender of an allowance.

Gas Schedule

For function calls that cause HTS token transfers the appropriate gas charges will be applied. Calls that emit events will be charged the event fees too.

We take the canonical max calls per second, and divide that into the 15MM gas per second, and add a 20% premium. The trick is figuring out which max TPX measure to use.

Function Base Cost Incremental Cost
Queries xx Gas 0 Gas
Approves xx Gas 0 Gas
Transfer xx Gas 0 Gas

Backwards Compatibility

The existing functions in the HTS precompile will remain in place, all prior usages of the precompile will function as they did before.

Security Implications

The same implications relating to contract keys and delegate calls outlined in HIP-206 also apply to the direct support of ERC-20 and ERC-721 contracts.

How to Teach This

Tutorials and SDK examples should be written showing how solidity can access the token accounts directly as ERC-20 and ERC-721 contracts.

HSCS documentation should be updated to explicitly enumerate the supported and non-supported contract functions.

Reference Implementation

// TODO

Rejected Ideas

Open Issues

None at this time.

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: