ConfidencePool allows staking while the agreement is already in UNDER_ATTACK. The protocol design expects this to be safe because late UNDER_ATTACK entrants should receive a vanishing / near-zero bonus share due to the k=2 time-weighted formula.
However, when no one has previously interacted with the pool during UNDER_ATTACK, a late whale can become the first observer by calling stake() near the end of the attack window. This call seals riskWindowStart at the late timestamp and resets the global stake-time accumulators to that late timestamp. Existing early stakers are later clamped to the same late riskWindowStart.
As a result, the intended time-weighted bonus distribution collapses into an effectively amount-weighted distribution between early stakers and the late whale. The late whale can capture almost the entire sponsor-funded bonus pool despite entering after the attack was already public and shortly before resolution.
In the PoC below:
Alice stakes 100 tokens before the attack.
Carol contributes 100 bonus tokens.
The agreement enters UNDER_ATTACK.
Nobody pokes or interacts with the pool for almost 30 days.
Bob stakes 10,000 tokens as the first pool interaction during UNDER_ATTACK.
One second later, the agreement resolves to PRODUCTION and the moderator flags SURVIVED.
Observed result:
Alice receives only ~0.99 bonus tokens.
Bob receives ~99.01 bonus tokens.
Control result with prompt pokeRiskWindow():
Alice receives almost the full 100 bonus tokens.
Bob receives only ~0.000000001 bonus tokens.
This breaks the intended economic invariant that late entrants during the risk window should receive a vanishing bonus share.
The root cause is the interaction between stake(), lazy risk-window observation, and _markRiskWindowStart().
stake() calls _observePoolState() before adding the new stake. When the registry is already in UNDER_ATTACK and riskWindowStart is still zero, _observePoolState() calls _markRiskWindowStart().
_markRiskWindowStart() sets riskWindowStart to the current block timestamp and resets the global accumulators:
This treats all existing stakers as if they entered at the late observation timestamp. Then the whale’s new stake is added at the same timestamp. When the pool resolves shortly after, both the honest early staker and the late whale have almost the same effective risk duration, so the bonus split becomes dominated by stake size.
The protocol documents that late entrants within the observed risk window should be crushed by the k=2 formula, but this only holds when the risk window was observed before the late stake. When the late stake itself is the first observation, the late entrant avoids the intended time-weight penalty.
Medium.
This occurs when:
the agreement enters UNDER_ATTACK,
no one calls pokeRiskWindow() and no other pool interaction occurs during the active-risk period,
a large staker enters late while the registry is still UNDER_ATTACK,
the agreement reaches a terminal state shortly afterward.
This is realistic because pokeRiskWindow() is permissionless but not guaranteed to be called. A rational whale can monitor the registry state and wait for pools whose riskWindowStart is still zero.
Medium.
The attacker does not steal staked principal, but they can redirect almost the entire sponsor-funded bonus pool away from honest early stakers. The loss is economic and directly affects the core incentive mechanism of the pool: early stakers are supposed to earn the bonus for bearing risk across the agreement term, while late entrants during an attack are supposed to receive a vanishing share.
The impact can approach 100% of the bonus pool, limited mainly by the attacker’s stake size relative to existing stakers.
Create the following file:
test/poc/LateObservationBonusDilutionCandidate.t.sol
Run:
Output:
The candidate test shows the late whale captures ~99.01 bonus tokens. The control test shows that with prompt observation, the same late whale receives only ~0.000000001 bonus tokens.
The simplest fix is to block stake() during UNDER_ATTACK, same as PROMOTION_REQUESTED, PRODUCTION, and CORRUPTED.
Alternatively, if UNDER_ATTACK deposits must remain allowed, the protocol should ensure that a stake call which first observes UNDER_ATTACK cannot reset existing stakers' effective entry time to the late observer’s timestamp in a way that makes the late stake amount-weighted against early stakers.
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.