52955 sc high a commission rate checkpoint is not created when adding a validator despite the commission rate being set leading to loss of validator commission
Submitted on: Aug 14th 2025 at 13:32:59 UTC by @frolic for Attackathon | Plume Network
Report ID: #52955
Report Type: Smart Contract
Severity: High
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/facets/ValidatorFacet.sol
Impact summary: Permanent freezing/loss of validator commission
Brief / Intro
When adding a new validator via the addValidator function in ValidatorFacet, reward rate checkpoints are created for each reward token but no commission rate checkpoint is created even when a nonzero commission is set on the validator. This causes the validator's commission history to be empty from inception, resulting in zero commission being applied until the first explicit commission change — leading to lost commission funds.
Vulnerability Details
In addValidator, reward rate checkpoints are initialized for each reward token using the current global rate:
for (uint256 i = 0; i < rewardTokens.length; i++) {
address token = rewardTokens[i];
$.validatorLastUpdateTimes[validatorId][token] = block.timestamp;
uint256 currentGlobalRate = $.rewardRates[token];
PlumeRewardLogic.createRewardRateCheckpoint($, token, validatorId, currentGlobalRate);
}However, there is no corresponding call to createCommissionRateCheckpoint to record the validator's starting commission. The validator struct does set the commission value:
validator.validatorId = validatorId;
@> validator.commission = commission; // <--- validator commission
validator.delegatedAmount = 0;Because no commission checkpoint is created at initialization, the checkpoint array for the validator is empty. Commission calculations use checkpointed history; an empty checkpoint history effectively results in a zero commission rate for the initial period. Thus the validator accrues no commission until a later explicit commission update creates a checkpoint.
Additionally, setValidatorCommission has an early return when the new commission equals the old commission, preventing creation of a checkpoint if one does not already exist but the desired starting commission equals the stored value:
function setValidatorCommission(
uint16 validatorId,
uint256 newCommission
) external onlyValidatorAdmin(validatorId) {
PlumeStakingStorage.Layout storage $ = PlumeStakingStorage.layout();
PlumeStakingStorage.ValidatorInfo storage validator = $.validators[validatorId];
//...................
// If the commission rate is the same, there's nothing to do.
if (oldCommission == newCommission) {
return;
}This means a validator whose commission was set at creation cannot force the creation of the missing checkpoint by calling setValidatorCommission if they attempt to set the same value — the function will return early and leave the checkpoint array empty.
Impact Details
No commission checkpoint at validator initialization causes the validator's commission rate to be treated as zero for the initial period.
Validator accrues no commission on rewards distributed before the first commission change that creates a checkpoint.
Commission that should have been accrued during this period is permanently lost.
Protocol commission accounting is incorrect from inception for affected validators until corrected.
Proof of Concept
Step: Validator attempts to claim commission
The validator calls
requestCommissionClaimfor a reward token.The contract settles commission, but because no commission checkpoint exists for the initial period, the commission rate defaults to zero.
Result: validator's accrued commission is zero despite user rewards.
References
ValidatorFacet.sol - addValidator function: https://github.com/plumenetwork/contracts/blob/fe67a98fa4344520c5ff2ac9293f5d9601963983/plume/src/facets/ValidatorFacet.sol#L147
PlumeRewardLogic.sol - createCommissionRateCheckpoint: https://github.com/plumenetwork/contracts/blob/fe67a98fa4344520c5ff2ac9293f5d9601963983/plume/src/lib/PlumeRewardLogic.sol#L757
Was this helpful?