42439 sc insight insight report for stakev2 contract
#42439 [SC-Insight] Insight Report for StakeV2 contract
Submitted on Mar 23rd 2025 at 22:59:30 UTC by @pxng0lin for Audit Comp | Yeet
Report ID: #42439
Report Type: Smart Contract
Report severity: Insight
Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol
Impacts:
Description
[LOW-01] Unused Local Variable
Severity: Low
Affected Contract
StakeV2.sol
Description
In the executeRewardDistribution
function (line 195), the local variable _islandTokens
is declared and assigned a value from the return of zapper.zapInNative
, but it's never used in the function.
(uint256 _islandTokens, uint256 vaultSharesMinted) =
zapper.zapInNative{value: amountToDistribute}(swap0, swap1, stakingParams, vaultParams);
Impact
While not a security vulnerability, unused variables make the code less clean and potentially indicate incomplete implementation or a misunderstanding of the function's purpose.
Recommendation
Remove the unused variable by replacing the declaration with:
(, uint256 vaultSharesMinted) =
zapper.zapInNative{value: amountToDistribute}(swap0, swap1, stakingParams, vaultParams);
If there's a specific reason for capturing the _islandTokens
value (for example, for future use), consider adding a comment explaining why it's being captured but not used.
[LOW-02] Missing Zero Address Validation in Constructor
Severity: Low
Affected Contract
StakeV2.sol
Description
The constructor accepts _stakingToken
, _zapper
, and _wbera
addresses but doesn't validate that these aren't the zero address:
constructor(IERC20 _stakingToken, IZapper _zapper, address owner, address initialManager, IWETH _wbera) Manager(owner, initialManager) {
stakingToken = _stakingToken;
zapper = _zapper;
wbera = _wbera;
}
Impact
Setting any of these critical components to the zero address would render the contract entirely unusable and necessitate redeployment.
Recommendation
Add zero address validation in the constructor:
constructor(IERC20 _stakingToken, IZapper _zapper, address owner, address initialManager, IWETH _wbera) Manager(owner, initialManager) {
require(address(_stakingToken) != address(0), "StakeV2: stakingToken is zero address");
require(address(_zapper) != address(0), "StakeV2: zapper is zero address");
require(address(_wbera) != address(0), "StakeV2: wbera is zero address");
stakingToken = _stakingToken;
zapper = _zapper;
wbera = _wbera;
}
Gas Optimisation
[GAS-01] Multiple Approval Patterns in Claim Functions
Severity: Gas Optimisation
Affected Contract
StakeV2.sol
Description
In each claim function (claimRewardsInNative
, claimRewardsInToken0
, etc.), there's a repetitive pattern of approving tokens to the zapper:
IERC20(redeemParams.vault).approve(address(zapper), amountToWithdraw);
Impact
Duplicated code increases contract size and deployment costs. It also makes maintenance more difficult as changes must be implemented in multiple places.
Recommendation
Move the approval logic to the _verifyAndPrepareClaim
function to eliminate code duplication and reduce gas costs associated with deployment. Update the function to handle the approval:
function _verifyAndPrepareClaim(uint256 amountToClaim, IZapper.VaultRedeemParams calldata redeemParams)
private
returns (IZapper.VaultRedeemParams memory)
{
// Existing code
// Add approval here
IERC20(redeemParams.vault).approve(address(zapper), amountToClaim);
return IZapper.VaultRedeemParams({
// existing params
});
}
[GAS-02] Duplicated Validation Logic
Severity: Gas Optimisation
Affected Contract
StakeV2.sol
Description
All claim functions (claimRewardsInNative
, claimRewardsInToken0
, etc.) perform the same setup and validation, resulting in duplicated code.
Impact
This results in increased contract size and gas costs for deployment. Additionally, it creates more complex maintenance requirements as changes must be made in multiple places.
Recommendation
Extract the common logic into a separate internal function that handles the setup for all claim operations:
function _setupClaim(uint256 amountToWithdraw) internal returns (uint256) {
_updateRewards(msg.sender);
uint256 userReward = earned[msg.sender];
require(userReward > 0, "No rewards to claim");
require(amountToWithdraw <= userReward, "Amount to claim exceeds rewards earned");
earned[msg.sender] -= amountToWithdraw;
totalVaultShares -= amountToWithdraw;
return amountToWithdraw;
}
Proof of Concept
Proof of Concept
Please see the main body of the report
Was this helpful?