CertiK reviewed Neutra Finance (Neutra - audit) on 10 Apr 2023: 27 findings, 0 critical; 13 marked fixed, 4 acknowledged, 10 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 | 0 | 0 | 2 |
| Medium | 0 | 4 | 4 |
| Low | 10 | 0 | 7 |
The function `__claimStakedNeuGlp()` or `__claimWant()` is used to calculate the claimable `want` or `snGlp` and transfer them to the users. ```solidity=248 uint256 totalBalance = totalSnGlpPerRound[round]; uint256 totalReceived = totalWantReceivedPerRound[round]; uint256 claimAmount = totalReceived * balance / totalBalance; ``` ```solidity=277 uint256 claimAmount = totalSnGlpReceivedPerRound[round] * balance / totalWantPerRound[round]; ``` ```solidity=306 function executeBatchPositions(bool _isWithdraw, bytes[] calldata _params, uint256 _dealAmount) external payable onlyHandlerAndAbove { require(msg.value >= executionFee * 2, "BatchRouter: not enough execution Fee"); uint256 amountIn = _isWithdraw ? totalSnGlpPerRound[currentWithdrawRound] : totalWantPerRound[currentDepositRound]; IRouter(router).executePositionsBeforeDealGlp{value: msg.value}(amountIn, _params, _isWithdraw); pendingDealAmount = _dealAmount; lastExecutionStatus = _isWithdraw; executed = true; emit ExecuteBatchPositions(_isWithdraw, amountIn); } ``` ```solidity=319 function confirmAndDealGlp() external onlyHandlerAndAbove { require(executed, "BatchRouter: executes positions first"); if (!lastExecutionStatus) { uint256 amountOut = IRouter(router).confirmAndBuy(pendingDealAmount, address(this)); _updateRewards(); totalSnGlpReceivedPerRound[currentDepositRound] = amountOut; ... } else { uint256 amountOut = IRouter(router).confirmAndSell(pendingDealAmount, address(this)); totalWantReceivedPerRound[currentWithdrawRound] = amountOut; ... } executed = false; } ``` As per the claimable amount calculation formula, the value of `totalWantReceivedPerRound[round]` or `totalSnGlpReceivedPerRound[round]` has a significant impact on the claimable amount. The amount of `totalWantReceivedPerRound[round]` or `totalSnGlpReceivedPerRound[round]` is ultimately determined by the `_dealAmount` parameter of the `executeBatchPositions` function. If the `_dealAmount` is insufficient, users may risk losing money, and it is essential to determine the appropriate quantity of `_dealAmount` to ensure that users can obtain the expected claimable tokens without any losses. Before proceeding, we would like to request confirmation from our customers on the correct approach for determining the right quantity of `_dealAmount`.
Performing integer division before multiplication truncates the low bits, losing the precision of the calculation. ```solidity=277 uint256 claimAmount = totalSnGlpReceivedPerRound[round] * balance / totalWantPerRound[round]; ``` ```solidity=284 uint256 esNeuClaimable = claimAmount * (cumulativeEsNeuRewardPerToken - cumulativeEsNeuRewardPerRound[round]) / PRECISION; ``` --- ```solidity=387 uint256 wbtcRatio = (aums[0] * MAX_BPS) / totalAum; ``` ```solidity=394 wbtcAmountIn = remainingAmount * wbtcRatio / (wbtcRatio + wethRatio); ``` --- ```solidity=404 uint256 sizeDelta = positionParam.size * _amount / _totalSupply; ``` ```solidity=408 uint256 usdOut = profitParam.hasProfit ? sizeDelta * profitParam.pnl / positionParam.size : 0; ```
The `StrategyVault` contract is responsible for using the `want` asset to purchase `glp` tokens for rebalancing. However, when `glp` tokens are sold, the `recipient` address that receives the `want` asset is specified by the input parameter instead of using the original address that paid for the `want` asset. Therefore, we would like to confirm with the client whether this current implementation aligns with the original project design.
When transferring deflationary ERC20 tokens, the input amount may not be equal to the received amount due to the charged transaction fee. For example, if a user sends 100 deflationary tokens (with a 10% transaction fee), only 90 tokens actually arrived to the contract. However, a failure to discount such fees may allow the same user to withdraw 100 tokens from the contract, which causes the contract to lose 10 tokens in such a transaction.
The variables `_executionFee`, `_fee`, and `_bps` do not have reasonable boundaries, so they can be given arbitrary values after deploying.
There is no storage gap preserved in the logic contract. Any logic contract that acts as a base contract that needs to be inherited by another upgradeable child should have a reasonable size of storage gap preserved for the new state variable introduced by the future upgrades.
There is no validation to check whether the length of the array `_selectors` is the same length as each of the arrays `_params`. In the event that any of these lengths are less than the length of `_selectors`, there is no revert of the function.
State variables that never change should be declared as `constant` to save gas.
The return value of the transfer()/transferFrom() call is not checked.
The return value of an external call is not stored in a local or state variable.
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