Rock Paper Scissors

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

Token Stake Mismanagement Leads to Supply Inflation

Summary

In token-based games within the RockPaperScissors contract, each player deposits one WinningToken into the contract via transferFrom. However, during game completion or cancellation, the contract does not return these original tokens. Instead, it calls mint to create new tokens for the winners or for refund. This leads to permanent accumulation of tokens in the contract and unbounded inflation of the token supply, which undermines the intended scarcity and fairness of the reward mechanism.

Vulnerability Details

In the game creation and joining functions for token-based games:

winningToken.transferFrom(msg.sender, address(this), 1);

This deposits a token into the contract. But during game resolution (_finishGame, _handleTie, _cancelGame), tokens are not returned with:

winningToken.transfer(player, 1);

Instead, the contract mints new tokens:

winningToken.mint(_winner, 2); // or 1 for tie/refund

This leads to several problems:

  • Original tokens stay locked in the contract forever, untracked and unused.

  • The supply of WinningToken increases uncontrollably as every game mints additional tokens.

  • There's no upper bound to the inflation, breaking any tokenomics that rely on limited supply.

  • The "deposit" mechanism behaves more like a burn, but without actually burning tokens or informing the user.

Impact

  • Token inflation: Supply expands unchecked with each game, diluting the value of existing tokens.

  • Asset lockup: Deposited tokens are never returned and effectively lost.

  • Economy imbalance: Games become a minting loophole rather than a fair reward mechanism.

  • Potential exploit: Players could farm infinite tokens by joining and cancelling games repeatedly.

Tools Used

  • Code review

  • ERC20 token logic analysis

  • Tokenomics impact reasoning

Recommendations

Refactor the token deposit and refund logic to manage tokens without minting new ones:

  1. Replace mint(...) with proper transfer(...) from contract back to players:

    winningToken.transfer(playerA, 1);
    winningToken.transfer(playerB, 1);
  2. Track token-based deposits with internal accounting (e.g., via mapping) if needed for security/auditability.

  3. Only mint tokens as rewards, not as refunds or replacement of deposits.

  4. If game-winning rewards are to be minted, burn the original deposit tokens explicitly to maintain net supply control:

    winningToken.burn(1); // after verifying ownership in contract

Or consider redesigning the token to use ERC721 or ERC1155 if each "stake" is unique or requires traceability.

Updates

Appeal created

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