MyCut

AI First Flight #8
Beginner FriendlyFoundry
EXP
View results
Submission Details
Severity: low
Valid

Total Rewards Not Equal to Sum of Individual Rewards

Root + Impact

Description

The ContestManager.createContest() function allows the contest creator to specify a totalRewards value independently of the individual rewards array. However, the contract never validates that totalRewards is equal to the sum of all values in the rewards array.

As a result, the Pot contract initializes its accounting using a trusted totalRewards value that may not accurately reflect the actual distribution defined by rewards. This breaks a fundamental invariant of the protocol:

Total funds deposited must equal the sum of all claimable rewards.

Because this invariant is not enforced, the internal accounting (remainingRewards) can become inconsistent with the actual reward allocations, leading to incorrect payouts and unsafe fund handling.

@> function createContest(address[] memory players, uint256[] memory rewards, IERC20 token, uint256 totalRewards)
public
onlyOwner
returns (address)
{
// Create a new Pot contract
Pot pot = new Pot(players, rewards, token, totalRewards);
contests.push(address(pot));
contestToTotalRewards[address(pot)] = totalRewards;
return address(pot);
}

Risk

Likelihood:

  • The vulnerability requires no special conditions or complex interactions.

  • It can be triggered at contest creation time using valid, non-reverting inputs.

  • There are no safeguards, assertions, or events that detect or prevent this misconfiguration.

  • Even unintentional misconfiguration by an honest owner can trigger the issue.

Impact:

  • Contract accounting becomes invalid

  • remainingRewards can be:

  1. Too large → leftover funds stolen via closePot

  2. Too small → underfunded claims revert

Proof of Concept

run the POC using the following command and it can be observed that despite the total rewards not being equal to the sum of individual rewards, the function compiles without any reverts.

uint[] fakeRewards = [3, 2];
function testRewardsNotqualToTotalRewards() public mintAndApproveTokens {
vm.startPrank(user);
ContestManager(conMan).createContest(players, fakeRewards, IERC20(weth), 10);
vm.stopPrank();
}
forge test --mt testRewardsNotqualToTotalRewards

Recommended Mitigation

it should be validated that the total rewards are indeed equal to the sum of individual reward amounts provided in the rewards array

+ uint256 sum;
+ for (...) sum += rewards[i];
+ require(sum == totalRewards, "Invalid totalRewards");
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Validated
Assigned finding tags:

[L-01] The logic for ContestManager::createContest is NOT efficient

## Description there are two major problems that comes with the way contests are created using the `ContestManager::createContest`. - using dynamic arrays for `players` and `rewards` leads to potential DoS for the `Pot::constructor`, this is possible if the arrays are too large therefore requiring too much gas - it is not safe to trust that `totalRewards` value supplied by the `manager` is accurate and that could lead to some players not being able to `claimCut` ## Vulnerability Details - If the array of `players` is very large, the `Pot::constructor` will revert because of too much `gas` required to run the for loop in the constructor. ```Solidity constructor(address[] memory players, uint256[] memory rewards, IERC20 token, uint256 totalRewards) { i_players = players; i_rewards = rewards; i_token = token; i_totalRewards = totalRewards; remainingRewards = totalRewards; i_deployedAt = block.timestamp; // i_token.transfer(address(this), i_totalRewards); @> for (uint256 i = 0; i < i_players.length; i++) { @> playersToRewards[i_players[i]] = i_rewards[i]; @> } } ``` - Another issue is that, if a `Pot` is created with a wrong `totalRewards` that for instance is less than the sum of the reward in the `rewards` array, then some players may never get to `claim` their rewards because the `Pot` will be underfunded by the `ContestManager::fundContest` function. ## PoC Here is a test for wrong `totalRewards` ```solidity function testSomePlayersCannotClaimCut() public mintAndApproveTokens { vm.startPrank(user); // manager creates pot with a wrong(smaller) totalRewards value- contest = ContestManager(conMan).createContest(players, rewards, IERC20(ERC20Mock(weth)), 6); ContestManager(conMan).fundContest(0); vm.stopPrank(); vm.startPrank(player1); Pot(contest).claimCut(); vm.stopPrank(); vm.startPrank(player2); // player 2 cannot claim cut because the pot is underfunded due to the wrong totalScore vm.expectRevert(); Pot(contest).claimCut(); vm.stopPrank(); } ``` ## Impact - Pot not created if large dynamic array of players and rewards is used - wrong totlRewards value leads to players inability to claim their cut ## Recommendations review the pot-creation design by, either using merkle tree to store the players and their rewards OR another solution is to use mapping to clearly map players to their reward and a special function to calculate the `totalRewards` each time a player is mapped to her reward. this `totalRewards` will be used later when claiming of rewards starts.

Support

FAQs

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

Give us feedback!