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

contributeBonus() unnecessarily inherits staking deposit restrictions, preventing bonus contributions during PROMOTION_REQUESTED

Author Revealed upon completion

Summary

The contributeBonus() function reuses _assertDepositsAllowed(_observePoolState()), which was specifically designed to restrict new stake deposits during the PROMOTION_REQUESTED phase. However, bonus contributions do not participate in stake weighting and are only snapshotted when flagOutcome() is executed.

As a result, bonus contributions are unnecessarily rejected during PROMOTION_REQUESTED, despite having no apparent impact on the pool's reward distribution or security model.


Finding Details

contributeBonus() currently performs the following check:

_assertDepositsAllowed(_observePoolState());

https://github.com/CodeHawks-Contests/2026-07-bc-confidence-pools/blob/main/src/ConfidencePool.sol#L271

The helper blocks deposits when the registry state is:

PROMOTION_REQUESTED
PRODUCTION
CORRUPTED

https://github.com/CodeHawks-Contests/2026-07-bc-confidence-pools/blob/main/src/ConfidencePool.sol#L727-L734

Its accompanying documentation explicitly states that the restriction exists for staking deposits:

/// UNDER_ATTACK is intentionally NOT blocked while PROMOTION_REQUESTED is.
/// ...
/// the asymmetry is about deposit timing
/// ...
/// PROMOTION_REQUESTED is the closing-window stretch where a late join would
/// be gameable.

This reasoning applies to stake deposits, since a late staker can influence stake-weighted bonus allocation.

However, contributeBonus() behaves differently:

  • it does not increase totalEligibleStake,

  • it does not affect any user's stake weight,

  • it simply increases totalBonus.

Later, when the moderator finalizes the pool, flagOutcome() snapshots both values independently:

snapshotTotalStaked = totalEligibleStake;
snapshotTotalBonus = totalBonus;

Therefore, bonus contributions remain eligible until the snapshot is taken by flagOutcome().

Since PROMOTION_REQUESTED is still a pre-finalization state, rejecting bonus additions during this phase appears unnecessarily restrictive.


Impact

This issue does not compromise protocol safety or lead to incorrect accounting.

However, it unnecessarily shortens the period during which external contributors can fund the bonus pool.

Consequently:

  • users willing to increase the incentive pool during PROMOTION_REQUESTED are prevented from doing so,

  • the protocol may end up distributing a smaller bonus pool than intended,

  • the behavior is inconsistent with the fact that the final bonus amount is only fixed once flagOutcome() snapshots totalBonus.

Overall, this reduces protocol flexibility without providing an obvious security benefit.


Recommendation

Consider separating staking restrictions from bonus contribution restrictions instead of sharing the same helper.

For example, contributeBonus() could permit contributions during PROMOTION_REQUESTED while still rejecting them after the outcome becomes immutable (e.g., once the pool reaches PRODUCTION, CORRUPTED, or after flagOutcome() has been executed).

This would allow bonus funding until the point where snapshotTotalBonus is taken, which more closely aligns with how the bonus accounting is finalized.

Support

FAQs

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

Give us feedback!