Rock Paper Scissors

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

`RockPaperScissors::_finishGame` Mints New Tokens Instead of Using Deposited Prize Pool ***

Summary

The internal _finishGame function incorrectly handles the prize distribution for token-based games. Instead of transferring the WinningTokens previously deposited by the players to the winner, it mints two new tokens, leaving the original deposited tokens locked within the contract.


Vulnerability Details

When players participate in a token-based game (identified by game.bet == 0), they each deposit 1 WinningToken into the RockPaperScissors contract via createGameWithToken and joinGameWithToken. At this point, the contract holds 2 tokens intended as the prize pool for that specific game.

However, when a winner is determined and _finishGame is called, the logic for token games executes the following line:

// In RockPaperScissors.sol, _finishGame function
} else {
// Token game
// Award winner tokens to the winner
winningToken.mint(_winner, 2); // <-- Mints 2 NEW tokens
emit GameFinished(_gameId, _winner, 0); // Emits with 0 prize, which is also potentially confusing
}

The winningToken.mint(_winner, 2); call creates two entirely new WinningTokens and sends them to the winner. It completely ignores the two tokens that were deposited by Player A and Player B, which remain held by the RockPaperScissors contract address.


Impact

This vulnerability has two primary negative impacts:

  • Locked Tokens: The 2 WinningTokens deposited by the players for each completed token game become permanently locked within the RockPaperScissors contract. There is no mechanism to withdraw or utilize these specific deposited tokens. Over time, the contract will accumulate a growing balance of unusable tokens.

  • Unnecessary Token Inflation: The WinningToken supply is inflated by 2 tokens for every completed token game. This contradicts the apparent intention of using deposited tokens as the prize pool and devalues existing tokens through unnecessary supply increase.

  • Deviation from Expected Behavior: Users expect the prize to consist of the tokens staked by the players, not newly created ones.


Tools Used

  • Manual code review.


Recommendations

The _finishGame function should be modified to transfer the existing tokens held by the contract to the winner, rather than minting new ones.

Replace the mint call with a transfer call:

} else {
// Token game
// Award winner tokens to the winner
- winningToken.mint(_winner, 2);
+ // Transfer the 2 deposited tokens from this contract to the winner
+ // Ensure the contract has sufficient balance (which it should if deposits worked)
+ require(winningToken.balanceOf(address(this)) >= 2, "Contract token balance insufficient"); // Safety check
+ winningToken.transfer(_winner, 2);
emit GameFinished(_gameId, _winner, 0); // Consider if emitting '2' as the prize amount is clearer for token games
}
Updates

Appeal created

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

Minting Instead of Transferring Staked Tokens

Mints new tokens upon game completion or cancellation for token-based games

Support

FAQs

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