MyCut

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

`claimCut` has no deadline, so a no-show can claim after the 90-day window and escape forfeiture, depriving in-time claimants of the redistributed pool

Root + Impact

Description

  • The protocol gives claimants 90 days to claim, after which unclaimed rewards are forfeited: the manager takes a cut and the remainder is distributed "to those who claimed in time."

  • claimCut enforces no deadline — it can be called at any time. A player who did not claim within 90 days (a no-show who should have forfeited) can still call claimCut and take their full reward. Doing so before the manager calls closePot shrinks remainingRewards to (or toward) zero, so the forfeited pool that should have been redistributed to the in-time claimants and skimmed by the manager never materializes.

function claimCut() public {
@> // no check on block.timestamp - i_deployedAt; claimable forever, even after the 90-day window
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);
}

Risk

Likelihood:

  • When a registered player does not claim during the 90-day window and then calls claimCut in the interval between day-90 eligibility and the manager's closeContest call.

  • When the manager does not close the pot in the same block that the window elapses — i.e. under normal operation, where closing is a manual, delayed action.

Impact:

  • The in-time claimants lose the redistributed forfeited pool the protocol guarantees them, and the manager loses their 10% cut.

  • Concretely, with [inTime, noShow] each owed 50: inTime claims, noShow claims late → remainingRewards == 0 at close → inTime receives no redistribution and the manager receives nothing, versus the designed manager +5 and inTime +45.

Proof of Concept

function testNoShowCanStillClaimAfterDeadline() public {
vm.startPrank(user);
contest = ContestManager(conMan).createContest(players, rewards, IERC20(weth), totalRewards);
ContestManager(conMan).fundContest(0);
vm.stopPrank();
// inTime claims within the window.
vm.prank(inTime);
Pot(contest).claimCut();
// 91 days pass — noShow has forfeited by design.
vm.warp(block.timestamp + 91 days);
// A forfeited player MUST NOT be able to claim after the deadline.
// Buggy code has no time gate, so this call succeeds and the expectRevert fails.
vm.expectRevert();
vm.prank(noShow);
Pot(contest).claimCut();
}

Recommended Mitigation

Enforce the claim window inside claimCut so post-window claims are rejected and unclaimed rewards are cleanly forfeited to the closePot redistribution path:

function claimCut() public {
+ if (block.timestamp - i_deployedAt >= 90 days) {
+ revert Pot__StillOpenForClaim();
+ }
address player = msg.sender;
uint256 reward = playersToRewards[player];
Updates

Lead Judging Commences

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