Core Contracts

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

Incorrect Boost Multiplier Calculation in BoostController Contract Leading to Wrong Results

https://github.com/Cyfrin/2025-02-raac/blob/main/contracts/core/governance/boost/BoostController.sol#L282-L293

Summary

The getBoostMultiplier function in the BoostController contract contains a logical error in calculating the boost multiplier. The function will always return MAX_BOOST (25000) if userBoost.amount > 0, which is incorrect.


Vulnerability Details

Explanation

The getBoostMultiplier function is designed to calculate a boost multiplier for a user in a specific pool. However, the calculation logic is flawed:

  1. Flawed Calculation:

    • The function calculates baseAmount as userBoost.amount * 10000 / MAX_BOOST.

    • It then returns userBoost.amount * 10000 / baseAmount, which simplifies to MAX_BOOST because baseAmount is proportional to userBoost.amount.

  2. Always Returns MAX_BOOST:

    • If userBoost.amount > 0, the function will always return MAX_BOOST (25000), regardless of the actual boost amount.

Root Cause in the Contract Function

The issue lies in the following lines of the getBoostMultiplier function:

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;
// Calculate actual boost multiplier in basis points
@> uint256 baseAmount = userBoost.amount * 10000 / MAX_BOOST;
@> return userBoost.amount * 10000 / baseAmount;
}

The calculation userBoost.amount * 10000 / baseAmount simplifies to MAX_BOOST because baseAmount is derived from userBoost.amount.


Proof of Concept

Scenario Example

  1. User with Boost: A user has a boost amount of 1000.

  2. Flawed Calculation:

    • baseAmount = 1000 * 10000 / 25000 = 400.

    • The function returns 1000 * 10000 / 400 = 25000 (MAX_BOOST).

  3. Incorrect Result: The function always returns MAX_BOOST if userBoost.amount > 0.


Impact

  • Incorrect Boost Multipliers: The function always returns MAX_BOOST if userBoost.amount > 0, leading to incorrect boost calculations.

  • Operational Inefficiency: The protocol's boost mechanism becomes unreliable, as it cannot provide meaningful boost multipliers based on the user's boost amount. Therefore, other contracts depending on reading states from this function will be given false results.


Tools Used

  • Manual Code Review


Recommendations

  1. Fix the Boost Multiplier Calculation:

    • Implement a correct calculation for the boost multiplier. For example, the boost multiplier could be proportional to the user's boost amount relative to MAX_BOOST.

    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;
    // Calculate boost multiplier as a proportion of MAX_BOOST
    uint256 boostMultiplier = MIN_BOOST + (userBoost.amount * (MAX_BOOST - MIN_BOOST)) / MAX_BOOST;
    return boostMultiplier;
    }

    This formula ensures that:

    • If userBoost.amount = 0, the multiplier is MIN_BOOST.

    • If userBoost.amount = MAX_BOOST, the multiplier is MAX_BOOST.

    • For values in between, the multiplier scales linearly.

Updates

Lead Judging Commences

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

BoostController::getBoostMultiplier always returns MAX_BOOST for any non-zero boost due to mathematical calculation error, defeating the incentive mechanism

Support

FAQs

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