The Ethereum blockchain [19, 79] allows users to build and deploy decentralized applications (DApps) that can accept and use its protocol-level cryptocurrency ETH. Many DApps also issue or use custom tokens. Such tokens could be financial products, in-house currencies, voting rights for DApp governance, or other valuable assets. To encourage interoperability with other DApps and web applications (exchanges, wallets, etc.), the Ethereum community accepted a popular token standard (specifically for fungible tokens) called ERC-20 . While numerous ERC-20 extensions or replacements have been proposed, ERC-20 remains prominent. Of the 2.5M  smart contracts on the Ethereum network, 260K are tokens  and 98% of these tokens are ERC-20 .
The development of smart contracts has been proven to be error-prone, and as a result, smart contracts are often riddled with security vulnerabilities. An early study in 2016 found that 45% of smart contracts at that time had vulnerabilities . ERC-20 tokens are subset of smart contracts and security is particularly important given that many tokens have considerable market capitalization (e.g., USDT, BNB, UNI, DAI, etc.). As tokens can be held by commercial firms, in addition to individuals, and firms need audited financial statements in certain circumstances, the correctness of the contract issuing the tokens is now in the purview of professional auditors. Later, we examine one static anaylsis tool from a ‘big-four’ auditing firm.
Ethereum has undergone numerous security attacks that have collectively caused more than US$100M in financial losses [27, 49, 47, 60, 52, 2]. Although research has been done on smart contract vulnerabilities in the past , we focus specifically on ERC-20 tokens.
We study all known vulnerabilities and cross-check their relevance to ERC-20 token contracts, systematizing a comprehensive set of 82 distinct vulnerabilities and best practices.
While not strictly a research contribution, we believe that our newly acquired specialized domain knowledge should be put to use. Thus, we provide a new ERC-20 implementation, TokenHook
, that is open source and freely available in both Vyper and Solidity.
TokenHook is positioned to increase software diversity: currently, no Vyper ERC-20 implementation is considered a reference implementation, and only one Solidity implementation is actively maintained (OpenZeppelin’s ). Relative to this implementation, TokenHook has enhanced security properties and stronger compliance with best practices.
Perhaps of independent interest, we report on differences between Vyper and Solidity when implementing the same contract.
We use TokenHook as a benchmark implementation to explore the completeness and precision of seven auditing tools that are widely used in industry to detect security vulnerabilities. We conclude that while these tools are better than nothing, they do not replace the role of a security expert in developing and reviewing smart contract code.
2 Sample of high profile vulnerabilities
In this section, we examine general attack vectors and cross-check their applicability to ERC-20 tokens. We sample some high profile vulnerabilities, typically ones that have been exploited in real world ERC-20 tokens[42, 34, 15, 13, 40]. For each, we (i) briefly explain technical details, (ii) the ability to affect ERC-20 tokens, and (iii) discuss mitigation techniques. Later we will compile a more comprehensive list of 82 vulnerabilities and best practices (see Table2), including these, however space will not permit us to discuss each one at the same level of detail as the ones we highlight in this section (however we will include a simple statement describing the issue and the mitigation).
2.1 Multiple withdrawal
This ERC-20-specific issue was originally raised in 2017 [75, 32]. It can be considered as a transaction-ordering  or front-running  attack. There are two ERC-20 functions (i.e., Approve() and transferFrom()) that can be used to authorize a third party for transferring tokens on behalf of someone else. Using these functions in an undesirable situation (i.e., front-running or race-condition) can result in allowing a malicious authorized entity to transfer more tokens than the owner wanted. There are several suggestions to extend ERC-20 standard (e.g., MonolithDAO  and its extension in OpenZeppelin ) by adding new functions (i.e., decreaseApproval() and increaseApproval()), however, securing transferFrom() method is the effective one while adhering specifications of the ERC-20 standard .
2.2 Arithmetic Over/Under Flows.
An integer overflow is a well known issue in many programming languages. For ERC-20, one notable exploit was in April 2018 that targeted the BEC Token  and resulted in some exchanges (e.g., OKEx, Poloniex, etc.) suspending deposits and withdrawals of all tokens. Although BEC developers had considered most of the security measurements, only line 261 was vulnerable [26, 49]. The attacker was able to pass a combination of input values to transfer large amount of tokens . It was even larger than the initial supply of the token, allowing the attacker to take control of token financing and manipulate the price. In Solidity, integer overflows do not throw an exception at runtime. This is by design and can be prevented by using the SafeMath library  wherein a+b will be replaced by a.add(b) and throws an exception in the case of arithmetic overflow. Vyper has built-in support for this issue and no need to use SafeMath library.
One of the most studied vulnerabilities is re-entrancy, which resulted in a US$50M attack on a DApp (called the DAO) in 2016 and triggered an Ethereum hard-fork to revert . At first glance, re-entrancy might seem inapplicable to ERC-20 however any function that changes internal state, such as balances, need to be checked. Further, some ERC-20 extensions could also be problematic. One example is ORBT tokens  which support token exchange with ETH without going through a crypto-exchange : an attacker can call the exchange function to sell the token and get back equivalent in ETH. However, if the ETH is transferred in a vulnerable way before reaching the end of the function and updating the balances, control is transferred to the attacker receiving the funds and the same function could be invoked over and over again within the limits of a single transaction, draining excessive ETH from the token contract. This variant of the attack is known as same-function re-entrancy, but it has three other variants: cross-function, delegated and create-based . Mutex  and CEI  techniques can be used to prevent it. In Mutex, a state variable is used to lock/unlock transferred ETH by the lock owner (i.e., token contract). The lock variable fails subsequent calls until finishing the first call and changing requester balance. CEI updates the requester balance before transferring any fund. All interactions (i.e., external calls) happen at the end of the function and prevents recursive calls. Although CEI does not require a state variable and consumes less Gas, developers must be careful enough to update balances before external calls. Mutex is more efficient and blocks cross-function attack at the beginning of the function regardless of internal update sequences. CEI can also be considered as a best practice and basic mitigation for the same-function re-entrancy. We implement a sell() and buy() function in TokenHook for exchanging between tokens and ETH. sell() allows token holders to exchange tokens for ETH and buy() accepts ETH by adjusting buyer’s token balance. It is used to buy and sell tokens at a fixed price (e.g., an initial coin offering (ICO), prediction market portfolios ) independent of crypto-exchanges, which introduce a delay (for the token to be listed) and fees. Both CEI and Mutex are used in TokenHook to mitigate two variants of re-entrancy attack.
2.4 Unchecked return values
In Solidity, sending ETH to external addresses is supported by three options: call.value(), transfer(), or send(). The transfer() method reverts all changes if the external call fails, while the other two return a boolean value and manual check is required to revert transaction to the initial state . Before the Istanbul hard-fork , transfer() was the preferred way of sending ETH. It mitigates reentry by ensuring ETH recipients would not have enough gas (i.e., a 2300 limit) to do anything meaningful beyond logging the transfer when execution control was passed to them. EIP-1884  has increased the gas cost of some opcodes that causes issues with transfer()111After Istanbul, the fallback() function consumes more than 2300 Gas if called via transfer() or send() methods.. This has led to community advice to use call.value() and rely on one of the above re-entrancy mitigations (i.e., Mutex or CEI) [77, 16]. This issue is addresses in Vyper and there is no need to check return value of send() function.
2.5 Frozen Ether
As ERC-20 tokens can receive and hold ETH, just like a user accounts, functions need to be defined to withdraw deposited ETH (including unexpected ETH). If these functions are not defined correctly, an ERC-20 token might hold ETH with no way of recovering it (cf. Parity Wallet ). If necessary, developers can require multiple signatures to withdraw ETH.
2.6 Unprotected Ether Withdrawal
Improper access control may allow unauthorized persons to withdraw ETH from smart contracts (cf. Rubixi ). Therefore, withdrawals must be triggered by only authorized accounts and ideally multiple parties.
2.7 State variable manipulation
The DELEGATECALL opcode enables a DApp to invoke external functions of other DApps and execute them in the context of calling contract (i.e., the invoked function can modify the state variables of the caller). This makes it possible to deploy libraries once and reuse the code in different contracts. However, the ability to manipulate internal state variables by external functions has lead to incidents where the entire contract was hijacked (cf. the second hack of Parity MultiSig Wallet ). Preventive techniques is to use Library keyword in Solidity to force the code to be stateless, where data is passed as inputs to functions and passed back as outputs and no internal storage is permitted . There are two types of Library: Embedded and Linked. Embedded libraries have only internal functions (EVM uses JUMP opcode instead of DELEGATECALL), in contrast to linked libraries that have public or external functions (EVM initiate a “message call”). Deployment of linked libraries generates a unique address on the blockchain while the code of embedded libraries will be added to the contract’s code . It is recommended to use Embedded libraries to mitigate this attack.
2.8 Balance manipulation
ERC-20 tokens generally receive ETH via a payable function  (i.e., receive(), fallback(), etc.), however, it is possible to send ETH without triggering payable functions, for example via selfdestruct() that is initiated by another contract . This can cause an oversight where ERC-20 may not properly account for the amount of ETH they have received . For example, A contract might use ETH balance to calculate exchange rate dynamically. Forcing ETH by attacker may affect calculations and get lower exchange rate. To fortify this vulnerability, contract logic should avoid using exact values of the contract balance and keep track of the known deposited ETH by a new state variable. Although we use address(this).balance in TokenHook, we do not check the exact value of it (i.e., address(this).balance == 0.5 ether)—we only check whether the contract has enough ETH to send out or not. Therefore, there is no need to use a new state variable and consume more Gas to track contract’s ETH. However, for developers who need to track it manually, we provide contractBalance variable. Two complementary functions are also considered to get current contract balance and check unexpected received ETH (i.e., getContractBalance() and unexpectedEther()).
2.9 Public visibility
In Solidity, visibility of functions are Public by default and they can be called by any external user/contract. In the Parity MultiSig Wallet hack , an attacker was able to call public functions and reset the ownership address of the contract, triggering a $31M USD theft. It is recommended to explicitly specify visibility of functions instead of default Public visibility.
3 A sample of best practices
We highlight a few best practices for developing DApps. Some best practices are specific to ERC-20, while others are generic for all DApps—in which case, we discuss their relevance to ERC-20.
3.1 Compliance with ERC-20.
According to the ERC-20 specifications, all six methods and two events must be implemented and are not optional. Tokens that do not implement all methods (e.g., GNT which does not implement the approve(), allowance() and transferFrom() functions due to front-running) can cause failed function calls from other applications. They might also be vulnerable to complex attacks (e.g., Fake deposit vulnerability, Missing return value bug).
3.2 External visibility.
Solidity supports two types of function calls: internal and external . Note that functions calls are different than functions visibility (i.e., Public, Private, Internal and External) which confusingly uses overlapping terminology. Internal function calls expect arguments to be in memory and the EVM copies the arguments to memory. Internal calls use JUMP opcodes instead of creating an EVM call.222Also known as “message call” when a contract calls a function of another contract. Conversely, External function calls create an EVM call and can read arguments directly from the calldata space. This is cheaper than allocating new memory and designed as a read-only byte-addressable space where the data parameter of a transaction or call is held. A best practice is to use external visibility when we expect that functions will be called externally.
3.3 Fail-Safe Mode.
In the case of a detected anomaly or attack on a deployed ERC-20 token, the functionality of the token can be frozen pending further investigation. For regulated tokens, the ability for a regulator to issue a ‘cease trade’ order is also generally required.
3.4 Firing events.
In ERC-20 standard, there are two defined events: Approval and Transfer. The first event logs successful allowance changes by token holders and the second logs successful token transfers by the transfer() and transferFrom(). These two events must be fired to notify external application on occurred changes. The external application (e.g., TokenScope) might use them to detect inconsistent behaviors, update balances, show UI notifications, or to check new token approvals. It is a best practice to fire an event for every state variable change.
3.5 Global or Miner controlled variables.
Since malicious miners have the ability to manipulate global Solidity variables (e.g., block.timestamp, block.number, block.difficulty, etc.), it is recommended to avoid these variables in ERC-20 tokens.
3.6 Proxy contracts.
An ERC-20 token can be deployed with a pair of contracts: a proxy contract that passes through all the function calls to a second functioning ERC-20 contract[69, 44]. One use of proxy contract is when upgrades are required—a new functional contract can be deployed and the proxy is modified to point at the update. Form audit point of view, it is recommended to have non-upgradable ERC-20 tokens.
3.7 DoS with Unexpected revert.
A function that attempts to complete many operations that individually may revert could deadlock if one operation always fails. For example, transfer() can throw an exception—if one transfer in a sequence fails, the whole sequence fails. One standard practice is to account for ETH owed and require withdrawals through a dedicated function. In TokenHook, ETH is only transferred to a single party in a single function sell(). It seems overkill to implement a whole accounting system for this. As a consequence, a seller that is incapable of receiving ETH (e.g., operating from a contract that is not payable) will be unable to sell their tokens for ETH. However they can recover by transferring the tokens to a new address to sell from.
3.8 Unprotected SELFDESTRUCT
Another vulnerability stemming from the second Parity wallet attack  is protecting the SELFDESTRUCT opcode which removes a contract from Ethereum. The self-destruct method is used to kill the contract and its associated storage. ERC-20 tokens should not contain SELFDESTRUCT opcode unless there is a multi approval mechanism.
3.9 DoS with block gas limit.
The use of loops in contracts is not efficient and requires considerable amount of Gas to execute. It might also cause DoS attack since blocks has a Gas limit. If execution of a function exceeds the block gas limit, all transactions in that block will fail. Hence, it is recommended to not use loops and rely on mappings variables in ERC-20 tokens.
TokenHook is our ERC20-compliant implementation written in Vyper (v. 0.2.8) and Solidity (v. 0.8.4) 333TokenHook deployed on Rinkeby at https://bit.ly/33wDENx (Solidity) and https://bit.ly/3dXaaPc (Vyper). Mainnet at https://bit.ly/35FMbAf (Solidity 0.5.11). It can be customized by developers, who can refer to each mitigation technique separately and address specific attacks. The presence of security vulnerability in supplementary layers (i.e., consensus, data, network. etc.) affect the entire Ethereum blockchain, not necessarily ERC-20 tokens. Therefore, vulnerabilities in other layers are assumed to be out of the scope. Required comments have been also added to clarify the usage of each function. Standard functionalities of the token (i.e., approve(), transfer(), transferFrom(), etc.) have been unit tested. A demonstration of token interactions and event triggering can also be seen on Etherscan.444Etherscan: https://bit.ly/33xHfL2, https://bit.ly/35TimMW and https://bit.ly/3eFAnAZ
Among the layers of the Ethereum blockchain, ERC-20 tokens fall under the Contract layer in which DApps are executed. The presence of a security vulnerability in supplementary layers affect the entire Ethereum blockchain, not necessarily ERC-20 tokens. Therefore, vulnerabilities in other layers are assumed to be out of the scope. (e.g., Indistinguishable chains at the data layer, the 51% attack at the consensus layer, Unlimited nodes creation at network layer, and Web3.js Arbitrary File Write at application layer).
Moreover, we exclude vulnerabilities identified in now outdated compiler versions. Examples: Constructor name ambiguity in versions before 0.4.22, Uninitialized storage pointer in versions before 0.5.0, Function default visibility in versions before 0.5.0, Typographical error in versions before 0.5.8, Deprecated solidity functions in versions before 0.4.25, Assert Violation in versions before 0.4.10, Under-priced DoS attack before EIP-150 & EIP-1884).
4.1 Security features
In our research, we developed 82 security vulnerabilities and best practices for ERC-20. We concentrate here on how TokenHook mitigates these attacks. While many of these attacks are no doubt very familiar to the reader, our emphasis is on their relevance to ERC-20.
4.1.1 Multiple Withdrawal Attack
Without our counter-measure, an attacker can use a front-running attack [8, 18] to transfer more tokens than what is intended (approved) by the token holder. We secure the transferFrom() function by tracking transferred tokens to mitigate the multiple withdrawal attack . Securing the transferFrom() function is fully compliant with the ERC-20 standard without the need of introducing new functions such as decreaseApproval() and increaseApproval().
4.1.2 Arithmetic Over/Under Flows
In Solidity implementation, we use the SafeMath library in all arithmetic operations to catch over/under flows. Using it in Vyper is not required due to built-in checks.
At first glance, re-entrancy might seem inapplicable to ERC-20. However any function that changes internal state, such as balances, need to be checked. We use Checks-Effects-Interactions pattern (CEI)  in both Vyper and Solidity implementations to mitigate same-function re-entrancy attack. Mutual exclusion (Mutex)  is also used to address cross-function re-entrancy attack. Vyper supports Mutex by adding @nonreentrant(<key>) decorator on a function and we use noReentrancy modifier in Solidity to apply Mutex. Therefore, both re-entrancy variants are addressed in TokenHook.
4.1.4 Unchecked return values
Unlike built-in support in Vyper, we must check the return value of call.value() in Solidity to revert failed fund transfers. It mitigates the unchecked return values attack while making the token contract compatible with EIP-1884 .
4.1.5 Frozen Ether
We mitigate this issue by defining a withdraw() function that allows the owner to transfer all Ether out of the token contract. Otherwise, unexpected Ether forced onto the token contract (e.g., from another contract running selfdestruct) will be stuck forever.
4.1.6 Unprotected Ether Withdrawal
We enforce authentication before transferring any funds out of the contract to mitigate unprotected Ether withdrawal. Explicit check is added to the Vyper code and onlyOwner modifier is used in Solidity implementation. It allows only owner to call withdraw() function and protects unauthorized Ether withdrawals.
4.1.7 State variable manipulation
In the Solidity implementation, we use embedded Library code (for SafeMath) to avoid external calls and mitigate the state variable manipulation attack. It also reduces gas costs since calling functions in embedded libraries requires less gas than external calls.
4.1.8 Function visibility
We carefully define the visibility of each function. Most of the functions are declared as External (e.g., Approve(), Transfer(), etc.) per specifications of ERC-20 standard.
4.2 Best practices and enhancements
We also take into account a number of best practices that have been accepted by the Ethereum community to proactively prevent known vulnerabilities . Again, we highlight several of these while placing the background details in the appendix.
4.2.1 Compliance with ERC-20
We implement all ERC-20 functions to make it fully compatible with the standard. Compliance is important for ensuring that other DApps and web apps (i.e., crypto-wallets, crypto-exchanges, web services, etc.) compose with TokenHook as expected.
4.2.2 External visibility
To improve performance, we apply an external visibility (instead of public visibility in the standard) for interactive functions (e.g., approve() and transfer(), etc.). External functions can read arguments directly from non-persistent calldata instead of allocating persistent memory by the EVM.
4.2.3 Fail-Safe Mode
We implement a ‘cease trade’ operation that will freeze the token in the case of new security threats or new legal requirements (e.g., Liberty Reserve  or TON cryptocurrency ). To freeze all functionality of TokenHook, the owner (or multiple parties) can call the function pause() which sets a lock variable. All critical methods are either marked with a notPaused modifier (in Solidity) or explicit check (in Vyper), that will throw exceptions until functionality is restored using unpause().
4.2.4 Firing events
We define nine extra events: Buy, Sell, Received, Withdrawal, Pause, Change, ChangeOwner, Mint and Burn. The name of each event indicates its function except Change event which logs any state variable updates. It can be used to watch for token inconsistent behavior (e.g., via TokenScope ) and react accordingly.
4.2.5 Proxy contracts
We choose to make TokenHook non-upgradable so it can be audited, and upgrades will not introduce new vulnerabilities that did not exist at the time of the initial audit.
4.2.6 Other enhancements
We also follow other best practices such as not using batch processing in sell() function to avoid DoS with unexpected revert issue, not using miner controlled variable in conditional statements, and not using SELFDESTRUCT.
4.3 Implementing in Vyper vs. Solidity
Although Vyper offers less features than Solidity (e.g., no class inheritance, modifiers, inline assembly, function/operator overloading, etc. ), the Vyper compiler includes built-in security checks. Table 1 provides a comparison between the two from the perspective of TokenHook (see  for a broader comparison on vulnerabilities). Security and performance are advantages of Vyper. However, Vyper may not be a preferred option for production (“Vyper is beta software, use with care” ), most of the auditing tools only support Solidity,555Vyper support is recently added to some tools (e.g., Crytic-compile, Manticore and Echidna). Slither integration is still in progress  and Solidity currently enjoys widespread implementation, developer tools, and developer experience.
|Arithmetic Over/Under Flows||Vul.||+||
|Unchecked return values||Vul.||+||
|Contract complexity||BP.||+||- 300 lines in Vyper have the same functionality as the Solidity with 500 lines.|
|Auditable||BP.||+||- Most of the auditing tools are able to analyze Solidity contracts.|
4.4 Need for another reference implementation
The authors of the ERC-20 standard reference two sample Solidity implementations: one that is actively maintained by OpenZeppelin  and one that has been deprecated by ConsenSys  (and now refers to the OpenZeppelin implementation). As expected, the OpenZeppelin template is very popular within the Solidity developers [57, 80, 51].
OpenZeppelin’s implementation is actually part of a small portfolio of implementations (ERC20, ERC721, ERC777, and ERC1155). Code reuse across the four implementations adds complexity for a developer that only wants ERC-20. This might be the reason for not supporting Vyper in OpenZeppelin’s implementation. No inheritance in Vyper requires different implementation than the current object-oriented OpenZeppelin contracts. Further, most audit tools are not able to import libraries/interfaces from external files (e.g., SafeMath.sol, IERC20.sol). By contrast, TokenHook uses a flat layout in a single file that is specific to ERC-20. It does not use inheritance in Solidity which allows similar implementation in Vyper.
TokenHook makes other improvements over the OpenZeppelin implementation. For example, OpenZeppelin introduces two new functions to mitigate the multiple withdraw attack: increaseAllowance() and decreaseAllowance(). However these are not part of the ERC-20 standard and are not interoperable with other applications that expect to use approve() and transferFrom(). TokenHook secures transferFrom() to prevent the attack (following ) and is interoperable with legacy DApps and web apps. Additionally, TokenHook mitigates the frozen Ether issue by introducing a withdraw() function, while ETH forced into the OpenZeppelin implementation is forever unrecoverable. Both contracts implement a fail-safe mode, however this logic is internal to TokenHook, while OpenZeppelin requires an external Pausable.sol contract.
Diversity in software is important for robustness and security [28, 29]. For ERC-20, a variety of implementations will reduce the impact of a single bug in a single implementation. For example, between 17 March 2017 and 13 July 2017, OpenZeppelin’s implementation used the wrong interface and affected 130 tokens . TokenHook increases the diversity of ERC-20 Solidity implementations and addresses the lack of a reference implementation in Vyper.
5 Auditing Tools and ERC-20
Finally, we conducted an experiment on code auditing tools using the Solidity implementation of TokenHook to understand the current state of automated volunerabiliy testing. Our results illuminate the (in)completeness and error-rate of such tools on one specific use-case (related work studies, in greater width and less depth, a variety of use-cases ). We did not adapt older tools that support significantly lower versions of the Solidity compiler (e.g., Oyente). We concentrated on Solidity as Vyper analysis is currently a paid services or penciled in for future support (e.g., Slither). The provided version number is based on the GitHub repository; tools without a version are web-based and were used in 2020:
5.1 Analysis of audit results
A total of 82 audits have been conducted by these auditing tools that are summarized in Tables 2, 3 and 4. Audits include best practices and security vulnerabilities. To compile the list of 82, we referenced the knowledge-base of each tool [72, 64, 6, 31, 38], understood each threat, manually mapped the audit to the corresponding SWC registry , and manually determined when different tools were testing for the same vulnerability or best practice (which was not always clear from the tools’ own descriptions). Since each tool employs different methodology to analyze smart contracts (e.g., comparing with violation patterns, applying a set of rules, using static analysis, etc.), there are false positives to manually check. Many false positives are not simply due to old/unmaintained rules but actually require tool improvement. We provide some examples in this section.
MythX detects Re-entrancy attack in the noReentrancy modifier. In Solidity, modifiers are not like functions. They are used to add features or apply some restriction on functions . Using modifiers is a known technique to implement Mutex and mitigate re-entrancy attack . This is a false positive and note that other tools have not identified the attack in modifiers.
ContractGuard flags Re-entrancy attack in transfer() function while countermeasures (based on both CEI and Mutex 2.3) are implemented.
Slither detects two low level call vulnerabilities . This is due to use of call.value() that is recommend way of transferring ETH after Istanbul hard-fork (EIP-1884). Therefore, adapting analyzers to new standards can improve accuracy of the security checks.
SmartCheck recommends not using SafeMath and check explicitly where overflows might be occurred. We consider this failed audit as false possible whereas utilizing SafeMath is a known technique to mitigate over/under flows. It also flags using a private modifier as a vulnerability by mentioning, “miners have access to all contracts’ data and developers must account for the lack of privacy in Ethereum”. However private visibility in Solidity concerns object-oriented inheritance not confidentiality. For actual confidentiality, the best practice is to encrypt private data or store them off-chain. The tool also warns against approve() in ERC-20 due to front-running attacks. Despite EIP-1884, it still recommends using of transfer() method with stipend of 2300 gas. There are other false positives such as SWC-105 and SWC-112 that are passed by other tools.
Securify detects the Re-entrancy attack due to unrestricted writes in the noReentrancy modifier . Modifiers are the recommended approach and are not accessible by users. It also flags Delegatecall to Untrusted Callee (SWC-112) while there is no usage of delegatecall() in the code. It might be due to use of SafeMath library which is an embedded library. In Solidity, embedded libraries are called by JUMP commands instead of delegatecall(). Therefore, excluding embedded libraries from this check might improve accuracy of the tool. Similar to SmartCheck, it still recommends to use the transfer() method instead of call.value().
EY token review considers decreaseAllowance and increaseAllowance as standard ERC-20 functions and if not implemented, recognizes the code as vulnerable to a front-running. These two functions are not defined in the ERC-20 standard  and considered only by this tool as mandatory functions. There are other methods to prevent the attack while adhering ERC-20 specifications (see Rahimian et al. for a full paper on this attack and the basis of the mitigation in TokenHook ). The tool also falsely detects the Overflow, mitigated through SafeMath. Another identified issue is Funds can be held only by user-controlled wallets. The tool warns against any token transfer to Ethereum addresses that belong to smart contracts. However, interacting with ERC-20 token by other smart contracts was one of the main motivations of the standard. It also checks for maximum 50000 gas in approve() and 60000 in transfer() method. We could not find corresponding SWC registry or standard recommendation on these limitations and therefore consider them as informational.
Odin raises Outdated compiler version issue due to locking solidity version to 0.5.11. We have used this version due to its compatibility with other auditing tools.
5.2 Comparing audits
After manually overriding the false positives, the average percentage of passed checks for TokenHook reaches to 99.5%. To pass the one missing check and reach a 100% success rate across all tools, we prepared the same code in Solidity version 0.8.4, however it cannot be audited anymore with most of the tools.
We repeated the same auditing process on the top ten tokens based on their market cap . The result of all these evaluation have been summarized in Table 5 by considering false positives as failed audits. This provides the same evaluation conditions across all tokens. Since each tool uses different analysis methods, number of occurrences are considered for comparisons. For example, MythX detects two re-entrancy in TokenHook; therefore, two occurrences are counted instead of one.
As it can be seen in Table 5, TokenHook has the least number of security flaws (occurrences) compared to other tokens. We stress that detected security issues for TokenHook are all false positives. We are also up-front that this metric is not a perfect indication of security. The other tokens may also have many/all false positives (such an analysis would be interesting future work), and not all true positives can be exploited . Mainly, we want to show this measurement as being consistent with our claims around the security of TokenHook. Had TokenHook, for example, had the highest number of occurrences, it would be a major red flag.
98% of tokens on Ethereum today implement ERC-20. While attention has been paid to the security of Ethereum DApps, threats to tokens can be specific to ERC-20 functionality. In this paper, we provide a detailed study of ERC-20 security, collecting and deduplicating applicable vulnerabilities and best practices, examining the ability of seven audit tools. Most importantly, we provide a concrete implementation of ERC-20 called TokenHook 666Compatible Solidity version of TokenHook (v. 0.5.11) deployed on Mainnet at https://bit.ly/35FMbAf and the latest Solidity (v. 0.8.4) on Rinkeby https://bit.ly/3tI139S. Vyper code at https://bit.ly/3dXaaPc.. It is designed to be secure against known vulnerabilities, and can serve as a second reference implementation to provide software diversity. We test it at Solidity version 0.5.11 (due to the limitation of the audit tools) and also provide it at version 0.8.4. Vyper implementation is also provided at version 0.2.8 to make ERC-20 contracts more secure and easier to audit. TokenHook can be used as template to deploy new ERC-20 tokens (e.g., ICOs, DApps, etc), migrate current vulnerable deployments, and to benchmark the precision of Ethereum audit tools.
-  (2019-12) Hardfork meta: istanbul. Note: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1679.md Cited by: §2.4.
-  (2017-07) An in-depth look at the parity multisig bug. Note: https://hackingdistributed.com/2017/07/22/deep-dive-parity-bug/ Cited by: §1, §2.7, §3.8.
-  (2018-02) Three methods to send ether by means of solidity. Note: https://medium.com/daox/three-methods-to-transfer-funds-in-ethereum-by-means-of-solidity-5719944ed6e9 Cited by: §2.4.
-  (2019-11) TokenScope: automatically detecting inconsistent behaviors of cryptocurrency tokens in ethereum. Note: http://www4.comp.polyu.edu.hk/~csxluo/TokenScope.pdf Cited by: §3.4, §4.2.4.
-  (2014) On decentralizing prediction markets and order books. In WEIS, Cited by: §2.3.
-  (2019-11) MythX swc coverage. Note: https://mythx.io/swc-coverage/ Cited by: item 5, §5.1.
-  (2020-06) Tokens. Note: https://github.com/ConsenSys/Tokens Cited by: §4.4.
-  (2018-03) Transaction-ordering attacks. Note: https://medium.com/coinmonks/solidity-transaction-ordering-attacks-1193a014884e Cited by: §2.1, §4.1.1.
-  (2018-06) Missing return value bug — at least 130 tokens affected. Note: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca Cited by: §3.1, §4.4.
-  (2020-06) BeautyChain (bec). Note: https://etherscan.io/address/0xc5d105e63711398af9bbff092d4b6769c82f793d Cited by: §2.2.
-  (2019-08) A survey of tools for analyzing ethereum smart contracts. Note: https://publik.tuwien.ac.at/files/publik_278277.pdf Cited by: §5.
-  (2020-03) Token implementation best practice. Note: https://consensys.github.io/smart-contract-best-practices/tokens/ Cited by: §4.2.
-  (2021-01) Ethereum smart contract security best practices. Note: https://consensys.github.io/smart-contract-best-practices/ Cited by: §2.
-  (2021-01) Re-entrancy. Note: https://solidity.readthedocs.io/en/latest/security-considerations.html#re-entrancy Cited by: §2.3.
-  (2020-01) Security considerations. Note: https://solidity.readthedocs.io/en/latest/security-considerations.html Cited by: §2.
-  (2020-08) Checks effects interactions pattern. Note: https://docs.soliditylang.org/en/latest/security-considerations.html#use-the-checks-effects-interactions-pattern Cited by: §2.4, §4.1.3.
-  (2020-05) What was ton and why it is over. Note: https://telegra.ph/What-Was-TON-And-Why-It-Is-Over-05-12 Cited by: §4.2.3.
-  (2019) SoK: transparent dishonesty: front-running attacks on blockchain. International Conference on Financial Cryptography and Data Security 1, pp. 380. Cited by: §2.1, §4.1.1.
-  (2014-05) Project repository. Note: https://github.com/ethereum Cited by: §1.
-  (2020-01) Solidity — solidity documentation. Note: https://solidity.readthedocs.io/en/latest/ Cited by: §3.2, §4.3.
-  (2021-01) Solidity — solidity documentation. Note: https://solidity.readthedocs.io/en/latest/contracts.html?highlight=library#libraries Cited by: §2.7.
-  (2021-06) Receive ether function. Note: https://docs.soliditylang.org/en/latest/contracts.html#receive-ether-function Cited by: §2.8.
-  (2020-04) Token tracker. Note: https://etherscan.io/tokens?sortcmd=remove&sort=marketcap&order=desc Cited by: §1, §5.2.
-  (2019-09) Token review. Note: https://review-tool.blockchain.ey.com Cited by: item 1.
-  (2015-11) ERC-20 token standard. Note: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md Cited by: §1, §5.1.
-  (2018-12) Osiris: hunting for integer bugs in ethereum smart contracts. Note: https://dl.acm.org/doi/10.1145/3274694.3274737 Cited by: §2.2.
-  (2016-09) A $50 million hack just showed that the dao was all too human — wired. Note: https://www.wired.com/2016/06/50-million-hack-just-showed-dao-human/ Cited by: §1, §2.3.
-  (1997) Building diverse computer systems. In Proceedings. The Sixth Workshop on Hot Topics in Operating Systems (Cat. No.97TB100133), pp. 67–72. External Links: Cited by: §4.4.
-  (1997-10) Computer immunology. Commun. ACM 40 (10), pp. 88–96. External Links: Cited by: §4.4.
-  (2016-11) Golem network token. Note: https://etherscan.io/address/0xa74476443119A942dE498590Fe1f2454d7D4aC0d#code Cited by: §3.1.
-  (2020-03) ContractGuard knowledge-base. Note: https://contract.guardstrike.com/#/knowledge Cited by: item 4, §5.1.
-  (2017-10) Resolution on the eip20 api approve / transferfrom multiple withdrawal attack #738. Note: https://github.com/ethereum/EIPs/issues/738 Cited by: §2.1.
-  (2019-03) Repricing for trie-size-dependent opcodes. Note: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1884.md Cited by: §2.4, §4.1.4.
-  (2019-08) A survey on ethereum systems security: vulnerabilities, attacks and defenses. Note: https://arxiv.org/pdf/1908.04507.pdf Cited by: §1, §2.
-  (2018-09) All you should know about libraries in solidity. Note: https://medium.com/coinmonks/all-you-should-know-about-libraries-in-solidity-dd8bc953eae7 Cited by: §2.7.
-  (2020-06) DEPOSafe: demystifying the fake deposit vulnerability. Note: https://arxiv.org/pdf/2006.06419.pdf Cited by: §3.1.
-  (2018-10) Slither – a solidity static analysis framework. Note: https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/ Cited by: §5.1.
-  (2020-03) Slither – detector documentation. Note: https://github.com/crytic/slither/wiki/Detector-Documentation#name-reused Cited by: item 6, §5.1.
-  (2020-06) Vyper: a security comparison with solidity based on common vulnerabilities. In Proceedings of the 2nd Conference on Blockchain Research and Applications for Innovative Networks and Services (BRAINS 2020), Cited by: §4.3.
-  (2019-05) Guy lando’s knowledge list. Note: https://github.com/guylando/KnowledgeLists/blob/master/EthereumSmartContracts.md Cited by: §2.
-  (2016-10) Making smart contracts smarter. Note: https://dl.acm.org/ft_gateway.cfm?id=2978309&ftid=1805715&dwn=1&CFID=86372769&CFTOKEN=b697c89273876526-8CBDF39B-A89A-31D2-F565B24919F796C6 Cited by: §1.
-  (2019-11) Comprehensive list of known attack vectors and common anti-patterns. Note: https://github.com/sigp/solidity-security-blog Cited by: §2.
-  (2019-10) Watch your language: our first vyper audit. Note: https://blog.trailofbits.com/2019/10/24/watch-your-language-our-first-vyper-audit/ Cited by: footnote 5.
-  (2018-04) Proxy patterns. Note: https://blog.openzeppelin.com/proxy-patterns/ Cited by: §3.6.
-  (2020-06) Contracts. Note: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol Cited by: item 3, §2.1, §4.4.
-  (2020-06) Contracts. Note: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol Cited by: §2.2.
-  (2017-07) The parity wallet hack explained. Note: https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/ Cited by: §1.
-  (2018-11) Security alert. Note: https://www.parity.io/security-alert-2/ Cited by: §2.5.
-  (2018-04) ALERT: new batchoverflow bug in multiple erc20 smart contracts. Note: https://blog.peckshield.com/2018/04/22/batchOverflow/ Cited by: §1, §2.2.
-  (2018-08) Smart contract vulnerabilities: vulnerable does not imply exploited. Note: https://www.usenix.org/system/files/sec21summer_perez.pdf Cited by: §5.2.
-  (2017-08) Building robust smart contracts with openzeppelin. Note: https://www.trufflesuite.com/tutorials/robust-smart-contracts-with-openzeppelin Cited by: §4.4.
-  (2017-07) A hacker stole $31m of ether — how it happened, and what it means. Note: https://www.freecodecamp.org/news/a-hacker-stole-31m-of-ether-how-it-happened-and-what-it-means-for-ethereum-9e5dc29e33ce/ Cited by: §1, §2.9.
-  (2019-07) Resolving the multiple withdrawal attack on erc20 tokens. Note: https://arxiv.org/abs/1907.00903 Cited by: §2.1, §4.1.1, §4.4, §5.1.
-  (2018-12) Overflow attack in ethereum smart contracts. Note: https://blockchain-projects.readthedocs.io/overflow.html Cited by: §2.2.
-  (2020-06) A new way to own and invest in real estate. Note: https://reinno.io/tokenization.html Cited by: §2.3.
-  (2020-07) Generate meaningful knowledge from ethereum. Note: https://reports.aleth.io/ Cited by: §1.
-  (2020-04) 7 openzeppelin contracts you should always use. Note: https://medium.com/better-programming/7-openzeppelin-contracts-you-should-always-use-5ba2e7953cc4 Cited by: §4.4.
-  (2018-12) Sereum: protecting existing smart contracts against re-entrancy. Note: https://arxiv.org/pdf/1812.05934.pdf Cited by: §2.3.
-  (2016-03) Rubixi contract. Note: https://etherscan.io/address/0xe82719202e5965Cf5D9B6673B7503a3b92DE20be#code Cited by: §2.6.
-  (2018-04) Myetherwallet servers are hijacked. Note: https://news.bitcoin.com/myetherwallet-servers-are-hijacked-in-dns-attack/ Cited by: §1.
-  (2018-12) The benefits of “buy” and “sell”. Note: https://medium.com/orbise/the-benefits-of-buy-and-sell-token-functions-dcea536aaf7c Cited by: §2.3.
-  (2017-10) Solidity modifier tutorial - control functions with modifiers. Note: https://coursetro.com/posts/code/101/Solidity-Modifier-Tutorial---Control-Functions-with-Modifiers Cited by: §5.1.
-  (2020-06) Smart contract weakness classification and test cases. Note: https://swcregistry.io/ Cited by: §5.1.
-  (2018-09) Knowledge-base. Note: https://github.com/smartdec/smartcheck Cited by: item 2, §5.1.
-  (2021-04) Solidity by example. Note: https://solidity-by-example.org/0.6/hacks/self-destruct/ Cited by: §2.8.
-  (2020-03) Verify a smart contract. Note: https://odin.sooho.io/ Cited by: item 7.
-  (2018-07) Ethereum in depth, part 2. Note: https://blog.openzeppelin.com/ethereum-in-depth-part-2-6339cf6bddb9/ Cited by: §3.2.
-  (2018-03) Solidity security patterns - forcing ether to a contract. Note: http://danielszego.blogspot.com/2018/03/solidity-security-patterns-forcing.html Cited by: §2.8.
-  (2018-03) Summary of ethereum upgradeable smart contract r&d — part 1–2018. Note: https://blog.indorse.io/ethereum-upgradeable-smart-contract-strategies-456350d0557c Cited by: §3.6.
-  (2021-07) ERC-20 tokens. Note: https://etherscan.io/tokens Cited by: §1.
-  (2018-08) Securify: practical security analysis of smart contracts. Note: https://arxiv.org/pdf/1806.01143.pdf Cited by: item 3, §5.1.
-  (2020-01) Securify v2.0. Note: https://github.com/eth-sri/securify2 Cited by: item 3, §5.1.
-  (2017-10) Reentrancy guard. Note: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol Cited by: §5.1.
-  (2017-04) MonolithDAO. Note: https://github.com/MonolithDAO/token/blob/master/src/Token.sol Cited by: §2.1.
-  (2016-11) Attack vector on erc20 api (approve/transferfrom methods) and suggested improvements. Note: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 Cited by: §2.1.
-  (2021-04) Pythonic smart contract language for the evm. Note: https://github.com/vyperlang/vyper Cited by: §4.3.
-  (2019-01) Mutex. Note: en.wikipedia.org/wiki/Mutual_exclusion Cited by: §2.3, §2.4, §4.1.3.
-  (2020-06) Liberty reserve. Note: https://en.wikipedia.org/wiki/Liberty_Reserve Cited by: §4.2.3.
-  (2016-03) Ethereum: a secure decentralised generalised transaction ledger. Note: http://gavwood.com/paper.pdf Cited by: §1.
-  (2018-08) Create and distribute your erc20 token with openzeppelin. Note: https://www.tooploox.com/blog/create-and-distribute-your-erc20-token-with-openzeppelin Cited by: §4.4.