Pot.closePot() misroutes and under-distributes remainingRewards.
Two independent logic flaws create permanent fund-loss behavior:
managerCut is transferred to msg.sender, which is the ContestManager contract in this call path, not the intended human manager/owner recipient.
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:
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.
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)
Reproduced with deterministic Foundry tests:
testClosePotLeavesLockedDustWhenNotAllPlayersClaim()
testManagerCutSentToContestManagerContractNotHumanOwner()
File:
test/MyCutFindings.t.sol
Run:
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).
Route manager cut to an explicit configured payout recipient (e.g., manager/owner address), not msg.sender.
Compute claimant redistribution with claimants.length (or define explicit residual policy) and avoid denominator mismatch.
Enforce accounting invariant in close flow:
managerCut + totalClaimantPaid + residual == remainingRewards
Add controlled rescue/withdraw pathway for unexpected residual tokens (strict access control + event emission).
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.