The article presents a list of ERC-20 integration issues that either arise from tokens not fully following the ERC-20 standard or implementing uncommon behaviors that might lead to security vulnerabilities.
Table of Contents
1. Missing Return Values
There are ERC-20 tokens that do not return the boolean values for some functions. This might lead to undefined behaviour.
Missing return value on transfer functions
bool result = ERC20(token).transfer(recipient, amount);
require(result, "transfer failed");
This implementation will revert on tokens that do not return true on successful transfer.
Tokens | – USDT on Ethereum – BNB on Ethereum – OMG on Ethereum – BADGER on Ethereum |
Example Issues | – Notional Audit – PoolTogether Audit |
Missing return value on some functions
The BNB token returns value on transferFrom
but is does not do that for transfer
function. This means that contracts that expect values being returned can pull BNB from the user’s account but cannot transfer it out.
Tokens | – BNB on Ethereum |
Issues | – Uniswap V1 Issue |
Returning false on successful transfers
There are some tokens that a bool return for transfer(s) function, but then return false
even when the transfer was successful.
Tokens | – Used to be Gold Tether on Ethereum, but it has been updated. |
Issues | – Visor Finance Audit |
Recommendation
It is recommended to use SafeERC20 library from OpenZeppelin that handle cases of transfer reverts and return values. However this will not handle tokens such as Gold Tether.
2. Approval Race Protection
There are some tokens that implemented it own protection against approval race issue which lead to even more complicated problems while integrating this tokens.
Tokens | – USDT on Ethereum |
Issues | – Duality Focus Audit |
Recommendation
It is recommended to use OpenZeppelin’s forceApprove
function. Another solution is to first execute approve
with amount 0
and then another call with the desired approve amount.
3. Reentrant Calls
Some tokens implement the ERC-777 standard, which enables the execution of hooks during token transfers. ERC-777 utilizes ERC-1820, allowing users to register hooks for specific actions. It supports registering two types of hooks: one for sending tokens and another for receiving them.
Developers can register contracts implementing the ERC777TokensSender
interface via ERC-1820 to execute code when tokens are sent. Similarly, contracts implementing the ERC777TokensRecipient
interface can be registered to execute code upon receiving tokens.
However, this design introduces potential vulnerabilities to reentrancy exploits, where attackers could hijack the execution flow through registered hooks.
Tokens | – imBTC on Ethereum |
Issues | – imBTC Uniswap Hack – Backed Protocol Audit |
Recommendation
When integrating and supporting ERC-777 tokens, it is advised to follow the CEI (Checks-Effects-Interactions) pattern or to use a reentrancy guard to prevent ERC-777 tokens from reentering the contract logic.
4. Fee on Transfer
There are tokens that implement fee on transfer. That means the amount specified while executing transfer
or transferFrom
functions will not be the amount received by the recipient, but it will be reduced by the fee.
Tokens | – STA on Ethereum – PAXG on Ethereum – USDT on Ethereum (it is possible to enable fees) – USDC on Ethereum (it is possible to enable fees) |
Issues | – Balancer Pool with STA Deflationary Token Incident – Timeswap Audit |
Recommendation
To support fee-on-transfer tokens, it is necessary to explicitly check the received amount of tokens by calculating the difference in token balances before and after the transfer. This ensures that only the received amount of tokens is used in the accounting logic.
5. Rebasing Tokens
There are rebasing tokens that modify balances outside of transfer functions. This leads to scenarios where the protocol’s accounting might be incorrect since the balance has rebased after the accounting event.
Tokens | – stETH on Ethereum – OHM on Ethereum – AMPL on Ethereum |
Issues | – Cally Audit – Agent Exchange Audit |
Recommendation
There is no correct way of handling tokens that modify balances outside of transfers. It is recommended to use the wrapped ER20 tokens of the corresponding rebasing/airdrop tokens.
6. Multiple Token Addresses
There are tokens that have multiple addresses. This means it is possible to refer to the token’s accounting and approval mechanism through two different addresses, which might lead to serious issues for protocols that assume the token has a single address.
Tokens | – Matic a native currency on Polygon has a corresponding ERC-20 token. – In the past TrueUSD had two addresses but it has been upgraded since then. |
Issues | – TrueUSD Compound Vulnerability |
Recommendation
While there is not a single correct way to support tokens with multiple token addresses, it is important for protocols to exercise caution in their accounting logic, especially when deploying to chains that have ERC-20 token representations of native currency.
7. Upgradable Tokens
Certain tokens are upgradable, enabling their owners to modify the token’s logic at any time. Changes to the token’s implementation can disrupt any smart contract that relies on its previous behavior.
Recommendation
Consider the ability of tokens being updated in the protocol threat modelling.
8. Flash Mintable Tokens
Some tokens support “flash minting”, allowing tokens to be minted for the duration of a single transaction, provided they are returned to the token contract by the end of the transaction. This is similar to a flash loan but does not require the tokens to exist before the transaction begins. A token capable of flash minting could potentially have a total supply of up to max uint256.
Tokens | – DAI on Ethereum |
Recommendation
Consider the ability of users to have high balance in the protocol threat modelling.
9. Tokens with Blocklists
Certain tokens implement an admin-controlled address blocklist at the contract level. If an address is blocked, transfers to and from that address are prohibited.
Malicious or compromised token owners can trap funds in a contract by adding its address to the blocklist. This can occur due to regulatory action against the contract itself, or against an individual user of the contract (e.g., a Uniswap liquidity provider), or it may be part of an extortion attempt targeting users of the blocked contract.
Tokens | – USDT on Ethereum – USDC on Ethereum |
Issues | – Opyn Audit |
Recommendation
It is important to implement logic following the pull over push pattern to ensure that a single user will not be able to execute a denial-of-service attack effectively shutting down some or all functionality of the protocol.
10. Pausable Tokens
Some tokens can be paused by an admin. Similar to the blocklist issue, an admin-controlled pause feature exposes token users to risks from a malicious or compromised token owner.
Recommendation
While there is not a single solution, it is important to note the ability of supported ERC-20 tokens to pause within the protocol’s threat model.
11. Low Decimals
Some tokens have low decimal precision. This can result in greater than expected precision loss.
Tokens | – USDT on Ethereum (6 decimals) – GUSD (Gemini USD) on Ethereum (2 decimals) |
Issues | – Numoen Audit |
Recommendation
It is important not to assume that the ERC-20 token has 18 decimals and to evaluate all mathematical calculations to ensure they do not lead to a loss of precision.
12. High Decimals
Some tokens have more than 18 decimals. This can lead to unexpected reverts due to overflow, posing a liveness risk to the contract.
Tokens | – Yam-V2 on Ethereum (24 decimals) |
Recommendation
It is important not to assume that the ERC-20 token has 18 decimals and to evaluate all mathematical calculations to ensure they do not lead to overflows given high number of decimals.
13. Use Of transferFrom
By The Owner
Some token implementations do not decrease the caller’s allowance if the sender is the same as the caller, making transferFrom behave like transfer in this scenario. In contrast, other implementations do decrease the caller’s allowance from the sender in transferFrom, even if the caller and sender are the same address. This results in different semantics for transfer
and transferFrom
functions in such cases.
Tokens | – DSToken Implementation |
Recommendation
Ensure that the contract does not use transferFrom
with the first argument provided by the user. A potential attacker could specify the contract’s own address as the argument and drain its balance.
14. No Revert on Failure
Some tokens do not revert on failure but instead return false. While this behavior technically complies with the ERC20 standard, it diverges from common Solidity coding practices.
Recommendation
It is recommended to use SafeERC20 library from OpenZeppelin that handle cases of transfer reverts and return values.
15. Revert on Zero Value Approvals
Some tokens revert when approving a zero value amount. Integrators may need to implement special handling for this logic when working with such tokens.
Tokens | BNB on Ethereum |
Recommendation
It is recommended to avoid implementing logic that would result in approving zero amount to the spender.
16. Revert on Approval To Zero Address
Some tokens revert when attempting to approve the zero address to spend tokens. Integrators may need to implement special handling for this scenario when working with such tokens.
Tokens | – OpenZeppelin ERC20 |
Recommendation
It is recommended to avoid implementing logic that would result in approving zero address as a spender.
17. Revert on Transfer to the Zero Address
Some tokens revert when attempting to transfer to address(0). This can disrupt systems that rely on transferring tokens to address(0) to burn them.
Tokens | – OpenZeppelin ERC20 |
Recommendation
It is recommended to avoid implementing logic that would result in transferring tokens to the zero address.
18. Revert on Large Approvals & Transfers
Some tokens (e.g., UNI, COMP) revert if the value passed to approve or transfer exceeds uint96. Both tokens handle a special case in their approve function where setting the allowance to type(uint96).max occurs if the approval amount is uint256(-1). This approach can potentially affect systems that depend on the exact value passed to approve being reflected in the allowances mapping.
Recommendation
In case the protocol is expected to support tokens that revert on large approvals and transfers, such as COMP
or UNI
tokens, it is important to ensure the protocol correctly handles scenarios involving large transfers and approvals.
19. Transfer of less than amount
Some tokens include a specific condition in their transfer functions where if amount == type(uint256).max
, only the user’s balance is transferred. This can potentially create problems for systems that transfer a user-provided amount to their contract and then store the same value in their storage without verifying the actual amount transferred.
Tokens | – cUSDCv3 on Ethereum |
Issues | – DYAD Audit |
Recommendation
In case the cUSDCv3
tokens are expected to be supported it is important to validate the special case of transferring type(uint256).max
and ensure its handled properly.
20. Permit Function That Do Not Follow ERC-2612
There are tokens that do not correctly implement ERC-2612 and its permit
function. These tokens might not revert for incorrect signatures resulting in successful execution.
Tokens | – DAI on Ethereum – RAI on Ethereum – Other examples include GLM, STAKE, CHAI, HAKKA, USDFL, HNY |
Issues | – PoolTogether Audit |
Recommendation
In case the permit
functionality is expect to be supported it is important to carefully integrate tokens that do not correctly implement ERC-2612.
21. Non string metadata
Some tokens encode metadata fields (name / symbol) as bytes32 instead of the string format specified by ERC20. This can lead to challenges when attempting to use metadata from these tokens.
Tokens | MKR on Ethereum |
Recommendation
It is recommended to either avoid using tokens’ names and symbols on-chain or creating correct adapter to handle tokens that returns bytes32 such as MKR. This is especially important since name
, symbol
and decimals
are optional functions according to ERC-20 standard.
22. Frontend – Code Injection Via Token Name
Some tokens with malicious intent have been observed to embed harmful JavaScript within their name attribute. This enables attackers to potentially extract private keys from users who interact with these tokens through vulnerable frontends.
Issues | – EtherDelta Issue |
Recommendation
It is recommended to treat the ERC-20 token metadata as malicious and encode it correctly on the frontend.