Rock Paper Scissors

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

Tokens permanently locked in contract

Summary

A critical vulnerability exists in the Rock-Paper-Scissors game smart contract's token handling mechanism where user-contributed tokens are permanently locked in the contract instead of being returned to players. When users join token-based games (where game.bet = 0), they transfer tokens to the contract, but during game completion, the contract mints new tokens instead of returning the original ones. This results in token inflation and permanent loss of users original tokens.

Vulnerability Details

The issue involves three contract functions:

In the joinGameWithToken function, players transfer tokens to the contract:

// Transfer token to contract
winningToken.transferFrom(msg.sender, address(this), 1);

However, in the _finishGame function, instead of transferring these tokens back, new tokens are minted:

// Handle token prizes - winner gets both tokens
if (game.bet == 0) {
// Mint a winning token
winningToken.mint(_winner, 2);
}

Similarly, in the handleTie function, new tokens are minted rather than returning original tokens:

// Return tokens for token games
if (game.bet == 0) {
winningToken.mint(game.playerA, 1);
winningToken.mint(game.playerB, 1);
}

This creates several significant problems:

  1. The original tokens transferred by players to the contract are never returned or used

  2. These tokens remain permanently locked in the contract with no mechanism to retrieve them

  3. Each completed game increases the total token supply, causing inflation

  4. Both players' originally staked tokens are effectively burned

Impact

  • Players permanently lose their original tokens staked in the game.

  • Every completed token-based game increases the token supply by either 1 token (when there's a winner who receives 2 tokens) or 2 tokens (when there's a tie).

  • The contract accumulates tokens with no mechanism to distribute them.

Tools Used

Manual code review

Recommendations

Modify the token prize distribution logic to return the original tokens rather than minting new ones:

  • For the _finishGame function:

// Handle token prizes - winner gets both tokens
if (game.bet == 0) {
// Transfer the tokens to the winner instead of minting
winningToken.transfer(_winner, 2); // Transfer both staked tokens
}
  • For the handleTie function:

// Return tokens for token games
if (game.bet == 0) {
// Return each player's original token
winningToken.transfer(game.playerA, 1);
winningToken.transfer(game.playerB, 1);
}

Additionally, consider implementing a token accounting system like a mapping to keep track of tokens in the contract and ensure the contract always has enough tokens to distribute back to users.

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.