Rock Paper Scissors

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

Permanent Ownership Assignment of WinningToken in RockPaperScissors Contract

Summary
The ownership of the WinningToken contract is permanently assigned to the specific instance of the RockPaperScissors contract that deploys it. There is no mechanism within RockPaperScissors to transfer this ownership, potentially hindering future upgrades or administrative changes related to the token.

Vulnerability Details
The RockPaperScissors constructor deploys a new WinningToken instance:

// In RockPaperScissors constructor
constructor() {
winningToken = new WinningToken(); // Deploys the token
adminAddress = msg.sender;
}

The WinningToken constructor inherits Ownable(msg.sender), assigning ownership to the deployer:

// In WinningToken constructor
constructor() ERC20("Rock Paper Scissors Winner Token", "RPSW") Ownable(msg.sender) {
// msg.sender here is the address of the deploying RockPaperScissors contract
}

The WinningToken contract has standard Ownable functions (owner(), transferOwnership()), but these can only be called by its current owner.
Crucially, the RockPaperScissors contract, despite being the owner of the WinningToken, lacks any function that would allow its adminAddress (or anyone else) to call winningToken.transferOwnership() to transfer the token's ownership to a different address (e.g., a new game contract, a DAO, or a multisig).
Therefore, the deployed WinningToken instance is permanently owned by the specific RockPaperScissors contract address that created it.

Impact
This isn't a direct exploit but a design limitation with potential future consequences:

  • Upgrade Difficulty: If a new version of the RockPaperScissors game contract needs to be deployed (e.g., to fix bugs or add features), the new contract cannot become the owner of the original WinningToken. This means the new game contract couldn't mint the existing token, potentially requiring a new, separate token or complex migration logic.

  • Administrative Inflexibility: If the project governance decides to transfer the administration of the token contract itself (e.g., to a multisig wallet for safer management), this is impossible without deploying a completely new token. The original token remains tied to the potentially obsolete game contract.

  • Loss of Control if Game Contract Compromised: While unlikely, if the RockPaperScissors contract itself had a severe vulnerability allowing arbitrary calls, an attacker could potentially misuse the token ownership (e.g., minting tokens). Separating token ownership could mitigate this risk.

Tools Used
Manual code review.

Recommendations
To add flexibility for future upgrades and administration, introduce a function within the RockPaperScissors contract that allows the current adminAddress to transfer the ownership of the WinningToken contract.

contract RockPaperScissors {
// ... existing code ...
WinningToken public immutable winningToken;
address public adminAddress;
// ... existing functions ...
+ /**
+ * @notice Transfers ownership of the WinningToken contract to a new address
+ * @dev Only callable by the current admin of the RockPaperScissors contract.
+ * @param _newOwner The address to transfer token ownership to.
+ */
+ function transferTokenOwnership(address _newOwner) external {
+ require(msg.sender == adminAddress, "Only admin can transfer token ownership");
+ // Requires WinningToken to implement Ownable's transferOwnership
+ winningToken.transferOwnership(_newOwner);
+ }
// ... rest of contract ...
}
Updates

Appeal created

m3dython Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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