MyCut

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

Missing Funding Status Tracking Allows Duplicate Funding of Contests

Root + Impact

The ContestManager contract does not track whether a contest has already been funded within its state. Consequently, the owner can accidentally call fundContest multiple times for the same contest index, which results in extra tokens being pulled from the owner's address and permanently locked or misallocated inside the target Pot contract.

Description

The contract allows the owner to deploy individual Pot contracts for contests using createContest and subsequently fund them using fundContest.

However, fundContest lacks any state validation or tracking (such as a boolean flag or status mapping) to check if a specific contest index has already received its token allocation. Because the execution flow remains completely stateless regarding the funding history, the transfer logic will execute every time the function is called for that index, as long as the owner has a sufficient token balance and allowance.

Solidity

function fundContest(uint256 index) public onlyOwner {
Pot pot = Pot(contests[index]);
IERC20 token = pot.getToken();
uint256 totalRewards = contestToTotalRewards[address(pot)];
@> //may fund the constst more than once so there is no
@> //keeping track of which pots have yet to receive funds or
@> //which ones have already received funds
if (token.balanceOf(msg.sender) < totalRewards) {
revert ContestManager__InsufficientFunds();
}
token.transferFrom(msg.sender, address(pot), totalRewards);
/**
trnasfer is made before approve is made which can result in a failed txn
and the bool is ignored
*/
}

Risk

Likelihood: Medium

  • The owner executes the fundContest function multiple times due to a script error, network congestion resulting in retries, or simple manual operational oversight.

Impact: High

  • User/Protocol Funds Drain: The owner's token balance is unintentionally depleted by sending redundant rewards to the same Pot.

  • Locked Capital: Excess funds transferred to the Pot contract become stuck or improperly distributed, as the Pot is only configured to allocate the original totalRewards amount to its players.

Proof of Concept

  1. The owner calls createContest to initialize a new contest requiring 1,000 tokens.

  2. The owner calls fundContest(0) for the first time, successfully transferring 1,000 tokens to the Pot.

  3. The owner accidentally calls fundContest(0) a second time due to a UI lag or duplicate transaction submission.

  4. The transaction successfully executes again, pulling an additional 1,000 tokens from the owner and leaving the Pot contract with 2,000 tokens instead of the required 1,000.

Recommended Mitigation

Introduce a state mapping to record when a contest has completed its funding phase, and add a custom error check to prevent subsequent execution.

Solidity

contract ContestManager is Ownable {
// ... existing state variables ...
// Track funding status of each contest address
mapping(address => bool) public isContestFunded;
error ContestManager__AlreadyFunded();
// ...
function fundContest(uint256 index) public onlyOwner {
address contestAddress = contests[index];
// Revert if the contest has already been funded
if (isContestFunded[contestAddress]) {
revert ContestManager__AlreadyFunded();
}
Pot pot = Pot(contestAddress);
IERC20 token = pot.getToken();
uint256 totalRewards = contestToTotalRewards[contestAddress];
if (token.balanceOf(msg.sender) < totalRewards) {
revert ContestManager__InsufficientFunds();
}
// Set funding status to true before the external transfer
isContestFunded[contestAddress] = true;
token.transferFrom(msg.sender, address(pot), totalRewards);
}
}
Updates

Lead Judging Commences

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