MyCut

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

closePot misroutes manager cut and locks undistributed rewards

Description

Pot.closePot() misroutes and under-distributes remainingRewards.


Two independent logic flaws create permanent fund-loss behavior:

  1. managerCut is transferred to msg.sender, which is the ContestManager contract in this call path, not the intended human manager/owner recipient.

  2. Claimant redistribution uses i_players.length as denominator but payout loop iterates only claimants, so if claimants.length < i_players.length, a deterministic remainder is left stranded in Pot.


Relevant code in src/Pot.sol:

i_token.transfer(msg.sender, managerCut);
uint256 claimantCut = (remainingRewards - managerCut) / i_players.length;

Risk

Likelihood: High

  • This executes in the normal lifecycle (closePot) after claim period.

  • Any run with partial participation (not all players claiming) triggers the under-distribution condition.


Impact: High

  • Permanent lock of part of rewards in Pot (economic loss and accounting drift).

  • Manager cut is routed to a contract address without a recovery path, making intended payout inaccessible.


Concrete impact example

Assume:

  • remainingRewards = 900

  • Manager cut 10% => 90

  • players = 3, claimants = 1


Current implementation computes claimant unit using players:

  • (900 - 90) / 3 = 270

  • Only 1 claimant is paid -> 270 distributed

  • 540 remains stranded in Pot

  • 90 manager cut goes to ContestManager contract (not manager EOA)


Proof of Concept

Reproduced with deterministic Foundry tests:

  • testClosePotLeavesLockedDustWhenNotAllPlayersClaim()

  • testManagerCutSentToContestManagerContractNotHumanOwner()


File:

  • test/MyCutFindings.t.sol


Run:

forge test --match-contract MyCutFindingsTest -vvv

Observed in test output:

  • Pot retains non-zero post-close balance (225 in fixture).

  • Owner/manager EOA receives 0 manager-cut tokens.

  • ContestManager contract token balance increases by manager-cut amount (50 in fixture).


Recommended Mitigation

  1. Route manager cut to an explicit configured payout recipient (e.g., manager/owner address), not msg.sender.

  2. Compute claimant redistribution with claimants.length (or define explicit residual policy) and avoid denominator mismatch.

  3. Enforce accounting invariant in close flow:

    • managerCut + totalClaimantPaid + residual == remainingRewards

  4. Add controlled rescue/withdraw pathway for unexpected residual tokens (strict access control + event emission).


- i_token.transfer(msg.sender, managerCut);
+ i_token.transfer(managerPayoutRecipient, managerCut);
- uint256 claimantCut = (remainingRewards - managerCut) / i_players.length;
+ uint256 claimantCut = (remainingRewards - managerCut) / claimants.length;
Updates

Lead Judging Commences

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