Last Man Standing

First Flight #45
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Severity: low
Valid

Grace Period Update Not Persistent Across Rounds

Root + Impact

Description

  • Normal behavior:
    When the owner updates the grace period, the new value should persist for all future rounds, not just the current one.

    Specific issue:
    The contract only updates gracePeriod for the current round. When resetGame is called, gracePeriod is set back to initialGracePeriod, ignoring any updates made during previous rounds. This means the admin’s changes are lost after each reset.

function updateGracePeriod(uint256 _newGracePeriod) external onlyOwner {
require(_newGracePeriod > 0, "Game: New grace period must be greater than zero.");
gracePeriod = _newGracePeriod;
emit GracePeriodUpdated(_newGracePeriod);
}
function resetGame() external onlyOwner gameEndedOnly {
// ...
gracePeriod = initialGracePeriod; // Reverts to old value
// ...
}

Risk

Likelihood:

  • This will occur every time the owner updates the grace period and starts a new round.

Impact:

  • Admin changes to the grace period are lost after each reset.

  • Users and off-chain systems may be misled about the actual grace period in effect.

Proof of Concept

The following test demonstrates the bug. The owner updates the grace period, ends the game, and resets it. The grace period reverts to its original value, not the updated one.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol";
import {Game} from "../src/Game.sol";
contract GameGracePeriodTest is Test {
Game public game;
address public deployer;
address public player;
uint256 public constant INITIAL_CLAIM_FEE = 0.1 ether;
uint256 public constant INITIAL_GRACE_PERIOD = 1 days;
uint256 public constant FEE_INCREASE_PERCENTAGE = 10;
uint256 public constant PLATFORM_FEE_PERCENTAGE = 5;
function setUp() public {
deployer = makeAddr("deployer");
player = makeAddr("player");
vm.deal(deployer, 10 ether);
vm.deal(player, 10 ether);
vm.startPrank(deployer);
game = new Game(
INITIAL_CLAIM_FEE,
INITIAL_GRACE_PERIOD,
FEE_INCREASE_PERCENTAGE,
PLATFORM_FEE_PERCENTAGE
);
vm.stopPrank();
}
function testGracePeriodNotPersistent() public {
// Owner updates gracePeriod to 3 days
vm.startPrank(deployer);
game.updateGracePeriod(3 days);
assertEq(game.gracePeriod(), 3 days, "Grace period should be updated to 3 days");
vm.stopPrank();
// Player claims throne so there is a king
vm.startPrank(player);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
vm.stopPrank();
// Fast forward past grace period and end the game
vm.warp(block.timestamp + 3 days + 1);
game.declareWinner();
// Owner resets the game
vm.startPrank(deployer);
game.resetGame();
vm.stopPrank();
// After reset, gracePeriod should revert to initialGracePeriod (1 day)
assertEq(game.gracePeriod(), INITIAL_GRACE_PERIOD, "Grace period should revert to initial value after reset");
}
}

Recommended Mitigation

Update both gracePeriod and initialGracePeriod together when changing the grace period:

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

Appeal created

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Grace period upgrade is not persistent after reset.

alicrali33 Auditor
4 months ago
0xrektified Auditor
4 months ago
inallhonesty Lead Judge
4 months ago
inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Grace period upgrade is not persistent after reset.

Support

FAQs

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

Give us feedback!