Rock Paper Scissors

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

Token Supply Inflation Through Incorrect Token Handling in Game Resolution

Summary

The contract incorrectly mints new tokens instead of returning the escrowed tokens to players during game cancellation or tie scenarios, leading to token supply inflation, a breakdown of the token economy and Permanent lockup of originally transferred tokens (no withdraw function for locked token

Vulnerability Details

When players use Winner Tokens to stake in games, the tokens are transferred to the contract and should be returned to players in certain scenarios. Instead, the contract mints new tokens in various resolution paths:

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

This issue causes:

  1. Original tokens remain locked in the contract permanently

  2. New tokens are minted needlessly, increasing total supply

  3. Token economy is disrupted with each game resolution

  4. Token value potentially diminishes due to inflation

Impact

This vulnerability results in:

  • Unbounded token supply inflation

  • Devaluation of Winner Tokens over time

  • Broken token economics

  • Permanent lockup of originally transferred tokens

  • Potential manipulation of token scarcity/value

Tools Used

  • Manual code review

  • Token flow analysis

  • Economic impact assessment

Recommendations

  1. Return Escrowed Tokens: Modify the resolution functions to transfer escrowed tokens instead of minting:

function _handleTie(uint256 _gameId) internal {
Game storage game = games[_gameId];
game.state = GameState.Finished;
// ETH handling...
// Return escrowed tokens
if (game.bet == 0) {
if (game.tokenAEscrowed > 0) {
winningToken.transfer(game.playerA, game.tokenAEscrowed);
}
if (game.tokenBEscrowed > 0) {
winningToken.transfer(game.playerB, game.tokenBEscrowed);
}
}
emit GameFinished(_gameId, address(0), 0);
}
Updates

Appeal created

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

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