CertiK reviewed Helper Coin (Helper Coin - Audit) on 12 Jan 2023: 16 findings, 0 critical; 12 marked fixed, 4 open.
Report hosted on skynet.certik.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 2 | 0 | 1 |
| Medium | 1 | 0 | 4 |
| Low | 1 | 0 | 7 |
Function `rewardPrimeHolders()` is used to distribute `prime_holders_balance` amount of tokens to token holders based on their holding time. Since the distribution does nothing with the holders' balances, the more accounts a user controls, the larger part of the total rewards he can get. This distribution mechanism would introduce unfair competition, and too many accounts would make the function costly.
The function `rewardUsersDaily()` mints tokens as rewards for token holders. However, it emits incorrect events indicating that the tokens are transferred from `prime_holders_address` to holders. ```solidity=774 emit Transfer(prime_holders_address, _holdersArray[i], _daily_reward_amount); ```
```solidity=270 if ( holdersMap[to].start_hold_timestamp == 0 && holdersMap[to].last_daily_reward_timestamp == 0 ) { holdersArray.push(to); holdersMap[to].start_hold_timestamp = block_timestamp; holdersMap[to].last_daily_reward_timestamp = block_timestamp; ``` The token recipient may be added to `holdersArray` and `holdersMap` even if the transfer amount is zero.
```solidity=252 modifier onlyWithdrawer( WithdrawInfo storage _withdrawInfo, uint256 _withdrawAmount, address _fundAddress ) { uint256 curFundBalance = _balances[_fundAddress]; if (msg.sender != _withdrawInfo.WithdrawerAddress) { revert NotAuthorised(); } if (_withdrawInfo.lastWithdrawTime + 30 days > block.timestamp) { revert RequiredTimeNotElapsed(); } if (_withdrawAmount > (curFundBalance * _withdrawInfo.allowedPercentage) / denominator) { revert AmountNotAuthorised(); } _withdrawInfo.lastWithdrawTime = block.timestamp; _; } ``` ```solidity=955 function withdrawDevelopmentBalance(address _to, uint256 _amount) external onlyWithdrawer(developmentWithdrawInfo, _amount, development_address) { _withdrawBalance(development_address, _to, _amount); } /** * @dev withdraw Marketing balance with amount equal to _amount, recipient is the _to address. */ function withdrawMarketingBalance(address _to, uint256 _amount) external onlyWithdrawer(marketingWithdrawInfo, _amount, marketing_address) { _withdrawBalance(marketing_address, _to, _amount); } /** * @dev withdraw LegalAndBackup balance with amount equal to _amount, recipient is the _to address. */ function withdrawLegalAndBackupBalance(address _to, uint256 _amount) external onlyWithdrawer(legalAndBackupWithdrawInfo, _amount, legal_and_backup_address) { _withdrawBalance(legal_and_backup_address, _to, _amount); } ``` The modifier `onlyWithdrawer` is used to restrict three privileged addresses `development_address`, `legal_and_backup_address`, and `marketing_address` from arbitrarily transferring tokens. The intended design is that only the corresponding withdrawers can withdraw their tokens. However, The three accounts can directly call transfer functions like `transfer()` in the parent contract `ERC20` to transfer tokens.
There are no upper limits restricting parameters of `setBurnFee()` and `setTransferFee()`, potentially enabling up to 100% of fees on transactions.
```solidity=1014 /** * @dev helper withdraw function for the above four functions. */ function _withdrawBalance( address _from, address _to, uint256 _amount ) private { _transfer(_from, _to, _amount); _isExemptFromFee[_to] = true; _isExemptFromReward[_to] = true; } ``` The comment says "helper withdraw function for the above four functions", but only three functions call the private function.
There should always be events emitted in the sensitive functions that are controlled by centralization roles.
```solidity=590 reward_array_info = RewardArrayInfo( _arrayData[0], _arrayData[1], _arrayData[2], _arrayData[3], _arrayData[4], _arrayData[5] ); length_info = LengthInfo( _arrayLengthData[0], _arrayLengthData[1], _arrayLengthData[2], _arrayLengthData[3], _arrayLengthData[4], _arrayLengthData[5] ); ``` Function `rewardPrimeHoldersWithInput()` does not check that the length of each array in `reward_array_info` is equal to the corresponding element in `length_info`, which may lead to incorrect reward distribution.
```solidity=303 holdTime >= _slotDaysInfo.slot2Days * 1 ``` The statement misses the multiplier `days` and should be `holdTime >= _slotDaysInfo.slot2Days * 1 days`.
Addresses should be checked before assignment or external call to make sure they are not zero addresses. ```solidity=155 development_address = _development_address; ``` - `_development_address` is not zero-checked before being used. --- ```solidity=161 management_address = _management_address; ``` - `_management_address` is not zero-checked before being used. --- ```solidity=167 marketing_address = _marketing_address; ``` - `_marketing_address` is not zero-checked before being used. --- ```solidity=173 prime_holders_address = _prime_holders_address; ``` - `_prime_holders_address` is not zero-checked before being used. --- ```solidity=179 core_holders_address = _core_holders_address; ``` - `_core_holders_address` is not zero-checked before being used.
```solidity=295 if (holdTime < _slotDaysInfo.slot1Days * 1 days) { sell_fee = (amount * _slotDaysFeeInfo.feeBeforeDaysSlot1) / denominator; } else if ( holdTime >= _slotDaysInfo.slot1Days * 1 days && holdTime < _slotDaysInfo.slot2Days * 1 days ) ``` 1. In the function `_transfer()`, the operand `holdTime >= _slotDaysInfo.slot1Days * 1 days` is always true because it is under the condition `else if`. The code at lines 302 - 320, 422 - 462, and 1021 - 1061 share the same issue. 2. In the internal functions `_distribute()` and `_withdrawBalance()`, the modifier `onlyCaller` is redundant because their callers already have the same modifier. 3. In the function `filterUsers()`, the variable `reward_info` is not used.
```solidity=1140 function _withdrawBalance( address _from, address _to, uint256 _amount ) private onlyCaller { _transfer(_from, _to, _amount); _isExemptFromFee[_to] = true; _isExemptFromFee[_to] = true; } ``` The statements `_isExemptFromFee[_to] = true;` at lines 1146 and 1147 are repeated.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (6)
1,044 projects reviewed · 1,375 reports · latest May 2025
See CertiK’s auditsSomething wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us