Summary
The current implementation incorrectly calculates the boost multiplier by using the already boosted amount as both numerator and denominator. This results in the maximum boost value always being returned regardless of the actual user position.
Vulnerability Details
Due to flawed formula implementation, the getBoostMultiplier function incorrectly returned the maximum boost (2.5x) regardless of the user's actual position.
Root Cause:
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/boost/BoostController.sol#L282
function getBoostMultiplier(
address user,
address pool
) external view override returns (uint256) {
if (!supportedPools[pool]) revert PoolNotSupported();
UserBoost storage userBoost = userBoosts[user][pool];
if (userBoost.amount == 0) return MIN_BOOST;
uint256 baseAmount = userBoost.amount * 10000 / MAX_BOOST;
return userBoost.amount * 10000 / baseAmount;
}
Impact
Always returns MAX_BOOST resulting in wrong boost calculation
Recommendations
Handle how Curve handles it as stated in the documentation.
function getBoostMultiplier(
address user,
address pool
) external view override returns (uint256) {
if (!supportedPools[pool]) revert PoolNotSupported();
(uint256 totalWeight, uint256 totalVotingPower, uint256 votingPower) = updateTotalWeight();
uint256 userVotingPower = veToken.getVotingPower(user, block.timestamp);
PoolBoost storage poolBoost = poolBoosts[pool];
UserBoost storage userBoost = userBoosts[user][pool];
if (userBoost.amount == 0) return MIN_BOOST;
uint256 D = poolBoost.workingSupply;
uint256 d = userBoost.amount;
uint256 v = userVotingPower;
uint256 V = totalVotingPower;
if (D == 0 || V == 0 || d == 0) return MIN_BOOST;
uint256 boost = (150 * D * v) / (V * d) + 10000;
return boost > MAX_BOOST ? MAX_BOOST : (boost < MIN_BOOST ? MIN_BOOST : boost);
}