Relevant GitHub Links
https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/boost/BoostController.sol#L247-L253
Summary
The removeBoostDelegation()
function in BoostController.sol directly updates pool metrics before removing delegation state, leading to permanent locking or incorrect release of boost power. The function fails to properly recalculate voting power and boost metrics after delegation removal.
Vulnerability Details
The vulnerability exists in how removeBoostDelegation()
handles boost power release:
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];
}
Key issues:
Pool metrics (totalBoost, workingSupply) are updated with raw delegation amount without considering time-weighted decay
No recalculation of boost power using BoostCalculator after delegation removal
Verified impacts:
Impact
Incorrect reward distributions due to wrong boost calculations - since pool total metrics are wrong
The impact compounds over time as more delegations expire and are removed with incorrect amounts
Tools Used
Manual Review
Recommendations
Refactor removeBoostDelegation()
to properly handle boost power release (treat this as a pseudocode).
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();
uint256 delegationAmount = delegation.amount;
delete userBoosts[from][msg.sender];
_updateVotingPower(from);
_updateUserBoost(msg.sender);
PoolBoost storage poolBoost = poolBoosts[msg.sender];
poolBoost.totalBoost = _calculateTimeWeightedBoost(poolBoost.totalBoost, delegationAmount);
poolBoost.workingSupply = _calculateWorkingSupply(msg.sender);
poolBoost.lastUpdateTime = block.timestamp;
emit DelegationRemoved(from, msg.sender, delegationAmount);
}