Summary
In BoostController#updateUserBoost(), totalBoost of poolBoosts[pool] is updated by newBoost. Since in delegateBoost() it has never increased, this line:
poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
will revert.
Vulnerability Details
In delegateBoost(), there's no increasing totalBoost of poolBoosts[pool] even new boost is deligated.
function updateUserBoost(address user, address pool) external override nonReentrant whenNotPaused {
...
if (newBoost >= oldBoost) {
poolBoost.totalBoost = poolBoost.totalBoost + (newBoost - oldBoost);
} else {
@> poolBoost.totalBoost = poolBoost.totalBoost - (oldBoost - newBoost);
}
...
}
As a result, if poolBoost.totalBoost = 0, update for this user and pool will revert due to arithmetic operation overflowed.
Proof Of Code
Test code is written in BoostController.test.js
describe("Delegation System", () => {
it("after delegation, update reverts", async () => {
const amount = ethers.parseEther("500");
const duration = 7 * 24 * 3600;
const newPool = await (await ethers.getContractFactory("MockPool")).deploy();
await expect(boostController.connect(manager).modifySupportedPool(newPool.getAddress(), true))
.to.emit(boostController, "PoolAdded")
.withArgs(newPool.getAddress());
await boostController.connect(user1).delegateBoost(newPool.getAddress(), amount, duration);
// Move time forward
await time.increase(duration);
await expect(
boostController.connect(user2).updateUserBoost(user1.address, newPool.getAddress())
).to.emit(boostController, "UpdateBoostFailed")
.withArgs(user1.address, newPool.getAddress(), amount);
});
}
Impact
updateUserBoost() is broken.
Tools Used
manual, hardhat
Recommendations
Increase totalBoost with amount when delegateBoost().
function delegateBoost(
address to,
uint256 amount,
uint256 duration
) external override nonReentrant {
...
delegation.amount = amount;
delegation.expiry = block.timestamp + duration;
delegation.delegatedTo = to;
delegation.lastUpdateTime = block.timestamp;
+ poolBoosts[to].totalBoost += amount;
emit BoostDelegated(msg.sender, to, amount, duration);
}