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

`withdraw()` Missing Expiry Check Allows Post-Expiry Withdrawals Without Bonus

Author Revealed upon completion

Summary

withdraw() has no check for block.timestamp >= expiry
Users can withdraw principal after pool expiry
as long as outcome remains UNRESOLVED
This causes them to forfeit their bonus entitlement.

Root Cause

function withdraw() external nonReentrant {
if (outcome != PoolStates.Outcome.UNRESOLVED) revert OutcomeAlreadySet();
// No expiry check here
// After expiry, outcome stays UNRESOLVED until claimExpired() called
// Window exists where post-expiry withdrawal possible
}

Impact

Functionality inconsistency.

Proof of Concept

Setup:
Pool expiry = Day 100
Alice staked = 100 tokens
Bonus pool = 1000 tokens
riskWindowStart = 0 (no attack observed)

Day 101: Nobody called claimExpired() yet
outcome = UNRESOLVED

Alice calls withdraw():
Gets: 100 tokens (principal only)

Alice should have called claimExpired():
Gets: 100 tokens + bonus share

Alice lost her bonus by using wrong function post-expiry

Mitigation

function withdraw() external nonReentrant {
if (outcome != PoolStates.Outcome.UNRESOLVED) revert OutcomeAlreadySet();
if (block.timestamp >= expiry) revert StakingClosed(); // ← ADD THIS
...
}

Support

FAQs

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

Give us feedback!