The contract integrates global snapshot freezing and individual state updates in an unsafe sequence inside the claimExpired() function. When a pool is resolved post-expiry, the global variables snapshotSumStakeTime and snapshotSumStakeTimeSq are frozen before the internal _clampUserSums(msg.sender) is called for the first claimer.
Because _clampUserSums forcefully scales up the individual registers userSumStakeTime and userSumStakeTimeSq to the minimum threshold without updating the global snapshot dynamically, the core pricing ratio in _bonusShare breaks down.
This mathematical desynchronization allows the first claimer to inflate their userScore against an outdated, lower globalScore, resulting in an illegitimate over-drain of the totalBonus pool. Consequently, subsequent stakers who attempt to call withdraw() hit a permanent mathematical panic underflow during the sumStakeTime -= userSumStakeTime execution, effectively causing a total Denial of Service and locking their principal capital inside the contract permanently.
Likelihood: High
This will occur whenever a pool passes its expiry deadline and the first-claiming user happens to be a large liquidity provider (whale) whose individual time-weighted states have not been synchronized or adjusted for an extended duration.
Tested locally on a native Forge environment ( FOUNDRY ) without external mock configurations. The execution log confirms the mathematical underflow and complete functional blockage on subsequent withdrawals:
Impact:
Hyper-Inflationary Reward Theft: A malicious first-claimer or a large whale can exploit the math to drain the totalBonus pool, extracting rewards far beyond their legitimate, fair shares.
Permanent Lock of User Funds: Because subsequent stakers will trigger a mathematical panic underflow during their withdrawal path, honest users lose the ability to pull out their principal capital. This results in a complete Denial of Service and traps user funds inside the smart contract indefinitely.
Recommended Mitigation
Execute and synchronize the _clampUserSums validation recursively across the entire eligible participant base BEFORE freezing and capturing static state values into the global snapshot variables inside the claimExpired() or resolution path.
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.
The contest is complete and the rewards are being distributed.