Liquid Staking

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

Stuck Rewards in Zero Staking Scenario

Summary

https://github.com/Cyfrin/2024-09-stakelink/blob/main/contracts/core/rewardsPools/RewardsPool.sol

The _updateRewardPerToken function is responsible for updating the rewardPerToken variable when new rewards are distributed. If the function is called when there are no stakers in the contract (i.e., totalStaked == 0), it reverts due to the following condition:

if (totalStaked == 0) revert NothingStaked();

Code Reference

/**
* @notice updates rewardPerToken
* @param _reward deposited reward amount
**/
function _updateRewardPerToken(uint256 _reward) internal virtual {
uint256 totalStaked = controller.totalStaked();
if (totalStaked == 0) revert NothingStaked(); // Reversion if no stakes
rewardPerToken += ((_reward * 1e18) / totalStaked);
}

Impact

This vulnerability leads to significant issues:

**Stuck Rewards: ** If there are no active stakers, any rewards deposited into the contract cannot be distributed. This results in the rewards remaining stuck indefinitely until at least one user stakes their tokens again.
User Frustration: Users expecting rewards from their deposits may become frustrated if they find that rewards are not being distributed due to the lack of stakers.
Increased Complexity: The system complexity increases as it relies on user behavior to reset the reward distribution mechanism.

Proof of Concept (PoC)

The following scenario illustrates how this vulnerability can impact the contract:

An external entity (e.g., a user or a contract) deposits rewards into the RewardsPool:

token.transfer(rewardsPoolAddress, 1000); // Depositing rewards

The distributeRewards function is called:

rewardsPool.distributeRewards();

If controller.totalStaked() returns 0 (i.e., there are no active stakers), the following will occur:

// totalStaked == 0 will trigger the revert
_updateRewardPerToken(1000); // Reverts with NothingStaked error

Tools Used

Manual Review

Recommendations

To mitigate this vulnerability, the following changes should be made:

Allow for No-Staking Scenario: Modify the _updateRewardPerToken function to handle the case where there are no stakers. Instead of reverting, it could simply return without modifying the rewardPerToken:

function _updateRewardPerToken(uint256 _reward) internal virtual {
uint256 totalStaked = controller.totalStaked();
if (totalStaked == 0) return; // Do nothing if no stakers
rewardPerToken += ((_reward * 1e18) / totalStaked);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Support

FAQs

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