Core Contracts

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

Users unable to remove expired delegations in BoostController

Author Revealed upon completion

Vulnerability Details

The BoostController contract enforces a design where only the recipient of a boost delegation (the pool) can remove the delegation after it expires. The original delegator (the user) has no ability to remove their expired delegation, creating an unnecessary dependency on the pool's action and potentially trapping user's delegation state.

The issue is in the removeBoostDelegation function:

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];
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];
}

Here we have a case:

  • Delegation expired(user chooses the time)

  • User boost is allocated to this pool

  • User cannot remove the boost delegation due to the strict check:

if (delegation.delegatedTo != msg.sender) revert DelegationNotFound();

Sponsor confirmed:

Alex Werner

"It is a bug, we indeed need the user to be able to remove the delegation."

Impact

  • Users are prevented from redelegating boosts to more efficient pools after expiry.

  • Pools can manipulate their boost metrics by retaining expired delegations, directly affecting yield calculations and reward distributions

Tools Used

Manual Review

Recommendations

Modify the removeBoostDelegation function to allow both the original delegator and the delegation recipient to remove expired delegations.

function removeBoostDelegation(address from) external override nonReentrant {
UserBoost storage delegation = userBoosts[from][msg.sender];
- if (delegation.delegatedTo != msg.sender) revert DelegationNotFound();
+ // Allow either the delegator (from) or the delegatee (delegation.delegatedTo) to remove
+ bool isAuthorizedCaller = (msg.sender == from || msg.sender == delegation.delegatedTo);
+ if (!isAuthorizedCaller) revert NotAuthorized();
if (delegation.expiry > block.timestamp) revert InvalidDelegationDuration();
// Update pool boost totals before removing delegation
- PoolBoost storage poolBoost = poolBoosts[msg.sender];
+ PoolBoost storage poolBoost = poolBoosts[delegation.delegatedTo];
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];
+ emit DelegationRemoved(from, delegation.delegatedTo, delegation.amount);
+ delete userBoosts[from][delegation.delegatedTo];
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 8 days ago
Submission Judgement Published
Validated
Assigned finding tags:

BoostController: Users unable to remove their own expired boost delegations, creating dependency on recipients and preventing efficient reallocation of boosts

Support

FAQs

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