Rock Paper Scissors

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

Missing ERC20 cpproval checks in `RockPaperScissors` when creating and joining the game with token

Summary

The RockPaperScissors contract allows players to create/join games using an ERC20 token (WinningToken). However, the functions createGameWithToken() and joinGameWithToken() call transferFrom() without first verifying that the player has approved the contract to spend tokens on their behalf.

Affected functions:

function createGameWithToken(uint256 _totalTurns, uint256 _timeoutInterval) external {
winningToken.transferFrom(msg.sender, address(this), 1); // No approval check
// ... rest of logic ...
}
function joinGameWithToken(uint256 _gameId) external {
winningToken.transferFrom(msg.sender, address(this), 1); // No approval check
// ... rest of logic ...
}

Impact

Technical Impact:

  • Transactions revert if allowance is insufficient, breaking game flow.

  • Partial state updates (e.g., playerB assigned but tokens not transferred) may leave the contract in an inconsistent state.

Business Impact

  • Poor UX: Players may repeatedly fail transactions due to missing approvals.

  • Frontrunning Risk: Attackers can invalidate pending transactions by resetting allowances (approve(contract, 0)) .

Tools Used

Manual reviews

Recommendations

Option 1: Explicit Approval Check

function createGameWithToken(uint256 _totalTurns, uint256 _timeoutInterval) external {
require(
winningToken.allowance(msg.sender, address(this)) >= 1,
"Approve contract to spend 1 token first"
);
winningToken.transferFrom(msg.sender, address(this), 1);
// ... rest of logic ...
}

Option 2: Use OpenZeppelin’s SafeERC20

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract RockPaperScissors {
using SafeERC20 for IERC20;
function createGameWithToken(uint256 _totalTurns, uint256 _timeoutInterval) external {
winningToken.safeTransferFrom(msg.sender, address(this), 1); // Auto-reverts if not approved
// ... rest of logic ...
}
}

And repeat the same for joinGameWithToken()

Updates

Appeal created

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

Broken Token Game Creation

createGameWithToken and joinGameWithToken functions will revert because they attempt transferFrom without requiring the user to first approve

Support

FAQs

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