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

MEV bots can steal bonus pool rewards with zero time-lock risk due to pre-risk entry flattening

Author Revealed upon completion

Description

In ConfidencePool.sol, the bonus pool is distributed to stakers based on a time-weighted quadratic formula. The intention is to reward honest early stakers and prevent late stakers from diluting the bonus. However, in _clampUserSums, the logic strictly overrides the entry time of early depositors, flattening their time-weight up to riskWindowStart. This means an MEV bot that flash-stakes exactly when UNDER_ATTACK triggers will receive the exact same bonus multiplier as an honest staker who locked their capital months in advance, completely diluting the bonus pool and stealing rewards from honest users with zero prior time-lock risk.

Risk

Likelihood:

  • MEV bots actively monitor mempool state transitions and will easily back-run UNDER_ATTACK registry changes to steal yield.

  • The logic flaw mathematically guarantees that bots receive maximum yield for minimum risk.

Impact:

  • The core economic incentive for early honest stakers is destroyed, as MEV bots can extract the bonus pool without taking any of the intended time-lock risk.

Proof of Concept

The flawed clamping logic in _clampUserSums artificially reduces the time-weight of early stakers to match that of late MEV bots:

function _clampUserSums(address user) internal view returns (uint256 finalSumStakeTime, uint256 finalSumStakeTimeSq) {
// ...
for (uint256 i = 0; i < len; ++i) {
StakeData memory s = records[i];
// @> Vulnerability: Flattens early deposits to riskWindowStart,
// giving them the same weight as MEV bots who stake exactly at riskWindowStart.
uint256 entry = s.timestamp;
if (entry < riskWindowStart) {
entry = riskWindowStart;
}
// ...

Recommended Mitigation

The clamping logic should preserve the seniority of early stakers. If late staking during a risk window is meant to earn zero bonus (as the inline documentation implies), the logic should explicitly skip deposits made after riskWindowStart, instead of flattening deposits made before it.

uint256 entry = s.timestamp;
- if (entry < riskWindowStart) {
- entry = riskWindowStart;
- }
+ // Deposits made during or after an attack earn zero bonus time weight
+ if (riskWindowStart != 0 && entry >= riskWindowStart) {
+ continue;
+ }

Support

FAQs

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

Give us feedback!