Core Contracts

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

Delegation Does Not Deduct from Delegator

Summary

In BoostController contract, users can delegate boosts to other users by calling delegateBoost function. However, there’s no deduction from the caller’s side, which leads to incorrect state and double spending.

Details

UserBoost is a complicated struct, because it attempts to track user boost data for both a specific pool, or delegation.

/**
@> * @notice Struct to track user boost data for a specific pool or delegation
* @param amount The amount of boost
* @param expiry The expiration timestamp for delegations
* @param delegatedTo The address the boost is delegated to (if applicable)
* @param lastUpdateTime The last time the boost was updated
*/
struct UserBoost {
uint256 amount;
uint256 expiry;
address delegatedTo;
uint256 lastUpdateTime;
}

Now, say Alice delegates her boost to Bob via delegateBoost. This code block will be fired:

uint256 userBalance = IERC20(address(veToken)).balanceOf(msg.sender);
if (userBalance < amount) revert InsufficientVeBalance();
@> UserBoost storage delegation = userBoosts[msg.sender][to];
if (delegation.amount > 0) revert BoostAlreadyDelegated(); //* but can delegate 0 amount
delegation.amount = amount;
delegation.expiry = block.timestamp + duration;
delegation.delegatedTo = to;
delegation.lastUpdateTime = block.timestamp;
emit BoostDelegated(msg.sender, to, amount, duration);

But there’s nothing taken from Alice’s boosting power. She did give some power to Bob, but did not lose anything herself. An example of how a user’s boosts for a pool is captured and changed is seen in updateUserBoost function.

This is how you get a user’s boost for a pool: UserBoost storage userBoost = userBoosts[user][pool];

And this is how you get for boost for a user (in delegateBoost): UserBoost storage delegation = userBoosts[msg.sender][to];

Back in updateUserBoost we also see how the pool totals are updated:

UserBoost storage userBoost = userBoosts[user][pool];
PoolBoost storage poolBoost = poolBoosts[pool];
uint256 oldBoost = userBoost.amount;
// Calculate new boost based on current veToken balance
uint256 newBoost = _calculateBoost(user, pool, 10000); // Base amount
userBoost.amount = newBoost;
userBoost.lastUpdateTime = block.timestamp;
// Update pool totals safely
if (newBoost >= oldBoost) {
poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
} else {
poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
}
poolBoost.workingSupply = newBoost; // Set working supply directly to new boost
poolBoost.lastUpdateTime = block.timestamp;

Now now, Alice is using the amount she has for this pool, and also she has given some to Bob, but her boosting power for this pool has not been deducted. So, Alice can spend her power freely, without using it up. She can give the same boost to Bob, Emily, Candice, and Louie. This can’t be good.

Impact

Double spending let’s the user to boost as much users as they want without losing boosting power themselves.

Updates

Lead Judging Commences

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

BoostController::delegateBoost lacks total delegation tracking, allowing users to delegate the same veTokens multiple times to different pools for amplified influence and rewards

Support

FAQs

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