Previously described at: ERC20 critical problems medium article.
ERC20 is the most common Ethereum token standard. It should be noted that it is also the first Ethereum's token standard as well.
It is also important that the original ERC20 proposal is a definition of token interface. EIP20 does not define a reference implementation for this token standard. Here is OpenZeppelin implementation of ERC20 token: https://github.com/OpenZeppelin/zeppelin-solidity/tree/master/contracts/token/ERC20
ERC20 token standard implementation assumes two ways of token transferring: (1) transfer
function and (2) approve + transferFrom
pattern.
The transfer function does not notify received that the token transfer happened. ERC20 token standard lacks even handling/communication model. This is a critical flaw of the standard that led to impossibility of error handling.
Also depositing tokens to a contract with transfer
function is an intuitive way of depositing crypto assets for users, however this will result in an unhandled error in case of ERC20 tokens which will cause a permanent loss of tokens if there is no specific function for extraction of the stuck ERC20 tokens in the recipient contract.
It should be noted that event handling is a well-known and standard practice in programming. In case of token standard, a token transfer should be considered an event. transfer
function of ERC20 does not provide any possibility to handle the transfer, i.e. it is silently increasing a balance variable of the receiver. It is impossible for the receiver to recognize that transfer
occurs if the receiver is a contract. As the result, the only correct way to make a token deposit to a contract is an approve + transferFrom
pattern.
In case of a mistake, the transfer
function MUST throw an error and revert a wrong transaction. Otherwise it will cause negative consequences (lost tokens) for end user. This is a very common and default practice in programming, called Exception Handling: https://en.wikipedia.org/wiki/Exception_handling
This has already led to the loss of millions of dollars for the whole Ethereum ecosystem at the moment. Every contract on Ethereum is a potential trap for ERC20 tokens (read ENS contract became token holder).
Expected transaction behavior: a transaction is executed and it is resulting in value transfer. In case of error, transfer of value will not occur.
This works exactly as expected in case of Ether transfers. If you send Ether to a contract that is not intended to work with Ether then the transaction will be rejected by the recipient smart-contract and the transfer of value will not occur.
This does not work as expected in case of ERC20 token. The transfer of tokens (i.e. transfer of value) will not be reverted or rejected by the recipient smart-contract due to lack of possibility to handle the transfer
function invocations. This is causing unexpected transfer
function behavior and produce unexpected result i.e. loss of funds for end users.
I often heard how developers declare (1) "Not a bug, user mistake" or (2) "This is not a bug or a security vulnerability, this is a specific feature of ERC20 standard design".
I'll explain why the described property of ERC20 standard is exactly a bug below.
REMINDER: Also, if a "specific feature of software design" has caused losses of millions of dollars for software users, then it is an awful design at least.
Software bug definition: https://en.wikipedia.org/wiki/Software_bug
I will quote this:
A software bug is an error, flaw, failure or fault in a computer program or system that causes it to produce an incorrect or unexpected result, or to behave in unintended ways.
The intention of transfer
function is to transfer tokens.
This is how the transfer function is specified at the ERC20 token standard:
transfer
function transfer(address _to, uint256 _value) returns (bool success)
Send _value amount of tokens to address _to
There is no phrase like "If you send your tokens to the address or a contract that is not intended to work with tokens then your tokens are permanently lost." We should keep in mind that we are talking about the Ethereum smart-contracts. In case of Ether transaction, every time a transaction is invoked incorrectly (a receiver is unable to handle transaction or a receiver is a contract that is not intended to work with Ether), an error is thrown and the transaction is reverted.
Thus, the expected behavior is that in the event of an error caused by the recipient's inability to handle the incoming transaction, such a transaction should fail.
Ethereum users are often familiar with Ether transactions. As the result, the expected behavior is that if a transfer of tokens is invoked incorrectly (a receiver is unable to handle transaction or a receiver is a contract that is not intended to work with Ether tokens), an error must be thrown.
The statement "... produce an incorrect or unexpected result, or to behave in unintended ways", which is a property of a program error, is of decisive importance here. The "bug of ERC20 transfers" is the unexpected behavior of transfer
function in this case.
Some developers could argue that these are my personal assumptions but let's dive deeper into Software Vulnerabilities definition first: https://en.wikipedia.org/wiki/Vulnerability_(computing)#Software_vulnerabilities
I will quote this:
Common types of software flaws that lead to vulnerabilities include: ... Blaming the Victim prompting a user to make a security decision without giving the user enough information to answer it.
The statement "... prompting a user to make a security decision without giving the user enough information to answer it. " which is a property of Software Vulnerability, is of decisive importance here.
There is no sufficient information at the ERC20 token standard definition about the behavior of transfer
function in case of the error-handling. A phrase "WARNING: If you will call this function to transfer your tokens to any contract then your tokens are permanently lost." MUST be added to the definition of transfer
function of the ERC20 token standard.
A phrase "Calling the transfer
function to transfer your tokens to contracts is prohibited in this token standard." MUST be added to the Abstract section of the ERC #20 token standard.
This is required to make each token and UI developer aware of what they are working with. This is what is missing at ERC20 standard.
As soon as this will be implemented, each token developer and UI developer MUST place a big red banner "WARNIGN: You are attempting to use ERC20 token, you should know that if you call a transfer
function to send tokens to a contract then your tokens are permanently lost." Otherwise it will be a Software Vulnerability: User Interface fault (https://en.wikipedia.org/wiki/User_interface).
-
QTUM, $1,204,273 lost. watch on Etherscan
-
EOS, $1,015,131 lost. watch on Etherscan
-
GNT, $249,627 lost. watch on Etherscan
-
STORJ, $217,477 lost. watch on Etherscan
-
Tronix , $201,232 lost. watch on Etherscan
-
DGD, $151,826 lost. watch on Etherscan
-
OMG, $149,941 lost. watch on Etherscan
-
STORJ, $102,560 lost. watch on Etherscan
These are only 8 token contracts that are shown as an example. Each Ethereum contract is a potential token trap for all the ERC20 tokens, thus, there are much more losses than I showed here.
Such contracts that are designed to work with tokens are at very danger zone: state channels, decentralized exchanges, payment services that accept tokens.
Let me summarize what is stated above. The specific feature of ERC20 token standard design that caused millions of dollars loss for token users IS a Software Bug and it can be classified as Software Vulnerability.
The specific design of ERC20 token standard functionality is producing unexpected result and causing unexpected behavior which is a property of software bug.
As ERC20 token standard is a smart-contract interface specification, then I can say that Interface fault, such as Blaming The Victim is taking place here. This is also a property of software vulnerability.
Saying "This is not a bug" is absolutely wrong. Definitely, this is a bug and a vulnerability. Saying "This is a user mistake" is a kind of Blaming the victim.
See ERC777
https://docs.openzeppelin.com/contracts/3.x/erc777