Rock Paper Scissors

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

Unchecked Token Allowances

Security Report

Vulnerability Details

The contract interacts with ERC20 tokens without confirming whether users have permitted it to do so. Specifically, it calls transferFrom() without ensuring the user has approved the contract to spend their tokens. This can cause transactions to fail unexpectedly.

Bug Description

The ERC20 token standard requires users to explicitly approve contracts before they can transfer tokens on the user's behalf. This is done using the approve(spender, amount) function.

However, in this codebase, functions like createGameWithToken() and joinGameWithToken() call transferFrom() directly, without checking or handling cases where the user has not approved the transfer.

As a result:

  • If the user forgets to approve the contract first, the transaction reverts.

  • The error isn't handled gracefully, and gas is wasted.

  • The experience is confusing to users who may not understand why the transfer failed.

How It Works

✅ ERC20 Approval Requirement

A typical ERC20 token interaction involves two steps:

  1. User grants approval:

    winningToken.approve(address(gameContract), 1);
  2. Contract attempts transfer:

    winningToken.transferFrom(msg.sender, address(this), 1);

If step 1 is skipped or the approved amount is insufficient, step 2 fails.

Problem in Code

The game contract assumes that step 1 has already been done:

// In createGameWithToken()
winningToken.transferFrom(msg.sender, address(this), 1); // No check or confirmation

There is no:

  • Allowance validation

  • User guidance or an informative error message

  • Fallback handling for failed transfers

Impact

  • ** Transaction Failures**: Users who forget to call approve() will see their transactions revert.

  • ** Gas Wastage**: Failed transactions still cost gas, which can add up quickly for users.

  • ** Poor UX**: No helpful errors or prompts mean users may not understand what went wrong or how to fix it.

  • ** Reputation Risk**: A frustrating and unpredictable experience can reduce trust in the dApp.

Tools Used

  • Manual Review

Recommended Mitigation Steps

  1. Pre-Check Allowance
    Use IERC20.allowance() to confirm that the user has approved the contract:

    require(
    winningToken.allowance(msg.sender, address(this)) >= 1,
    "Insufficient token allowance"
    );
  2. Improve UX

    • Add frontend guidance prompting users to approve tokens before interacting.

    • If gas is cheap and usage patterns allow, consider separating approval and gameplay into two steps.

  3. Optional: Meta-Transaction Pattern
    If user experience is critical, explore meta-transactions or permit-based flows (ERC20Permit) to eliminate the need for manual approval.

Updates

Appeal created

m3dython Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
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.