50168 sc insight unused and duplicated functions should be removed from rewardsfacet and stakingfacet
Submitted on Jul 22nd 2025 at 08:30:52 UTC by @Vanshika for Attackathon | Plume Network
- Report ID: #50168
- Report Type: Smart Contract
- Report severity: Insight
- Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/facets/RewardsFacet.sol
Description
Brief / Intro
RewardsFacet contains two internal functions that are duplicates. One of the duplicates is never used and should be removed. There are other duplicate unused functions in StakingFacet.
Vulnerability Details
- In
RewardsFacet,_earned()and_earnedView()are duplicates of one another. _calculateTotalEarned()(which callsearned()) is a duplicate of_calculateTotalEarnedView()(which calls_earnedView())._calculateTotalEarnedView()is called byearned()andgetClaimableReward()which are both external and identical — one of them can be safely removed._calculateTotalEarned()and_earned()are never called; only their duplicates are used, so they should be removed.- In
StakingFacet:_calculateAndClaimAllRewards()is never used. A similar function_calculateAndClaimAllRewardsWithCleanup()is used duringrestakeRewards()and contains all logic in_calculateAndClaimAllRewards(), making the former redundant._performRestakeWorkflow()is another internal function that is never called._performStakeSetup()contains all logic in_performRestakeWorkflow()and is called during staking and restaking.- Other unused functions include
_updateCommissionClaim(),_updateRewardClaim(), and_updateWithdrawalAmounts().
Impact Details
Multiple duplicate and unused functions across the facets lead to bloated code. Removing dead/duplicate code reduces maintenance burden and improves readability. There is no functional exploit described — the report is an insight to improve code quality.
Proof of Concept
Some duplicated code from RewardsFacet (excerpt):
Proof-of-concept code (click to expand)
// --- Internal View Function (_earned) ---
function _earned(address user, address token, uint16 validatorId) internal returns (uint256 rewards) {
PlumeStakingStorage.Layout storage $ = PlumeStakingStorage.layout();
uint256 userStakedAmount = $.userValidatorStakes[user][validatorId].staked;
if (userStakedAmount == 0) {
return $.userRewards[user][validatorId][token];
}
(uint256 userRewardDelta,,) =
PlumeRewardLogic.calculateRewardsWithCheckpoints($, user, validatorId, token, userStakedAmount);
rewards = $.userRewards[user][validatorId][token] + userRewardDelta;
return rewards;
}
// --- View-only helper functions ---
function _earnedView(address user, address token, uint16 validatorId) internal view returns (uint256 rewards) {
PlumeStakingStorage.Layout storage $ = PlumeStakingStorage.layout();
uint256 userStakedAmount = $.userValidatorStakes[user][validatorId].staked;
if (userStakedAmount == 0) {
return $.userRewards[user][validatorId][token];
}
(uint256 userRewardDelta,,) =
PlumeRewardLogic.calculateRewardsWithCheckpointsView($, user, validatorId, token, userStakedAmount);
rewards = $.userRewards[user][validatorId][token] + userRewardDelta;
return rewards;
}
function _calculateTotalEarned(address user, address token) internal returns (uint256 totalEarned) {
PlumeStakingStorage.Layout storage $ = PlumeStakingStorage.layout();
uint16[] memory validatorIds = $.userValidators[user];
// Sum across all validators
for (uint256 i = 0; i < validatorIds.length; i++) {
uint16 validatorId = validatorIds[i];
// _earned correctly handles all validator states (active, inactive, slashed)
// by calling calculateRewardsWithCheckpoints, which respects the slashedAtTimestamp.
totalEarned += _earned(user, token, validatorId);
}
return totalEarned;
}
function _calculateTotalEarnedView(address user, address token) internal view returns (uint256 totalEarned) {
PlumeStakingStorage.Layout storage $ = PlumeStakingStorage.layout();
uint16[] memory validatorIds = $.userValidators[user];
for (uint256 i = 0; i < validatorIds.length; i++) {
uint16 validatorId = validatorIds[i];
// _earnedView correctly handles all validator states (active, inactive, slashed)
// by calling calculateRewardsWithCheckpointsView, which respects the slashedAtTimestamp.
totalEarned += _earnedView(user, token, validatorId);
}
return totalEarned;
}
function earned(address user, address token) external view returns (uint256) {
return _calculateTotalEarnedView(user, token);
}
function getClaimableReward(address user, address token) external view returns (uint256) {
return _calculateTotalEarnedView(user, token);
}
Recommendations (implicit)
- Remove unused duplicate functions in
RewardsFacet(_earned,_calculateTotalEarned, etc.) and inStakingFacet(_calculateAndClaimAllRewards,_performRestakeWorkflow,_updateCommissionClaim,_updateRewardClaim,_updateWithdrawalAmounts, etc.). - Keep only the view or non-view variant that is actually used, and ensure external functions are not duplicated (e.g., remove either
earned()orgetClaimableReward()if they are semantically identical and redundant).
(Note: No additional code changes are suggested in this report beyond removing unused/duplicate functions to reduce code bloat.)