Rock Paper Scissors

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

Token Accumulation and Inflation in Token-Based Games

Summary

The RockPaperScissors contract collects tokens from players in token-based games but never uses these collected tokens. Instead, it mints new tokens for winners and refunds. This leads to tokens being permanently locked in the contract and unnecessary inflation of the token supply, potentially devaluing the token over time.

Vulnerability Details

When players create or join a token-based game, they transfer tokens to the contract:

// In createGameWithToken():
winningToken.transferFrom(msg.sender, address(this), 1);
// In joinGameWithToken():
winningToken.transferFrom(msg.sender, address(this), 1);

However, when distributing prizes or refunds, the contract mints new tokens instead of using the ones it collected:

// In _finishGame() for token games:
if (game.bet == 0) {
// Mint a winning token
winningToken.mint(_winner, 2);
}
// In _handleTie() for token games:
if (game.bet == 0) {
winningToken.mint(game.playerA, 1);
winningToken.mint(game.playerB, 1);
}
// In _cancelGame() for token games:
if (game.bet == 0) {
if (game.playerA != address(0)) {
winningToken.mint(game.playerA, 1);
}
if (game.playerB != address(0)) {
winningToken.mint(game.playerB, 1);
}
}

This means that for every token game played:

  1. The contract collects 2 tokens (1 from each player)

  2. It mints 2 new tokens (either both to the winner or 1 to each player in a tie/cancel)

  3. The original 2 tokens remain locked in the contract forever

Impact

This token handling mechanism has several severe impacts:

  1. Permanent Token Lock: Tokens transferred to the contract are never used or returned, effectively removing them from circulation permanently.

  2. Token Supply Inflation: For each game played, the total supply of tokens increases by 2, leading to inflation that could devalue the token over time.

  3. Economic Imbalance: As more games are played, the ratio of locked tokens to circulating tokens increases, potentially creating economic instability in the token ecosystem.

  4. Contract Insolvency Risk: If the contract is ever upgraded or replaced, the locked tokens may become permanently inaccessible.

  5. Misaligned Incentives: The current mechanism incentivizes playing more games to mint more tokens, rather than focusing on winning games based on skill.

This is classified as a high severity issue because it directly impacts the economic model of the protocol and could lead to significant devaluation of the token over time.

Tools Used

  • Manual code review

  • Static analysis of the contract's token handling mechanisms

  • Economic analysis of the token supply model

Recommendations

Implement a proper token recycling mechanism that uses the collected tokens instead of minting new ones:

  1. Use transfer instead of mint for prize distribution:

    // In _finishGame() for token games:
    if (game.bet == 0) {
    // Transfer the collected tokens to the winner
    winningToken.transfer(_winner, 2);
    }
  2. For tie scenarios, return the original tokens:

    // In _handleTie() for token games:
    if (game.bet == 0) {
    winningToken.transfer(game.playerA, 1);
    winningToken.transfer(game.playerB, 1);
    }
  3. For cancellations, return the original tokens:

    // In _cancelGame() for token games:
    if (game.bet == 0) {
    if (game.playerA != address(0)) {
    winningToken.transfer(game.playerA, 1);
    }
    if (game.playerB != address(0)) {
    winningToken.transfer(game.playerB, 1);
    }
    }
  4. If a fee mechanism is implemented (as suggested in the previous issue), consider burning tokens for the fee rather than keeping them in the contract:

    // In _finishGame() for token games with fee:
    if (game.bet == 0) {
    // Calculate fee (e.g., 10% of 2 tokens)
    uint256 fee = 2 * PROTOCOL_FEE_PERCENT / 100;
    // Burn fee tokens
    winningToken.burn(address(this), fee);
    // Transfer remaining tokens to winner
    winningToken.transfer(_winner, 2 - fee);
    }

These changes would prevent token inflation and ensure that tokens are properly recycled within the ecosystem, maintaining the token's economic value over time.

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

Support

FAQs

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