Rock Paper Scissors

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

Token Stakes Permanently Locked with Supply Inflation in Token-Based Games

Submission Details

Description

The RockPaperScissors contract permanently locks player token stakes in token-based games while minting new tokens for payouts, causing continuous supply inflation and effective loss of player deposits.

Summary

In token-based games, 1 RPSW token is transferred from each player to the contract through transferFrom() calls when creating and joining games. However, these tokens are never transferred back to players. Instead, when a game completes, is tied, or is canceled, the contract mints new tokens for the players rather than returning the original staked tokens. This design flaw permanently locks tokens in the contract with no mechanism to recover them and creates continuous token supply inflation, devaluing the token over time.

Vulnerability Details

The issue occurs in three primary contract locations:

  1. Token collection without return mechanism:

// In createGameWithToken()
winningToken.transferFrom(msg.sender, address(this), 1);
// In joinGameWithToken()
winningToken.transferFrom(msg.sender, address(this), 1);
  1. Minting new tokens instead of returning staked ones:

// In _finishGame() (winner case)
if (game.bet == 0) {
// Mint new tokens instead of returning original stakes
winningToken.mint(_winner, 2);
}
// In _handleTie() (tie case)
if (game.bet == 0) {
winningToken.mint(game.playerA, 1);
winningToken.mint(game.playerB, 1);
}
// In _cancelGame() (game canceled)
if (game.bet == 0) {
if (game.playerA != address(0)) {
winningToken.mint(game.playerA, 1);
}
if (game.playerB != address(0)) {
winningToken.mint(game.playerB, 1);
}
}
  1. Missing token recovery functionality:
    The contract lacks any function to withdraw, burn, or otherwise retrieve the locked tokens.

For each completed token-based game, two tokens are permanently locked in the contract, and two new tokens are minted, increasing the total supply. This creates an inflationary mechanism with no bounds, as the WinningToken contract has no maximum supply cap.

Impact

  1. Permanent token loss for players: Players never recover their original staked tokens.

  2. Unchecked token supply inflation: Each token-based game increases total token supply by 2 tokens.

  3. Token value dilution: As more games are played, the uncapped inflation devalues existing tokens.

  4. Economic imbalance: The design undermines any economic utility model for the token.

This is classified as High severity because it:

  • Results in permanent loss of user assets (the staked tokens)

  • Creates an economic design flaw that systemically affects all users

  • Increases in impact as more token-based games are played

Tools Used

Manual code review

Recommendations

Implement one of the following solutions:

Option 1: Return original staked tokens (preferred)

// In _finishGame() for token games
if (game.bet == 0) {
// Transfer both staked tokens to winner
winningToken.transfer(_winner, 2);
}
// Similar changes in _handleTie() and _cancelGame()

Option 2: Burn and mint exact amounts

// In _finishGame() for token games
if (game.bet == 0) {
// Burn the staked tokens
winningToken.burn(address(this), 2);
// Mint the exact same amount to winner
winningToken.mint(_winner, 2);
}
Updates

Appeal created

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