Last Man Standing

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

Owner Can Manipulate `gracePeriod` Mid-Round to Frontrun

Root + Impact

Description

  • The gracePeriod defines how long the game must wait after the last throne claim before a winner can be declared. It is used to control the pacing of the game and to determine when a round is considered over.

  • However, the contract allows the owner to arbitrarily change the gracePeriod at any time using the updateGracePeriod() function. This creates a critical trust dependency: the timing of when a winner can be declared is no longer guaranteed by the protocol but is subject to owner manipulation. For example, the owner could shorten the period right after a claim, enabling themselves or a favored address to call declareWinner() earlier than expected. This can be used to frontrun other participants, resulting in unfair or manipulated outcomes.

function updateGracePeriod(uint256 _newGracePeriod) external onlyOwner {
require(_newGracePeriod > 0, "Game: New grace period must be greater than zero.");
@> gracePeriod = _newGracePeriod;
emit GracePeriodUpdated(_newGracePeriod);
}

Risk

Likelihood:

  • Happens whenever the game is in progress and the owner decides to alter the grace period for personal advantage or based on inside information. Since the owner is often considered trustworthy, the likelihood is rather low.

  • Occurs more frequently in smaller games or games without governance or audits, where the owner is not publicly accountable.

Impact:

  • Undermines fairness and trust assumptions - users expect consistent timing rules that are not alterable mid-round.

  • Enables frontrunning and game manipulation by reducing the grace period just before calling declareWinner(), effectively guaranteeing the win.

Proof of Concept

// Step 1: Alice claims the throne
game.claimThrone{value: claimFee}(); // Alice is currentKing
// Step 2: Users expect gracePeriod = 3 days
// Step 3: Owner reduces it to 10 seconds
game.setGracePeriod(10); // Malicious action
// Step 4: Owner or ally immediately calls declareWinner()
vm.warp(block.timestamp + 11);
game.declareWinner(); // Alice is frontrun unfairly

The exploit occurs because Alice believed she had up to 3 days to remain king, but the owner maliciously changed the rules mid-round by reducing the grace period to 10 seconds. This allowed the owner (or an ally) to call declareWinner() almost immediately, cutting Alice's round short and claiming the win or ending the game before any competition.

Recommended Mitigation

The vulnerability can be mitigated by allowing the owner to update the future round parameter for the grace period at any time, but preventing the change from taking effect mid-round, preserving game integrity.

+ uint256 public nextRoundGracePeriod; // The grace period for the next new round
...
constructor(
uint256 _initialClaimFee,
uint256 _gracePeriod,
uint256 _feeIncreasePercentage,
uint256 _platformFeePercentage
) Ownable(msg.sender) { // Set deployer as owner
...
+ nextRoundGracePeriod = _platformFeePercentage;
...
}
...
function resetGame() external onlyOwner gameEndedOnly {
currentKing = address(0);
lastClaimTime = block.timestamp;
pot = 0;
claimFee = initialClaimFee;
- gracePeriod = initialGracePeriod;
+ gracePeriod = nextRoundGracePeriod;
gameEnded = false;
gameRound = gameRound + 1;
// totalClaims is cumulative across rounds, not reset here, but could be if desired.
emit GameReset(gameRound, block.timestamp);
}
...
function updateGracePeriod(uint256 _newGracePeriod) external onlyOwner {
require(_newGracePeriod > 0, "Game: New grace period must be greater than zero.");
- gracePeriod = _newGracePeriod;
+ nextRoundGracePeriod = _newGracePeriod;
emit GracePeriodUpdated(_newGracePeriod);
}
...
Updates

Appeal created

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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