Root Cause: Essential state variables reset in wrong function or missing -> Impact: Owner cannot restart game, players locked out, rounds stall indefinitely - echoing real-world DeFi freezes
WHY WOULD ANYONE TRUST THE OWNER: It's a Game something similar to a gamble. So we can't expect the owner to be fair. The owner can change the rules at any time, so we can't expect the game to be fair.
The Game contract implements two key functions for ending and restarting rounds-declareWinner() and resetGame()-but their state-reset logic is inconsistently placed and omits important updates:
declareWinner()
Sets gameEnded = true, awards pot, resets pot to 0.
Missing: should also reset lastClaimTime, increment gameRound, and clear transient flags.
resetGame()
Resets currentKing, lastClaimTime, pot, claimFee, gracePeriod, gameEnded, and increments gameRound.
Issues:
lastClaimTime reset here is too late (should occur on declareWinner()).
pot reset is redundant (already zeroed).
gameRound is incremented here, but only allowed after game end, making a gap where no one can start next round.
Because the timing and placement of these resets are mismatched:
The owner cannot call resetGame() until after a winner is declared.
A winner cannot be declared unless prior round’s conditions are met (valid king & expired grace).
No way to resume or restart if something goes wrong mid-round (e.g., nobody claims, or a player crashes the flow).
declareWinner()✅ Properly marks the game as ended and awards the pot.
❌ Does not reset lastClaimTime - next round’s reference timestamp.
❌ Does not increment gameRound - round number remains stale.
❌ Does not reset flags that may impact other functions (e.g., a locked flag if added).
resetGame()✅ Clears currentKing, resets fees & periods.
❌ Round counter increment belongs in declareWinner().
❌ lastClaimTime only set here, causing a window where neither declareWinner() nor resetGame() can be called.
❌ Inflexible flow: if declareWinner() fails (e.g., edge-case revert), resetGame() is blocked until owner manually triggers a proper end.
Likelihood: Medium
This effectively freezes the game until the exact sequence of events occurs, with no manual override. Similar patterns have led to major DeFi protocol freezes, where missing epoch increments or mis-ordered resets halted rewards, trading, or governance.
Impact: High
Total Game Freeze: Neither declareWinner() nor resetGame() can progress if state deviates—owner and players are locked out.
Revenue & Engagement Loss: Stalled rounds mean no fees collected, no gameplay.
Trust Erosion: Players lose confidence knowing a single mistake halts the entire system.
Operational Risk: No emergency exit or admin rescue path increases governance overhead and complexity.
Real-World DeFi Analogues:
Olympus DAO Epoch Freeze (Mar 2021)
The staking contract forgot to increment epoch after each rebase. As a result, no rewards could be distributed for multiple cycles, effectively freezing all stakers until an off-chain intervention fixed the state.
Yam Finance Rebase Lock-up (Aug 2020)
A logic error caused the rebasing schedule to skip a critical state update, leading to an irreversible state where rebases never triggered, halting token supply adjustments and collapsing community confidence.
These examples underscore how misplaced or missing state updates in critical functions can halt entire DeFi protocols-sometimes for days—requiring complex governance or manual fixes.
Tools Used:
Foundry Test Suite
Chat-GPT AI Assistance (Report Grammar Check & Improvements)
Manual Review
step 1: go to test/Game.t.sol file
step 2: paste the above code ⬆️
step 3: run the test suite
step 4: See the Output
Scenario:
New Round Never Starts
Owner/players follow protocol: claimThrone(), wait grace, declareWinner().
Suppose an off-by-one error in grace check causes declareWinner() revert or.
Let's say, Due to high entry or game fees, no one claims the throne.
Game is not marked ended, so resetGame() cannot be called - stuck forever.
Force-End Needed But Impossible
No admin override exists to forcibly end a round or restart.
These gaps in state transitions lock the contract’s progression.
Consolidate State Resets in declareWinner()
Simplify resetGame()
Add Emergency Admin Function
Call the resetGame function inside declareWinner function
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.