MyCut

First Flight #23
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Claimants who claimed in time receive lesser rewards than expected

Summary

The protocol states that remaining rewards in the pot after the claim deadline would be distributed equally to claimants who claimed in time.

However when the closePot function is called, all players including players who have not claimed in time would get an equal share of the remaining rewards.

Vulnerability Details

The vulnerability lies in line 57 of the Pot contract.

Instead of using claimants.length which represents the number of claimants who claimed in time, i_players.length which represents all the registered players of the pot is used instead. This results in an inaccurate representation of the intention stated by the protocol.

Proof Of Concept

Working test case

function testIncorrectClaimantCut() public mintAndApproveTokens {
address player3 = makeAddr("player3");
players = [player1, player2, player3];
rewards = [32, 30, 38];
totalRewards = 100;
uint256 claimantsLength = 0;
// Create contest and fund pot
vm.startPrank(user);
contest = ContestManager(conMan).createContest(players, rewards, IERC20(ERC20Mock(weth)), totalRewards);
ContestManager(conMan).fundContest(0);
vm.stopPrank();
// Player 1 and player 2 claim their cut
vm.startPrank(player1);
Pot(contest).claimCut();
claimantsLength++;
assertEq(ERC20Mock(weth).balanceOf(player1), rewards[0]);
vm.stopPrank();
vm.startPrank(player2);
Pot(contest).claimCut();
claimantsLength++;
assertEq(ERC20Mock(weth).balanceOf(player2), rewards[1]);
vm.stopPrank();
// Close pot
vm.warp(91 days);
vm.startPrank(user);
ContestManager(conMan).closeContest(contest);
vm.stopPrank();
uint256 unclaimedRewards = totalRewards - rewards[0] - rewards[1];
uint256 managerCut = unclaimedRewards / 10;
// Expected claimantCut: 17
uint256 expectedClaimantCut = (unclaimedRewards - managerCut) / claimantsLength;
// Player 1 and player 2 not getting their expected claimant cut
assertNotEq(ERC20Mock(weth).balanceOf(player1), rewards[0] + expectedClaimantCut);
assertNotEq(ERC20Mock(weth).balanceOf(player2), rewards[1] + expectedClaimantCut);
}

Impact

The vulnerability leads to claimants who claimed in time to receive lesser rewards than the protocol promised after the pot is closed.

Tools Used

Foundry, manual review

Recommended Mitigation

To mitigate this vulnerability, the closePot function in the Pot contract should calculate claimantCut using the length of claimants instead of i_players.

function closePot() external onlyOwner {
...
...
if (remainingRewards > 0) {
uint256 managerCut = remainingRewards / managerCutPercent;
i_token.transfer(msg.sender, managerCut);
- uint256 claimantCut = (remainingRewards - managerCut) / i_players.length;
+ uint256 claimantCut = (remainingRewards - managerCut) / claimants.length;
for (uint256 i = 0; i < claimants.length; i++) {
_transferReward(claimants[i], claimantCut);
}
}
}
Updates

Lead Judging Commences

equious Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Incorrect distribution in closePot()

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.