Core Contracts

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

Incorrect Pool Boost Update in removeBoostDelegation

Summary

In the BoostController::removeBoostDelegation function, the contract attempts to update the pool’s boost totals before removing a delegation. However, it incorrectly uses msg.sender (the delegation recipient) as the key for the poolBoosts mapping, instead of the pool address.

Details

removeBoostDelegation in an attempt to Update pool boost totals before removing delegation incorrectly passes msg.sender to the poolBoost mapping, which will have no effect, because the mappings are defined as such:

  • userBoosts: Maps (user => pool => UserBoost).

  • poolBoosts: Maps (pool => PoolBoost).

Here’s the function in question:

function removeBoostDelegation(
address from
) external override nonReentrant {
UserBoost storage delegation = userBoosts[from][msg.sender];
if (delegation.delegatedTo != msg.sender) revert DelegationNotFound();
if (delegation.expiry > block.timestamp)
revert InvalidDelegationDuration();
// Update pool boost totals before removing delegation
PoolBoost storage poolBoost = poolBoosts[msg.sender]; //@audit this is wrong
if (poolBoost.totalBoost >= delegation.amount) {
poolBoost.totalBoost -= delegation.amount;
}
if (poolBoost.workingSupply >= delegation.amount) {
poolBoost.workingSupply -= delegation.amount;
}
poolBoost.lastUpdateTime = block.timestamp;
emit DelegationRemoved(from, msg.sender, delegation.amount);
delete userBoosts[from][msg.sender];
}

As it can be seen, this function can only be called by the delegation recipient due to if (delegation.delegatedTo != msg.sender) revert DelegationNotFound();

So, when the delegation recipient calls this function this line fires PoolBoost storage poolBoost = poolBoosts[msg.sender]; which is wrong. It should have passed the pool to update the poolBoost state, not the delegation recipient’s address.

Since this implementation is wrong, the subsequent if statements will fail silently, totalBoost and workingSupply of the pool won’t change, the state will be updated to a faulty state, breaking the application logic.

Impact

  • State Corruption: The poolBoosts mapping is updated with the wrong key (msg.sender instead of the pool address).

  • Incorrect Accounting: The pool’s boost totals are not updated correctly, leading to inaccurate reward distributions.

  • Silent Failure: The function does not revert, but the state is corrupted, making it difficult to detect the issue.

Recommendation

Incorrect Implementation:

PoolBoost storage poolBoost = poolBoosts[msg.sender]; // WRONG: Uses recipient address

Correct Implementation (as seen in updateUserBoost and getPoolBoost):

PoolBoost storage poolBoost = poolBoosts[pool]; // CORRECT: Uses pool address
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BoostController's delegation system fundamentally broken due to missing pool associations, treating recipient addresses as pools and never properly updating pool boost metrics

Support

FAQs

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