Rock Paper Scissors

First Flight #38
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Invalid

Ownership Misconfiguration

Summary

The WinningToken contract suffers from centralized minting logic that depends solely on the deployer (owner). This design introduces operational inefficiencies and a critical centralization risk: only the deployer can distribute rewards, making the system impractical and vulnerable to misuse or compromise.

Vulnerability Details

Location:

constructor() ERC20("Rock Paper Scissors Winner Token", "RPSW") Ownable(msg.sender) {

Issue:
The Ownable contract is initialized with msg.sender, making the deployer the sole owner with exclusive access to the mint() function. This design assumes the deployer will remain actively involved in minting new tokens whenever players win games.

This creates two main issues:

  1. Manual Intervention: The reward distribution is not automated. A human must manually mint tokens by calling mint().

  2. Centralization Risk: If the owner’s private key is lost or compromised, attackers could mint an unlimited supply of tokens, leading to potential inflation and loss of trust in the token system.

Symptoms:

function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}

Only the owner can call this method, and ownership is initially bound to a single address via Ownable(msg.sender).

Impact

  • The game can't automatically reward winners with tokens

  • Single Point of Failure: If the owner’s key is compromised, unlimited minting is possible, leading to:

    • Token devaluation

    • Loss of fairness and trust in the reward mechanism

    • Potential total failure of the token economy

Tools Used

  • Manual Code Review: Detected the Ownable(msg.sender) pattern as the source of minting centralization.

  • Contextual Analysis: Considered the expected interaction between RockPaperScissors and WinningToken for automatic reward distribution.

Recommendations

  1. Transfer Ownership to the Game Contract:
    During deployment, pass the address of the RockPaperScissors contract to Ownable:

    constructor(address gameContract) ERC20("Rock Paper Scissors Winner Token", "RPSW") Ownable(gameContract) {
  2. Or Introduce Role-Based Access Control (RBAC):
    Replace Ownable with AccessControl and assign a MINTER_ROLE to the RockPaperScissors contract:

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    constructor(address gameContract) {
    _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
    _grantRole(MINTER_ROLE, gameContract);
    }
    function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
    _mint(to, amount);
    }
  3. General Best Practices:

    • Avoid hardcoding msg.sender ownership unless the contract is strictly admin-managed.

    • Automate or decentralized logic for core business processes (like token rewards) to improve robustness and scalability.


Updates

Appeal created

m3dython Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Design choice
m3dython Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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