MyCut

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

players and rewards arrays length mismatch not validated in createContest, causing silent incorrect reward mapping

Root + Impact

Description

  • Neither createContest nor the Pot constructor validates that players.length == rewards.length. If rewards has fewer entries than players, the last players get mapped to 0 rewards and can never claim. If rewards has more entries, the extra rewards are allocated to address(0) and permanently locked.

constructor(address[] memory players, uint256[] memory rewards, IERC20 token, uint256 totalRewards) {
i_players = players;
i_rewards = rewards;
// No length check
for (uint256 i = 0; i < i_players.length; i++) {
playersToRewards[i_players[i]] = i_rewards[i]; // index out of bounds or wrong mapping
}
}

Risk

Likelihood:

  • Human error in passing mismatched arrays is common

  • Will silently succeed with wrong state rather than reverting

Impact:

  • Players with index beyond rewards.length get zero allocation

  • Those players can never claim and their share is locked

Proof of Concept

The following test demonstrates that passing a rewards array shorter than players array causes no revert.
The loop silently maps zero rewards to the last players, locking their share permanently:
function testMismatchedArraysSilentlyFails() public mintAndApproveTokens {
address[] memory mismatchedPlayers = new address[](3);
mismatchedPlayers[0] = player1;
mismatchedPlayers[1] = player2;
mismatchedPlayers[2] = makeAddr("player3");
uint256[] memory shortRewards = new uint256[](2);
shortRewards[0] = 2;
shortRewards[1] = 2;
// player3 has no corresponding reward entry
vm.startPrank(user);
// No revert - deploys successfully with broken state
contest = ContestManager(conMan).createContest(
mismatchedPlayers, shortRewards, IERC20(weth), 4
);
ContestManager(conMan).fundContest(0);
vm.stopPrank();
// player3 has zero reward mapped - can never claim
assertEq(Pot(contest).checkCut(makeAddr("player3")), 0);
// player3's share locked in contract with no recovery
vm.prank(makeAddr("player3"));
vm.expectRevert(Pot.Pot__RewardNotFound.selector);
Pot(contest).claimCut();
}

Recommended Mitigation

constructor(address[] memory players, uint256[] memory rewards, IERC20 token, uint256 totalRewards) {
+ require(players.length == rewards.length, "Players and rewards length mismatch");
i_players = players;
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!