MyCut

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

closePot() never marks the pot as settled, so players who missed the 90-day window still claim their full reward after the settlement

Root + Impact

Description

closePot() performs the final settlement, but it never marks the pot as settled, and claimCut() has no awareness of it.

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);
}
​
function closePot() external onlyOwner {
...
if (remainingRewards > 0) {
uint256 managerCut = remainingRewards / managerCutPercent;
i_token.transfer(msg.sender, managerCut);
uint256 claimantCut = (remainingRewards - managerCut) / i_players.length;
for (uint256 i = 0; i < claimants.length; i++) {
_transferReward(claimants[i], claimantCut);
}
@> // no closed flag, playersToRewards of non-claimants is untouched
}
}

According to the protocol description, the 90-day window exists so that the manager can take a cut and distribute the remainder "equally to those who claimed in time". Players who miss the window are meant to be excluded.

They are not. After the settlement, every player who did not claim still has a non-zero entry in playersToRewards and can call claimCut() to withdraw their full original reward from the same balance that was just used for the settlement. The late claim is indistinguishable from a timely one, so the funds that the settlement was about to redistribute are taken by players the settlement was supposed to exclude.

Risk

Likelihood: High. Any player who did not claim before the deadline can claim immediately after the settlement, at a point where the pot still holds the undistributed remainder. No special conditions are required.

Impact: Medium. Rewards reserved for the settlement are drained by late claimants, so players who claimed in time receive less than the settlement computes, and the manager's cut is taken from a pool that then shrinks. The documented settlement rule is violated in every contest where a player misses the deadline.

Proof of Concept

Four players with 100 each, pool of 400. One player claims, the contest is settled after 91 days, and a player who did not claim then withdraws the full 100 anyway.

// Actors:
// - p1: claimed before the deadline, receives the settlement share
// - p2: did not claim before the deadline, claims after the settlement
// - Manager: triggers the settlement
​
function test_poc_claim_after_settlement() public {
vm.prank(p1);
Pot(contest).claimCut(); // p1 takes 100, remainingRewards = 300
​
vm.warp(block.timestamp + 91 days);
ContestManager(mgr).closeContest(contest); // manager 30, p1 67, pot holds 203
​
assertEq(token.balanceOf(contest), 203);
​
vm.prank(p2);
Pot(contest).claimCut(); // late claimant is still served
​
assertEq(token.balanceOf(p2), 100); // paid in full after the settlement
assertEq(token.balanceOf(contest), 103); // funds reserved for settlement are gone
}

Recommended Mitigation

Mark the pot as settled and reject any claim once that flag is set, so the 90-day window is enforced.

+ bool private s_closed;
+
function claimCut() public {
+ if (s_closed) revert Pot__PotClosed();
address player = msg.sender;
...
}
​
function closePot() external onlyOwner {
...
+ s_closed = true;
if (remainingRewards > 0) {
...
}
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 10 minutes 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!