Rock Paper Scissors

First Flight #38
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: low
Invalid

Unchecked ERC20 Transfer Return Values May Lead to Inconsistent Game States

Summary

The RockPaperScissors contract ignores the return values from transferFrom calls when handling token transfers in createGameWithToken and joinGameWithToken functions. While many ERC20 tokens revert on failure, some tokens (like USDT) may return false instead of reverting. This could lead to games being created or joined without actual token transfers occurring.

Vulnerability Details

In both createGameWithToken and joinGameWithToken functions, the contract calls transferFrom on the WinningToken contract but does not check the return value:

// In createGameWithToken
winningToken.transferFrom(msg.sender, address(this), 1);
// In joinGameWithToken
winningToken.transferFrom(msg.sender, address(this), 1);

According to the ERC20 standard, transferFrom returns a boolean value indicating success or failure. OpenZeppelin's implementation reverts on failure, but this behavior is not guaranteed across all ERC20 tokens, especially non-standard implementations.

While the current WinningToken implementation (based on OpenZeppelin's ERC20) reverts on failed transfers, the contract should follow defensive programming practices by checking return values, especially if there's any possibility of the token contract being upgraded or replaced in the future.

Impact

The impact is considered low because:

  1. The current WinningToken implementation would revert on failed transfers

  2. No immediate funds are at risk

  3. The issue would only manifest if the token implementation were changed or a different token were used

However, if the token implementation were changed or if the contract were adapted to use other tokens, this could lead to:

  • Games being created or joined without actual token transfers

  • Inconsistent contract state versus actual token balances

  • Players potentially losing tokens if the game completes but token transfers had failed

Tools Used

Slither static analysis and manual review

Recommendations

Implement proper return value checking for all token transfers. OpenZeppelin's SafeERC20 library is recommended for this purpose:

// Import SafeERC20
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// Use the library
using SafeERC20 for IERC20;
// In createGameWithToken
winningToken.safeTransferFrom(msg.sender, address(this), 1);
// In joinGameWithToken
winningToken.safeTransferFrom(msg.sender, address(this), 1);

Alternatively, if SafeERC20 is not preferred, explicitly check the return values:

// In createGameWithToken
require(winningToken.transferFrom(msg.sender, address(this), 1), "Token transfer failed");
// In joinGameWithToken
require(winningToken.transferFrom(msg.sender, address(this), 1), "Token transfer failed");

This ensures that the contract state is only updated when token transfers are successful, maintaining consistency between game state and token balances.

Updates

Appeal created

m3dython Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Missing Check on External Call Return Value

ERC20 implementation typically reverts on transfer failures

m3dython Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Missing Check on External Call Return Value

ERC20 implementation typically reverts on transfer failures

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.