MyCut

First Flight #23
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

`Pot::closePot` distributes a cut to the manager without a mechanism for the owner to withdraw the tokens from the ContestManager contract

Summary

The Pot::closePot function distributes a cut of the remaining rewards to the manager. However, there is no function in place for the owner to withdraw these tokens, leading to the tokens being locked in the contract.

Vulnerability Details

The closePot function in the Pot contract allows the owner to close the pot and distribute the remaining rewards. A portion of these rewards is allocated to the manager. However, the contract does not provide a mechanism for the owner to withdraw these tokens after they have been transferred to the contract. As a result, the manager's cut remains locked in the contract.

function closePot() external onlyOwner {
if (block.timestamp - i_deployedAt < 90 days) {
revert Pot__StillOpenForClaim();
}
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);
}
}
}

Proof Of Concept

After the pot has ended (90 days have passed), the owner can call the closePot function, which will distribute a cut of the remaining rewards to the manager. However, there is no way for the owner to withdraw these tokens from the contract.

Place the following test into TestMyCut.t.sol

function testManagerCutLocked() public mintAndApproveTokens {
vm.startPrank(user);
rewards = [200, 200];
contest = ContestManager(conMan).createContest(players, rewards, IERC20(ERC20Mock(weth)), 400);
ContestManager(conMan).fundContest(0);
vm.stopPrank();
vm.startPrank(player1);
Pot(contest).claimCut();
vm.stopPrank();
vm.warp(91 days);
assertEq(ERC20Mock(weth).balanceOf(conMan), 0);
vm.startPrank(user);
ContestManager(conMan).closeContest(contest);
vm.stopPrank();
// Check if manager's cut is locked in the contract
uint256 managerBalance = ERC20Mock(weth).balanceOf(conMan);
assert(managerBalance > 0);
}

Impact

If the closePot function is called, the manager's cut of the remaining rewards will be transferred to the contract, but there is no way for the owner to withdraw these tokens. This can lead to:

  • Tokens being locked in the contract indefinitely.

  • Inability for the manager to access their allocated rewards.

Tools Used

  • Solidity compiler

  • Manual code review

  • Foundry

Recommendations

To mitigate this vulnerability, implement a withdraw function that allows the owner to withdraw the amount of tokens from the contract. Here is an updated version of the Pot contract with the recommended changes

Place the following code in ContestManager.sol

function withdraw(address token, uint256 amount) external onlyOwner {
IERC20(token).transfer(msg.sender, amount);
}
Updates

Lead Judging Commences

equious Lead Judge 9 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Owner's cut is stuck in ContestManager

Support

FAQs

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