MyCut

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

`closePot()` Function Can Be Called Multiple Times Even After the Funds are distributed

Root + Impact

Description

  • Describe the normal behavior in one or more sentences


    The pot should be closed once after 90 days and funds distributed exactly once.A fundamental principle of smart contract security is the Checks-Effects-Interactions (CEI) pattern. This function performs the "Interaction" (transferring tokens) without ever recording the "Effect" (updating the state to reflect that funds have been distributed).


  • Explain the specific issue or problem in one or more sentences


    There is no closed flag or state update preventing repeated calls. remainingRewards is not zeroed after distribution. The owner can call closePot() multiple times and repeatedly extract managerCut.

    Even though the admin is "Trusted," the protocol's safety should not rely on the admin's restraint. If the admin's private key is compromised, or if an automated script malfunctions, the entire contract balance—including funds belonging to other active pots—could be drained.

function closePot() external onlyOwner {
...
uint256 managerCut = remainingRewards / managerCutPercent; // @> recalculated each call
i_token.transfer(msg.sender, managerCut);
...
}

No state update occurs afterward

Risk

Likelihood:

  • Occurs whenever owner calls closePot() more than once

  • Occurs naturally due to lack of state transition protection

Impact:

  • Manager can repeatedly drain funds

  • Contract accounting becomes inconsistent

  • Severe trust and financial failure

Proof of Concept

Initial State: remainingRewards = 1,000. Contract Balance = 10,000.
Call 1: Owner calls closePot(). Manager receives 100 tokens. (Contract Balance: 9,900).
Call 2: Owner calls closePot() again. remainingRewards is still 1,000. Manager receives another 100 tokens. (Contract Balance: 9,800).
Repeat: This can be looped until the contract balance is insufficient to cover the transfer.

Recommended Mitigation

+ bool private closed;
function closePot() external onlyOwner {
+ require(!closed, "Already closed");
if (block.timestamp - i_deployedAt < 90 days) {
revert Pot__StillOpenForClaim();
}
+ closed = true;
if (remainingRewards > 0) {
uint256 managerCut = remainingRewards / managerCutPercent;
i_token.transfer(msg.sender, managerCut);
uint256 claimantCut = (remainingRewards - managerCut) / claimants.length;
for (uint256 i = 0; i < claimants.length; i++) {
_transferReward(claimants[i], claimantCut);
}
+ remainingRewards = 0;
}
}
Updates

Lead Judging Commences

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