CertiK reviewed FlokiFi (Flokifi Staking) on 6 Nov 2023: 12 findings, 0 critical; 10 marked fixed, 2 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 | 0 | 0 | 2 |
| Low | 0 | 0 | 7 |
In the contract `FlokiRewardToken` the role `DEFAULT_ADMIN_ROLE` has authority over the functions shown in the diagram below. Any compromise to the `DEFAULT_ADMIN_ROLE` account may allow the hacker to take advantage of this authority and mint arbitrary amounts of tokens. 
The function `stake()` does not check the `_minimumStakeTimestamp` not greater than the `endsAt`. Then there will be a period after `endsAt` with no rewards. However, when the user unstakes before `_minimumStakeTimestamp` the user will pay a penalty fee.
When the user calls the extendStake function, the duration will be changed, which means the duration multiplier should be changed as well. However, there is no update of unstakeTimestamp in this function.
The function pauseStaking incorrectly change the state of `isPaused`.
In the constructor of the PenaltyFee contract, the `penaltyBasis` will be set by the parameter and it can be close to the MULTIPLIER_BASIS. Hence the `penaltyFee` can be bigger than the stake amount which is unreasonable.
In the function `extendRewardsDuration()`, the extended duration will be added to the old duration, the endsAt will be delayed for the extended duration as well. So this function needs to be called immediately after the old duration is over.
In the functions `getUserRewards()` and `rewardPerToken()`, the calculation uses the decimal to avoid the divide truncation issue. However, the base digit of the multiplication is missing in the calculation.
When the function `setStakingDuration` is called, no previous reward tokens are left since the previous duration is over. However, there is no new reward tokens transferred in the contract. The similar issue is existing in the function `extendRewardsDuration`.
In the function `lastTimeRewardApplicableForUser`, the code logic will check whether the `block.timestamp` is greater than the unstakeTimestamp[_user]. If yes, it will directly return the unstakeTimestamp[_user]. If not, it means `block.timestamp` is not greater than the unstakeTimestamp[_user]. Since the _unstakeTimestamp must be not greater than endsAt which is restricted when staking, so the `block.timestamp` must be smaller than endsAt. ```solidity function lastTimeRewardApplicableForUser(address _user) public view returns (uint256) { if (block.timestamp > unstakeTimestamp[_user]) { return unstakeTimestamp[_user]; } return block.timestamp < endsAt ? block.timestamp : endsAt; } ```
The contract is serving as the underlying entity to interact with third-party `stakingToken`. The scope of the audit treats third-party entities as black boxes and assumes their functional correctness. However, in the real world, third parties can be compromised and this may lead to lost or stolen assets. ```solidity=127 stakingToken.safeTransferFrom(msg.sender, address(this), _amount); ```
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (7)
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