Liquid Staking

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

updateFee should call _updateStrategyRewards first

Summary

StakingPool contract's updateFee function does not call _updateStrategyRewards before modifying the fee structure.
This omission can lead to incorrect fee calculations and distributions, resulting in users not receiving the fees they were entitled to under the previous fee structure.

Vulnerability Details

The StakingPool contract manages staking operations, including the distribution of fees to users and fee receivers. The contract allows the owner to update fees through the updateFee function.

However, when fees are updated, the function does not call _updateStrategyRewards to settle any pending rewards and fees before the change. This can lead to incorrect fee distributions, as the pending rewards are calculated using the new fee structure rather than the one in effect when the rewards were accrued.

Contract: StakingPool.sol
358: function updateFee(
359: uint256 _index,
360: address _receiver,
361: uint256 _feeBasisPoints
362: ) external onlyOwner {
363: require(_index < fees.length, "Fee does not exist");
364:
365: if (_feeBasisPoints == 0) {
366: fees[_index] = fees[fees.length - 1];
367: fees.pop();
368: } else {
369: fees[_index].receiver = _receiver;
370: fees[_index].basisPoints = _feeBasisPoints;
371: }
372:
373: require(_totalFeesBasisPoints() <= 4000, "Total fees must be <= 40%");
374: }

As can be seen, the new fee strructure overwrites the existing one which might cause loss of funds for the users if the fees are decreased.

Without calling _updateStrategyRewards before changing the fees, any pending rewards are calculated using the new fee structure instead of the one in place when the rewards were earned. Because he _updateStrategyRewards function calculates fees based on the current fees array.

You can see the concerned part in the function;

Contract: StakingPool.sol
556: if (totalRewards > 0) {
557: receivers[receivers.length - 1] = new address[]();
558: feeAmounts[feeAmounts.length - 1] = new uint256[]();
559: totalFeeCount += fees.length;
560:
561: for (uint256 i = 0; i < fees.length; i++) {
562: receivers[receivers.length - 1][i] = fees[i].receiver;
563: feeAmounts[feeAmounts.length - 1][i] =
564: (uint256(totalRewards) * fees[i].basisPoints) /
565: 10000;
566: totalFeeAmounts += feeAmounts[feeAmounts.length - 1][i];
567: }

Impact

Incorrect fee distribution

Tools Used

Manual Review

Recommendations

Call _updateStrategyRewards before modifying fees

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Appeal created

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`updateStrategyRewards` is not called before adding & updating the fees

It should be called with try and catch to avoid DOS by receiver.

Support

FAQs

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