Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Optimize splitRewards Function with Unchecked Loop Increment

Summary

Contract: LSTRewardSplitter.sol
Line: 173

The proposed change implements an unchecked block for the loop counter increment in the _splitRewards function, optimizing gas usage. In Solidity versions 0.8.0 and above, automatic overflow and underflow checks are implemented by default, increasing gas costs for arithmetic operations. By using an unchecked block, we instruct the compiler to skip these checks for the loop counter increment.

This optimization is considered safe because the number of iterations is bounded by fees.length, which is controlled by the contract owner and is extremely unlikely to approach the maximum value of uint256 (2^256 - 1). The unchecked block is applied only to the loop counter increment, not to any reward or fee calculations.

Vulnerability Details

Original code:

function _splitRewards(uint256 _rewardsAmount) private {
for (uint256 i = 0; i < fees.length; ++i) {
Fee memory fee = fees[i];
uint256 amount = (_rewardsAmount * fee.basisPoints) / 10000;
if (fee.receiver == address(lst)) {
IStakingPool(address(lst)).burn(amount);
} else {
lst.safeTransfer(fee.receiver, amount);
}
}
principalDeposits = lst.balanceOf(address(this));
emit RewardsSplit(_rewardsAmount);
}

Impact


Gas savings, while modest per iteration (approximately 3-5 gas), can accumulate in contracts that are called frequently or have loops that iterate multiple times. For example:
If fees.length = 10, the total savings would be around 30-50 gas.
If fees.length = 100, the total savings would be around 300-500 gas.

Tools Used

Remix IDE Desktop

Recommendations

Proposed Code:

function _splitRewards(uint256 _rewardsAmount) private {
for (uint256 i = 0; i < fees.length;) {
Fee memory fee = fees[i];
uint256 amount = (_rewardsAmount * fee.basisPoints) / 10000;
if (fee.receiver == address(lst)) {
IStakingPool(address(lst)).burn(amount);
} else {
lst.safeTransfer(fee.receiver, amount);
}
unchecked {
++i;
}
}
principalDeposits = lst.balanceOf(address(this));
emit RewardsSplit(_rewardsAmount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.