Rock Paper Scissors

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

Player B is not validated when joining a game with token, allowing multiple players to enter the same game instance.

Summary

The RockPaperScissors::joinGameWithToken function does not validate whether a Player B is already assigned to a game instance. As a result, multiple users can join as Player B, with only the most recent one being stored, causing previous participants’ token to be irretrievably locked in the contract.

Vulnerability Details

In the RockPaperScissors::joinGameWithToken,there is no check to verify whether a player B is already present in the game. When a new player B joins the game, they overwrite the previous player B’s address. The previous player’s token remain locked in the contract, with no way to retrieve them.

function joinGameWithToken(uint256 _gameId) external {
Game storage game = games[_gameId];
require(game.state == GameState.Created, "Game not open to join");
require(game.playerA != msg.sender, "Cannot join your own game");
require(block.timestamp <= game.joinDeadline, "Join deadline passed");
require(game.bet == 0, "This game requires ETH bet");
require(
winningToken.balanceOf(msg.sender) >= 1,
"Must have winning token"
);
winningToken.transferFrom(msg.sender, address(this), 1);
@> //@audit There's no check to see if a player B is already in the game
game.playerB = msg.sender;
emit PlayerJoined(_gameId, msg.sender);
}

Impact

Multiple players can join as player B in a single game, but only the latest one will be recorded. The tokens of all previous player B entries will remain locked in the contract.

Tools Used

Manual review

Recommendations

You must check whether a game already has a player B before allowing a player to join.

function joinGameWithToken(uint256 _gameId) external {
Game storage game = games[_gameId];
require(game.state == GameState.Created, "Game not open to join");
require(game.playerA != msg.sender, "Cannot join your own game");
require(block.timestamp <= game.joinDeadline, "Join deadline passed");
require(game.bet == 0, "This game requires ETH bet");
require(
winningToken.balanceOf(msg.sender) >= 1,
"Must have winning token"
);
+ require(game.playerB == address(0) ,"PlayerB already in the game");
winningToken.transferFrom(msg.sender, address(this), 1);
game.playerB = msg.sender;
emit PlayerJoined(_gameId, msg.sender);
}
Updates

Appeal created

m3dython Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Absence of State Change on Join Allows Player B Hijacking

Game state remains Created after a player joins

Support

FAQs

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