MyCut

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

ERC20 `transfer`/`transferFrom` return values are ignored throughout the protocol, so a non-reverting failed transfer is treated as success

Severity: L · Impact: M · Likelihood: L

Description

  • The protocol moves tokens with the raw transfer/transferFrom calls and never checks the boolean return value.

  • ERC20 tokens that signal failure by returning false (instead of reverting) will pass silently: funding can appear to succeed while no tokens moved, and reward payouts can be recorded as complete while the recipient received nothing.

// src/Pot.sol:L64-L66
function _transferReward(address player, uint256 reward) internal {
@> i_token.transfer(player, reward); // return value ignored (also L55 closePot manager cut)
}
// src/ContestManager.sol:L37
@> token.transferFrom(msg.sender, address(pot), totalRewards); // return value ignored

Risk

Likelihood

  • Low: the contest scope states "Standard ERC20 Tokens Only," and standard tokens revert on failure. The issue bites only with non-standard/return-false tokens.

Impact

  • Medium where it applies: accounting desync — remainingRewards and claimants are updated as if paid, so users/owner can permanently lose funds on a silent failure.

Proof of Concept

With a token whose transfer returns false instead of reverting: in claimCut (src/Pot.sol:L37) the state is updated (playersToRewards[player]=0, remainingRewards -= reward, claimants.push) and then _transferReward silently fails — the player is marked as paid but received nothing, and their entry is zeroed so they cannot retry. The same pattern lets fundContest report success without funding the Pot.

Recommended Mitigation

Use OpenZeppelin SafeERC20, which reverts on a false return.

// src/Pot.sol:L4-L5 and call sites
+ import {SafeERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
contract Pot is Ownable(msg.sender) {
+ using SafeERC20 for IERC20;
...
- i_token.transfer(player, reward);
+ i_token.safeTransfer(player, reward);

Apply safeTransferFrom likewise at src/ContestManager.sol:L37. Why: safe* reverts on failure, so state is never advanced against a transfer that did not happen.

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!