MyCut

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

`Pot` constructor never validates `sum(rewards) <= totalRewards`, allowing an over-allocated Pot where late claimers revert with arithmetic underflow on `remainingRewards`

Root + Impact

Description

  • The Pot constructor accepts rewards and totalRewards independently and never checks that the sum of rewards is less than or equal to totalRewards. ContestManager.createContest likewise forwards the values without validation.

  • claimCut subtracts each claim from remainingRewards (initialized to totalRewards). When the cumulative claimed amount exceeds totalRewards, the next remainingRewards -= reward underflows and reverts under Solidity 0.8 arithmetic checks, permanently locking out the last claimant despite their valid configured reward.

// src/Pot.sol :: constructor — no sum-vs-total check
constructor(address[] memory players, uint256[] memory rewards, IERC20 token, uint256 totalRewards) {
i_players = players;
i_rewards = rewards;
i_token = token;
i_totalRewards = totalRewards;
@> remainingRewards = totalRewards; // no verification that sum(rewards) <= totalRewards
// ...
}
function claimCut() public {
address player = msg.sender;
uint256 reward = playersToRewards[player];
if (reward <= 0) revert Pot__RewardNotFound();
playersToRewards[player] = 0;
@> remainingRewards -= reward; // underflow when sum(claims so far) > totalRewards
claimants.push(player);
_transferReward(player, reward);
}

Risk

Likelihood:

  • Requires the admin (Trusted) to misconfigure the Pot at creation time, supplying rewards whose sum exceeds totalRewards. The protocol provides no on-chain guard against this fat-finger.

  • A typo, a stale spreadsheet, or copy-paste error in the deployment script triggers it.
    Impact:

  • Whichever player's claim is the one to push cumulative claims past totalRewards is DoS'd — their valid claim reverts.

  • This breaks the protocol's core guarantee ("authorized claimants have 90 days to claim"). The user cannot recover even by retrying after the window closes.

  • Funding inconsistency: fundContest only deposits totalRewards, so even without the underflow, the Pot would not have enough tokens to pay everyone. The bug surfaces as a revert rather than a graceful refusal.

Proof of Concept

function test_sumRewardsExceedsTotalRewardsBreaksLastClaimer() public {
address[] memory players = new address[](3);
players[0] = alice; players[1] = bob; players[2] = carol;
// sum = 6000 but totalRewards = 5000 — accepted without validation
uint256[] memory rewards = new uint256[](3);
rewards[0] = 2000e18; rewards[1] = 2000e18; rewards[2] = 2000e18;
vm.startPrank(owner);
address potAddr = manager.createContest(players, rewards, IERC20(address(token)), 5000e18);
manager.fundContest(0);
vm.stopPrank();
Pot pot = Pot(potAddr);
vm.prank(alice); pot.claimCut();
vm.prank(bob); pot.claimCut();
// carol's valid 2000 claim cannot succeed — remainingRewards underflow
vm.prank(carol);
vm.expectRevert();
pot.claimCut();
}

Run with forge test --match-test test_sumRewardsExceedsTotalRewardsBreaksLastClaimer -vvv. Test passes (revert confirmed).

Recommended Mitigation

Validate the invariant in the constructor and reject misconfigured Pots up front.

constructor(address[] memory players, uint256[] memory rewards, IERC20 token, uint256 totalRewards) {
+ require(players.length == rewards.length, "Pot: length mismatch");
+ uint256 sum;
+ for (uint256 i = 0; i < rewards.length; i++) {
+ sum += rewards[i];
+ }
+ require(sum <= totalRewards, "Pot: rewards exceed totalRewards");
i_players = players;
i_rewards = rewards;
// ...
}

Optionally, add the same check (or a higher-level wrapper) in ContestManager.createContest so the failure happens at the entry point rather than deep in the deployed Pot.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 17 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!