Core Contracts

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

Wrong Pool Update in removeBoostDelegation Messes Up Boost Totals

Summary

The removeBoostDelegation function tries to update boost totals using the wrong address—it uses the person removing the delegation (msg.sender) instead of an actual pool. This mixes up the data meant for pools and could mess up how the system tracks boosts.

Vulnerability Details

This problem happens in the removeBoostDelegation function, which is supposed to clean up an expired delegation.

The main issue is that the function thinks the person calling it (msg.sender) is a pool, when really it’s just a regular user. Here’s the code:

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();
PoolBoost storage poolBoost = poolBoosts[msg.sender];
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];
}
  • Who’s Who:

    • from is the user who gave the boost (the delegator).

    • msg.sender is the user who got the boost (the recipient) and is now removing it because it expired.

    It finds the delegation in userBoosts[from][msg.sender]—this part is fine and shows msg.sender is a user, not a pool. The line PoolBoost storage poolBoost = poolBoosts[msg.sender]; says, “Let’s update the boost totals for msg.sender.” Then it takes away the delegated amount:

    poolBoost.totalBoost -= delegation.amount;
    poolBoost.workingSupply -= delegation.amount;

    But poolBoosts is a special list (mapping(address => PoolBoost) private poolBoosts;) meant to hold boost info for pools—like Pool X or Pool Y—not regular users.

How Pools Are Normally Used

Look at how the contract usually handles pools:

  • In updateUserBoost:

    function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
    // ... (checks)
    PoolBoost storage poolBoost = poolBoosts[pool];
    // ... (updates poolBoost.totalBoost and poolBoost.workingSupply)
    }

    Here, poolBoosts[pool] uses a pool address—like a specific staking pool where boosts are applied. The pool comes from supportedPools, a list of real pool addresses.

  • In getPoolBoost:

    function getPoolBoost(address pool) external view returns (uint256, uint256, uint256, uint256) {
    if (!supportedPools[pool]) revert PoolNotSupported();
    PoolBoost storage boost = poolBoosts[pool];
    // ... (returns pool data)
    }
    • Again, it’s poolBoosts[pool]—always a pool address.

Everywhere else, poolBoosts is for pools, not users. But in removeBoostDelegation, it uses poolBoosts[msg.sender], the recipient’s address as if they’re a pool.

  • msg.sender is the person who got the delegated boost, not a pool where boosts are used. Pools are separate things tracked in supportedPools.

  • When someone delegates boost (in delegateBoost), it’s saved as userBoosts[from][to], but it doesn’t say which pool it’s for. So when it’s removed, the contract doesn’t know which pool to update—it just guesses msg.sender is the pool

Impact

  1. The delegated boost should’ve affected a real pool (like Pool X), but since the contract doesn’t know which one, it doesn’t update the right place when removing it. Pool totals stay wrong.

  2. The boost totals (totalBoost and workingSupply) get changed for poolBoosts[msg.sender]—a user’s address that’s not even a pool. This puts junk data in the poolBoosts list where pool info should be.

Tools Used
Manual review

Recommendations

Track Pools in Delegation: When boost is delegated (in delegateBoost), save which pool it’s for

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.