Ccswap Security Assessment
←
→
Page content transcription
If your browser does not render page correctly, please read the page content below
Security Assessment ccswap May 7th, 2021
ccswap Security Assessment Summary This report has been prepared for ccswap smart contracts, to discover issues and vulnerabilities in the source code of their Smart Contract as well as any contract dependencies that were not part of an officially recognized library. A comprehensive examination has been performed, utilizing Manual Review and Static Analysis techniques. The auditing process pays special attention to the following considerations: Testing the smart contracts against both common and uncommon attack vectors. Assessing the codebase to ensure compliance with current best practices and industry standards. Ensuring contract logic meets the specifications and intentions of the client. Cross referencing contract structure and implementation against similar smart contracts produced by industry leaders. Thorough line-by-line manual review of the entire codebase by industry experts. The security assessment resulted in findings that ranged from critical to informational. We recommend addressing these findings to ensure a high level of security standards and industry practices. We suggest recommendations that could better serve the project from the security perspective: Enhance general coding practices for better structures of source codes; Add enough unit tests to cover the possible use cases given they are currently missing in the repository; Provide more comments per each function for readability, especially contracts are verified in public; Provide more transparency on privileged activities once the protocol is live.
ccswap Security Assessment Overview Project Summary Project Name ccswap Platform Ethereum Language Solidity Codebase https://github.com/ccswap/contract_review Commits 3078f831e01cc2afe98923119beff1989c26047d Audit Summary Delivery Date May 07, 2021 Audit Methodology Manual Review, Static Analysis Key Components Vulnerability Summary Total Issues 19 Critical 0 Major 2 Medium 0 Minor 3 Informational 14 Discussion 0
ccswap Security Assessment Audit Scope ID file SHA256 Checksum CCT CCToken_flat.sol abd3e90f0f55432488e1654e497120202157fb2126085b8cd349f02f2541baf0 MCV MasterChefV2.sol 2e9a17053d4971b8e15f4f59ea12050ac4cdcac6c7fa1f98fae6ab41c692d95d MCK Migrator_flat.sol 6679ba4dc3d730782c2c9c0a052dfd83a7513ad585b481a0dd1d41c60fa159e5 TTL TeamTimeLock_flat.sol 0a6760f35d48a44b6b787f45f59ee2faa7a6a30b828313fe40fe010a7cb2e910
ccswap Security Assessment Findings Critical 0 (0.00%) Major 2 (10.53%) 19 Medium Minor 0 (0.00%) 3 (15.79%) Total Issues Informational 14 (73.68%) Discussion 0 (0.00%) ID Title Category Severity Status CCT-01 Proper Usage of public and external type Coding Style Informational Acknowledged Language CCT-02 Unlocked Compiler Version Declaration Informational Acknowledged Specific MCC-01 Proper Usage of public and external type Coding Style Informational Acknowledged Language MCC-02 Unlocked Compiler Version Declaration Informational Acknowledged Specific MCK-01 Proper Usage of public and external type Coding Style Informational Acknowledged MCK-02 Lack of Input Validation Volatile Code Minor Resolved MCK-03 Pair Creation With Zero Migration Balance Gas Optimization Informational Acknowledged Language MCK-04 Unlocked Compiler Version Declaration Informational Acknowledged Specific MCK-05 Potential Front-Running For Migration Blocking Control Flow Major Acknowledged MCV-01 Proper Usage of public and external type Coding Style Informational Acknowledged MCV-02 Missing Some Important Checks Volatile Code Minor Acknowledged MCV-03 add() Function Not Restricted Volatile Code Minor Acknowledged MCV-04 Missing Emit Events Gas Optimization Informational Acknowledged MCV-05 Array Index Out of Bound Volatile Code Informational Acknowledged
ccswap Security Assessment ID Title Category Severity Status Take startBlock into Consideration in MCV-06 Logical Issue Informational Acknowledged getMultiplier() MCV-07 Boolean Equality Gas Optimization Informational Acknowledged MCV-08 Potential Front-Running For Migration Blocking Control Flow Major Acknowledged TTL-01 Proper Usage of public and external type Coding Style Informational Acknowledged Language TTL-02 Unlocked Compiler Version Declaration Informational Acknowledged Specific
ccswap Security Assessment CCT-01 | Proper Usage of public and external type Category Severity Location Status Coding CCToken_flat.sol: 397, 414, 440, 448, 459, 477, 495, 514, 688, 697, Informational Acknowledged Style 1256, 1264, 1269, 1282 Description “public” functions that are never called by the contract should be declared “external” . When the inputs are arrays, “external” functions are more efficient than “public” functions. Examples: Functions like : renounceOwnership() , transferOwnership() , add() , set() , setMigrator() , MasterChef.migrate() , deposit() , withdraw() , emergencyWithdraw() , setPause() , Migrator.migrate() , symbol() , decimals() , transfer() , allowance() , approve() , transferFrom() , increaseAllowance() , decreaseAllowance() , renounceOwnership() , transferOwnership() , addMinter() , delMinter() , getMinter() , getBalance() , setBeneficiary() Recommendation Consider using the “external” attribute for functions never called from the contract. Alleviation The development team considered this is a good advice, but will still follow the openzeppeling standard lib here.
ccswap Security Assessment CCT-02 | Unlocked Compiler Version Declaration Category Severity Location Status Language Specific Informational CCToken_flat.sol: 3, 31, 112, 330, 638, 709, 1009 Acknowledged Description The compiler version utilized throughout the project uses the “^” prefix specifier, '>=0.6.0=0.6.2
ccswap Security Assessment MCC-01 | Proper Usage of public and external type Category Severity Location Status Coding Style Informational MasterChef_flat.sol: 953, 962 Acknowledged Description “public” functions that are never called by the contract should be declared “external” . When the inputs are arrays, “external” functions are more efficient than “public” functions. Examples: Functions like : renounceOwnership() , transferOwnership() , add() , set() , setMigrator() , MasterChef.migrate() , deposit() , withdraw() , emergencyWithdraw() , setPause() , Migrator.migrate() , symbol() , decimals() , transfer() , allowance() , approve() , transferFrom() , increaseAllowance() , decreaseAllowance() , renounceOwnership() , transferOwnership() , addMinter() , delMinter() , getMinter() , getBalance() , setBeneficiary() Recommendation Consider using the “external” attribute for functions never called from the contract. Alleviation The development team considered this is a good advice, but will still follow the openzeppeling standard lib here.
ccswap Security Assessment MCC-02 | Unlocked Compiler Version Declaration Category Severity Location Status Language Specific Informational MasterChef_flat.sol: 5, 86, 304, 497, 574, 875, 903 Acknowledged Description The compiler version utilized throughout the project uses the “^” prefix specifier, '>=0.6.0=0.6.2
ccswap Security Assessment MCK-01 | Proper Usage of public and external type Category Severity Location Status Coding Style Informational Migrator_flat.sol: 237 Acknowledged Description “public” functions that are never called by the contract should be declared “external” . When the inputs are arrays, “external” functions are more efficient than “public” functions. Examples: Functions like : renounceOwnership() , transferOwnership() , add() , set() , setMigrator() , MasterChef.migrate() , deposit() , withdraw() , emergencyWithdraw() , setPause() , Migrator.migrate() , symbol() , decimals() , transfer() , allowance() , approve() , transferFrom() , increaseAllowance() , decreaseAllowance() , renounceOwnership() , transferOwnership() , addMinter() , delMinter() , getMinter() , getBalance() , setBeneficiary() Recommendation Consider using the “external” attribute for functions never called from the contract. Alleviation The development team considered this is a good advice, but will still follow the openzeppeling standard lib here.
ccswap Security Assessment MCK-02 | Lack of Input Validation Category Severity Location Status Volatile Code Minor Migrator_flat.sol: 225 Resolved Description Missing validation for the input variables _chef , _oldFactory in function Migrator.constructor() . Recommendation Consider adding below checks to ensure these input variables are not equal to address(0) : require(_chef != address(0), "Migrator : _chef is zero address"); chef = _chef; require(_oldFactory != address(0), "Migrator : _oldFactory is zero address"); oldFactory = _oldFactory; Alleviation The development team heeded our advice and resolved this issue in commit 2ea789f0b79a8f839a6fc211f938d27c04f62caa.
ccswap Security Assessment MCK-03 | Pair Creation With Zero Migration Balance Category Severity Location Status Gas Optimization Informational Migrator_flat.sol: 237~256(Migrator) Acknowledged Description In the unlikely situation when the migrated pool does have any balance for migration, migrate() function is expected to simply return. However, it is interesting to notice that the return does not happen until the new ccswap pair is created, which may cost lots of gas. Recommendation Move the balance detection logic earlier so that we can simply return without migration and new pair creation if the balance is zero. For example: function migrate(IUniswapV2Pair orig) public returns (ICCPair) { require(msg.sender == chef, "Migrator: not from master chef"); require(block.number >= notBeforeBlock, "Migrator: too early to migrate"); require(orig.factory() == oldFactory, "Migrator: not from old factory"); uint256 lp = orig.balanceOf(msg.sender); if (lp == 0) return pair; Alleviation The development replied that: Not detec at first because we need to return pair object to masterChef.
ccswap Security Assessment MCK-04 | Unlocked Compiler Version Declaration Category Severity Location Status Language Specific Informational Migrator_flat.sol: 3, 27, 84, 135 Acknowledged Description The compiler version utilized throughout the project uses the “^” prefix specifier, '>=0.6.0=0.6.2
ccswap Security Assessment MCK-05 | Potential Front-Running For Migration Blocking Category Severity Location Status Control Flow Major Migrator_flat.sol: 237~256(Migrator) Acknowledged Description The migrate() function in MasterChef contract has a final check after the migration, i.e., require(bal == newLpToken.balanceOf(address(this)), "migrate: bad"). However, in the mint() function of CCPair, the minted liquidity amount to be returned is only set to the old ccswap's pool token ammount when _totalSupply == 0. This means that the whole migration process assumes the migration transaction is the first to mint the new LP tokens (of this particular trading pair), otherwise it will fail. If the migration transaction is not the first to mint new LP tokens, the first transaction that successfully mints the new LP tokens will lead to _totalSupply != 0. In other words, the migration transaction will be forced to take the execution path liquidity = SafeMath.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1). This is a confirmed issue in Sushiswap, and they solved this issue by streamlining the entire deployment script. Recommendation To ensure a smooth migration process, we need to guarantee the first minting of new LP tokens is launched by the migration transaction. To achieve that, we need to prevent any unintended minting (of new LP tokens) between deploy CCFactory and configure MasterChef. A natural approach is to complete the initial three steps, deploy CCFactory, deploy migrator and configure MasterChef within the same transaction, best facilitated by a contract-coordinated deployment. Alleviation The development team confirmed that this is a known issue. To solve this issue, they will first set the migrator of factory and check there is not pairs in the factory. After that nobody can add liquidity until migrator migrate this pool, which ensures no front running.
ccswap Security Assessment MCV-01 | Proper Usage of public and external type Category Severity Location Status Coding Style Informational MasterChefV2.sol: 117, 133, 142, 147, 212, 228, 243, 267 Acknowledged Description “public” functions that are never called by the contract should be declared “external” . When the inputs are arrays, “external” functions are more efficient than “public” functions. Examples: Functions like : renounceOwnership() , transferOwnership() , add() , set() , setMigrator() , MasterChef.migrate() , deposit() , withdraw() , emergencyWithdraw() , setPause() , Migrator.migrate() , symbol() , decimals() , transfer() , allowance() , approve() , transferFrom() , increaseAllowance() , decreaseAllowance() , renounceOwnership() , transferOwnership() , addMinter() , delMinter() , getMinter() , getBalance() , setBeneficiary() Recommendation Consider using the “external” attribute for functions never called from the contract. Alleviation The development team considered this is a good advice, but will still follow the openzeppeling standard lib here.
ccswap Security Assessment MCV-02 | Missing Some Important Checks Category Severity Location Status Volatile MasterChefV2.sol: 147~156, 172~183, 194~209, 212~225, 228~240, 243 Minor Acknowledged Code ~254 Description Functions migrate() , pendingCC() , updatePool() , deposit() , withdraw() , emergencyWithdraw() are missing parameter validations. Recommendation Consider adding below checks in the functions mentioned before, to ensure the token of the given pool is valid: require (address(pool.lpToken) != address(0), "MC: _pid is incorrect"); For example: function migrate(uint256 _pid) public notPause { require(address(migrator) != address(0), "migrate: no migrator"); PoolInfo storage pool = poolInfo[_pid]; require (address(pool.lpToken) != address(0), "MC: _pid is incorrect"); IERC20 lpToken = pool.lpToken; ... } Alleviation The development team replied that they ensure every pool has nonzero lp token.
ccswap Security Assessment MCV-03 | add() Function Not Restricted Category Severity Location Status Volatile Code Minor MasterChefV2.sol: 115~130(MasterChef) Acknowledged Description The comment for function add() mentioned "DO NOT add the same LP token more than once. Rewards will be messed up if you do." The total amount of reward eggReward in function updatePool() will be incorrectly calculated if the same LP token is added into the pool more than once in function add() . However, the code does not reflect as the comment behaviors as there isn’t any valid restriction on preventing this issue. The current implementation is relying on the trust of the owner to avoid repeatedly adding same LP token to the pool, as the function will only be called by the owner. Recommendation Detect whether the given pool for addition is a duplicate of an existing pool. The pool addition is only successful when there is no duplicate. One solution is add a check function in add(). For example: function massUpdatePools() public { uint256 length = poolInfo.length; for (uint256 pid = 0; pid < length; ++pid) { require(poolInfo[_pid].lpToken != _lpToken, "add: existing pool?"); } } Or using mapping of addresses -> booleans , which can restrict the same address being added twice. Alleviation The development team replied that they will choose another simpler way for gas saving. In addition, even if a duplicated tokens added, they can set its allocation point to zero to undo it.
ccswap Security Assessment MCV-04 | Missing Emit Events Category Severity Location Status Gas MasterChefV2.sol: 133~139(MasterChef), 142~144(MasterChef), Informational Acknowledged Optimization 257~264(MasterChef) Description Function that affect the status of sensitive variables should be able to emit events as notifications to customers. safeCCTransfer() set() setMigrator() Recommendation Consider adding events for sensitive actions, and emit it in the function. For function safeCCTransfer() , consider to emit events with parameters _to and _amount in case _amount > ccBal .
ccswap Security Assessment MCV-05 | Array Index Out of Bound Category Severity Location Status Volatile Code Informational MasterChefV2.sol: (133), (147), (172), (194), (212), (228), (243) Acknowledged Description There's no sanity check to validate if a pool is existing. The current implementation simply relies on the implicit, compiler-generated bound-checks of arrays to ensure the pool index stays within the array range [0, poolInfo.length-1]. However, considering the importance of validating given pools and their numerous occasions, a better alternative is to make explicit the sanity checks by introducing a new modifier. Recommendation Apply necessary sanity checks to ensure the given _pid is legitimate by adding a new modifier validatePool to functions set() , deposit() , withdraw() , emergencyWithdraw() , pendingMany() and updatePool() . modifier validatePoolByPid(uint256 _pid) { require (_pid < poolInfo.length , "Pool does not exist") ; _; }
ccswap Security Assessment MCV-06 | Take startBlock into Consideration in getMultiplier() Category Severity Location Status Logical Issue Informational MasterChefV2.sol: 159~169(MasterChef) Acknowledged Description We notice that this function does not take into account the initial block (startBlock) from which the rewards start to apply. As a result, when a normal user gives arbitrary arguments, it could return wrong reward multiplier Recommendation Apply additional sanity checks at the beginning of the getMultiplier() function so that the internal _from parameter can be adjusted to take startBlock into account. _from = _from >= startBlock ? _from : startBlock;
ccswap Security Assessment MCV-07 | Boolean Equality Category Severity Location Status Gas Optimization Informational MasterChefV2.sol: 273(MasterChef) Acknowledged Description Boolean constants can be used directly and do not need to be compared to true or false. Example: require(paused == false, "Mining has been suspended"); Recommendation Consider changing it as following example: require(!paused, "Mining has been suspended");
ccswap Security Assessment MCV-08 | Potential Front-Running For Migration Blocking Category Severity Location Status Control Flow Major MasterChefV2.sol: 147~156(MasterChef) Acknowledged Description The migrate() function in MasterChef contract has a final check after the migration, i.e., require(bal == newLpToken.balanceOf(address(this)), "migrate: bad"). However, in the mint() function of CCPair, the minted liquidity amount to be returned is only set to the old ccswap's pool token ammount when _totalSupply == 0. This means that the whole migration process assumes the migration transaction is the first to mint the new LP tokens (of this particular trading pair), otherwise it will fail. If the migration transaction is not the first to mint new LP tokens, the first transaction that successfully mints the new LP tokens will lead to _totalSupply != 0. In other words, the migration transaction will be forced to take the execution path liquidity = SafeMath.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1). This is a confirmed issue in Sushiswap, and they solved this issue by streamlining the entire deployment script. Recommendation To ensure a smooth migration process, we need to guarantee the first minting of new LP tokens is launched by the migration transaction. To achieve that, we need to prevent any unintended minting (of new LP tokens) between deploy CCFactory and configure MasterChef. A natural approach is to complete the initial three steps, deploy CCFactory, deploy migrator and configure MasterChef within the same transaction, best facilitated by a contract-coordinated deployment. Alleviation The development team confirmed that this is a known issue. To solve this issue, they will first set the migrator of factory and check there is not pairs in the factory. After that nobody can add liquidity until migrator migrate this pool, which ensures no front running.
ccswap Security Assessment TTL-01 | Proper Usage of public and external type Category Severity Location Status Coding Style Informational TeamTimeLock_flat.sol: 612, 639 Acknowledged Description “public” functions that are never called by the contract should be declared “external” . When the inputs are arrays, “external” functions are more efficient than “public” functions. Examples: Functions like : renounceOwnership() , transferOwnership() , add() , set() , setMigrator() , MasterChef.migrate() , deposit() , withdraw() , emergencyWithdraw() , setPause() , Migrator.migrate() , symbol() , decimals() , transfer() , allowance() , approve() , transferFrom() , increaseAllowance() , decreaseAllowance() , renounceOwnership() , transferOwnership() , addMinter() , delMinter() , getMinter() , getBalance() , setBeneficiary() Recommendation Consider using the “external” attribute for functions never called from the contract. Alleviation The development team considered this is a good advice, but will still follow the openzeppeling standard lib here.
ccswap Security Assessment TTL-02 | Unlocked Compiler Version Declaration Category Severity Location Status Language Specific Informational TeamTimeLock_flat.sol: 3, 221, 302, 495, 571 Acknowledged Description The compiler version utilized throughout the project uses the “^” prefix specifier, '>=0.6.0=0.6.2
ccswap Security Assessment Appendix Finding Categories Centralization / Privilege Centralization / Privilege findings refer to either feature logic or implementation of components that act against the nature of decentralization, such as explicit ownership or specialized access roles in combination with a mechanism to relocate funds. Gas Optimization Gas Optimization findings do not affect the functionality of the code but generate different, more optimal EVM opcodes resulting in a reduction on the total gas cost of a transaction. Mathematical Operations Mathematical Operation findings relate to mishandling of math formulas, such as overflows, incorrect operations etc. Logical Issue Logical Issue findings detail a fault in the logic of the linked code, such as an incorrect notion on how block.timestamp works. Control Flow Control Flow findings concern the access control imposed on functions, such as owner-only functions being invoke-able by anyone under certain circumstances. Volatile Code Volatile Code findings refer to segments of code that behave unexpectedly on certain edge cases that may result in a vulnerability. Data Flow Data Flow findings describe faults in the way data is handled at rest and in memory, such as the result of a struct assignment operation affecting an in-memory struct rather than an in-storage one. Language Specific
ccswap Security Assessment Language Specific findings are issues that would only arise within Solidity, i.e. incorrect usage of private or delete. Coding Style Coding Style findings usually do not affect the generated byte-code but rather comment on how to make the codebase more legible and, as a result, easily maintainable. Inconsistency Inconsistency findings refer to functions that should seemingly behave similarly yet contain different code, such as a constructor assignment imposing different require statements on the input variables than a setter function. Magic Numbers Magic Number findings refer to numeric literals that are expressed in the codebase in their raw format and should otherwise be specified as constant contract variables aiding in their legibility and maintainability. Compiler Error Compiler Error findings refer to an error in the structure of the code that renders it impossible to compile using the specified version of the project.
ccswap Security Assessment Disclaimer This report is subject to the terms and conditions (including without limitation, description of services, confidentiality, disclaimer and limitation of liability) set forth in the Services Agreement, or the scope of services, and terms and conditions provided to the Company in connection with the Agreement. This report provided in connection with the Services set forth in the Agreement shall be used by the Company only to the extent permitted under the terms and conditions set forth in the Agreement. This report may not be transmitted, disclosed, referred to or relied upon by any person for any purposes without CertiK’s prior written consent. This report is not, nor should be considered, an “endorsement” or “disapproval” of any particular project or team. This report is not, nor should be considered, an indication of the economics or value of any “product” or “asset” created by any team or project that contracts CertiK to perform a security assessment. This report does not provide any warranty or guarantee regarding the absolute bug-free nature of the technology analyzed, nor do they provide any indication of the technologies proprietors, business, business model or legal compliance. This report should not be used in any way to make decisions around investment or involvement with any particular project. This report in no way provides investment advice, nor should be leveraged as investment advice of any sort. This report represents an extensive assessing process intending to help our customers increase the quality of their code while reducing the high level of risk presented by cryptographic tokens and blockchain technology. Blockchain technology and cryptographic assets present a high level of ongoing risk. CertiK’s position is that each company and individual are responsible for their own due diligence and continuous security. CertiK’s goal is to help reduce the attack vectors and the high level of variance associated with utilizing new and consistently changing technologies, and in no way claims any guarantee of security or functionality of the technology we agree to analyze.
ccswap Security Assessment About Founded in 2017 by leading academics in the field of Computer Science from both Yale and Columbia University, CertiK is a leading blockchain security company that serves to verify the security and correctness of smart contracts and blockchain-based protocols. Through the utilization of our world-class technical expertise, alongside our proprietary, innovative tech, we’re able to support the success of our clients with best-in-class security, all whilst realizing our overarching vision; provable trust for all throughout all facets of blockchain.
You can also read