FoundrySolidityLayer 2
7.25 ETH
Submission Details
Impact: high
Likelihood: high

Permanently disables withdrawals once active-risk staging is entered. No manual override.

Author Revealed upon completion

Root + Impact

Description

The withdraw() function checks whether the registry is in an active-risk state or if riskWindowStart != 0. However, when active-risk staging is entered, riskWindowStart is permanently set to a non-zero value, which permanently locks and disables all future withdrawals.

function withdraw() external nonReentrant {
if (outcome != PoolStates.Outcome.UNRESOLVED) revert OutcomeAlreadySet();
IAttackRegistry.ContractState state = _observePoolState();
// `riskWindowStart` is the pool's one-way record that risk has materialised;
// gate on it so an upstream registry rewind cannot re-open withdrawals.
if (
riskWindowStart != 0
|| (state != IAttackRegistry.ContractState.NOT_DEPLOYED
&& state != IAttackRegistry.ContractState.NEW_DEPLOYMENT
&& state != IAttackRegistry.ContractState.ATTACK_REQUESTED)
) {
revert WithdrawsDisabled();
}

Risk

Likelihood:

  • An active-risk state (UNDER_ATTACK or PROMOTION_REQUESTED) is observed on the registry during the pool's lifecycle.

  • The riskWindowStart variable is permanently flipped to a non-zero timestamp, permanently restricting the ability of stakers to withdraw their funds.

Impact:

  • Stakers' principal tokens become permanently locked in the contract once any active-risk state is reached, preventing them from exiting even if the pool ultimately survives or expires.

  • Funds can only be recovered through specific resolution outcomes rather than normal user-initiated withdrawals.

Proof of Concept

Recommended Mitigation

allow manual override riskWindowStart or adjust the riskWindowStart check to allow withdrawals under proper conditions

Support

FAQs

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

Give us feedback!