Core Contracts

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

Incorrect workingSupply Update in updateUserBoost

Summary

In the BoostController::updateUserBoost function, the contract incorrectly updates the pool’s workingSupply by setting it to the user’s new boost value (newBoost) instead of summing the total working supply including boosts. This results in an incorrect workingSupply value, which is critical for reward calculations.

Details

First, from IBoostController we see that PoolBoost struct is as follows:

/**
* @notice Struct to track pool-wide boost metrics
* @param totalBoost The total boost amount for the pool
* @param workingSupply The total working supply including boosts
* @param baseSupply The base supply without boosts
* @param lastUpdateTime The last time pool boosts were updated
*/
struct PoolBoost {
uint256 totalBoost;
uint256 workingSupply;
uint256 baseSupply;
uint256 lastUpdateTime;
}

Here, this comment is critical for us: ** @param workingSupply The total working supply including boosts*

Now, let’s look at BoostController::updateUserBoost:

/**
* @notice Updates the boost value for a user in a specific pool
* @param user Address of the user whose boost is being updated
* @param pool Address of the pool for which to update the boost
* @dev Calculates new boost based on current veToken balance and updates pool totals
*/
function updateUserBoost(
address user,
address pool
) external override nonReentrant whenNotPaused {
if (paused()) revert EmergencyPaused();
if (user == address(0)) revert InvalidPool(); //@audit low wrong revert statement
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);
}
//@audit WRONG!
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
);
}

As you can see, the whole pool’s poolBoost is set as this one individual user’s newBoost, which is wrong since the poolBoost’s workingSupply should be The total working supply including boosts.

Following this error, the event PoolBoostUpdated is also emitted with the wrong poolBoost.workingSupply value.

Impact

  • State Corruption: The pool’s workingSupply no longer reflects the true total boost in the pool.

  • Misleading Events: Off-chain systems relying on the PoolBoostUpdated event will receive incorrect data.

Recommendation

newBoost should be included in poolBoost.workingSupply, not replace it.

Updates

Lead Judging Commences

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

BoostController::updateUserBoost overwrites workingSupply with single user's boost value instead of accumulating, breaking reward multipliers and allowing last updater to capture all benefits

Support

FAQs

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