MyCut

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

Missing balance validation allows claiming more rewards than available in contract

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

  • The normal behavior should be: Pot receives tokens via fundContest(), then distributes them to claimants

  • Explain the specific issue or problem in one or more sentences

  • The claimCut() function allows players to claim their rewards without verifying that the Pot contract actually has sufficient token balance.

// Root cause in the codebase with @> marks to highlight the relevant section
// Pot.sol - Lines 37-47
function claimCut() public {
address player = msg.sender;
uint256 reward = playersToRewards[player];
if (reward <= 0) {
revert Pot__RewardNotFound();
}
playersToRewards[player] = 0; // @audit State updated before balance check
remainingRewards -= reward; // @audit State updated before balance check
claimants.push(player);
_transferReward(player, reward); // @audit No balance validation
}
function _transferReward(address player, uint256 reward) internal {
i_token.transfer(player, reward); // @audit Will fail if balance insufficient
}

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • High - This occurs when:

    • Manager forgets to call fundContest() after creating pot

    • Manager funds with insufficient amount

  • Reason 2

Impact:

  • Impact 1

  • Critical Impact:

    1. Players claim rewards but receive nothing (combined with H-03)

    2. First claimants might succeed, last claimants always fail

  • Impact 2

Proof of Concept

function testUnfundedPotAcceptsClaims() public {
// Create pot without funding
address[] memory players = new address[](2);
players[0] = alice;
players[1] = bob;
uint256[] memory rewards = new uint256[](2);
rewards[0] = 1000;
rewards[1] = 1000;
pot = new Pot(players, rewards, token, 2000);
// Note: No funding step - pot balance is 0
// Alice claims - should fail but doesn't due to unchecked transfer
vm.prank(alice);
pot.claimCut(); // Succeeds!
// Check: Alice's reward is marked as claimed
assertEq(pot.checkCut(alice), 0); // Can't claim again
// Check: Alice received 0 tokens
assertEq(token.balanceOf(alice), 0); // No tokens received
// Check: Contract thinks reward was distributed
assertEq(pot.getRemainingRewards(), 1000); // Decreased by 1000
}

Recommended Mitigation

- remove this code
+ add this code// Pot.sol
function claimCut() public {
address player = msg.sender;
uint256 reward = playersToRewards[player];
if (reward <= 0) {
revert Pot__RewardNotFound();
}
+ // Validate pot has sufficient balance
+ require(i_token.balanceOf(address(this)) >= reward, "Insufficient pot balance");
playersToRewards[player] = 0;
remainingRewards -= reward;
claimants.push(player);
i_token.safeTransfer(player, reward); // Using SafeERC20 from H-03 fix
}
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!