Normally, after close, the pot should be emptied: cut to the manager, shares to claimants, remainder swept.
The two divisions independently drop remainders and no terminal sweep exists, so every pot whose numbers do not divide evenly strands (rem mod 10) + (rem - mc) mod players.length tokens forever.
Likelihood:
Almost every realistic reward pool contains numbers that do not divide evenly by 10 and by the player count — the dust accrues on each such contest as a matter of course.
Impact:
Verified: total 505 with three players of 100 leaves 2 tokens stranded per pot; across many pots this is a slow permanent leak of protocol funds.
Individually small, the stranded dust compounds with every other freeze issue in this report (all of which also rely on the missing sweep), so the same one-line mitigation increases every fix's effectiveness.
PoC explanation: The test demonstrates the floor-division dust on a realistic non-even pot. Setup: a 3-player pot, each with reward 100, funded with totalRewards = 505. Step: all three players claim their 300, leaving remainingRewards = 205; after 91 days the admin closes the pot. Arithmetic: managerCut = 205 / 10 = 20 (flooring 205 % 10 = 5), then claimantCut = (205 - 20) / 3 = 61 (flooring the remaining 185 % 3 = 2), paying out 20 + 3 × 61 = 203. Final assertion: the pot retains exactly 2 WETH — the two independent floor remainders — with no sweep function anywhere in the protocol to extract them. Every pot whose numbers do not divide evenly by 10 and by the player count strands the same kind of dust, making this a slow but permanent leak across all contests. Run with: forge test --match-test test_POC10_DustLocked -vv (PASS; asserts the residual 2).
⚠ Combination note: the sweep is only effective after V1's recipient fix — sweeping to msg.sender inside closePot would send the dust to the non-withdrawable ContestManager; and the sweep should run AFTER the idempotency flag (V4) so it cannot be repeated.
### \[H-03] Precision loss can lead to rewards getting stuck in the pot forever **Description:** When contest manager closes the pot by calling `Pot::closePot`, 10 percent of the remaining rewards are transferred to the contest manager and the rest are distributed equally among the claimants. It does this by dividing the rewards by the manager's cut percentage which is 10. Then the remaining rewards are divided by the number of players to distribute equally among claimants. Since solidity allows only integer division this will lead to precision loss which will cause a portion of funds to be left in the pot forever. Each pot follows the same method, so as number of pots grow, the loss of funds is very significant. **Impact:** Reward tokens get stuck in the pot forever which causes loss of funds. **Proof of code:** Add the below test to `test/TestMyCut.t.sol` ```javascript function testPrecisionLoss() public mintAndApproveTokens { ContestManager cm = ContestManager(conMan); uint playersLength = 3; address[] memory p = new address[](playersLength); uint256[] memory r = new uint256[](playersLength); uint tr = 86; p[0] = makeAddr("_player1"); p[1] = makeAddr("_player2"); p[2] = makeAddr("_player3"); r[0] = 20; r[1] = 23; r[2] = 43; vm.startPrank(user); address pot = cm.createContest(p, r, weth, tr); cm.fundContest(0); vm.stopPrank(); console.log("\n\ntoken balance in pot before: ", weth.balanceOf(pot)); vm.prank(p[1]); // player 2 Pot(pot).claimCut(); vm.prank(p[0]); // player 1 Pot(pot).claimCut(); vm.prank(user); vm.warp(block.timestamp + 90 days + 1); cm.closeContest(pot); console.log( "\n\ntoken balance in pot after closing pot: ", weth.balanceOf(pot) ); assert(weth.balanceOf(pot) != 0); } ``` Run the below test command in terminal ```Solidity forge test --mt testPrecisionLoss -vv ``` Which results in the below output ```Solidity [⠒] Compiling... [⠆] Compiling 1 files with 0.8.20 [⠰] Solc 0.8.20 finished in 2.57s Compiler run successful! Ran 1 test for test/TestMyCut.t.sol:TestMyCut [PASS] testPrecisionLoss() (gas: 936926) Logs: token balance in pot before: 86 token balance in pot after closing pot: 1 Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.75ms (654.60µs CPU time) Ran 1 test suite in 261.16ms (1.75ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests) ``` If you observe the output you can see the pot still has rewards despite distributing them to claimants. **Recommended Mitigations:** Fixed-Point Arithmetic: Utilize a fixed-point arithmetic library or implement a custom solution to handle fee calculations with greater precision.
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.