Core Contracts

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

When updating a user's boost in BoostController.sol, it will always return the boost multiplier instead of the correct boosted amount

Summary

A user's boost is calculated based on the current veRAAC Token balance and the boost multiplier that is represented in basis points. In BoostController.sol, updateUserBoost() will calculate a user's new boosted amount. However, due to hardcoding the input to pass in _calculateBoost()function that is being called internally, it will result in the updateUserBoost()function to return the boost multiplier instead.

Vulnerability Details

function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
if (paused()) revert EmergencyPaused();
if (user == address(0)) revert InvalidPool();
if (!supportedPools[pool]) revert PoolNotSupported();
UserBoost storage userBoost = userBoosts[user][pool];
PoolBoost storage poolBoost = poolBoosts[pool];
uint256 oldBoost = userBoost.amount;
// Calculate new boost based on current veToken balance
uint256 newBoost = _calculateBoost(user, pool, 10000); // Base amount
userBoost.amount = newBoost;

In the above function from BoostController.sol, line 11 calls _calculateBoost()- for the last parameter, 10000 is hardcoded.

BoostController._calculateBoost()

In the snippet below from BoostController._calculateBoost(), it now passes 10000 to be passed in BoostCalculator.calculateTimeWeightedBoost().

(uint256 boostBasisPoints, uint256 boostedAmount) = BoostCalculator.calculateTimeWeightedBoost(
params,
userBalance,
totalSupply,
amount
);

BoostCalculator.calculateTimeWeightedBoost()

Now in the function below, 10000 is passed as amount. In line 22, the boost basis points is being calculated via calculateBoost(). This returns basis points in the range of 10000 to 25000, as defined by the minimum and maximum basis points for boost multiplier.

Next in line 29, it now calculates the boostedAmount, taking (10000 * boostBasisPoints) / 10000, hence resulting in boostedAmount = boostBasisPoints.

function calculateTimeWeightedBoost(
BoostState storage state,
uint256 userBalance,
uint256 totalSupply,
uint256 amount
) internal view returns (uint256 boostBasisPoints, uint256 boostedAmount) {
if (totalSupply == 0 || amount == 0) {
return (0, amount);
}
// Create parameters struct for calculation
BoostParameters memory params = BoostParameters({
maxBoost: state.maxBoost,
minBoost: state.minBoost,
boostWindow: state.boostWindow,
totalWeight: state.totalWeight,
totalVotingPower: state.totalVotingPower,
votingPower: state.votingPower
});
// Get boost multiplier in basis points (e.g., 10000 = 1x, 25000 = 2.5x)
boostBasisPoints = calculateBoost(
userBalance,
totalSupply,
params
);
// Calculate boosted amount: amount * (boost / 10000)
boostedAmount = (amount * boostBasisPoints) / 10000;
return (boostBasisPoints, boostedAmount);
}

Now back to BoostController.updateUserBoost(), the userBoost.amount = newBoost, which is the boostedAmount.

[updateUserBoost](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/boost/BoostController.sol#L189)

userBoost.amount = newBoost;

This hence stores the boostBasisPoints as the total boost of the user, rather than storing the actual boosted amount.

Impact

User's boosted amount is incorrectly stored. The user's boost multiplier is stored in basis points instead. This will gravely affect the calculation of boosted amount for users.

Tools Used

Manual

Recommendations

Ensure the amountpassed in BoostController._calculateBoost() is not hardcoded and instead passes the user's actual base amount before being multiplied by boost multiplier.

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.