MyCut

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

Already-Claimed Players Receive an Additional Reward During closePot()

The same user is treated as both:

  1. Someone who already received their reward.

  2. Someone entitled to the remaining rewards.

Description

  • When a player claims their assigned reward through claimCut(), their reward should be considered consumed.

    The player's reward is set to zero:

    playersToRewards[player] = 0;

    Therefore, the player should not receive the same entitlement again unless the protocol explicitly defines a separate bonus.


  • Specific issue

    claimCut() adds every successful claimant to the claimants array:

    claimants.push(player);

    Later, closePot() sends another amount to every address in claimants.

    Therefore, every player who already claimed their reward receives an additional payment when the Pot closes.

function claimCut() public {
...
playersToRewards[player] = 0;
​
// @> Player is added to the list used for future payouts
claimants.push(player);
​
_transferReward(player, reward);
}
​
Then:
​
for (uint256 i = 0; i < claimants.length; i++) {
// @> Already-paid players receive another payment
_transferReward(claimants[i], claimantCut);
}

Risk

Likelihood:

  • Every successful claimant is added to claimants.

  • Every address in claimants is paid again during closePot().

Impact:

  • Claimants receive duplicate payouts.

  • The intended reward allocation can be exceeded.

  • Other participants may receive fewer rewards because additional tokens are sent to already-paid users.

Proof of Concept

function testClaimantReceivesSecondPayment() public {
address alice = makeAddr("alice");
​
// Alice has a 100 token reward.
// Alice claims it.
​
vm.prank(alice);
pot.claimCut();
​
assertEq(token.balanceOf(alice), 100);
​
// Advance 90 days.
vm.warp(block.timestamp + 90 days);
​
pot.closePot();
​
// Alice receives claimantCut in addition to the original 100.
assertGt(token.balanceOf(alice), 100);
}
​

Recommended Mitigation

function claimCut() public { address player = msg.sender; uint256 reward = playersToRewards[player]; if (reward <= 0) { revert Pot__RewardNotFound(); } playersToRewards[player] = 0; remainingRewards -= reward; - claimants.push(player); _transferReward(player, reward); }
Updates

Lead Judging Commences

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