ConfidencePool intentionally lets the pool owner update expiry until the first successful stake. After the first stake, stake() sets expiryLocked = true, and later setExpiry() calls revert.
The issue is that stake() accepts only an amount and does not let the first staker provide an expected or minimum expiry. A sponsor can change expiry after the staker inspected the pool but before the staker's first deposit is ordered, and the deposit will still succeed while locking the sponsor-mutated deadline.
Likelihood:
The condition occurs when the pool owner changes expiry before the first successful stake is ordered.
The first staker's transaction has no expiry precondition, so it can succeed against the new value as long as the mutated expiry is still in the future and satisfies the 30-day minimum lead rule.
Impact:
The first staker can deposit into a pool term different from the one they inspected before signing.
Extending the expiry can lengthen the time before mechanical EXPIRED resolution when withdrawal later becomes disabled by active-risk registry state; shortening it can reduce the expected term and alter the staker's bonus/risk assumptions.
Clone and pin the audited repository:
Save the following file as test/poc/ExpiryMutabilityCounterexample.t.sol:
Run the PoC with Foundry:
Observed output summary with forge Version: 1.7.2-nightly, Solidity 0.8.26:
The first passing test shows that the sponsor changes expiry from the value Alice inspected, Alice's later stake() succeeds, and the first stake locks the mutated expiry. The second passing test shows the main bound: the sponsor cannot set a near-immediate expiry because setExpiry() reverts with ExpiryTooSoon().
Consider adding an expiry precondition to stake() so a depositor can bind the relevant pool term they inspected before signing. One approach is to require an exact expected expiry:
If exact matching is too strict for integrations, a minExpiry or [minExpiry, maxExpiry] guard could provide slippage-style protection while still allowing stakers to reject materially different pool terms.
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.