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

`withdraw()` remains open through `ATTACK_REQUESTED`

Author Revealed upon completion

Root + Impact

No party suffers a fund loss. The exiting staker forfeits all bonus, and remaining stakers' shares increase proportionally. Abstract coverage quality is reduced but no material harm occurs.

Description

  • withdraw() is permitted while the registry is in pre-attack states and blocks once active-risk materializes (UNDER_ATTACK onward), enforced by the one-way riskWindowStart latch (I-9).

  • The withdraw gate at line 297 allows exit when state == ATTACK_REQUESTED. An informed staker who detects an attack has been requested can withdraw their full principal before the registry escalates to UNDER_ATTACK — forfeiting bonus but escaping with principal. Remaining stakers' bonus shares increase proportionally. No party suffers fund loss.

// ConfidencePool.sol:withdraw — line 297
@> && state != IAttackRegistry.ContractState.ATTACK_REQUESTED) // allows exit

Risk

Likelihood:

Reason 1 — The `ATTACK_REQUESTED` state is a normal part of the registry lifecycle before `UNDER_ATTACK`. Any staker monitoring the registry can exit at this point.
Reason 2 — No privileged access or special timing is required — the function is permissionless for any staker.

Impact:

Impact 1 — The exiting staker forfeits all bonus entitlement. Their weight is stripped from global accumulators, benefiting remaining stakers.
Impact 2 — Abstract coverage quality is reduced — stakers exit before risk materializes. No material victim.

Proof of Concept

function test_LD1_WithdrawOpenThroughAttackRequested() public {
_stake(alice, 100 * ONE);
attackRegistry.setAgreementState(IAttackRegistry.ContractState.ATTACK_REQUESTED);
uint256 aliceBefore = token.balanceOf(alice);
vm.prank(alice);
pool.withdraw();
assertEq(token.balanceOf(alice) - aliceBefore, 100 * ONE);
assertEq(pool.eligibleStake(alice), 0);
}

Forge output:

[PASS] test_LD1_WithdrawOpenThroughAttackRequested()
Withdraw succeeds during ATTACK_REQUESTED

Recommended Mitigation

This is a documented design trade-off (DESIGN.md §9). Removing ATTACK_REQUESTED from the allowed withdraw states would close the exit window earlier, preventing stakers from escaping with principal once an attack is requested. The cost is reduced flexibility for stakers during the pre-UNDER_ATTACK phase.

- && state != IAttackRegistry.ContractState.ATTACK_REQUESTED)

Remove ATTACK_REQUESTED from the allowed set.

Support

FAQs

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

Give us feedback!