As of 3 Nov 2026, nsurance has 4 audit reports by 2 security firms on Trustblock; the latest is by CertiK, dated 27 Feb 2024. Those reports list no open critical findings. It also holds 1 deployed contract on 1 chain.
Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| nfa - staking/buyback/burn | Security firm: CertiK | Date: | Scope: Files named in the report | 1 critical finding, fixed | Open · skynet.certik.com |
| NFAI Buyback | Security firm: SolidProof | Date: | Scope: Not recorded (see the report) | No open critical findings | Open · IPFS |
| NFAIV2 | Security firm: CertiK | Date: | Scope: Files named in the report | No open critical findings | Open · skynet.certik.com |
| NFAi | Security firm: CertiK | Date: | Scope: 1 address on Ethereum | No open critical findings | Open · skynet.certik.com |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 1 |
| High | 6 | 0 | 1 |
| Medium | 2 | 0 | 1 |
| Low | 23 | 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
Open the reportIn 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.
Open the reportIn the contract `NFAIV2` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority and perform, including but not limited to, the following operations: - Include/Exclude any address from trading when trading is not active. - Enable trading if it was not yet enabled. - Transfer all ETH held by the contract to any receiver. - Transfer all tokens held by the contract to any receiver.  In the contract `Ownable` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority and transfer or renounce the ownership of the contract. 
Open the reportAll of the `NFAi` tokens are sent to the contract deployer or a wallet indicated by the contract deployer when deploying the contract. This could be a centralization risk as the anonymous deployer or the wallet indicated by it can distribute tokens without obtaining the consensus of the community. Any compromise to the deployer account or the wallet indicated by it that holds undistributed tokens may allow the attacker to steal and sell tokens on the market, resulting in severe damage to the project.
Open the reportIn the contract `NFAi` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority.  In the contract `Ownable` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority. 
Open the reportAll **NFAi** tokens are sent to the contract address and the two EOA addresses when deploying the contract. This is a potential centralization risk as these privileged addresses can distribute **NFAi** tokens without the consensus of the community. - The 75% of token supply is sent to contract address, and the contract owner will use them to add liquidity. - The 15% of token supply is sent to `0xCE14646cEBc61f9b9d92B40fA9acec18F5c06661`. - The 10% of token supply is sent to `0x114AF948c4fc58E6AB41E412a8711b54f0639CCa`.
Open the reportDivision operations may result in loss of precision. There maybe a small amount of `NFAi` loss based on the fee formulas. ```solidity=920 fees = (amount * 99) / 100; tokensForLiquidity += (fees * buyLiquidityFee) / buyTotalFees; tokensForOperations += (fees * buyOperationsFee) / buyTotalFees; tokensForTreasury += (fees * buyTreasuryFee) / buyTotalFees; } // on sell else if (automatedMarketMakerPairs[to] && sellTotalFees > 0) { fees = (amount * sellTotalFees) / 100; tokensForLiquidity += (fees * sellLiquidityFee) / sellTotalFees; tokensForOperations += (fees * sellOperationsFee) / sellTotalFees; tokensForTreasury += (fees * sellTreasuryFee) / sellTotalFees; } // on buy else if (automatedMarketMakerPairs[from] && buyTotalFees > 0) { fees = (amount * buyTotalFees) / 100; tokensForLiquidity += (fees * buyLiquidityFee) / buyTotalFees; tokensForOperations += (fees * buyOperationsFee) / buyTotalFees; tokensForTreasury += (fees * buyTreasuryFee) / buyTotalFees; } ```
Open the reportIt is expected that non-reverting invocations of `transferFrom()` that return `true` decrease the allowance of the address in `msg.sender` for the address in `from` by the value in `amount`. An allowance that equals `type(uint256).max` is treated as an exception and interpreted as an unlimited allowance that does not need to be reduced in order for this check to pass. The `transferFrom` function in this contract violates this property.
Open the reportIn 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.
Open the reportSometimes, 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 ); ```
Open the reportIn `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); ```
Open the reportThe 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; } ```
Open the reportIn 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()`.
Open the reportThe `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.
Open the reportThe cited input `bucketId` value is missing a check that it is not `address(0)`.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe report gives no description here.
Open the reportThe change of `owner` by function `transferOwnership()` overrides the previously set `owner` with the new one without guaranteeing the new `owner` has the ability to actuate transactions on-chain.
Open the reportThe approval function of the ERC20 standard possesses a well-known vulnerability whereby two consecutive approvals can be exploited to instead “reset” the approval by consuming the approval that is meant to be replaced with a new one.
Open the reportThe error message in #L661 is incorrect since the function `updateMaxBuyAmount()` is only used to update max buy amount. ```solidity=654 function updateMaxBuyAmount(uint256 newNum) external onlyOwner { require( newNum >= ((totalSupply() * 5) / 1000) / 1e18, "Cannot set max buy amount lower than 0.5%" ); require( newNum <= ((totalSupply() * 2) / 100) / 1e18, "Cannot set buy sell amount higher than 2%" ); maxBuyAmount = newNum * (10**18); emit UpdatedMaxBuyAmount(maxBuyAmount); } ``` The error message in #L1074 is incorrect since the function `setTreasuryAddress()` is only used to update treasury address. ```solidity=1071 function setTreasuryAddress(address _treasuryAddress) external onlyOwner { require( _treasuryAddress != address(0), "_operationsAddress address cannot be 0" ); treasuryAddress = payable(_treasuryAddress); emit UpdatedTreasuryAddress(_treasuryAddress); } ```
Open the reportThe transaction may be charged fees, so the max holding of the receiver should be the sum of the held tokens and the amount received from the transfer. The fees should not be included in the max holding. ```solidity=869 require( amount + balanceOf(to) <= maxWallet, "Max Wallet Exceeded" ); ``` - Max holding is checked. ```solidity=946 amount -= fees; ``` - Receiving amount is adjusted. ```solidity=276 _balances[recipient] += amount; ``` - Balance is increased by the adjusted amount.
Open the reportThere should always be events emitted in the sensitive functions that are controlled by centralization roles.
Open the reportA sandwich attack may happen when an attacker observes a transaction swapping tokens or adding liquidity without setting restrictions on slippage or minimum output amount. The attacker can manipulate the exchange rate by front running (before the transaction being attacked) a transaction to purchase one of the assets and make profits by back running (after the transaction being attacked) a transaction to sell the asset. The following functions are called without setting restrictions on slippage or minimum output amount, so transactions triggering these functions are vulnerable to sandwich attacks, especially when the input amount is large: - `swapExactTokensForETHSupportingFeeOnTransferTokens()`
Open the reportThe change of `_owner` by function `transferOwnership()` overrides the previously set `_owner` with the new one without guaranteeing the new `_owner` is able to actuate transactions on-chain.
Open the reportThe contract is serving as the underlying entity to interact with one or more third party protocols. The scope of the audit treats third party entities as black boxes and assume their functional correctness. However, in the real world, third parties can be compromised and this may lead to lost or stolen assets. In addition, upgrades of third parties can possibly create severe impacts, such as increasing fees of third parties, migrating to new LP pools, etc. ```solidity=414 IDexRouter public dexRouter; ``` - The contract `NFAi` interacts with third party contract with `IDexRouter` interface via `dexRouter`.
Open the report```solidity=490 event SwapAndLiquify( uint256 tokensSwapped, uint256 ethReceived, uint256 tokensIntoLiquidity ); ``` ```solidity=498 event UpdatedPrivateMaxSell(uint256 amount); ```
Open the reportThe return value of an external call is not stored in a local or state variable. ```solidity=979 dexRouter.addLiquidityETH{value: ethAmount}( address(this), tokenAmount, 0, // slippage is unavoidable 0, // slippage is unavoidable address(0xdead), block.timestamp ); ``` --- ```solidity=1140 dexRouter.addLiquidityETH{value: address(this).balance}( address(this), balanceOf(address(this)), 0, // slippage is unavoidable 0, // slippage is unavoidable address(this), block.timestamp ); ``` --- ```solidity=1158 IERC20(lpPair).approve(address(dexRouter), lpAmount); ``` --- ```solidity=1161 dexRouter.removeLiquidityETH( address(this), lpAmount, 1, // slippage is unavoidable 1, // slippage is unavoidable msg.sender, block.timestamp ); ``` --- ```solidity=1197 dexRouter.addLiquidityETH{value: address(this).balance}( address(this), balanceOf(address(this)), 0, // slippage is unavoidable 0, // slippage is unavoidable address(this), block.timestamp ); ```
Open the reportContracts that authorize users using the `tx.origin` variable are typically vulnerable to phishing attacks which can trick users into performing authenticated actions on the vulnerable contract. There are multiple reasons why using `tx.origin` is not safe from the security perspective. Refer to : * [Solidity Documentation on `tx.origin`](https://docs.soliditylang.org/en/latest/security-considerations.html#tx-origin) * <https://github.com/ethereum/solidity/issues/683> ```solidity=832 require( _holderLastTransferTimestamp[tx.origin] < block.number - 2 && _holderLastTransferTimestamp[to] < block.number - 2, "_transfer:: Transfer Delay enabled. Try again later." ); ```
Open the reportThe initial value of `sellingEnabled` is `true`, and the only function that can update it - `setSellingEnabled()` - requires its value to be equal to `false` first. In short, there is no way to update `sellingEnabled`, it will always be true. Therefore, the variable `sellingEnabled` is useless. There is even less need to check it. ```solidity=454 bool public sellingEnabled = true; ``` ```solidity=863 require(sellingEnabled, "Selling is disabled"); ``` ```solidity=1101 function setSellingEnabled() external onlyOwner { require(!sellingEnabled, "Selling already enabled!"); sellingEnabled = true; emit EnabledSelling(); } ```
Open the reportThe 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.
Open the report| Address | Chain | Source code |
|---|---|---|
| 0x8eec…79690x8eecaad83a1ea77bd88a818d4628fafc4cad7969 | Chain: Ethereum | Source code: Verified source |
Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.
Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="nfa-labs"></div></body>
Paste the code where the label should appear. It uses no API credit and updates by itself.
Something 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