51479 sc high inaccurate reward calculation post validator slashing due to premature timestamp update on token removal
Submitted on Aug 3rd 2025 at 08:15:40 UTC by @light279 for Attackathon | Plume Network
Report ID: #51479
Report Type: Smart Contract
Report severity: High
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/facets/RewardsFacet.sol
Impacts:
Theft of unclaimed yield
Protocol insolvency
Description
Brief/Intro
The Plume staking and reward distribution mechanism includes a vulnerability wherein users may lose a portion of their eligible rewards if a reward token is removed after the validator they delegated to has been slashed. Although the system is designed to allow users to claim rewards accrued until the validator’s slashing time, an interaction between slashing and token removal updates internal timestamps in a way that prevents proper reward calculation.
Vulnerability Details
When a validator is slashed through ValidatorFacet::slashValidator, a validatorToSlash.slashedAtTimestamp is set and the validator becomes inactive. Normally, users should be able to claim their pending rewards accrued until the slashing occurred. The claim process flows through several functions:
RewardsFacet::claim(address token,uint16 validatorId)→RewardsFacet::_processValidatorRewards→PlumeRewardLogic.updateRewardsForValidatorAndToken→PlumeRewardLogic::calculateRewardsWithCheckpoints
In the slashed branch of PlumeRewardLogic::calculateRewardsWithCheckpoints the code calculates the effective end time as the slashed timestamp (and respects a token removal timestamp if earlier), then uses validatorLastUpdateTime to determine whether to add an additional segment of rewards between validatorLastUpdateTime and that effective end time:
However, if RewardsFacet::removeRewardToken is called after the validator is slashed, it internally calls PlumeRewardLogic.updateRewardPerTokenForValidator, which updates:
even for slashed validators. Example snippet:
Because validatorLastUpdateTime is updated to a time after slashedAtTimestamp, subsequent calls to calculateRewardsWithCheckpoints will find:
to be false (effectiveEndTime == slashedAtTimestamp < validatorLastUpdateTime), preventing the additional reward-per-token increase from being calculated for the period between the previous validatorLastUpdateTime and the slashing time. This results in user rewards accrued up to the slashing moment becoming unclaimable.
Impact Details
This results in loss of unclaimed user rewards that should have been claimable up to the slashing timestamp. Since the tokens are not stolen but rendered unclaimable due to incorrect accounting, the impact classification is:
High — Theft of unclaimed yield
This affects all delegators of a validator that has been slashed and subsequently had its reward token removed, causing unexpected reward loss without any direct malicious activity.
Proof of Concept
Claim After Token Removal
The user now tries to claim rewards.
In
calculateRewardsWithCheckpoints,effectiveEndTime = slashedAtTimestamp = T1.The condition
if (effectiveEndTime > validatorLastUpdateTime)becomes false because validatorLastUpdateTime = T2 > T1.No
rewardPerTokenIncreaseis calculated for the interval up to the slash, and the call to_calculateRewardsCorereceives an outdatedcurrentCumulativeRewardPerToken.As a result, rewards that should have been claimable up to T1 are not awarded to the user.
Notes / Observations
The bug arises from updating
validatorLastUpdateTimeswhen handling token removal for already-slashed validators. This update should not advance the last-update time beyond theslashedAtTimestampused for reward calculation, because it effectively erases the period for which rewards should be computed at claim time.The flow where
updateRewardPerTokenForValidatoris invoked during token removal leads to the incorrect timestamp progression.
Was this helpful?