52931 sc high validators can not claim their commissions after the reward token removal
Submitted on Aug 14th 2025 at 11:49:48 UTC by @Slayer for Attackathon | Plume Network
Report ID: #52931
Report Type: Smart Contract
Report severity: High
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/facets/ValidatorFacet.sol
Impacts:
Permanent freezing of funds
Description
Brief/Intro
The requestCommissionClaim function prevents validators from claiming earned commission after a reward token has been removed from the system. While the removeRewardToken function correctly updates accrued validator commissions, the commission claim process requires the token to still be active, creating a permanent lock on validator funds.
Vulnerability Details
When a reward token is removed:
// RewardsFacet.sol:removeRewardToken()
for (uint256 i = 0; i < $.validatorIds.length; i++) {
uint16 validatorId = $.validatorIds[i];
// Final update to current time to settle all rewards up to this point
PlumeRewardLogic.updateRewardPerTokenForValidator($, token, validatorId);
// Create a final checkpoint with a rate of 0 to stop further accrual definitively
PlumeRewardLogic.createRewardRateCheckpoint($, token, validatorId, 0);
}
// Update the mapping
$.isRewardToken[token] = false;The function correctly:
Settles all pending validator commission up to the removal timestamp
Stores commission in
$.validatorAccruedCommission[validatorId][token]Sets
$.isRewardToken[token] = false
However, when validators try to claim this commission:
function requestCommissionClaim(
uint16 validatorId,
address token
)
external
onlyValidatorAdmin(validatorId)
nonReentrant
_validateValidatorExists(validatorId)
_validateIsToken(token)The _validateIsToken modifier implementation:
modifier _validateIsToken(address token) {
if (!PlumeStakingStorage.layout().isRewardToken[token]) {
revert TokenDoesNotExist(token);
}
_;
}Problem flow:
While users can claim rewards from removed tokens because RewardsFacet._validateTokenForClaim() includes fallback logic:
if (!isActive) {
// Check if there are previously earned/stored rewards
// ... fallback logic for removed tokens
if (!hasRewards) {
revert TokenDoesNotExist(token);
}
}validators have no such mechanism.
Impact Details
Validators lose all accrued commission from removed reward tokens — effectively permanent freezing of those funds.
Proof of Concept
Call
removeRewardToken(...)→$.isRewardToken[token] = false.Validator commission is updated and stored in
$.validatorAccruedCommission[validatorId][token].
Validator calls
requestCommissionClaim(validatorId, removedToken)._validateIsToken(removedToken)checks$.isRewardToken[removedToken]→ returnsfalse.Function reverts with
TokenDoesNotExist(token), blocking the claim.
References
(Add any relevant links to documentation or code)
Was this helpful?