Summary
when we call delegateBoost, we do not update total boost and working supply. but when delegation gets expired we call removeBoostDelegation, we decrease poolBoost.totalBoost and poolBoost.workingSupply.
Vulnerability Details
The delegateBoost() function does not update totalBoost and workingSupply when a boost is delegated. However, removeBoostDelegation() decreases these values upon expiration, leading to an inconsistency in boost accounting.
When a boost is delegated, poolBoost.totalBoost and poolBoost.workingSupply remain unchanged. This can be seen in the below code:
/contracts/core/governance/boost/BoostController.sol:213
213: function delegateBoost(
214: address to,
215: uint256 amount,
216: uint256 duration
217: ) external override nonReentrant {
218: if (paused()) revert EmergencyPaused();
219: if (to == address(0)) revert InvalidPool();
220: if (amount == 0) revert InvalidBoostAmount();
221: if (duration < MIN_DELEGATION_DURATION || duration > MAX_DELEGATION_DURATION)
222: revert InvalidDelegationDuration();
223:
224: uint256 userBalance = IERC20(address(veToken)).balanceOf(msg.sender);
225: console.log("userBalance",userBalance,amount);
226: if (userBalance < amount) revert InsufficientVeBalance();
227:
228: UserBoost storage delegation = userBoosts[msg.sender][to];
229: if (delegation.amount > 0) revert BoostAlreadyDelegated();
230:
231: delegation.amount = amount;
232: delegation.expiry = block.timestamp + duration;
233: delegation.delegatedTo = to;
234: delegation.lastUpdateTime = block.timestamp;
235:
236: emit BoostDelegated(msg.sender, to, amount, duration);
237: }
When the delegation expires and removeBoostDelegation() is called, these values are reduced as seen in the below code:
/contracts/core/governance/boost/BoostController.sol:244
244: function removeBoostDelegation(address from) external override nonReentrant {
245: UserBoost storage delegation = userBoosts[from][msg.sender];
246: if (delegation.delegatedTo != msg.sender) revert DelegationNotFound();
247: if (delegation.expiry > block.timestamp) revert InvalidDelegationDuration();
248:
249:
250: PoolBoost storage poolBoost = poolBoosts[msg.sender];
251: if (poolBoost.totalBoost >= delegation.amount) {
252: poolBoost.totalBoost -= delegation.amount;
253: }
254: if (poolBoost.workingSupply >= delegation.amount) {
255: poolBoost.workingSupply -= delegation.amount;
256: }
257: poolBoost.lastUpdateTime = block.timestamp;
258:
259: emit DelegationRemoved(from, msg.sender, delegation.amount);
260: delete userBoosts[from][msg.sender];
261: }
Impact
Incorrect boost tracking may lead to governance miscalculations, unfair voting power distribution, or inaccurate working supply calculations.
Tools Used
Manual Review, Unit Testing
Recommendations
Update delegateBoost() to increase totalBoost and workingSupply when a boost is delegated.