MyCut

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

Missing validation that sum of rewards equals totalRewards allows inconsistent funding and reward accounting

Root + Impact

Description

  • The protocol expects totalRewards to equal the total amount distributed across all players.

  • However, there is no validation ensuring that the sum of rewards[] matches totalRewards, allowing contests to be created with inconsistent reward configurations.

// ContestManager.sol
function createContest(
address[] memory players,
uint256[] memory rewards,
IERC20 token,
uint256 totalRewards
)
public
onlyOwner
returns (address)
{
// @> Missing validation that sum(rewards) == totalRewards
Pot pot = new Pot(players, rewards, token, totalRewards);
contests.push(address(pot));
contestToTotalRewards[address(pot)] = totalRewards;
return address(pot);
}
// Pot.sol
constructor(
address[] memory players,
uint256[] memory rewards,
IERC20 token,
uint256 totalRewards
) {
i_rewards = rewards;
i_totalRewards = totalRewards;
// @> Assumes totalRewards matches actual distributed rewards
remainingRewards = totalRewards;
}

Risk

Likelihood:

  • Contest parameters are manually configured during creation, making misconfiguration realistic.

  • The contract accepts inconsistent totals without validation, allowing invalid accounting states.

Impact:

  • Reward distribution becomes inconsistent with the funded amount.

  • Excess or unallocated funds may remain in the contract until manually recovered by the owner.


Proof of Concept

A contest is created where:

rewards = [100 ether, 200 ether, 200 ether]; // sum = 500 ether
totalRewards = 1000 ether;

The contract is funded with 1000 ether, but only 500 ether is assigned to players:

playersToRewards[user1] = 100 ether;
playersToRewards[user2] = 200 ether;

Result:

1000 ether funded
500 ether claimable
500 ether unallocated

The remaining funds are not distributed via the reward logic and require manual recovery (e.g., closing the pot by the owner).


Recommended Mitigation

Validate that the sum of rewards matches totalRewards before deploying the Pot.

function createContest(
address[] memory players,
uint256[] memory rewards,
IERC20 token,
uint256 totalRewards
)
public
onlyOwner
returns(address)
{
+ uint256 sum;
+ for (uint256 i = 0; i < rewards.length; i++) {
+ sum += rewards[i];
+ }
+ require(
+ sum == totalRewards,
+ "Invalid total rewards"
+ );
Pot pot = new Pot(players, rewards, token, totalRewards);
contests.push(address(pot));
contestToTotalRewards[address(pot)] = totalRewards;
return address(pot);
}

This enforces the invariant:

Total funded rewards == Total distributed rewards
Updates

Lead Judging Commences

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