52464 sc high commission rounding mismatch under payment bug

Submitted on Aug 11th 2025 at 01:06:24 UTC by @BeastBoy for Attackathon | Plume Network

  • Report ID: #52464

  • Report Type: Smart Contract

  • Report severity: High

  • Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/plume/src/lib/PlumeRewardLogic.sol

  • Impacts: Protocol insolvency

Summary

A rounding mismatch between how validator commission and per-user commission are computed causes a systematic loss (burn) of reward funds. Validator commission uses floor division while user commission uses ceiling division. The mismatch means Σ(user commissions) > validator’s accrued commission, permanently shaving small amounts from the reward pool every settlement.

Description

In updateRewardPerTokenForValidator the validator’s commission is calculated with floor division:

uint256 grossReward = (totalStaked * rewardPerTokenIncrease) / REWARD_PRECISION;
uint256 commissionDeltaForValidator = (grossReward * commissionRateForSegment) / REWARD_PRECISION;
validatorAccruedCommission += commissionDeltaForValidator;

This rounds down. In _calculateRewardsCore, each user’s commission is charged with ceiling division:

uint256 grossUser = (userStake * rewardPerTokenDelta) / REWARD_PRECISION;
uint256 commissionForThisSegment = _ceilDiv(grossUser * effectiveCommissionRate, REWARD_PRECISION);
totalCommissionAmountDelta += commissionForThisSegment;

Rounding up per-staker but rounding down for the validator’s total introduces a consistent deficit: users collectively pay more commission than the validator receives. No reconciliation logic restores the difference.

Example: two stakers with a 1 Wei reward can cause users to pay 2 Wei of commission while the validator receives 0 Wei — permanently removing 2 Wei from the pool.

Impact

Recommendation

Use the same rounding rule on both sides so that Σ(user commissions) exactly equals the validator’s accrued commission. Concretely:

  • Replace the validator’s floor division with a ceiling (_ceilDiv) to match per-user rounding, or

  • Switch users to floor division (remove per-user ceil) to match the validator-side floor.

Either approach removes the systematic bias; prefer the approach consistent with intended economic behavior and documented invariants.

Proof of Concept

Show PoC (Forge test demonstrating discrepancy and a fixed variant)

Notes

  • Do not change any external links or query parameters; the target file link above is preserved as-is.

  • The core issue is a deterministic arithmetic rounding inconsistency — fixing it requires making rounding symmetric between the aggregated validator calculation and per-user calculations.

Was this helpful?