Normal behavior: Token transfers should revert on failure so the protocol never enters an inconsistent state where state is updated but funds are not moved.
The issue: Three ERC20 transfer() and transferFrom() calls ignore their return values. For non-reverting ERC20 tokens (e.g. USDT), a failed transfer returns false instead of reverting. The protocol updates state — deducting remainingRewards, pushing to claimants, transferring manager cut — while no tokens actually move. Funds are permanently lost with no error raised.
Likelihood:
Any ERC20 token that returns false instead of reverting on failure triggers silent loss — USDT and similar tokens are widely used.
The protocol accepts any standard ERC20 token — no whitelist restricts token behavior.Impact:
Player rewards are deducted from remainingRewards and playersToRewards zeroed out — player cannot reclaim even though no tokens were received.
Manager cut is transferred from state before token movement — unclaimed rewards are permanently locked in the contract.
The following Foundry test demonstrates that claimCut() silently succeeds even when the underlying ERC20 transfer returns false. A mock ERC20 that returns false on transfer() is used. The player's reward is zeroed in state, claimants is updated, but no tokens are received. The player cannot claim again.
Replace all three raw transfer() and transferFrom() calls with OpenZeppelin's SafeERC20 wrappers. safeTransfer() and safeTransferFrom() revert on failure regardless of whether the token returns false or reverts. Import SafeERC20 and apply using SafeERC20 for IERC20 in both contracts.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.