Core Contracts

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

Boost delegation can be removed even if the BoostController is paused, updating the pool's boost accounting

Summary

It is possible to remove boost delegation for a specific user or pool even if the contract is paused, which should not be allowed, as the operation involves a state update

Vulnerability Details

In the `BoostController.sol` there are functions in which user can delegate and remove delegation from other users. These functions update both the userBoosts and poolBoosts mappings, where the boosts of the users and pools are tracked. It is not possible to delegate boosts if the contract is in the paused state, however there is no check if the contract is paused when removing delegation.

/**
* @notice Removes an expired boost delegation
* @param from Address that delegated the boost
* @dev Can only be called by the delegation recipient after expiry
*/
//@audit-issue Can also be called if the contract is paused
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];
}

According to the official RAAC documentation, emergency control must be able to pause all operations. However, this is not the case for removeBoostDelegation. One could argue that this is not critical, as there is a check to determine whether the delegation has expired, but there are updates to the poolBoosts mapping, where the pool's totalBoost, workingSupply, and lastUpdateTime are updated.

POC

Add this test to the BoostController.test.js and see that it will pass:

it("should not allow the delegation removal if the system is paused", async () => {
const amount = ethers.parseEther("500");
const duration = 7 * 24 * 3600;
await boostController.connect(user1).delegateBoost(user2.address, amount, duration);
// Move time forward
await time.increase(duration);
await boostController.connect(manager).setEmergencyShutdown(true);
expect(await boostController.paused()).to.be.true;
await expect(
boostController.connect(user2).removeBoostDelegation(user1.address)
).to.emit(boostController, "DelegationRemoved")
.withArgs(user1.address, user2.address, amount);
});

Impact

The state is updated when the contract is paused. While the likelihood of this happening is relatively high, the current impact is medium

Tools Used

Manual Review

Recommendations

Add whenNotPaused modifier to the removeBoostDelegation function:

function removeBoostDelegation(address from) external override nonReentrant whenNotPaused {}
Updates

Lead Judging Commences

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

BoostController::removeBoostDelegation lacks pause check, allowing state modifications during emergency pauses and undermining contract safety mechanisms

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

BoostController::removeBoostDelegation lacks pause check, allowing state modifications during emergency pauses and undermining contract safety mechanisms

Support

FAQs

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

Give us feedback!