CertiK reviewed nsurance (nfa - staking/buyback/burn) on 27 Feb 2024: 16 findings, 1 critical; 7 marked fixed, 9 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 | 2 | 0 | 1 |
| Medium | 0 | 0 | 0 |
| Low | 7 | 0 | 5 |
In the contract `AssetRewardContract` the role `ADMIN_ROLE` has authority over the functions shown in the diagram below.  - `depositFutureRewards(uint256 amount, uint256 availableFromTime)`: Allows depositing of future rewards to be distributed at a later time, accessible by accounts with the `ADMIN_ROLE`. - `setUserStakesContract(address newContract)`: Sets the address of the user stakes contract, which can only be called by accounts with the `ADMIN_ROLE`. - `directDepositRewards(uint256 depositAmount)`: Directly deposits rewards for distribution, requiring the caller to have the `ADMIN_ROLE`. - `updateProportions(uint256[12] calldata newProportions)`: Updates the proportions of rewards for different assets, accessible by accounts with the `ADMIN_ROLE`. - `emergencyWithdrawRewards()`: Allows the withdrawal of rewards from the contract as an emergency measure, restricted to accounts with the `ADMIN_ROLE`. - `distributeReward(address staker, uint256 expectedBucketId)`: Distributes the calculated reward to a staker, which can only be called by the `REWARD_MANAGER` role. Any compromise to the `ADMIN_ROLE` account may allow the hacker to take advantage of this authority and update the sensitive settings and execute sensitive functionalities of the protocol. In the contract `AssetRewardContract` the role `REWARD_MANAGER` has authority over the functions shown in the diagram below.  - `distributeReward(address staker, uint256 expectedBucketId)`: Distributes the calculated reward to a staker, which can only be called by the `REWARD_MANAGER` role. Any compromise to the `REWARD_MANAGER` account may allow the hacker to take advantage of this authority and update the sensitive settings and execute sensitive functionalities of the protocol. ----- In the contract `NFAiRewardManager` the role `REWARD_MANAGER` has authority over the functions shown in the diagram below.  - `addRewardContractToBucket(uint256 bucketId, address rewardContractAddress)`: Adds a reward contract to a specific bucket, accessible by accounts with the `REWARD_MANAGER` role. - `removeRewardContractFromBucket(uint256 bucketId, address rewardContractAddress)`: Removes a reward contract from a specific bucket, requiring the caller to have the `REWARD_MANAGER` role. - `createRewardContractForBucket(uint256 bucketId, uint256 epochDuration, uint256 _epochStartTime, address rewardsToken, uint256[12] calldata proportions)`: Creates a new reward contract for a specific bucket, can be called by an account with the `REWARD_MANAGER` role. - `setReceiptTokenForBucket(uint256 bucketId, bool enableReceiptToken)`: Sets whether a receipt token is used for a specific bucket, accessible by accounts with the `REWARD_MANAGER` role. - `depositRewardToAssetManager(uint256 bucketId, uint256 amount, uint256 rewardContractIndex)`: Deposits rewards to the asset manager for a specific bucket, can only be performed by an account with the `REWARD_MANAGER` role. - `claimRewards(address user, uint256 bucketId, uint256 stakeIndex)`: Allows a user to claim rewards for a specific bucket and stake, accessible by accounts with the `REWARD_MANAGER` role. - `depositFutureRewardsToAssetManager(uint256 bucketId, uint256 amount, uint256 timeOfRelease, uint256 rewardContractIndex)`: Deposits future rewards to the asset manager, requiring the `REWARD_MANAGER` role to execute. Any compromise to the `REWARD_MANAGER` account may allow the hacker to take advantage of this authority and update the sensitive settings and execute sensitive functionalities of the protocol. ----- In the contract `NFAiUserStakes` the role `ADMIN_ROLE` has authority over the functions shown in the diagram below.  - `changeEligibleToken(uint256 bucketId, address newEligibleToken)`: Changes the eligible token for staking in a specific bucket, accessible by accounts with the `ADMIN_ROLE`. - `createBucket(uint256 bucketId, address[] calldata rewardContractsAddresses, address eligibleTokenAddress, uint256 withdrawalFee, bool _usesReceiptToken)`: Creates a new staking bucket with associated reward contracts, token eligibility, withdrawal fee, and receipt token usage settings, can only be called by an account with the `ADMIN_ROLE`. - `removeRewardContractFromBucket(uint256 bucketId, address rewardContractsAddress)`: Removes an associated r
In the contract `wNuni`, the role `MINTER_BURNER_ROLE has the authority to update the token balance of an arbitrary account without sanity restriction. Any compromise to the `MINTER_BURNER_ROLE account may allow a hacker to take advantage of this authority and manipulate users' balances.
In contract ``, the address `feeMaster` is set to `0x000008159ce9adE13270F4A134Bd34bF54349f01`, which is a hardcoded address. Without plaform information and context, the security of this address is unknown.
Sometimes, when approving a non-zero amount directly to an address, it's necessary to check the current allowance beforehand to determine if it needs to be cleared. Otherwise, 'safeApprove' will revert. This vulnerability will impact consequent listings and cause them to fatally fail for a token that has been used in the past and is part of the non-compliant EIP-20 tokens. ```solidity=186 IERC20(rewardContract.REWARDS_TOKEN()).safeApprove( address(rewardContract), amount ); ``` ```solidity=250 IERC20(rewardContract.REWARDS_TOKEN()).safeApprove( address(rewardContract), amount ); ```
In `unstakeAndClaimRewards()`, `totalDistributedReward` will be reset to 0 if `buckets[bucketId].usesReceiptToken` returns false: ```solidity=355 } else { totalDistributedReward = 0; } ``` If the reset is done, then the actual amount of the distributed reward will be reset to 0 in the emitted message in the below event: ```solidity=519 emit UnstakeAndClaim(msg.sender, totalRewardAmount, totalUnstakedAmount, bucketId); ```
The logic within `distributeReward` improperly adjusts `stakeEpoch` to `MAX_EPOCHS` if it is less than `MAX_EPOCHS`. This adjustment could lead to inaccuracies in calculating the distributed rewards, potentially affecting the fairness of reward distribution among stakeholders. When distributing rewards, if a stake's current epoch is less than the maximum defined epochs (`MAX_EPOCHS`), the function erroneously adjusts the epoch to `MAX_EPOCHS`. This could skew the reward distribution, particularly affecting stakes that are not supposed to receive rewards in the max epoch yet. ```solidity=252 if(stakeEpoch < MAX_EPOCHS) { stakeEpoch = MAX_EPOCHS; } ```
In the `distributeReward()` function, an issue arises when a malicious user inputs a `staker` that results in `USER_STAKES_CONTRACT.getUserStakeCount(staker)` returning 0. This scenario leads to `totalRewardAmount` being set to 0, and subsequently, the `_transferReward(staker, expectedBucketId, totalRewardAmount)` function is called with a reward amount of 0. This operation unnecessarily consumes gas without transferring any rewards, indicating a need for optimization to prevent wasteful transactions. ```solidity // Original code snippet function distributeReward( address staker, uint256 expectedBucketId ) external nonReentrant onlyRole(REWARD_MANAGER) returns (uint256) { uint256 totalRewardAmount = 0; uint256 userStakeCount = USER_STAKES_CONTRACT.getUserStakeCount(staker); for (uint256 i; i < userStakeCount; ++i) { ... } _transferReward(staker, expectedBucketId, totalRewardAmount); return totalRewardAmount; } ``` In the above snippet, if `userStakeCount` is 0, `totalRewardAmount` remains 0, leading to a gas-consuming but reward-less call to `_transferReward()`.
The `NFAiRewardManager` smart contract contains multiple functions crucial for the management of reward contracts within a system. These functions lack proper input validation, which may allow malicious actors to exploit these functions with invalid or harmful inputs. Lack of input validation allows passing of zero addresses or invalid parameters to functions, leading to potential vulnerabilities.
The cited input `bucketId` value is missing a check that it is not `address(0)`.
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