MyCut

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

Unchecked ERC20 Return Values Allow Silent Transfer Failures

Root + Impact

Description

The contracts use direct transfer() and transferFrom() calls without checking return values. Some ERC20 tokens (like USDT) don't revert on failure but return false. This allows silent failures where the contract state updates as if transfers succeeded when they actually failed, leading to accounting mismatches and potential fund loss.

// In fundContest():
token.transferFrom(msg.sender, address(pot), totalRewards); // No return check
// In _transferReward():
function _transferReward(address player, uint256 reward) internal {
i_token.transfer(player, reward); // No return check
}
// In closePot():
i_token.transfer(msg.sender, managerCut); // No return check

Risk

Impact:

  1. Players can claim rewards without actually receiving tokens if transfer fails silently.

  2. Contest funding can fail silently, leaving Pot unfunded while appearing funded. 3. Manager cut and redistribution can fail without detection. This breaks the entire reward distribution mechanism.

Proof of Concept

// Deploy malicious token that returns false on transfer
contract MaliciousToken is IERC20 {
function transfer(address, uint256) external pure returns (bool) {
return false; // Silent failure
}
function transferFrom(address, address, uint256) external pure returns (bool) {
return false; // Silent failure
}
}
// Create contest with malicious token
// fundContest() appears to succeed but no tokens transferred
// Players call claimCut(), state updates but receive no tokens
// playersToRewards[player] = 0 but player got nothing

Recommended Mitigation

// Use SafeERC20 library:
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract Pot {
using SafeERC20 for IERC20;
function _transferReward(address player, uint256 reward) internal {
i_token.safeTransfer(player, reward); // Will revert on failure
}
}
contract ContestManager {
using SafeERC20 for IERC20;
function fundContest(uint256 index) public onlyOwner {
// ...
token.safeTransferFrom(msg.sender, address(pot), totalRewards);
}
}
Updates

Lead Judging Commences

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