MyCut

AI First Flight #8
Beginner FriendlyFoundry
EXP
View results
Submission Details
Impact: medium
Likelihood: low
Invalid

createContest accepts an empty players array, so closePot reverts with division by zero and permanently locks all Pot funds

Root + Impact

createContest should reject a contest configuration that can never be closed. It deploys a Pot without validating players.length, and once such a Pot is funded, closePot divides by i_players.length == 0 and reverts with Panic(0x12). The revert rolls back even the manager cut, so the Pot can never be closed and every funded token stays locked with no withdrawal path.

// ContestManager.sol
function createContest(address[] memory players, uint256[] memory rewards, IERC20 token, uint256 totalRewards)
public
onlyOwner
returns (address)
{
@> Pot pot = new Pot(players, rewards, token, totalRewards); // no players.length check
// Pot.sol
uint256 claimantCut = (remainingRewards - managerCut) / i_players.length; // == 0 for an empty players array

Risk

Likelihood: Low. The owner creates a contest with an empty players array and funds it; no code path prevents either step.

Impact: Medium. The Pot permanently reverts on close, locking the full funded balance (including the manager cut) with no recovery path.

Proof of Concept

Verified locally with forge 1.7.1 / solc 0.8.28 (one setup-only time-warp cheatcode):

function test_emptyPlayersClosePotLocksFunds() public {
address[] memory players = new address[](0);
uint256[] memory rewards = new uint256[](0);
address emptyPot = cm.createContest(players, rewards, token, 100);
cm.fundContest(0);
_advance90Days();
bool reverted;
try cm.closeContest(emptyPot) {
reverted = false;
} catch {
reverted = true;
}
assertTrue(reverted); // Panic(0x12): division by zero
assertEq(token.balanceOf(emptyPot), 100);
assertEq(Pot(emptyPot).getRemainingRewards(), 100);
}

Recommended Mitigation

function createContest(address[] memory players, uint256[] memory rewards, IERC20 token, uint256 totalRewards)
public
onlyOwner
returns (address)
{
+ if (players.length == 0) revert ContestManager__EmptyPlayers();
+ if (players.length != rewards.length) revert ContestManager__LengthMismatch();
Pot pot = new Pot(players, rewards, token, totalRewards);
- uint256 claimantCut = (remainingRewards - managerCut) / i_players.length;
+ uint256 claimantCut = i_players.length == 0
+ ? 0
+ : (remainingRewards - managerCut) / i_players.length;
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!