The BoostState in the veRAACToken contract is updated with a single user's voting power instead of the global total.
The Issue is in _updateBoostState function in the veRAACToken contract and BoostCalculator.calculateTimeWeightedBoost function.
_updateBoostState is called in lock, increase, and extend after modifying a user’s lock.
Update:
_updateBoostState sets:
_boostState.votingPower = _votingState.calculatePowerAtTimestamp(user, block.timestamp) → Individual user’s current voting power
_boostState.totalVotingPower = totalSupply() → Total veRAAC supply (system-wide voting power)
_boostState.totalWeight = _lockState.totalLocked → Total locked RAAC tokens
Each call overwrites votingPower with the latest user’s power, not an aggregate or system total.
Boost Calculation:
calculateTimeWeightedBoost passes BoostState.votingPower into params.votingPower, but calculateBoost doesn’t use params.votingPower. It uses veBalance (user’s current balance) and totalVeSupply (passed as totalSupply).
Thus, while votingPower is incorrectly set to a single user’s value, it doesn’t directly affect the boost calculation in the current implementation.
BoostState has both votingPower and totalVotingPower, suggesting votingPower might have been intended for per-user tracking. However, it’s not used in calculateBoost.
If votingPower were meant to be the system total, the bug would cause params.votingPower to reflect one user’s power, but this isn’t the case here.
this doesn’t currently affect boost calculations because calculateBoost ignores params.votingPower, relying on veBalance and totalVeSupply.
Modify the _updateBoostState function to use totalSupply() instead of balanceOf(user) when updating BoostState.totalVotingPower.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.