The withdraw() function is designed to allow stakers to exit the pool only during the pre-attack phase. According to the design documentation, withdrawals should be permanently disabled once the agreement enters an active-risk state (e.g., UNDER_ATTACK), which is marked by riskWindowStart being set.
The function's check for disabling withdrawals is: if (riskWindowStart != 0 || (state != ... && state != ... && state != ATTACK_REQUESTED)). The ATTACK_REQUESTED state is incorrectly included in the list of states where withdrawals are permitted. Per the documentation (DESIGN.md, section 9), ATTACK_REQUESTED is a pre-attack state where withdrawals should be allowed. However, the _isActiveRiskState helper function, which determines when to set riskWindowStart, does not include ATTACK_REQUESTED.
This creates a scenario where the agreement can be in the ATTACK_REQUESTED state, a user can call stake() (which does not set riskWindowStart), and then another user can call withdraw(), which will succeed because riskWindowStart is still 0 and ATTACK_REQUESTED is on the allowlist. This violates the design intent that any deposit made after the pre-attack staging phase should be locked until resolution.
Likelihood: Medium
Reason 1
This occurs when the underlying agreement is in the ATTACK_REQUESTED state.
Reason 2
A user must call withdraw() during this specific state before any other interaction causes riskWindowStart to be set.
Impact: Medium
Impact 1
This allows a staker to escape their position after the pre-attack phase has ended, which undermines a core mechanic of the protocol.
Impact 2
t violates the documented lifecycle where withdrawals are permanently disabled from UNDER_ATTACK onward, creating a discrepancy between the code and the intended behavior described in DESIGN.md
The _isActiveRiskState function should be updated to include ATTACK_REQUESTED as an active-risk state. This will ensure that any interaction with the pool while the agreement is in this state correctly sets riskWindowStart, which in turn will properly disable the withdraw function as intended.
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.