CertiK reviewed BEBE on 20 Aug 2024: 29 findings, 1 critical; 22 marked fixed, 7 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 | 1 |
| High | 0 | 0 | 2 |
| Medium | 0 | 0 | 5 |
| Low | 7 | 0 | 14 |
The function `claimIncome()` allows users to claim their percentage of mined tokens the day after they are mined. However, the timestamp at which a user is able to be claimed will differ, and those who stake in a block whose `block.timestamp` is a multiple of `SECONDS_PER_DAY` will be able to claim before any other users. In general, the lower the value of the timestamp of the block when staking modulo `SECONDS_PER_DAY`, the sooner they will be able to claim tokens. This creates an unfair advantage as users who stake at a more advantageous `block.timestamp` will be able to sell tokens before other users.
The lines cited above set variables to their default value. The keyword `delete` can be used in their place as it more closely matches the semantics of the logic.
The function `importStake()` can only be called by the owner and is designed to be able to add users stakes without the need for the user to transfer tokens or call `stake()`. However, this function will update the stored `totalPower` for the input `stakeTime` date, which in some cases can cause rewards to be misallocated. If the `stakeTime` is for a date that has not yet been mined and no stakes are made for that date when it is reached, then it will cause `_incomePools[incomeDay].totalPower` to be nonzero, so that the function `confirmAddIncomePool()` will not compute the total power for that income day and thus allocate funds based only on the stakes added via `importStake()`, when it should also include the previous stakes in the calculation. If the `stakeTime` is for a date where some users have already claimed their income, then it will add to the `totalPower`, however, as some amount was already claimed this will cause more than the `poolAmount` to be allocated so that the last users will be unable to claim all tokens they are entitled to. In addition, there are other cases where inputs may provide incorrect rewards.
### BEBEStakeManager The function `importStake()` does not perform the following checks: - It does not check `_stakeEnabled` is true. This may be by design as it is a privileged function, however, we would like to ensure it aligns with the design intent. - It does not check if `_userActiveStake[staker[i]]` is already staked. This can override a users stake unexpectedly. - It does not check that the input `staker[i]` is not `address(0)`. If it is set to the zero address accidentally it will cause income to be unable to be claimed. - It does not check that the `amount[i]` are greater than or equal to the `MINIMUM_STAKE` and less than or equal to the `MAXIMUM_STAKE`. This can allow any amount to be staked. - It does not check the length of the `staker`, `stakeTime`, and `amount` arrays are the same. If the lengths differ, then the inputs are likely incorrect and may cause incorrect values to be set.
Functions that update state variables should emit relevant events as notifications.
When calculating the percentage of the `poolAmount` that a users should be given in a day it rounds down. As such, after all users claim for that day, the entire `poolAmount` will not be claimed, so that the remaining amount will be locked in the contract.
The `MINIMUM_STAKE` and `MAXIMUM_STAKE` parameters were removed so that users can now stake any amount of tokens. This can lead to greater rounding errors for users as there can be a larger difference between users stakes. However, there are 15 decimals of precision used so we give this a *minor* severity. For example if a user staked only 10 tokens and another user staked 1e16 tokens so that the total power is 1e16+10. Then the calculated income for the user who staked 10 tokens (so that instance.amount=10): ```solidity income = (((instance.amount * DECIMAL_SCALER) / _incomePools[day].totalPower) * _incomePools[day].poolAmount) / DECIMAL_SCALER; ``` Will be (((10 * 1e15) / (1e16+10)) * _incomePools[day].poolAmount) / 1e15 However (10 * 1e15) / (1e16+10) = 1e16/(1e16+10) will round down to zero so that this amount will be equal to 0. In addition, this allows a user to accidentally stake a small amount, in which case they would need to stake with a different address if they wish to correct their mistake. Note that at the end stages of mining, there will a small amount of tokens mined, that will not be able to be distributed as income. In the extreme case when `mine()` is called it will mine only 1 token. Unless there is a single user, then this will not be able to be claimed by any user.
The report names a repository or files. Open the report to see its scope.
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