Core Contracts

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

Incorrect workingSupply Calculation in updateUserBoost

Summary

The poolBoost.workingSupply in updateUserBoost function is being set to the user's new boost amount instead of the total boosted weight within the pool. Every time an user updates their boost, poolBoost.workingSupply is reset to only that user's new boost instead of aggregating all active users’ boosts.

Vulnerability Details

According to the comment,

https://github.com/Cyfrin/2025-02-raac/blob/main/contracts/interfaces/core/governance/IBoostController.sol#L27

* @param workingSupply The total working supply including boosts

In updateUserBoost, the function

1 - Gets the storage of PoolBoost and UserBoost.

2- Gets the user's oldBoost from userBoost.amount.

3 -Calculate the newBoost of the user.

4- Updates the userBoost.amount with newBoost

5 - Incorrectly updates the poolBoost.workingSupply with the user's newBoost.

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

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

Impact

  1. The function getPoolBoost returns wrong workingSupply value. If the poolBoost.workSupply is used for reward distribution in the future, then could lead to inaccurate boost calculations for users, impacting their reward distribution.

  2. If updateUserBoost is to update the user's workingSupply after delegateBoost, the recipient's workingSupply keeps being replaced instead of aggregating every delegateBoost.

Tools Used

Manual code review

Recommended Mitigation

It should properly update the workingSupply like how totalBoost is updated:

PoolBoost storage poolBoost = poolBoosts[pool];
if (newBoost >= oldBoost) {
poolBoost.workingSupply += (newBoost - oldBoost);
} else {
poolBoost.workingSupply -= (oldBoost - newBoost);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 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.

Give us feedback!