Normally, the first interaction during UNDER_ATTACK or PROMOTION_REQUESTED records riskWindowStart and resets the global accounting moments so that every existing stake is treated as entering at the beginning of the observed risk period.
Even if the agreement remains attackable until the pool expires, claimExpired() should still resolve the pool and allow stakers to recover their principal and bonus without requiring moderator intervention.
However, a stake accepted before the risk window can later overflow when _markRiskWindowStart() recalculates the global squared moment using a newer timestamp.
A total stake can satisfy:
and therefore be accepted by stake(), while later satisfying:
The active-risk observation then reverts completely, leaving riskWindowStart equal to zero.
While the registry remains in an active-risk state, every subsequent interaction that calls _observePoolState() reaches the same overflow. This includes claimExpired() after the pool expiry, preventing the permissionless expiry backstop from resolving the pool.
The attacker does not need to control the registry or any trusted role. The overflow is triggered by the registry following its normal lifecycle from ATTACK_REQUESTED to UNDER_ATTACK.
Impact: Medium
Every attempt to observe the active-risk state reverts, so the pool cannot record riskWindowStart.
claimExpired() continues to revert after the pool expiry, preventing honest stakers from recovering their principal and bonus through the intended permissionless backstop.
All funds held by the pool remain inaccessible beyond the agreed expiry until a trusted registry role moves the agreement to a terminal state.
The expiry backstop therefore fails precisely when it is needed to resolve the pool without waiting for moderator intervention.
Likelihood: Medium
The attack requires an allowlisted standard ERC-20 with sufficiently large legitimate raw-unit balances.
The malicious position must be deposited before the pool first observes an active-risk state.
A normal protocol transition from ATTACK_REQUESTED to UNDER_ATTACK is sufficient; the attacker does not need to control the registry, pool owner or moderator.
The duration of the lock depends on how long the registry remains in UNDER_ATTACK or PROMOTION_REQUESTED.
This PoC demonstrates that a stake valid at deposit time can later overflow during the risk-window moment reset, causing both pokeRiskWindow() and the post-expiry claimExpired() backstop to revert.
Cap the total eligible stake using expiry, which is the largest timestamp that _markRiskWindowStart() can use.
A stronger long-term fix would store accounting moments using timestamps relative to riskWindowStart, instead of multiplying token balances by full Unix timestamps.
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.