MyCut

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

Users can claim rewards after the 90-day deadline, harming other claimants and the owner.

Root + Impact

claimCut() does not enforce the 90-day claim window. Players can claim after the deadline — reducing or eliminating the remainingRewards that should be distributed between the owner and on-time claimants via closePot().

Field Value
Severity Medium
Likelihood Medium

Description

The protocol documentation states that claimants have 90 days to claim rewards. After that, the manager takes 10% of unclaimed funds and the rest is distributed to on-time claimants. However, claimCut() contains no timestamp check, so players can claim at any time before the owner calls closePot(), bypassing the deadline entirely.

function claimCut() public {
@> // missing check: if (block.timestamp > i_deployedAt + 90 days) revert Pot__ClaimPeriodOver();
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: Affects every deployed Pot. Any player who missed the deadline can exploit this as long as the owner has not yet called closePot(). No complex setup required.

Impact: Late claims reduce remainingRewards before closePot() runs. In the worst case, if all players claim late, remainingRewards reaches 0 and the owner receives nothing (managerCut = 0) and on-time claimants receive no bonus distribution. Concrete PoC example: 4 players, 3 claim on time. remainingRewards = 1e18. If player4 claims late, remainingRewards drops to 0 — owner loses 1e17 WETH (10% cut) and on-time claimants lose their 3e17 WETH bonus share.

Proof of Concept

Setup: 4 players, 1 WETH each. 3 claim before the deadline. Player2 claims after 90 days. Owner closes the pot — receives nothing.

function test_Players_Can_ClaimCut_After_Deadline() public {
// There are 4 players, 1 weth each
// 3 players call claimCut() before the deadline
address[3] memory pClaim = [player1, player3, player4];
for (uint256 i; i < pClaim.length; i++) {
vm.prank(pClaim[i]);
pot.claimCut();
}
// Advance past 90 days — claim period should be closed
vm.warp(block.timestamp + 90 days + 5);
uint256 remaninRewardsBefore = pot.getRemainingRewards();
// remainingRewards = 1e18 — should be reserved for owner + claimant bonus
console.log("remainingRewards within deadline:", remaninRewardsBefore);
// Player2 claims after the deadline — this should revert but does not
vm.prank(player2);
pot.claimCut();
uint256 remaninRewardsAfter = pot.getRemainingRewards();
// remainingRewards = 0 — owner and claimants get nothing from closePot()
console.log("remainingRewards after deadline: ", remaninRewardsAfter);
// Owner closes the pot
vm.prank(ownerContest);
contest.closeContest(address(pot));
// Owner receives 0 — 1e17 WETH lost due to missing deadline enforcement
assertEq(weth.balanceOf(address(contest)), 0);
}
Logs:
remainingRewards within deadline: 1000000000000000000
remainingRewards after deadline: 0

Recommended Mitigation

Add a custom error and a timestamp check at the start of claimCut().

+ error Pot__ClaimPeriodOver();
function claimCut() public {
+ if (block.timestamp > i_deployedAt + 90 days) revert Pot__ClaimPeriodOver();
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 18 days 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!