Core Contracts

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

Incorrect Working Supply Update

Summary

The working supply for pools is incorrectly overwritten with individual user boost amounts rather than being aggregated. Each call to updateUserBoost() replaces the entire pool's working supply with the single user's latest boost value.

Affected File: contracts/core/governance/boost/BoostController.sol
Function: updateUserBoost()

Error

// ❌ Incorrect implementation
poolBoost.workingSupply = newBoost; // Overwrites total with single user's value
// ✅ Correct implementation should be:
poolBoost.workingSupply += (newBoost - oldBoost);

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

function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
// ...
PoolBoost storage poolBoost = poolBoosts[pool];
uint256 oldBoost = userBoost.amount;
uint256 newBoost = _calculateBoost(user, pool, 10000);
userBoost.amount = newBoost;
// ... existing totalBoost update ...
// Vulnerability: Overwriting working supply
poolBoost.workingSupply = newBoost; // ❌ Incorrect
// ...
}

Impact

The Working Supply of the pool will be affected!

Example Scenario:

  1. Pool Parameters

  • Total rewards: 1000 tokens

  • 2 users with identical 1x boosts (10,000 basis points each)

  • Correct total working supply should be: 10,000 + 10,000 = 20,000

  1. Faulty Calculation

  • After User1 update: WorkingSupply = 10,000

  • After User2 update: WorkingSupply = 10,000 (overwrites previous)

  • Total working supply = 10,000 (while actual boosts sum to 20,000)

Recommendations

function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
if (newBoost >= oldBoost) {
poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
poolBoost.workingSupply = poolBoost.workingSupply + (newBoost - oldBoost); // Add difference
} else {
poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
poolBoost.workingSupply = poolBoost.workingSupply - (oldBoost - newBoost); // Subtract difference
}
}
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!