Core Contracts

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

Pool Working Supply Overwrite in BoostController Breaks Accounting

Summary

The updateUserBoost function in BoostController incorrectly resets the pool's working supply instead of maintaining a cumulative total of all users' boosts, this breaks the pool's boost accounting system and affecting reward calculations.

Vulnerability Details

The workingSupply should track total working supply for all users in the pool but, the current implementation overwrites entire working supply with single user's boost

Each call erases previous users' contributions which contradicts the documented behavior: "workingSupply: The total working supply including boosts"

function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
UserBoost storage userBoost = userBoosts[user][pool];
PoolBoost storage poolBoost = poolBoosts[pool];
uint256 oldBoost = userBoost.amount;
uint256 newBoost = _calculateBoost(user, pool, 10000);
// Update pool totals
if (newBoost >= oldBoost) {
poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
} else {
poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
}
poolBoost.workingSupply = newBoost; //@audit CRITICAL: Overwrites entire working supply
}

Impact

It breaks pool boost accounting of the protocol which will result in:

  • Broken boost incentives and Incorrect reward distributions

  • Unfair reward allocations

Tools Used

  • Manual code review

Recommendations

Implement delta-based working supply updates:

function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
// ... existing checks ...
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;
// Update pool totals with deltas
if (newBoost >= oldBoost) {
poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
poolBoost.workingSupply = poolBoost.workingSupply + (newBoost - oldBoost);
} else {
poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
poolBoost.workingSupply = poolBoost.workingSupply - (oldBoost - newBoost);
}
emit BoostUpdated(user, pool, newBoost);
emit PoolBoostUpdated(pool, poolBoost.totalBoost, poolBoost.workingSupply);
}
Updates

Lead Judging Commences

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