Core Contracts

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

delegateBoost not applied correctly

Author Revealed upon completion

Summary

In the BoostController contract the delegateBoost function is meant to allow a user (delegator) to delegate their veRAAC balance to another user (delegatee), and the boost is supposed to be calculated later using _calculateBoost. However, due to the behavior of updateUserBoost, the delegated boost is not applied correctly.

Vulnerability Details

After user1 (the delegator) delegates their veRAAC balance to user2 (the delegatee), user2 is expected to gain a boost based on the delegated amount. However, when updateUserBoost is called for user2, it resets their boost to the minimum. This happens because:

Boost Calculation is Based on veRAAC Balance

  • The _calculateBoost function, which is called inside updateUserBoost, calculates the new boost purely based on the user's current veRAAC balance.

  • Since user2 has no veRAAC of their own (only delegated veRAAC), their calculated boost does not reflect the delegation and _calculateBoost will end up returning amount which is the MIN_BOOST=10000 .

    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;
    userBoost.lastUpdateTime = block.timestamp;
    // Update pool totals safely
    if (newBoost >= oldBoost) {
    poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
    } else {
    poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
    }
    poolBoost.workingSupply = newBoost; // Set working supply directly to new boost
    poolBoost.lastUpdateTime = block.timestamp;
    emit BoostUpdated(user, pool, newBoost);
    emit PoolBoostUpdated(
    pool,
    poolBoost.totalBoost,
    poolBoost.workingSupply
    );
    }
    function _calculateBoost(
    address user,
    address pool,
    uint256 amount
    ) internal view returns (uint256) {
    if (amount == 0) revert InvalidBoostAmount();
    if (!supportedPools[pool]) revert PoolNotSupported();
    // Get current weights without modifying state
    (
    uint256 totalWeight,
    uint256 totalVotingPower,
    uint256 votingPower
    ) = updateTotalWeight();
    // newBoost is only calculated based on user’s veRAAC balance, ignoring delegated boost.
    @> uint256 userBalance = IERC20(address(veToken)).balanceOf(user);
    uint256 totalSupply = IERC20(address(veToken)).totalSupply();
    if (userBalance == 0 || totalSupply == 0) {
    @> return amount;
    }
    // Create parameters struct for calculation
    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;
    }

Delegation Data is Not Considered

  • While delegateBoost correctly records the delegation in userBoosts[msg.sender][to], this data is not used when updating the delegatee’s actual boost.

  • The system only looks at veRAAC balance, meaning the delegation does not translate into a working boost for the recipient.

Impact

Users expecting to receive a boost from delegation will not actually benefit from it.

The pool boost calculations become incorrect, affecting reward distributions.

Tools Used

Manual Review

Recommendations

  • Modify _calculateBoost to consider delegated veRAAC balance when computing user boost.

  • Ensure updateUserBoost accounts for delegation, either by reading from userBoosts or by separately tracking delegated boosts in pool calculations.

Updates

Lead Judging Commences

inallhonesty Lead Judge 8 days ago
Submission Judgement Published
Validated
Assigned finding tags:

BoostController::_calculateBoost ignores delegated veToken amounts when calculating boost, rendering the entire delegation mechanism non-functional and breaking reward distribution

Support

FAQs

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