51171 sc insight redundant storage reads and unnecessary checks in reward rate checkpoint logic lead to inefficient gas usage
Submitted on Jul 31st 2025 at 18:10:39 UTC by @farman1094 for Attackathon | Plume Network
Report ID: #51171
Report Type: Smart Contract
Report severity: Insight
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/lib/PlumeRewardLogic.sol
Impacts: Inefficient gas usage due to redundant storage reads and unnecessary checks
Description
Brief / Intro
There is an optimization opportunity in the PlumeRewardLogic contract: the validator reward rate checkpoint array is read from storage multiple times within the same execution path. Additionally, a zero-length check is performed in a child function while the parent function already guarantees existence. These issues do not affect correctness, but they cause unnecessary SLOADs which increase gas costs.
Vulnerability Details
In getEffectiveRewardRateAt and findRewardRateCheckpointIndexAtOrBefore functions, the code repeatedly retrieves the checkpoints from storage:
PlumeStakingStorage.RateCheckpoint[] storage checkpoints = $.validatorRewardRateCheckpoints[validatorId][token];These arrays are only read (no writes). Reading them once into memory in the parent and passing the memory array to the child would avoid repeated SLOADs:
PlumeStakingStorage.RateCheckpoint[] memory checkpoints = $.validatorRewardRateCheckpoints[validatorId][token];
uint256 idx = findRewardRateCheckpointIndexAtOrBefore(checkpoints, timestamp);Also, the child currently contains a redundant zero-length check:
if (len == 0) {
return 0; // Indicates no checkpoints, caller might use global rate.
}If the parent already verifies that checkpoints exist, this check is unnecessary and can be removed to improve clarity and efficiency.
Impact Details
No direct exploit leads to fund theft or privilege escalation. The issue increases gas consumption for callers interacting with staking-related functions, reducing efficiency and potentially increasing user costs.
Proof of Concept
PoC — call path leading to redundant reads
A user (or contract) calls a function that ends up calling:
getEffectiveRewardRateAt($, validatorId, token, timestamp)Inside getEffectiveRewardRateAt, the checkpoints are read from storage:
PlumeStakingStorage.RateCheckpoint[] storage checkpoints = $.validatorRewardRateCheckpoints[validatorId][token];This SLOAD costs gas proportional to the array size.
References
Target file: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/lib/PlumeRewardLogic.sol
Was this helpful?