{"id":21388633,"url":"https://github.com/sec-bit/baderc20fix","last_synced_at":"2026-01-04T03:04:31.601Z","repository":{"id":117651857,"uuid":"136563888","full_name":"sec-bit/badERC20Fix","owner":"sec-bit","description":"Fix for the contracts which are not compatible with ERC20 ","archived":false,"fork":false,"pushed_at":"2019-10-15T12:49:54.000Z","size":800,"stargazers_count":25,"open_issues_count":0,"forks_count":7,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-01-23T00:27:00.544Z","etag":null,"topics":["erc20","smart-contracts"],"latest_commit_sha":null,"homepage":"https://secbit.io","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sec-bit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-06-08T03:55:05.000Z","updated_at":"2022-09-21T23:30:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"a6a3b445-bc64-4db9-9b89-749723f15c40","html_url":"https://github.com/sec-bit/badERC20Fix","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sec-bit%2FbadERC20Fix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sec-bit%2FbadERC20Fix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sec-bit%2FbadERC20Fix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sec-bit%2FbadERC20Fix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sec-bit","download_url":"https://codeload.github.com/sec-bit/badERC20Fix/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243871524,"owners_count":20361375,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["erc20","smart-contracts"],"created_at":"2024-11-22T12:18:56.373Z","updated_at":"2026-01-04T03:04:31.512Z","avatar_url":"https://github.com/sec-bit.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# badERC20Fix  [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Join the chat at https://gitter.im/sec-bit/Lobby](https://badges.gitter.im/sec-bit/Lobby.svg)](https://gitter.im/sec-bit/Lobby) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n\nRead the docs in Chinese: https://github.com/sec-bit/badERC20Fix/blob/master/README_CN.md\n\n---\n\nAn enormous amount of ERC20 Token contracts do not follow EIP20 strictly [[1]](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md), which undermines DApp developing significantly.\n\nSpecially, after Solidity compiler's upgrading to version 0.4.22 on April 17th, compiled Solidity code would become incompatible with a few non-standard smart contracts [[2]](https://github.com/ethereum/solidity/issues/4116), causing difficulties in DApp programming [[3]](https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca).\n\nPlease refer to SECBIT team's former reports for details [[4]](https://mp.weixin.qq.com/s/1MB-t_yZYsJDTPRazD1zAA)[[5]](https://medium.com/loopring-protocol/an-incompatibility-in-smart-contract-threatening-dapp-ecosystem-72b8ca5db4da). We would introduce this solution in areas of analysis, application scenarios and gas estimation.\n\n## Token Contracts with Return-Value Incompatibility\n\nIf you want to get a list of incompatible Token contracts for DApp and DEX developing, please refer to [awesome-buggy-erc20-tokens](https://github.com/sec-bit/awesome-buggy-erc20-tokens) repo.\n\n- [Incompatible transfer()](https://github.com/sec-bit/awesome-buggy-erc20-tokens/blob/master/csv/transfer-no-return.o.csv)\n- [Incompatible transferFrom()](https://github.com/sec-bit/awesome-buggy-erc20-tokens/blob/master/csv/transferFrom-no-return.o.csv)\n- [Incompatible approve()](https://github.com/sec-bit/awesome-buggy-erc20-tokens/blob/master/csv/approve-no-return.o.csv)\n\n## Code Satisfying ERC20 Specification\n\n```js\ncontract TokenStd {\n    function transfer(address _to, uint256 _value) public returns (bool success) {\n        return true;\n    }\n    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {\n        return true;\n    } \n    function approve(address _spender, uint256 _value) public returns (bool success) {\n        return true;\n    }\n}\n```\n\n## Code Unsatisfying ERC20 Specification\n\n```js\n// NEVER USE THIS\ncontract TokenNoStd {\n    function transfer(address _to, uint256 _value) public {\n    }\n    function transferFrom(address _from, address _to, uint256 _value) public {\n    }\n    function approve(address _spender, uint256 _value) public {\n    }\n}\n```\n\n## Sides Affected\n\nSmart contracts would get affected if they are built in Solidity version \u003e= 0.4.22 and they require calling `transfer()`, `transferFrom()` or `approve()` in other ERC20 Token contracts.\n\nIt mainly affects the 2 sides below:\n\n- Decentralized exchanges (DEX)\n- DApps using ERC20 Token\n\n## Consequences\n\n- Several non-standard Token contracts are not able to perform transactions normally\n- Part of tokens monitored by the contract might get locked forever\n\nIt is safe to say that a growing number of ERC20 Token API callings would fail(functions like `transfer()` would fail in the end due to revert conducted by EVM) as lots of DApps get upgraded.\n\n\n## Analysis\n\nThe incompatibility results partly from not strictly following ERC20 standard, also from the inconsistent behavior in Solidity compiler.\n\nTake a look at the code sample:\n\n```js\npragma solidity ^0.4.18;\n\ninterface Token {\n    function transfer(address _to, uint256 _value) returns (bool success);\n}\n\ncontract DApp {\n  function doTransfer(address _token, address _to, uint256 _value) public {\n    require(Token(_token).transfer(_to, _value));\n  }\n}\n```\n\nThis sample calls `transfer()` in the target ERC20 contract and compiles differently in Solidity 0.4.21 and 0.4.22:\n\n### 0.4.21\n\n![0.4.21](img/dis_0421.png)\n\n### 0.4.22\n\n![0.4.22](img/dis_0422.png)\n\nObviously, the bytecode compiled by 0.4.22 version checks that if RETURNDATASIZE is smaller than 32. The target contract will not pass the test without a return value, thus triggering revert. The solution is bypassing the auto-test on RETURNDATASIZE by compiler and handling manually.\n\n## Solution for DApp and DEX\n\n```js\nfunction isContract(address addr) internal {\n    assembly {\n        if iszero(extcodesize(addr)) { revert(0, 0) }\n    }\n}\n\nfunction handleReturnData() internal returns (bool result) {\n    assembly {\n        switch returndatasize()\n        case 0 { // not a std erc20\n            result := 1\n        }\n        case 32 { // std erc20\n            returndatacopy(0, 0, 32)\n            result := mload(0)\n        }\n        default { // anything else, should revert for safety\n            revert(0, 0)\n        }\n    }\n}\n\nfunction asmTransfer(address _erc20Addr, address _to, uint256 _value) internal returns (bool result) {\n    // Must be a contract addr first!\n    isContract(_erc20Addr);  \n    // call return false when something wrong\n    require(_erc20Addr.call(bytes4(keccak256(\"transfer(address,uint256)\")), _to, _value));\n    // handle returndata\n    return handleReturnData();\n}\n```\n\nOne approach is to apply `call()` to invoke `transfer()` and check by getting `returndatasize()` with inline assembly code manually.\n\n- Getting 0 means that the called ERC20 contract transfers successfully without a return value.\n\n- Getting 32 means that the contract meets with ERC20 standard. Please directly call `returndatacopy()` and get the return value for test by `mload()`.\n\n- Else call `revert`.\n\n`returndatacopy()` copies 0-32 Byte in RETURNDATA to 0-32 Byte in memory for `mload()` getting the return value afterwards.\n\n```ruby\nmemory[destOffset:destOffset+length] = RETURNDATA[offset:offset+length]\n```\n\nThe full middle layer code needs to support `transferFrom()` and `approve()`. Please refer to [badERC20Fix.sol](badERC20Fix.sol) for details. Forking for test and discussion is also welcomed.\n\nAside from this, solutions by Lukas Cremer [[6]](https://gist.github.com/lukas-berlin/0f7005301f29e3881ad15449e68c2486#file-gistfile1-txt) and BrendanChou [[7]](https://gist.github.com/BrendanChou/88a2eeb80947ff00bcf58ffdafeaeb61) employs non-standard `function transfer(address to, uint value) external` interface for function calling.\n\nWe suggest not to follow this pattern, as the community is supposed to promote interfacing meeting with ERC20 standard.\n\n## Application Scenarios\n\nDApp and DEX developers using Solidity 0.4.22 and above should load the encapsulated ERC20AsmFn Library in the repo and apply to the standard ERC20 contract (`using ERC20AsmFn for ERC20`).\n\nSubstitute `asmTransfer()`, `asmTransferFrom()` and `asmApprove()` for `transfer()`, `transferFrom()`, `approve()` when it comes to calling an ERC20 Token contract.\n\nPlease handle return values in functions above accordingly.\n\nEach `transfer()` by `asmTransfer()` calling consumes 244 more gas than before by our computation. We can actually optimize further by calling directly with the parsed function signature.\n\n```js\nrequire(_erc20Addr.call(bytes4(keccak256(\"transfer(address,uint256)\")), _to, _value));\n```\n\ncan change to\n\n```js\nrequire(_erc20Addr.call(0xa9059cbb, _to, _value));\n```\n\nThus the excessive gas cost would reduce from **244** to **96**.\n\nSECBIT team would keep an eye on this ERC20 incompatibility and offer solutions with top security and efficiency continuously. We welcome further discussions to build a better open-source security community.\n\n## Reference\n\n- [1] EIP20 Specification, https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md\n\n- [2] Relevant Solidity Issues, https://github.com/ethereum/solidity/issues/4116\n\n- [3] Report by Lukas Cremer, https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca\n\n- [4] Report about incompatible Token contracts by SECBIT team, https://mp.weixin.qq.com/s/1MB-t_yZYsJDTPRazD1zAA\n\n- [5] An Incompatibility in Ethereum Smart Contract Threatening dApp Ecosystem, https://medium.com/loopring-protocol/an-incompatibility-in-smart-contract-threatening-dapp-ecosystem-72b8ca5db4da\n\n- [6] Solution by Lukas Cremer, https://gist.github.com/lukas-berlin/0f7005301f29e3881ad15449e68c2486#file-gistfile1-txt\n\n- [7] Solution by BrendanChou, https://gist.github.com/BrendanChou/88a2eeb80947ff00bcf58ffdafeaeb61\n\n## License\n\n[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsec-bit%2Fbaderc20fix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsec-bit%2Fbaderc20fix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsec-bit%2Fbaderc20fix/lists"}