Rock Paper Scissors

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

Incorrect token handling leads to token inflation and loss

Summary

The core logic for handling the WinningToken in token based games where bet == 0 is flawed. When players create or join token games they transfer 1 WinningToken to the RockPaperScissors contract using transferFrom. However the contract never transfers these deposited tokens back out.

Vulnerability Details

The critical flaw lies in how these staked tokens are handled at the conclusion of the game. For a token game (game.bet == 0), the winner is supposed to receive the opponent's staked token plus potentially a bonus token. However the code executes winningToken.mint(_winner, 2). Instead of transferring the 2 tokens held by the contract it mints 2 entirely new tokens and gives them to the winner.

In case of a tie in a token game, each player should get their staked token back. The code, however, executes winningToken.mint(game.playerA, 1) and winningToken.mint(game.playerB, 1). Again it mints new tokens instead of returning the original ones held by the contract.

Impact

Every WinningToken transferred into the RockPaperScissors contract via transferFrom remains permanently locked within the contract's address. There is no code path to transfer these specific tokens out. The WinningToken supply increases every time a token game finishes, is tied or is cancelled because new tokens are minted instead of circulating the existing ones held by the contract. This breaks the intended tokenomics where tokens are primarily won and reused. Players end up with newly created tokens while their original stakes are lost forever in the contract.

A user could repeatedly create token games, have them cancelled and receive newly minted tokens each time which inflates their balance and the total supply while their initially staked tokens get locked

Recommendations

This is what I would add to make it secure:

// Handle token prizes - winner gets both tokens held by the contract
if (game.bet == 0) {
// Transfer the 2 tokens held by the contract to the winner
// Requires the contract to hold tokens transferred in by players
bool success = winningToken.transfer(_winner, 2);
require(success, "Token transfer failed");
} else {
// Mint a winning token for ETH games too (this part is okay as it's a reward)
winningToken.mint(_winner, 1);
}
Updates

Appeal created

m3dython Lead Judge 4 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

m3dython Lead Judge 4 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.