Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

Incorrect Boost Calculation in BoostController

Summary

In BoostController.sol, the _calculateBoost function uses a hardcoded base amount of 10000, leading to incorrect boost scaling. This results in improper reward distribution as boosts aren't calculated based on the actual staked amount.

Vulnerability Details

The problem in _calculateBoost is: BoostController.sol#_calculateBoost

function _calculateBoost(
address user,
address pool,
uint256 amount
) internal view returns (uint256) {
// ... the validation checks ...
(uint256 totalWeight, uint256 totalVotingPower, uint256 votingPower) = updateTotalWeight();
uint256 userBalance = IERC20(address(veToken)).balanceOf(user);
uint256 totalSupply = IERC20(address(veToken)).totalSupply();

Impact

  • The function uses raw balances instead of voting power for calculations: #L99

uint256 userBalance = IERC20(address(veToken)).balanceOf(user);

Should use veToken.getVotingPower(user) to account for lock duration.

BoostCalculator.BoostParameters memory params = BoostCalculator.BoostParameters({
maxBoost: boostState.maxBoost,
minBoost: boostState.minBoost,
boostWindow: boostState.boostWindow,
totalWeight: totalWeight,
totalVotingPower: totalVotingPower,
votingPower: votingPower
});

Doesn't properly scale with the input amount parameter.

  • The max boost cap calculation: #L126

uint256 maxBoostAmount = amount * MAX_BOOST / 10000;

Uses a fixed divisor of 10000 which doesn't align with the voting power scale.

Tools Used

Manual Review

Recommendations

We should replace the hardcoded value of 10000 with the actual staked amount or relevant parameter. This would make the boost calculation more accurate and fair.

function _calculateBoost(
address user,
address pool,
uint256 stakedAmount // Use actual staked amount
) internal view returns (uint256) {
if (stakedAmount == 0) revert InvalidBoostAmount();
if (!supportedPools[pool]) revert PoolNotSupported();
(uint256 totalWeight, uint256 totalVotingPower, uint256 votingPower) = updateTotalWeight();
uint256 userVotingPower = veToken.getVotingPower(user);
uint256 totalVeSupply = veToken.getTotalVotingPower();
BoostCalculator.BoostParameters memory params = BoostCalculator.BoostParameters({
maxBoost: boostState.maxBoost,
minBoost: boostState.minBoost,
boostWindow: boostState.boostWindow,
totalWeight: totalWeight,
totalVotingPower: totalVotingPower,
votingPower: votingPower
});
uint256 boost = BoostCalculator.calculateBoost(
userVotingPower,
totalVeSupply,
params
);
return (stakedAmount * boost) / PRECISION; // <
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BoostController::updateUserBoost uses hardcoded 10000 base amount, storing basis points instead of actual boosted amount

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BoostController::updateUserBoost uses hardcoded 10000 base amount, storing basis points instead of actual boosted amount

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.