Core Contracts

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

Users Cannot Remove Delegation Before Expiry

Summary

In BoostController.sol, users can delegate their boost to another address using delegateBoost(). However, when a user tries to remove their delegation via removeBoostDelegation(), the function only deletes the delegation but does not reset internal delegation flags.

Vulnerability Details

This causes stale delegation data to persist, preventing the user from:

  1. Calling deboost() to reclaim their boost.

  2. Unstaking their veTokens until the delegation expires naturally.

** User Delegates Boost**

// User1 delegates boost to Attacker
await boostController.connect(user1).delegateBoost(attacker.address, ethers.parseEther("100"), 7 * 24 * 3600);

user1 delegates 100 veTokens of boost to attacker for 7 days.

** User1 Tries to Remove Delegation**

await boostController.connect(attacker).removeBoostDelegation(user1.address);

The function deletes the delegation but does not reset delegation tracking flags.

User1 Cannot Call deboost()

await expect(boostController.connect(user1).deboost())
.to.be.revertedWith("Delegation still active");

The contract still considers User1's boost delegated, even though the attacker removed the delegation.

User1 must wait the full 7 days before reclaiming their boost.

The removeBoostDelegation() function does exist and correctly removes expired delegations. However, the vulnerability is still partially present because:

Delegation Can’t Be Removed Before Expiry → Users must wait for expiry, even if they want to revoke delegation early.

Impact

User Cannot Remove Delegation Immediately

  • Once a user delegates their boost, they cannot remove it before expiry.

  • The contract will still consider the delegation active until the expiration timestamp is reached.

Tools Used

Manual Review

Recommendations

Reset All Flags

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);
// Reset delegation state (Fixes stale delegation lock)
delete userBoosts[from][msg.sender];
userBoosts[from][msg.sender] = UserBoost({
amount: 0,
expiry: 0,
delegatedTo: address(0),
lastUpdateTime: block.timestamp
});
emit BoostReset(from, msg.sender);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months 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.

Give us feedback!