Core Contracts

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

poolBoost.workingSupply is overwritten every updateUserBoost() call

Author Revealed upon completion

Summary

The updateUserBoost function, in BoostController contract, incorrectly updates the pool’s working supply by directly setting it to the new boost amount instead of properly adjusting it. This results in incorrect calculations of the pool’s total boost, potentially leading to inaccurate reward distributions and unfair advantages for certain users.

Vulnerability Details

The issue arises because poolBoost.workingSupply is being set directly to newBoost, rather than adjusting the total boost based on the changes in individual users' boosts. This means that when multiple users are contributing boosts to the same pool, each update call could effectively erase previous contributions, leaving only the last updated user's boost reflected in the pool’s working supply.

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;
uint256 newBoost = _calculateBoost(user, pool, 10000);
userBoost.amount = newBoost;
userBoost.lastUpdateTime = block.timestamp;
if (newBoost >= oldBoost) {
poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
} else {
poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
}
@> poolBoost.workingSupply = newBoost; // Overwrites previous boost, ignoring other users' contributions
poolBoost.lastUpdateTime = block.timestamp;
emit BoostUpdated(user, pool, newBoost);
emit PoolBoostUpdated(
pool,
poolBoost.totalBoost,
poolBoost.workingSupply
);
}

Impact

  • Users who updated their boosts earlier might see their contributions completely erased, since poolBoost.workingSupply is overwritten instead of adjusted.

  • Rewards distribution, liquidity incentives, or governance mechanisms relying on boost calculations may become inaccurate and unfair.

  • Attackers could potentially manipulate the system by repeatedly updating their boost and overriding the entire pool’s working supply.

Tools Used

Manual Review

Recommendations

Instead of directly setting poolBoost.workingSupply = newBoost, the function should adjust the existing working supply by adding or subtracting the difference in boost values:

poolBoost.workingSupply = poolBoost.workingSupply + (newBoost - oldBoost)
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 days 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

RToken::burn incorrectly calculates amountScaled using rayMul instead of rayDiv, causing incorrect token burn amounts and breaking the interest accrual mechanism

inallhonesty Lead Judge 10 days 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

RToken::burn incorrectly calculates amountScaled using rayMul instead of rayDiv, causing incorrect token burn amounts and breaking the interest accrual mechanism

Support

FAQs

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