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

CORRUPTED settlement leaves live stake and bonus accounting stale after funds leave

Author Revealed upon completion

Description

  • After a CORRUPTED settlement moves the pool balance to the recovery address or good-faith attacker, public accounting should make clear that stakers no longer have live claimable principal or bonus in the pool.

  • CORRUPTED paths update corruptedReserve, bountyClaimed, and bountyEntitlement, but they do not clear totalEligibleStake, eligibleStake[user], or totalBonus. After a bad-faith claimCorrupted() drains the pool, the public state can still report the original eligible stake and bonus even though no staker can claim in the CORRUPTED outcome and the token balance is zero.

// src/ConfidencePool.sol
function claimCorrupted() external nonReentrant {
if (outcome != PoolStates.Outcome.CORRUPTED) revert OutcomeNotSet();
if (goodFaith && bountyClaimed < bountyEntitlement) revert MustClaimBountyFirst();
uint256 toSweep = stakeToken.balanceOf(address(this));
if (toSweep == 0) revert NothingToSweep();
corruptedReserve = toSweep <= corruptedReserve ? corruptedReserve - toSweep : 0;
if (!goodFaith) {
bountyClaimed = bountyEntitlement;
}
if (!claimsStarted) claimsStarted = true;
stakeToken.safeTransfer(recoveryAddress, toSweep);
// @> totalEligibleStake, eligibleStake[user], and totalBonus are left unchanged.
emit ClaimCorrupted(msg.sender, recoveryAddress, toSweep);
}
function sweepUnclaimedCorrupted() external nonReentrant {
...
corruptedReserve = 0;
bountyClaimed = bountyEntitlement;
...
// @> Aggregate live-looking stake and bonus accounting is still not cleared.
stakeToken.safeTransfer(recoveryAddress, amount);
}

Risk

Likelihood:

  • Every CORRUPTED pool with existing stake or bonus can execute one of these settlement paths.

  • Users and integrations can query public accounting after settlement.

Impact:

  • Public views can show nonzero eligible stake and bonus after the pool has been drained and stakers have no valid claim path.

  • Indexers, frontends, and accounting tools can overstate live liabilities or show stakers as still funded after a CORRUPTED settlement.

Under the CodeHawks matrix, funds are not directly at risk, but state handling is inadequate. This is Low severity.

Recommended Mitigation

Clear aggregate live-liability fields when the CORRUPTED settlement fully consumes the pool's accounted reserve, or expose outcome-aware claimable views that return zero in CORRUPTED outcomes.

function claimCorrupted() external nonReentrant {
...
corruptedReserve = toSweep <= corruptedReserve ? corruptedReserve - toSweep : 0;
+ if (corruptedReserve == 0) {
+ totalEligibleStake = 0;
+ totalBonus = 0;
+ }
...
}

Per-user eligibleStake cannot be fully cleared without enumerating stakers, so frontends should use an outcome-aware claimable view or treat eligibleStake as historical once outcome == CORRUPTED.

Support

FAQs

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

Give us feedback!