CertiK reviewed Nimbus Platform (NimbusPlatform ET - NFT) on 30 Sep 2022: 22 findings, 0 critical; 18 marked fixed, 4 acknowledged.
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 | 0 | 4 | 2 |
| Medium | 0 | 0 | 3 |
| Low | 0 | 0 | 13 |
The `initialize` function can be called by the contract `owner` multiple times. This introduces a centralization risk where contract `owner` can be change important parameters of contract such as address of the staking and router contracts.
In the function `buyStakingSet()`, we call `deposit()` but do not check if the call succeeds. We should ensure the call succeeds otherwise a users balance can be updated with a positive amount without actually depositing assets.
A reentrancy attack can occur when the contract creates a function that makes an external call to another untrusted contract before resolving any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the external call resolved the effects. ### External call(s) ```solidity=191 _withdrawUserRewards(tokenId, tokenOwner, nbuReward, cakeReward); ``` * This function call executes the following external call(s). * In `StakingSet._withdrawUserRewards`, * `NbuStaking.getReward()` * In `TransferHelper.safeTransfer`, * `(success,data) = token.call(abi.encodeWithSelector(0xa9059cbb,to,value))` * In `StakingSet._withdrawUserRewards`, * `GnbuStaking.getReward()` * In `StakingSet._withdrawUserRewards`, * `TransferHelper.safeTransfer(address(nbuToken),tokenOwner,totalNbuReward)` * In `StakingSet._withdrawUserRewards`, * `CakeStaking.deposit(CAKE_PID,0)` * In `StakingSet._withdrawUserRewards`, * `TransferHelper.safeTransfer(address(cakeToken),tokenOwner,totalCakeReward)` ```solidity=195 NbuStaking.withdraw(userSupply.NbuStakingAmount); ``` ```solidity=196 GnbuStaking.withdraw(userSupply.GnbuStakeNonce); ``` ```solidity=197 CakeStaking.withdraw(CAKE_PID, userSupply.CakeBnbAmount); ``` ```solidity=199 TransferHelper.safeTransfer(address(nbuToken), tokenOwner, userSupply.NbuStakingAmount); ``` ```solidity=200 TransferHelper.safeTransfer(address(gnbuToken), tokenOwner, userSupply.GnbuStakingAmount); ``` ```solidity=201 pancakeRouter.removeLiquidityETH(address(cakeToken), userSupply.CakeBnbAmount, 0, 0, tokenOwner, block.timestamp); ``` ### State variables written after the call(s) ```solidity=203 userSupply.IsActive = false; ``` ```solidity=204 userSupply.BurnTime = block.timestamp; ``` --- ### External call(s) ```solidity=199 _withdrawUserRewards(tokenId, tokenOwner, nbuReward, cakeReward); ``` * This function call executes the following external call(s). * In `TransferHelper.safeTransfer`, * `(success,data) = token.call(abi.encodeWithSelector(0xa9059cbb,to,value))` * In `StakingSetBusd._withdrawUserRewards`, * `NbuStaking.getReward()` * In `StakingSetBusd._withdrawUserRewards`, * `GnbuStaking.getReward()` * In `StakingSetBusd._withdrawUserRewards`, * `TransferHelper.safeTransfer(address(nbuToken),tokenOwner,totalNbuReward)` * In `StakingSetBusd._withdrawUserRewards`, * `CakeStaking.deposit(CAKE_PID,0)` * In `StakingSetBusd._withdrawUserRewards`, * `TransferHelper.safeTransfer(address(cakeToken),tokenOwner,totalCakeReward)` ```solidity=203 NbuStaking.withdraw(userSupply.NbuStakingAmount); ``` ```solidity=204 GnbuStaking.withdraw(userSupply.GnbuStakeNonce); ``` ```solidity=205 CakeStaking.withdraw(CAKE_PID, userSupply.CakeBnbAmount); ``` ```solidity=207 TransferHelper.safeTransfer(address(nbuToken), tokenOwner, userSupply.NbuStakingAmount); ``` ```solidity=208 TransferHelper.safeTransfer(address(gnbuToken), tokenOwner, userSupply.GnbuStakingAmount); ``` ```solidity=209 pancakeRouter.removeLiquidityETH(address(cakeToken), userSupply.CakeBnbAmount, 0, 0, tokenOwner, block.timestamp); ``` ### State variables written after the call(s) ```solidity=211 userSupply.IsActive = false; ``` ```solidity=212 userSupply.BurnTime = block.timestamp; ```
The deadline of the transaction is set to the current `block.timestamp`. This may not leave a sufficient amount of time for the function calls `swapExactETHForTokens()` and `swapExactBNBForTokens()` to succeed.
The linked statements are external calls to independent contracts. We should ensure the calls succeed since the rewards amounts are dependent on the success of these calls.
Performing integer division before multiplication truncates the low bits, losing the precision of calculation. ```solidity=123 uint CakeEAmount = amount / 100 * 30; ``` --- ```solidity=131 uint CakeEAmount = amount / 100 * 30; ```
The functions which are never called internally within the contract should have external visibility for gas optimization. ```solidity=250 function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { ``` --- ```solidity=344 function getUserTokens(address user) public view returns (uint[] memory) { ``` --- ```solidity=352 function balanceOf(address owner) public view virtual override returns (uint256) { ``` --- ```solidity=363 function name() public view virtual override returns (string memory) { ``` --- ```solidity=367 function symbol() public view virtual override returns (string memory) { ``` --- ```solidity=372 function approve(address to, uint256 tokenId) public virtual override { ``` --- ```solidity=389 function setApprovalForAll(address operator, bool approved) public virtual override { ``` --- ```solidity=397 function transferFrom(address from, address to, uint256 tokenId) public virtual override { ``` --- ```solidity=403 function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { ```
The **require** can be used to check for conditions and throw an exception if the condition is not met. It is better to provide a string message containing details about the error that will be passed back to the caller.
Addresses should be checked before assignment or external call to make sure they are not zero addresses. File: HubRouting.sol (Line 25, Function `transferOwnership`) ```sol newOwner = transferOwner; ``` - `transferOwner` is not zero-checked before being used. --- File: HubRouting.sol (Line 79) ```sol constructor(address _StakingMain) { StakingMain = _StakingMain; } ``` - `_StakingMain` is not zero-checked before being used. --- File: StakingMain.sol (Line 148, Function `transferOwnership`) ```sol newOwner = transferOwner; ``` - `transferOwner` is not zero-checked before being used. --- File: StakingSetStorage.sol (Line 104, Function `transferOwnership`) ```sol newOwner = transferOwner; ``` - `transferOwner` is not zero-checked before being used.
*This issue is considered benign because the reentrancy only acts as a double call.* A reentrancy attack can occur when the contract creates a function that makes an external call to another untrusted contract before resolving any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the external call resolved the effects. ### External call(s) ```solidity=252 NbuStaking.getReward(); ``` * This call sends Ether. ```solidity=253 GnbuStaking.getReward(); ``` * This call sends Ether. ```solidity=258 TransferHelper.safeTransfer(address(nbuToken), tokenOwner, totalNbuReward); ``` * This call sends Ether. ### State variables written after the call(s) ```solidity=259 weightedStakeDate[tokenId] = block.timestamp; ``` --- ### External call(s) ```solidity=252 NbuStaking.getReward(); ``` * This call sends Ether. ```solidity=253 GnbuStaking.getReward(); ``` * This call sends Ether. ```solidity=258 TransferHelper.safeTransfer(address(nbuToken), tokenOwner, totalNbuReward); ``` * This call sends Ether. ```solidity=261 CakeStaking.deposit(CAKE_PID, 0); ``` * This call sends Ether. ### State variables written after the call(s) ```solidity=263 tikSupplies[tokenId].CurrentRewardDebt = user.rewardDebt; ``` ```solidity=264 tikSupplies[tokenId].CurrentCakeShares = user.amount; ``` --- ### External call(s) ```solidity=264 NbuStaking.getReward(); ``` * This call sends Ether. ```solidity=265 GnbuStaking.getReward(); ``` * This call sends Ether. ```solidity=270 TransferHelper.safeTransfer(address(nbuToken), tokenOwner, totalNbuReward); ``` * This call sends Ether. ### State variables written after the call(s) ```solidity=271 weightedStakeDate[tokenId] = block.timestamp; ``` --- ### External call(s) ```solidity=264 NbuStaking.getReward(); ``` * This call sends Ether. ```solidity=265 GnbuStaking.getReward(); ``` * This call sends Ether. ```solidity=270 TransferHelper.safeTransfer(address(nbuToken), tokenOwner, totalNbuReward); ``` * This call sends Ether. ```solidity=273 CakeStaking.deposit(CAKE_PID, 0); ``` * This call sends Ether. ### State variables written after the call(s) ```solidity=275 tikSupplies[tokenId].CurrentRewardDebt = user.rewardDebt; ``` ```solidity=276 tikSupplies[tokenId].CurrentCakeShares = user.amount; ``` --- ### External call(s) ```solidity=286 TransferHelper.safeTransferFrom(purchaseToken, msg.sender, stakingSet, _amount); ``` * This call sends Ether. ### State variables written after the call(s) ```solidity=289 _userTokens[msg.sender].push(tokenCount); ``` ```solidity=288 tokenCount++; ``` ```solidity=290 _mint(msg.sender, tokenCount); ``` * This function call executes the following assignment(s). * In `StakingMain._mint`, * `_balances[to] += 1` *Note: Only a sample of 3 assignments (out of 4) are shown above.* --- ### External call(s) ```solidity=85 (uint256 nbuAmount,uint256 gnbuAmount,uint256 cakeLPamount) = makeSwaps(amountBNB); ``` * This function call executes the following external call(s). * In `StakingSet.makeSwaps`, * `amountsBnbCakeSwap = pancakeRouter.swapExactETHForTokens{value: (CakeEAmount / 2) / MULTIPLIER}(0,path,address(this),block.timestamp)` * In `StakingSet.makeSwaps`, * `(amountBnbCake,liquidityBnbCake) = pancakeRouter.addLiquidityETH{value: (amount - CakeEAmount / 2) / MULTIPLIER}(address(cakeToken),amountsBnbCakeSwap[1],0,0,address(this),block.timestamp)` * In `StakingSet.makeSwaps`, * `amountsBnbNbuStaking = nimbusRouter.swapExactBNBForTokens{value: NbuAmount}(0,path,address(this),block.timestamp)` * In `StakingSet.makeSwaps`, * `amountsBnbGnbuStaking = nimbusRouter.swapExactBNBForTokens{value: NbuAmount}(0,path,address(this),block.timestamp)` * This call sends Ether. ```solidity=87 NbuStaking.stake(nbuAmount); ``` * This call sends Ether. ### State variables written after the call(s) ```solidity=88 _balancesRewardEquivalentNbu[tokenId] += nbuAmount; ``` --- ### External call(s) ```solidity=85 (uint256 nbuAmount,uint256 gnbuAmount,uint256 cakeLPamount) = makeSwaps(amountBNB); ``` * This function call executes the following external call(s). * In `StakingSet.makeSwaps`, * `amountsBnbCakeSwap = pancakeRouter.swapExactETHForTokens{value: (CakeEAmount / 2) / MULTIPLIER}(0,path,address(this),block.timestamp)` * In `StakingSet.makeSwaps`, * `(amountBnbCake,liquidityBnbCake) = pancakeRouter.addLiquidityETH{value: (amount - CakeEAmount / 2) / MULTIPLIER}(address(cakeToken),amountsBnbCakeSwap[1],0,0,address(this),block.timestamp)` * In `StakingSet.makeSwaps`, * `amountsBnbNbuStaking = nimbusRouter.swapExactBNBForTokens{value: NbuAmount}(0,path,addres
*This finding has a minor impact because the reentrancy only causes out-of-order events.* A reentrancy attack can occur when the contract creates a function that makes an external call to another untrusted contract before resolving any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the external call resolved the effects. ### External call(s) ```solidity=191 _withdrawUserRewards(tokenId, tokenOwner, nbuReward, cakeReward); ``` * This function call executes the following external call(s). * In `StakingSet._withdrawUserRewards`, * `NbuStaking.getReward()` * In `TransferHelper.safeTransfer`, * `(success,data) = token.call(abi.encodeWithSelector(0xa9059cbb,to,value))` * In `StakingSet._withdrawUserRewards`, * `GnbuStaking.getReward()` * In `StakingSet._withdrawUserRewards`, * `TransferHelper.safeTransfer(address(nbuToken),tokenOwner,totalNbuReward)` * In `StakingSet._withdrawUserRewards`, * `CakeStaking.deposit(CAKE_PID,0)` * In `StakingSet._withdrawUserRewards`, * `TransferHelper.safeTransfer(address(cakeToken),tokenOwner,totalCakeReward)` ```solidity=195 NbuStaking.withdraw(userSupply.NbuStakingAmount); ``` ```solidity=196 GnbuStaking.withdraw(userSupply.GnbuStakeNonce); ``` ```solidity=197 CakeStaking.withdraw(CAKE_PID, userSupply.CakeBnbAmount); ``` ```solidity=199 TransferHelper.safeTransfer(address(nbuToken), tokenOwner, userSupply.NbuStakingAmount); ``` ```solidity=200 TransferHelper.safeTransfer(address(gnbuToken), tokenOwner, userSupply.GnbuStakingAmount); ``` ```solidity=201 pancakeRouter.removeLiquidityETH(address(cakeToken), userSupply.CakeBnbAmount, 0, 0, tokenOwner, block.timestamp); ``` ### Events emitted after the call(s) ```solidity=206 emit BurnStakingSet(tokenId, userSupply.NbuStakingAmount, userSupply.GnbuStakingAmount, userSupply.CakeBnbAmount); ``` --- ### External call(s) ```solidity=199 _withdrawUserRewards(tokenId, tokenOwner, nbuReward, cakeReward); ``` * This function call executes the following external call(s). * In `TransferHelper.safeTransfer`, * `(success,data) = token.call(abi.encodeWithSelector(0xa9059cbb,to,value))` * In `StakingSetBusd._withdrawUserRewards`, * `NbuStaking.getReward()` * In `StakingSetBusd._withdrawUserRewards`, * `GnbuStaking.getReward()` * In `StakingSetBusd._withdrawUserRewards`, * `TransferHelper.safeTransfer(address(nbuToken),tokenOwner,totalNbuReward)` * In `StakingSetBusd._withdrawUserRewards`, * `CakeStaking.deposit(CAKE_PID,0)` * In `StakingSetBusd._withdrawUserRewards`, * `TransferHelper.safeTransfer(address(cakeToken),tokenOwner,totalCakeReward)` ```solidity=203 NbuStaking.withdraw(userSupply.NbuStakingAmount); ``` ```solidity=204 GnbuStaking.withdraw(userSupply.GnbuStakeNonce); ``` ```solidity=205 CakeStaking.withdraw(CAKE_PID, userSupply.CakeBnbAmount); ``` ```solidity=207 TransferHelper.safeTransfer(address(nbuToken), tokenOwner, userSupply.NbuStakingAmount); ``` ```solidity=208 TransferHelper.safeTransfer(address(gnbuToken), tokenOwner, userSupply.GnbuStakingAmount); ``` ```solidity=209 pancakeRouter.removeLiquidityETH(address(cakeToken), userSupply.CakeBnbAmount, 0, 0, tokenOwner, block.timestamp); ``` ### Events emitted after the call(s) ```solidity=214 emit BurnStakingSet(tokenId, userSupply.NbuStakingAmount, userSupply.GnbuStakingAmount, userSupply.CakeBnbAmount); ``` --- ### External call(s) ```solidity=252 NbuStaking.getReward(); ``` ```solidity=253 GnbuStaking.getReward(); ``` ### Events emitted after the call(s) ```solidity=255 emit BalanceNBURewardsNotEnough(tokenOwner, tokenId, totalNbuReward); ``` --- ### External call(s) ```solidity=252 NbuStaking.getReward(); ``` ```solidity=253 GnbuStaking.getReward(); ``` ```solidity=258 TransferHelper.safeTransfer(address(nbuToken), tokenOwner, totalNbuReward); ``` ```solidity=261 CakeStaking.deposit(CAKE_PID, 0); ``` ```solidity=266 TransferHelper.safeTransfer(address(cakeToken), tokenOwner, totalCakeReward); ``` ### Events emitted after the call(s) ```solidity=268 emit WithdrawRewards(tokenOwner, tokenId, totalNbuReward, totalCakeReward); ``` --- ### External call(s) ```solidity=264 NbuStaking.getReward(); ``` ```solidity=265 GnbuStaking.getReward(); ``` ### Events emitted after the call(s) ```solidity=267 emit BalanceNBURewardsNotEnough(tokenOwner, tokenId, totalNbuReward); ``` --- ### External call(s) ```solidity=264 NbuStaking.getReward(); ``` ```solidity=265 GnbuStaking.getReward(); ``` ```solidity=270 TransferHelper.safeTransfer(address(nbuToken), tokenOwner, totalNbuReward); ```
The following functions should only be called by the `hubRouting` contract : - ``buyStakingSet()`` - `withdrawUserRewards()` - `burnStakingSet()` The functions do include a check; however, we should use the modifier `onlyHub`.
The source file does not specify SPDX license identifier in the attached Solidity files.
The return value of the transfer()/transferFrom() call is not checked. ```solidity=289 IBEP20(tokenAddress).transfer(to, amount); ``` --- ```solidity=301 IBEP20(tokenAddress).transfer(to, amount); ``` --- ```solidity=536 IBEP20(tokenAddress).transfer(to, amount); ```
The return value of an external call is not stored in a local or state variable. ```solidity=201 pancakeRouter.removeLiquidityETH(address(cakeToken), userSupply.CakeBnbAmount, 0, 0, tokenOwner, block.timestamp); ``` --- ```solidity=209 pancakeRouter.removeLiquidityETH(address(cakeToken), userSupply.CakeBnbAmount, 0, 0, tokenOwner, block.timestamp); ``` --- ```solidity=334 IBEP20(token).approve(spender, allowance); ``` --- ```solidity=346 IBEP20(token).approve(spender, allowance); ``` --- ```solidity=53 IBEP20(_nbuToken).approve(_nimbusRouter, type(uint256).max); ``` --- ```solidity=54 IBEP20(_gnbuToken).approve(_nimbusRouter, type(uint256).max); ``` --- ```solidity=55 IBEP20(_nbuToken).approve(_NbuStaking, type(uint256).max); ``` --- ```solidity=56 IBEP20(_gnbuToken).approve(_GnbuStaking, type(uint256).max); ``` --- ```solidity=57 IBEP20(_nbuToken).approve(_nimbusRouter, type(uint256).max); ``` --- ```solidity=58 IBEP20(_gnbuToken).approve(_nimbusRouter, type(uint256).max); ``` --- ```solidity=58 IBEP20(_lpBnbCake).approve(_CakeStaking, type(uint256).max); ``` --- ```solidity=59 IBEP20(_lpBnbCake).approve(_pancakeRouter, type(uint256).max); ``` --- ```solidity=59 IBEP20(_nbuToken).approve(_NbuStaking, type(uint256).max); ``` --- ```solidity=60 IBEP20(CakeStaking.CAKE()).approve(_pancakeRouter, type(uint256).max); ``` --- ```solidity=60 IBEP20(_gnbuToken).approve(_GnbuStaking, type(uint256).max); ``` --- ```solidity=61 IBEP20(_busdToken).approve(_nimbusRouter, type(uint256).max); ``` --- ```solidity=63 IBEP20(_lpBnbCake).approve(_CakeStaking, type(uint256).max); ``` --- ```solidity=64 IBEP20(_lpBnbCake).approve(_pancakeRouter, type(uint256).max); ``` --- ```solidity=65 IBEP20(CakeStaking.CAKE()).approve(_pancakeRouter, type(uint256).max); ```
The state variable `target` is never assigned an address.
The linked functions are equivalent to the compiler-generated getter functions for the respective variables.
The linked variables assigned in the constructor can be declared as `immutable`. Immutable state variables can be assigned during contract creation but will remain constant throughout the lifetime of a deployed contract. A big advantage of immutable variables is that reading them is significantly cheaper than reading from regular state variables since they will not be stored in storage.
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