Summary
The working supply for pools is incorrectly overwritten with individual user boost amounts rather than being aggregated. Each call to updateUserBoost() replaces the entire pool's working supply with the single user's latest boost value.
Affected File: contracts/core/governance/boost/BoostController.sol
Function: updateUserBoost()
Error
// ❌ Incorrect implementation
poolBoost.workingSupply = newBoost; // Overwrites total with single user's value
// ✅ Correct implementation should be:
poolBoost.workingSupply += (newBoost - oldBoost);
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/boost/BoostController.sol#L198
function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
PoolBoost storage poolBoost = poolBoosts[pool];
uint256 oldBoost = userBoost.amount;
uint256 newBoost = _calculateBoost(user, pool, 10000);
userBoost.amount = newBoost;
poolBoost.workingSupply = newBoost;
}
Impact
The Working Supply of the pool will be affected!
Example Scenario:
Pool Parameters
Total rewards: 1000 tokens
2 users with identical 1x boosts (10,000 basis points each)
Correct total working supply should be: 10,000 + 10,000 = 20,000
Faulty Calculation
After User1 update: WorkingSupply = 10,000
After User2 update: WorkingSupply = 10,000 (overwrites previous)
Total working supply = 10,000 (while actual boosts sum to 20,000)
Recommendations
function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
if (newBoost >= oldBoost) {
poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
poolBoost.workingSupply = poolBoost.workingSupply + (newBoost - oldBoost);
} else {
poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
poolBoost.workingSupply = poolBoost.workingSupply - (oldBoost - newBoost);
}
}