Nut DeFi security record

BNB Chainnut-defi.comX

As of 3 Nov 2026, Nut DeFi has 1 audit report on Trustblock, by CertiK, dated 20 Jan 2025. That report lists no open critical findings. It also holds 1 deployed contract on 1 chain.

Critical findings
No open critical findings
In 1 report.
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.

Audits1

Newest first

AuditSecurity firmDateScopeCritical findingsReport
Nut DeFi - AuditSecurity firm: CertiKDate: Scope: 1 address on BNB ChainNo open critical findingsOpen · skynet.certik.com

Findings33

Statuses are the security firm’s own, from each report.

SeverityOpenAcknowledgedFixed
Critical000
High030
Medium608
Low6010
Medium`findRoot` Lacks Depth Limitation, Posing a DoS RNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

In `findRoot`, there is no limit on the depth of the upper-level inviter search: ```solidity=912 function findRoot(address user) public view returns (bool) { address current = user; while (current != address(0)) { address inviter = _userInfo[current].inviter; if (inviter == receiveAddress) { return true; } if (inviter == address(0)) { break; } current = inviter; } return false; } ``` When there are too many upper-level inviters, `findRoot` can consume a large amount of gas from the user, potentially leading to DoS vulnerability.

Open the report
MediumFlawed and Vulnerable `excludeLpProvider` SettingNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

In the `_transfer` function, the following code contains flaws and is vulnerable to attacks: `nut_live.sol` ```solidity=545 if (lpBalance > lpAmount) { uint256 debtAmount = lpBalance - lpAmount; uint256 maxDebtAmount = _lastMaybeAddLPAmount * IERC20(mainPair).totalSupply().div(_balances[mainPair]); if (debtAmount > maxDebtAmount) { excludeLpProvider[lastMaybeAddLPAddress] = true; ``` Firstly, `maxDebtAmount` uses division before multiplication, while PancakeSwap uses multiplication before division in its calculation: `nut_live.sol` ```solidity=547 uint256 maxDebtAmount = _lastMaybeAddLPAmount * IERC20(mainPair).totalSupply().div(_balances[mainPair]); ``` Due to rounding, this may cause `maxDebtAmount` to be smaller than the actual value, incorrectly setting `lastMaybeAddLPAddress` as `excludeLpProvider`. Secondly, for any user adding liquidity, an attacker can transfer LP from the main pair (with any amount greater than 0) to the user between their liquidity addition and the next NUT transfer. This would increase the `debtAmount`, causing it to exceed `maxDebtAmount` and prevent the user from receiving staking rewards. Finally, during remove liquidity, due to calculation issues, the liquidity might be calculated as greater than the actual amount, causing `_userLPAmount[to]` to be smaller than the actual value: `nut_live.sol` ```solidity=623 if (isRemoveLP) { if (!_feeWhiteList[to]) { takeFee = true; uint256 liquidity = (amount * ISwapPair(_mainPair).totalSupply() + 1).div(balanceOf(_mainPair) - 1); if (from != address(_swapRouter)) { liquidity = (amount * ISwapPair(_mainPair).totalSupply() + 1).div(balanceOf(_mainPair) - amount - 1); } require(_userLPAmount[to] >= liquidity, ">uLP"); _userLPAmount[to] -= liquidity; } } ``` If the user adds liquidity after this, it may cause the `debtAmount` to be larger than the actual value, causing it to exceed `maxDebtAmount` and incorrectly setting `lastMaybeAddLPAddress` as `excludeLpProvider`.

Open the report
MediumIncorrect Method of Identifying Remove LiquidityNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

In the transfer function, there are two instances where the code logic checks whether it is in the process of removing liquidity. The first instance requires that the `from` address is the main pair and the `to` address is not in the fee whitelist. At this point, if the USDT amount in the pair is less than or equal to the amount recorded in the reserves, it is considered as a remove liquidity action : `nut_live.sol` ```solidity=595 isRemoveLP = _isRemoveLiquidity(); ``` `nut_live.sol` ```solidity=988 function _isRemoveLiquidity() internal view returns (bool isRemove){ ISwapPair mainPair = ISwapPair(_mainPair); (uint r0,uint256 r1,) = mainPair.getReserves(); address tokenOther = _usdt; uint256 r; if (tokenOther < address(this)) { r = r0.div(10 ** 12); } else { r = r1.div(10 ** 12); } uint256 bal = (IERC20(tokenOther).balanceOf(_mainPair)).div(10 ** 12); isRemove = r >= bal; } ``` However, the PancakeSwap swap function allows flash loans. During a flash loan, both USDT and NUT tokens may be reduced simultaneously and transferred to the contract. The contract only needs to repay the loan and pay the fee in the `pancakeCall` to maintain the K value greater than or equal to its original value. This situation would also be recognized as remove liquidity. In this case, if the user has liquidity in the pair, the `_userLPAmount` recorded in the token contract will be reduced, thereby decreasing the reward. If not, the transaction may fail. `nut_live.sol` ```solidity=623 if (isRemoveLP) { if (!_feeWhiteList[to]) { takeFee = true; uint256 liquidity = (amount * ISwapPair(_mainPair).totalSupply() + 1).div(balanceOf(_mainPair) - 1); if (from != address(_swapRouter)) { liquidity = (amount * ISwapPair(_mainPair).totalSupply() + 1).div(balanceOf(_mainPair) - amount - 1); } require(_userLPAmount[to] >= liquidity, ">uLP"); _userLPAmount[to] -= liquidity; } } ``` Meanwhile, the transaction fee will be recognized as remove LP and will be fully destroyed. The other instance logic requires that the `from` address is `address(_swapRouter)`. However, the router will only actively transfer NUT tokens out of the contract through the `removeLiquidityETHSupportingFeeOnTransferTokens` function, which only supports ETH and other token pairs, i.e., ETH and NUT pairs. Therefore, this operation does not refer to remove liquidity for the main pair (USDT + NUT) and should not set `isRemoveLP` to true: `nut_live.sol` ```solidity=619 if (from == address(_swapRouter)) { isRemoveLP = true; } ``` `PancakeRouter.sol` ```solidity=172 function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) public virtual override ensure(deadline) returns (uint amountETH) { (, amountETH) = removeLiquidity( token, WETH, liquidity, amountTokenMin, amountETHMin, address(this), deadline ); TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this))); IWETH(WETH).withdraw(amountETH); TransferHelper.safeTransferETH(to, amountETH); } ``` At the same time, the liquidity calculation logic for `from == address(_swapRouter)` should be modified: `nut_live.sol` ```solidity=623 if (isRemoveLP) { if (!_feeWhiteList[to]) { takeFee = true; uint256 liquidity = (amount * ISwapPair(_mainPair).totalSupply() + 1).div(balanceOf(_mainPair) - 1); if (from != address(_swapRouter)) { liquidity = (amount * ISwapPair(_mainPair).totalSupply() + 1).div(balanceOf(_mainPair) - amount - 1); } require(_userLPAmount[to] >= liquidity, ">uLP"); _userLPAmount[to] -= liquidity; } } ``` Otherwise, removing liquidity operations for the ETH-NUT pair will also be treated as operations for the main pair (USDT-ETH), reducing the user's liquidity amount(`_userLPAmount`) and causing error in the liquidity statistics within the contract.

Open the report
MediumIncorrect Time Interval for `progressLPBlock` HanNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

In the `processStakingRewards` function, when calculating the reward cycle, `progressLPBlock` records the BSC block height, while `lpRewardTimeDebt` is a time interval in seconds: `nut_live.sol` ```solidity=378 uint256 public lpRewardTimeDebt = 5 days; ``` `nut_live.sol` ```solidity=830 if (progressLPBlock + lpRewardTimeDebt > block.number) { ``` Since the BSC block time is 3 seconds per block, the actual reward cycle is not 5 days but 3 * 5 = 15 days, which is three times longer than the setting time.

Open the report
MediumNo Cap on FeesNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

The `_removeLPFee` variables in the contract have no set limits, allowing fees to potentially reach 100%. If this occurs, users would receive no tokens from the contract, resulting in a complete loss of their investment.

Open the report
MediumPotential Arbitrage via `autoBurnLiquidityPairTokNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

The `autoBurnLiquidityPairTokens` function in the `NUT` contract introduces a **liquidity burn mechanism**, which reduces the circulating supply of LP tokens. While this mechanism is intended to increase scarcity and stabilize the token price, it inadvertently creates **arbitrage opportunities**, especially when `_swapPairList` includes multiple trading pairs. When `_swapPairList` includes multiple trading pairs (e.g., `NUT/USDT` and `NUT/WETH`), the arbitrage becomes potentially more profitable: 1. The attacker identifies that a burn operation is imminent on one pair (e.g., `NUT/USDT`). 2. The attacker buys `NUT` on a **different pair** (e.g., `NUT/WETH`) where the price is still unaffected by the burn. 3. The attacker sells a few `NUT` on `NUT/WETH` and burn operation will occur on `NUT/USDT`. As a result, the price of `NUT` increases in the `NUT/USDT` pair. 4. The attacker sells the purchased `NUT` tokens at the higher price to gain some profits.

Open the report
MediumRouter Should Not Be in The Fee Exempt WhitelistNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

Some restrictions can be bypassed by swapping tokens and removing liquidity to the router and then getting tokens from the router. When the tokens are transferred to the router, the use uses the feature of the router function ***`removeLiquidityETHSupportingFeeOnTransferTokens`***, which will send all the specified tokens in the router to a specified address. Because the router is not supposed to receive tokens for any user other than remove liquidity.

Open the report
MediumUser's Reward Cycle Can Be Manipulated to IncreasNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

In `processStakingRewards`, after a certain time period, the function will iterate through the `lpProviders` array and settle the rewards for the addresses it encounters: `nut_live.sol` ```solidity=852 while (gasUsed < gas && iterations < shareholderCount) { if (currentLPIndex >= shareholderCount) { currentLPIndex = 0; } shareHolder = lpProviders[currentLPIndex]; (uint256 userUSDTContribution,) = getUserLiquidityContribution(shareHolder); if (!excludeLpProvider[shareHolder]) { pairBalance = mainPair.balanceOf(shareHolder); lpAmount = _userLPAmount[shareHolder]; if (lpAmount < pairBalance) { pairBalance = lpAmount; } // Only reward if USDT equivalent meets 30 USDT minimum and reward interval has passed if (userUSDTContribution >= lpHoldCondition && block.timestamp > _lastLPRewardTimes[shareHolder] + lpRewardTimeDebt) { // Calculate 0.8% daily reward in USDT, then convert to NUT using pool price uint256 dailyUsdtReward = (userUSDTContribution.mul(DAILY_REWARD_RATE)).div(1000); uint256 rewardAmountInNUT = (dailyUsdtReward.mul(rNut)).div(rUsdt); rewardAmountInNUT *= 2; // Calculate total reward for the 5-day interval rewardAmount = rewardAmountInNUT.mul(5); if (rewardAmount > 0) { _lastLPRewardTimes[shareHolder] = block.timestamp; processInviterRewards(shareHolder, rewardAmount); _rewardTransfer(shareHolder, rewardAmount); } } } gasUsed += (gasLeft - gasleft()); gasLeft = gasleft(); currentLPIndex++; iterations++; } ``` However, the `lpProviders` array can be artificially extended by minting main pair LP tokens. When the array grows too long and the gas required to iterate through it exceeds the reward gas limit, users will not receive the corresponding rewards for each cycle. This situation causes users to be unable to receive their rewards for an extended period.

Open the report
LowAllowance Reduction May Not Match the Actual TranNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

When transferring between two non-fee whitelist addresses using transferFrom, if the from address has less than 100 wei remaining after the transfer, the transfer amount will be reduced so that the user is left with at least a 100 wei balance: `nut_live.sol` ```solidity=562 if (!_feeWhiteList[from] && !_feeWhiteList[to]) { uint256 maxSellAmount; uint256 remainAmount = 10 ** (_decimals - 4); uint256 balance = _balances[from]; if (balance > remainAmount) { maxSellAmount = balance - remainAmount; } if (amount > maxSellAmount) { amount = maxSellAmount; } } ``` However, the transferFrom function will still deduct the original amount of the allowance. `nut_live.sol` ```solidity=513 function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) { _transfer(sender, recipient, amount); if (_allowances[sender][msg.sender] != MAX) { _allowances[sender][msg.sender] = _allowances[sender][msg.sender] - amount; } return true; } ```

Open the report
LowConfirmation of Dependency on `_swapPairList` anNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

The contract logic related to `_swapPairList` is tightly coupled with the `_mainPair` (the primary liquidity pool pair, typically NUT/USDT). Specifically, the logic for **buying**, **selling**, and **fee calculations** depends heavily on whether the sender or recipient is part of `_swapPairList`. However, there is no explicit restriction or validation in the contract to ensure that `_swapPairList` only includes the `_mainPair`. This creates a potential issue if other pairs are unintentionally or maliciously added to `_swapPairList`. ```solidity=700 else if (_swapPairList[sender]) {//Buy uint256 usdtAmount = (tAmount.mul(tokenPrice())).div(10**6); require(usdtAmount <= MAX_BUY_AMOUNT, "You can only buy max 20,000 usdt"); require(block.timestamp >= lastSwapTime[recipient] + MIN_SWAP_INTERVAL, "Must wait 2 minutes between transactions"); lastSwapTime[recipient] = block.timestamp; swapFeeAmount = (tAmount.mul(_buyFundFee + _buyDevFee)).div(10000); } else if (_swapPairList[recipient]) {//Sell require(block.timestamp >= lastSwapTime[recipient] + MIN_SWAP_INTERVAL, "Must wait 2 minutes between transactions"); lastSwapTime[recipient] = block.timestamp; isSell = true; swapFeeAmount = (tAmount.mul(_sellReservePoolFee)).div(10000); destroyFeeAmount = (tAmount.mul(_sellDestroyFee)).div(10000); } ``` In the `NUT` contract, the tracked information, such as `tokenPrice` and restrictions (e.g., `MAX_BUY_AMOUNT`, `MIN_SWAP_INTERVAL`), is derived from the `_mainPair` (e.g., NUT/USDT). The logic assumes that the `_swapPairList` corresponds to the main trading pair (`_mainPair`), where price calculations and restrictions are meaningful. If additional pairs (e.g., NUT/ETH or unrelated token pairs) are added to `_swapPairList`, the logic for **buying** and **selling** may break or behave incorrectly.

Open the report
Show 8 more
LowFunctions With `_` as Name Prefix Are Not `privatNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

Functions with names starting with `_` should be declared as `private`/`internal`.

Open the report
LowInvalid Use of Access Control ModifierNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

The functions marked as 'view' or 'pure' are unnecessarily restricted by the `onlyWhiteList` modifier. These functions are designed to be read-only, meaning they do not modify the state on the blockchain. However, they are restricted so that only a specific address can call them. It's important to note that even private state variables can be read off-chain, rendering the access restriction on these functions ineffective.

Open the report
LowManually Triggerable Reward DelayNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

After updating the relevant information of `lastMaybeAddLPAddress`, it was not reset to zero: ```solidity=551 if (lastMaybeAddLPAddress != address(0)) { uint256 lpBalance = IERC20(mainPair).balanceOf(lastMaybeAddLPAddress); if (lpBalance > 0) { uint256 lpAmount = _userLPAmount[lastMaybeAddLPAddress]; if (lpBalance > lpAmount) { _addLpProvider(lastMaybeAddLPAddress); _userLPAmount[lastMaybeAddLPAddress] = lpBalance; // NCK-22 NCK-20 _lastLPRewardTimes[lastMaybeAddLPAddress] = _getTomorrowsMidnight(); } } _userInfo[lastMaybeAddLPAddress].rewardAmount = calculateStakingRewards(lastMaybeAddLPAddress); } ``` As a result, during transfers that do not interact with the pair, the LP-related information of `lastMaybeAddLPAddress` will still be updated. If a small amount of LP tokens is transferred into `lastMaybeAddLPAddress`, it will trigger the logic inside `lpBalance > lpAmount` and update `_lastLPRewardTimes[lastMaybeAddLPAddress]` to the next natural day’s midnight.

Open the report
LowMissing Zero Address ValidationNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

The cited address input is missing a check that it is not `address(0)`.

Open the report
LowRedundant Code ComponentsNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

The linked statements do not affect the functionality of the codebase and appear to be either leftovers from test code or older functionality.

Open the report
LowThe `batchSetUserInfo` does not set `_userLPAmounNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

After the `receiveAddress` transfers the corresponding LP to the user, `batchSetUserInfo` will be called by the owner to update the corresponding `rewardAmount` and `_userLPAmount`: `NuToken.sol` ```solidity=1044 function batchSetUserInfo(address[] memory addr) external onlyOwner { require(addr.length > 0, "No addresses provided"); uint256 ownerLpBalance = 0; for (uint i = 0; i < addr.length; i++) { address user = addr[i]; uint256 lpBalance = IERC20(_mainPair).balanceOf(user); _addLpProvider(user); _userLPAmount[user] = lpBalance; ownerLpBalance += lpBalance; _lastLPRewardTimes[user] = _getTomorrowsMidnight();// NCK-22 _userInfo[user].rewardAmount = calculateStakingRewards(user); } _userLPAmount[receiveAddress] = _userLPAmount[receiveAddress] - ownerLpBalance; _userInfo[receiveAddress].rewardAmount = calculateStakingRewards(receiveAddress); } ``` However, there are several situations where the updated `_userLPAmount[receiveAddress]` may not match the actual LP token balance of `receiveAddress`, such as: 1. The `addr` list contains duplicate addresses, causing the `ownerLpBalance` to be double-counted. 2. The address in `addr` already holds LP tokens before the transfer to `receiveAddress`, which results in the `ownerLpBalance` recording an inflated value. Therefore, it is recommended to update the value of `_userLPAmount[receiveAddress]` to reflect the actual amount of LP tokens held by `receiveAddress`.

Open the report
LowUsage of `transfer()` for sending Native TokensNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

After [EIP-1884](https://eips.ethereum.org/EIPS/eip-1884) was included in the Istanbul hard fork, it is not recommended to use `.transfer()` or `.send()` for transferring native tokens as these functions have a hard-coded value for gas costs making them obsolete as they are forwarding a fixed amount of gas, specifically `2300`. This can cause issues in case the linked statements are meant to be able to transfer funds to other contracts instead of EOAs.

Open the report
LowVariables That Could Be Declared as ImmutableNut DeFi - Audit · CertiK · 20 Jan 2025Fixed

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.

Open the report

Contracts1

AddressChainSource code
0x29fdf61b4ab5a4af38c745d4e1ef282525d4978dChain: BNB ChainSource code: Verified source

Show this on your site

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.

Content

Facts

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.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="nut-defi"
></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