MyCut

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

## M-5 — Unsafe ERC20 integration (raw transfer, ignored return values, no SafeERC20)

Root + Impact

All token movement uses raw transfer/transferFrom and discards the boolean return. If a token returns false without reverting, claimCut still zeroes playersToRewards and decrements remainingRewards, permanently losing the reward. Fee-on-transfer tokens under-fund the pot; blacklist/hook tokens can brick claimCut/closePot.

Description

// src/ContestManager.sol
token.transferFrom(msg.sender, address(pot), totalRewards); // @> return value ignored
​
// src/Pot.sol
function _transferReward(address player, uint256 reward) internal {
i_token.transfer(player, reward); // @> return value ignored
}
function claimCut() public {
...
playersToRewards[player] = 0; // @> state zeroed regardless of transfer success
remainingRewards -= reward;
...
}

Risk

Likelihood: Using any token that is not strictly standard (false-return, fee-on-transfer, rebasing, blacklist).

Impact: Silent non-payment with state loss; under-funded pot; bricked claims/close.

Proof of Concept

Token-behavior dependent; the state-before-transfer ordering in claimCut is the exploitable primitive:

// Conceptual: with a token whose transfer() returns false instead of reverting
function testFalseReturnLosesReward() public {
// 1. pot funded, alice has reward
// 2. alice calls claimCut()
// 3. playersToRewards[alice] == 0 and remainingRewards reduced
// 4. token.transfer() returns false, no tokens move -> alice's reward is gone
assertEq(pot.checkCut(alice), 0);
assertEq(token.balanceOf(alice), 0);
}

Recommended Mitigation

- import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
+ import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
+ import {SafeERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
+ using SafeERC20 for IERC20;
​
- token.transferFrom(msg.sender, address(pot), totalRewards);
+ token.safeTransferFrom(msg.sender, address(pot), totalRewards);
​
- i_token.transfer(player, reward);
+ i_token.safeTransfer(player, reward);

Updates

Lead Judging Commences

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