Last Man Standing

First Flight #45
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Impact: medium
Likelihood: low
Invalid

Claim Fee Parameters Can Be Modified Mid-Game

Summary

The contract owner can modify initial claim fee and fee increase percentage during an active game round, potentially disrupting game economics and player expectations.

Description

The updateClaimFeeParameters() function allows the owner to change initialClaimFee and feeIncreasePercentage at any time, including during active gameplay. While initialClaimFee only affects future rounds, feeIncreasePercentage immediately impacts how the current claimFee will grow in the ongoing game.

Root Cause

No restriction prevents fee parameter updates during active gameplay:

function updateClaimFeeParameters(
uint256 _newInitialClaimFee,
uint256 _newFeeIncreasePercentage
) external onlyOwner isValidPercentage(_newFeeIncreasePercentage) {
require(_newInitialClaimFee > 0, "Game: New initial claim fee must be greater than zero.");
initialClaimFee = _newInitialClaimFee; // Only affects future rounds
feeIncreasePercentage = _newFeeIncreasePercentage; // Immediately affects current game!
emit ClaimFeeParametersUpdated(_newInitialClaimFee, _newFeeIncreasePercentage);
}

Impact

  • Economic Manipulation: Owner can change fee growth rate mid-game to favor certain outcomes

  • Unfair Advantage: Players who planned based on original fee increase rate are disadvantaged

  • Trust Issues: Game economics become unpredictable and manipulable

  • Strategic Disruption: Mid-game changes can invalidate player strategies

Proof of Concept

function testClaimFeeParametersUpdateMidGame() public {
uint256 originalFeeIncrease = game.feeIncreasePercentage();
// Player1 claims throne
vm.prank(player1);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
uint256 expectedNextFee = INITIAL_CLAIM_FEE + (INITIAL_CLAIM_FEE * originalFeeIncrease) / 100;
assertEq(game.claimFee(), expectedNextFee);
// Owner dramatically changes fee increase mid-game
vm.prank(deployer);
game.updateClaimFeeParameters(INITIAL_CLAIM_FEE, 50); // Change from 10% to 50%
// Player2 claims throne
vm.prank(player2);
game.claimThrone{value: expectedNextFee}();
// Next fee calculation now uses NEW percentage (50% instead of 10%)
uint256 newExpectedFee = expectedNextFee + (expectedNextFee * 50) / 100;
assertEq(game.claimFee(), newExpectedFee);
// Player3 now faces much higher fee than originally expected
assertGt(newExpectedFee, expectedNextFee * 140 / 100); // More than 40% higher
}
function testInitialClaimFeeUpdateImpact() public {
// Start a game
vm.prank(player1);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
// Fast forward and end the game
vm.warp(block.timestamp + GRACE_PERIOD + 1);
game.declareWinner();
// Owner changes initial claim fee before reset
vm.prank(deployer);
game.updateClaimFeeParameters(0.5 ether, FEE_INCREASE_PERCENTAGE); // 5x increase
// Reset game
vm.prank(deployer);
game.resetGame();
// New round requires much higher initial fee
assertEq(game.claimFee(), 0.5 ether);
// This could surprise players expecting original fee
vm.prank(player2);
vm.expectRevert("Game: Insufficient ETH sent to claim the throne.");
game.claimThrone{value: INITIAL_CLAIM_FEE}(); // Old fee amount fails
}

Recommended Mitigation

Restrict fee parameter updates to prevent mid-game changes:

function updateClaimFeeParameters(
uint256 _newInitialClaimFee,
uint256 _newFeeIncreasePercentage
) external onlyOwner isValidPercentage(_newFeeIncreasePercentage) {
require(_newInitialClaimFee > 0, "Game: New initial claim fee must be greater than zero.");
require(gameEnded || currentKing == address(0), "Game: Cannot update fee parameters during active game");
initialClaimFee = _newInitialClaimFee;
feeIncreasePercentage = _newFeeIncreasePercentage;
emit ClaimFeeParametersUpdated(_newInitialClaimFee, _newFeeIncreasePercentage);
}
Updates

Appeal created

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
icon0x Submitter
4 months ago
inallhonesty Lead Judge
4 months ago
inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!