Core Contracts

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

updateUserBoost() sets workingSupply to diminished value, effecting all boost rewards

Description

updateUserBoost() incorrectly overwrites the current poolBoost.workingSupply with newBoost instead of adding/subtracting to it. The correct implementation ought to be:

File: contracts/core/governance/boost/BoostController.sol
177: function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
178: if (paused()) revert EmergencyPaused();
179: if (user == address(0)) revert InvalidPool();
180: if (!supportedPools[pool]) revert PoolNotSupported();
181:
182: UserBoost storage userBoost = userBoosts[user][pool];
183: PoolBoost storage poolBoost = poolBoosts[pool];
184:
185: uint256 oldBoost = userBoost.amount;
186: // Calculate new boost based on current veToken balance
187: uint256 newBoost = _calculateBoost(user, pool, 10000); // Base amount
188:
189: userBoost.amount = newBoost;
190: userBoost.lastUpdateTime = block.timestamp;
191:
192: // Update pool totals safely
193: if (newBoost >= oldBoost) {
194: poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
+ 194: poolBoost.workingSupply = poolBoost.workingSupply + (newBoost - oldBoost);
195: } else {
196: poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
+ 196: poolBoost.workingSupply = poolBoost.workingSupply - (oldBoost - newBoost);
197: }
- 198: poolBoost.workingSupply = newBoost; // Set working supply directly to new boost
199: poolBoost.lastUpdateTime = block.timestamp;
200:
201: emit BoostUpdated(user, pool, newBoost);
202: emit PoolBoostUpdated(pool, poolBoost.totalBoost, poolBoost.workingSupply);
203: }

Note that as per the interface:

File: contracts/interfaces/core/governance/IBoostController.sol
24: /**
25: * @notice Struct to track pool-wide boost metrics
26: * @param totalBoost The total boost amount for the pool
27:@---> * @param workingSupply The total working supply including boosts
28: * @param baseSupply The base supply without boosts
29: * @param lastUpdateTime The last time pool boosts were updated
30: */
31: struct PoolBoost {
32: uint256 totalBoost;
33: uint256 workingSupply;
34: uint256 baseSupply;
35: uint256 lastUpdateTime;
36: }

Impact

Incorrect value used in both removeBoostDelegation() and getPoolBoost(). Any rewards calculated based on this will now be highly reduced. Imagine:

// Initial state
Pool has 100 users
Each user has 1000 token boost
Correct workingSupply should be: 100,000 tokens
// After bug triggers
Some user updates with 100 token boost
workingSupply becomes: 100 tokens (99.9% reduction)
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.