Summary
In the BoostController contract, the calculateBoost() and _calculateBoost() functions calculate a user's boost without capping the boosted amount when it falls below the MIN_BOOST or exceeds the MAX_BOOST. This uncapped calculation can lead to inaccurate boost allocations, potentially impacting reward distribution and governance weight calculations.
Vulnerability Details
Missing Cap in _calculateBoost and calculateBoost
Both _calculateBoost() and calculateBoost() functions are responsible for calculating the boosted amount for a user in a pool. However, they lack proper boundary checks for the calculated boost:
_calculateBoost() does not cap the boosted amount to MIN_BOOST, though it caps the maximum boosted amount to a user-specified limit.
calculateBoost() entirely omits capping for both MIN_BOOST and MAX_BOOST.
This could lead to scenarios where a user receives a boost below the allowed MIN_BOOST, affecting their reward share negatively or, conversely, exceeding the MAX_BOOST, giving them an unfair advantage.
BoostController::_calculateBoost:
function _calculateBoost(address user, address pool, uint256 amount) internal view returns (uint256) {
if (amount == 0) revert InvalidBoostAmount();
if (!supportedPools[pool]) revert PoolNotSupported();
(uint256 totalWeight, uint256 totalVotingPower, uint256 votingPower) = updateTotalWeight();
uint256 userBalance = IERC20(address(veToken)).balanceOf(user);
uint256 totalSupply = IERC20(address(veToken)).totalSupply();
if (userBalance == 0 || totalSupply == 0) {
return amount;
}
BoostCalculator.BoostParameters memory params = BoostCalculator.BoostParameters({
maxBoost: boostState.maxBoost,
minBoost: boostState.minBoost,
boostWindow: boostState.boostWindow,
totalWeight: totalWeight,
totalVotingPower: totalVotingPower,
votingPower: votingPower
});
(uint256 boostBasisPoints, uint256 boostedAmount) =
BoostCalculator.calculateTimeWeightedBoost(params, userBalance, totalSupply, amount);
@>
if (boostedAmount < amount) {
return amount;
}
uint256 maxBoostAmount = amount * MAX_BOOST / 10000;
if (boostedAmount > maxBoostAmount) {
return maxBoostAmount;
}
return boostedAmount;
}
BoostController::calculateBoost:
function calculateBoost(address user, address pool, uint256 amount)
external
view
override
returns (uint256 boostBasisPoints, uint256 boostedAmount)
{
if (!supportedPools[pool]) revert UnsupportedPool();
(uint256 totalWeight, uint256 totalVotingPower, uint256 votingPower) = updateTotalWeight();
uint256 userVotingPower = veToken.getVotingPower(user, block.timestamp);
BoostCalculator.BoostParameters memory params = BoostCalculator.BoostParameters({
maxBoost: boostState.maxBoost,
minBoost: boostState.minBoost,
boostWindow: boostState.boostWindow,
totalWeight: totalWeight,
totalVotingPower: totalVotingPower,
votingPower: votingPower
});
return BoostCalculator.calculateTimeWeightedBoost(params, userVotingPower, totalVotingPower, amount);
@>
}
Proof of Concept
Scenario 1: Boost Below MIN_BOOST
Alice delegates her tokens to a pool with a small amount.
Her calculated boost falls below the defined MIN_BOOST due to her low voting power.
Since _calculateBoost() does not cap to MIN_BOOST, Alice’s effective boost is lower than intended, reducing her rewards.
Scenario 2: Boost Above MAX_BOOST
Bob, a whale, delegates a significant amount of tokens.
The calculated boost exceeds the MAX_BOOST limit.
In calculateBoost(), there is no capping mechanism, resulting in an unfairly high boost for Bob.
Impact
Users could receive boosts lower than MIN_BOOST, impacting their reward distribution.
Users could exceed the MAX_BOOST, leading to governance and reward distribution imbalances.
Tools Used
Recommendations
Fix for _calculateBoost
- if (boostedAmount < amount) {
- return amount;
+ if (boostedAmount < MIN_BOOST) {
+ return MIN_BOOST;
}
Fix for calculateBoost
+ if (boostedAmount < MIN_BOOST) {
+ boostedAmount = MIN_BOOST;
+ } else if (boostedAmount > MAX_BOOST) {
+ boostedAmount = MAX_BOOST;
+ }
These changes ensure that the calculated boost stays within the allowed range, maintaining protocol stability and fairness.