50402 sc low single rate assumption ignores checkpoints in slashed case
Submitted on Jul 24th 2025 at 09:10:11 UTC by @BeastBoy for Attackathon | Plume Network
Report ID: #50402
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/lib/PlumeRewardLogic.sol
Impacts: Theft of unclaimed yield
Description
In the slashed‑validator branch of calculateRewardsWithCheckpoints, the code reads a single rate at the moment of the last global update and applies it across the entire interval without honoring any intermediate checkpoints:
PlumeStakingStorage.RateCheckpoint memory effectiveRewardRateChk =
getEffectiveRewardRateAt($, token, validatorId, validatorLastUpdateTime);
uint256 effectiveRewardRate = effectiveRewardRateChk.rate;
uint256 rewardPerTokenIncrease = timeSinceLastUpdate * effectiveRewardRate;
currentCumulativeRewardPerToken += rewardPerTokenIncrease;Because it never segments the window by the array of validatorRewardRateCheckpoints, any rate changes that occurred between validatorLastUpdateTime and the slash (or token removal) are completely ignored. In contrast, the unslashed path builds a distinctTimestamps array of every checkpoint and correctly applies each piecewise rate.
Impact
Rewards are under- or over-paid whenever rates changed during the paused interval, leading to incorrect payouts and economic imbalance (theft or loss of unclaimed yield).
Recommendation
Replace the single‑rate bump with the same segmented loop used in _calculateRewardsCore, or invoke a corrected updateRewardPerTokenForValidator up to the slash timestamp so that all stored checkpoints are applied and global state advances before per‑user calculations.
Proof of Concept
Was this helpful?