Introduction
CoinFabrik was asked to audit the contracts for the iCherry.Finance project. First we will provide a summary of our discoveries and then we will show the details of our findings.
Contracts
The audited contracts are:
- MemberManager.sol: Data structure for managing members, which is already deployed on the tron network at address TRCKRkrUm9L2hz8DwgWoE2hpGNrzNez1cY
- USDT.sol: The main contract is used for managing the rewards of each member.
The audited contracts and their md5sum are:
USDT.sol | f7b0f29ab655d27bec38a6c889c11f75 |
MemberManager.sol | 60ab2aeeda764e39f969f2e77ad404f8 |
Analyses
The following analyses were performed:
- Misuse of the different call methods
- Integer overflow errors
- Division by zero errors
- Outdated version of Solidity compiler
- Front running attacks
- Reentrancy attacks
- Misuse of block timestamps
- Softlock denial of service attacks
- Functions with excessive gas cost
- Missing or misused function qualifiers
- Needlessly complex code and contract interactions
- Poor or nonexistent error handling
- Failure to use a withdrawal pattern
- Insufficient validation of the input parameters
- Incorrect handling of cryptographic signatures
Detailed findings
Severity Classification
Security risks are classified as follows:
- Critical: These are issues that we manage to exploit. They compromise the system seriously. They must be fixed immediately.
- Medium: These are potentially exploitable issues. Even though we did not manage to exploit them or their impact is not clear, they might represent a security risk in the near future. We suggest fixing them as soon as possible.
- Minor: These issues represent problems that are relatively small or difficult to take advantage of but can be exploited in combination with other issues. These kinds of issues do not block deployments in production environments. They should be taken into account and be fixed when possible.
- Enhancement: These kinds of findings do not represent a security risk. They are best practices that we suggest to implement.
This classification is summarized in the following table:
SEVERITY | EXPLOITABLE | ROADBLOCK | TO BE FIXED |
Critical | Yes | Yes | Immediately |
Medium | In the near future | Yes | As soon as possible |
Minor | Unlikely | No | Eventually |
Enhancement | No | No | Eventually |
Issues Found by Severity
Critical severity
No issues of this type were found.
Medium severity
No issues of this type were found.
Minor severity
Inadequate check of return values
In the LPTokenWrapper contract, there are calls made to the USDT contract without checking the return value. According to ERC20, which TRC20 is fully compatible with, contracts must check return values for this. Using the following snippet this could be easily fixed:
require(usdt.transfer(msg.sender, amount)), "Unable to transfer");
Not implementing this could make the transfers fail silently. Anyways, the stake and withdrawal codes will be executed successfully.
Enhancements
USDT interface is not defined properly
In the USDT.sol file, the interface for the USDT contract is defined as:
interface USDT {
function totalSupply() external view returns (uint256);
function transfer(address _to, uint256 _value) external;
function transferFrom(address _from, address _to, uint256 _value) external;
function balanceOf(address who) external view returns (uint256);
function approve(address _spender, uint256 _value) external;
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
}
However, the USDT contract in the tron network has a different interface. Considering the pragma for the file (^0.5.0), it should be noted that the contracts compiled with newer solc versions will have returndatasize and returndatacopy opcodes enabled by default. For more information see here. When using TRC20, the compliant contracts respond to the following interface:
contract TRC20 {
function totalSupply() constant returns (uint theTotalSupply);
function balanceOf(address _owner) constant returns (uint balance);
function transfer(address _to, uint _value) returns (bool success);
function transferFrom(address _from, address _to, uint _value) returns (bool success);
function approve(address _spender, uint _value) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint remaining);
event Transfer(address indexed _from, address indexed _to, uint _value);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}
Also, the correct interface for the USDT contract is the following:
interface USDT {
function transfer(address _to, uint _value) returns (bool);
function transferFrom(address _from, address _to, uint _value) returns (bool);
function balanceOf(address who) constant returns (uint);
function oldBalanceOf(address who) constant returns (uint);
function approve(address _spender, uint _value) returns (bool);
function allowance(address _owner, address _spender) constant returns (uint remaining);
function totalSupply() public constant returns (uint);
//...
event DestroyedBlackFunds(address indexed _blackListedUser, uint _balance);
event Issue(uint amount);
event Redeem(uint amount);
event Deprecate(address newAddress);
}
Since the USDT token contract has a correct TRC20 interface implementation, this does not make it impossible for the contracts to interact, but this should taken into account, particularly because the contracts do not pay heed to the return values of the functions (in the LPTokenWrapper contract, for example, there are calls to usdt.transfer() and usdt.transferFrom() without checking if the transferences are successful).
Gas usage for functions
Gas usage could be optimized by declaring many of the functions in MemberManager.sol as external:
contract MemberManager is Basic {
//...
function getTotalMember() public view returns (uint256)
function infoMember(address _member) external view returns (address parent, address[] memory refs);
function addMember(address _member, address _parent) external;
function getRef(address _member, uint256 _index) external view returns (address);
function getParent(address _member) external view returns (address _parent);
function getRefLength(address _member)external view returns (uint256 _length);
function getParentTree(address _member, uint256 _deep) external view returns (address[] memory);
//...
}
Conclusion
We found the contracts to be simple and straightforward. There were a few difficulties with following the standards and a small consideration with the gas usage. To sum up, the code is well-written and no important issues were found.
Disclaimer: This audit report is not a security warranty, investment advice, or an approval of the ICherry Finance since CoinFabrik has not reviewed its platform. Moreover, it does not provide a smart contract code faultlessness guarantee.
Appendix: Automated tool analysis
Mythril
- Integer Arithmetic Bugs
These are in the context of a for that does i++ every cycle. Taking this into account, it can be seen that these do not generate issues.
- An assertion violation was triggered.
This is triggered in the case of possible access to an array that is out of bounds, because the contract can be called with any index. Since in practice the index is not greater than 3 in one case (_index) and in another the set array is not accessed in the code. Therefore these are not considered issues.
Slither
MemberManager.sol:
- Pragma version>=0.4.25<0.6.0 (MemberManager.sol#1) allows old versions
- addMod(address), removeMod(address), changeOwner(address), getTotalMember() infoMember(address), addMember(address,address), getRef(address,uint256), getParent(address), getRefLength(address), getParentTree(address,uint256), isParent(address,address) should be declared external
This optimization would lead to reduced gas usage
USDT.sol:
- CherryRewardsUSDT.notifyRewardAmount performs a multiplication on the result of a division
This is a mistake on the part of Slither, since it is taking the two branches of an if as the same block of code. In fact, in the false branch the correct order for avoiding the result flooring to 0 is being used.
- USDT has incorrect ERC20 function interface
While these contracts will be deployed on the Tron network, which has the TRC-20 standard, slither is advising the contracts to follow the ERC20 standard. This is done so any usage of return values is taken correctly. However, the contracts audited don’t use the return values for USDT functions, but it is still advised to correct these warnings.
- Reentrancy in CherryRewardsUSDT
In these cases, the calls are to known contracts, supposedly, but we could not audit them: USDT and Cherry, of which we do have the interface. Presumably these contracts are known by the team and will be set up properly when deploying the contract.
Slither also detected calls to the member manager contract such as in the stake function, but such is a known contract
- Dangerous comparisons in CherryRewardsUSDT
Slither detects that these contracts use timestamps for comparisons. However, since the durations being considered are in the order of a week there is not much of a malicious miner.
- Address.isContract(address) and Address._functionCallWithValue(address,bytes,uint256,string) use assembly
Slither detects the library Address as using assembly. While this is true, it does not necessarily mean there is a vulnerability, and further analysis lets us discard this.
- Pragma version^0.5.0 (USDT.sol#1) allows old versions
- Low level calls in Address.sendValue(address,uint256) (USDT.sol#33-45) and Address._functionCallWithValue(address,bytes,uint256,string) (USDT.sol#89-117)
Slither detects low level calls and outputs it as an interesting result. It doesn’t necessarily mean there are vulnerabilities
- Parameters _rewardDistribution (USDT.sol#380) and _user (USDT.sol#481) are not in mixedCase
Slither detects problems with style, but further analysis lets us discard this, since it is just that there is an underscore as first character.
- transferOwnership(address), getStats(address) should be declared external
Slither detects these functions could be declared external which would lead to less gas usage, as they are not used inside their own contracts. This is correct and should be considered.