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

Late UNDER_ATTACK staker can capture a meaningful bonus share through zero-elapsed risk-window fallback

Author Revealed upon completion

Root + Impact

Description

The bonus distribution logic can give a meaningful bonus share to a staker who enters only after the agreement is already in the UNDER_ATTACK state.

A staker who enters after the active-risk phase has already started should not be treated as economically equivalent to stakers who were exposed before that phase. However, when the active-risk window starts and ends in the same timestamp, the bonus calculation falls back to amount-weighted distribution.

This allows a late UNDER_ATTACK staker to capture a meaningful part of the bonus and dilute earlier stakers.

The affected route is:

  • stake()

  • _assertDepositsAllowed()

  • _observePoolState()

  • _markRiskWindowStart()

  • _markRiskWindowEnd()

  • flagOutcome()

  • claimSurvived() / claimExpired()

  • _bonusShare()

Deposits are allowed during UNDER_ATTACK. If the late deposit is the interaction that first observes the active-risk state, riskWindowStart is recorded at the current timestamp. If the terminal state is then observed in the same timestamp, riskWindowEnd is recorded at the same timestamp.

This creates riskWindowStart == riskWindowEnd.

When this happens, the time-weighted bonus score becomes zero and _bonusShare() falls back to amount-weighted distribution. The late UNDER_ATTACK staker is then included in the same amount-weighted bonus cohort as earlier stakers.

Risk

Likelihood

Medium.

The issue requires a specific ordering where a stake is added during UNDER_ATTACK and the risk window resolves without measurable elapsed time. This path is reachable in the tested state sequence and does not require a non-standard ERC20 token, malicious token behavior, owner privilege, or direct contract modification.

Impact

High.

The affected value is the bonus pool allocated to stakers. A late entrant can receive a meaningful share of that bonus despite entering only after the active-risk phase is already active.

In the large-stake scenario, the late entrant captures almost the entire bonus pool, heavily diluting the earlier staker.

This breaks the intended risk/reward relationship between earlier exposed stakers and late active-risk entrants.

Proof of Concept

A Foundry test was added under:

test/internal_round3/Round3UnderAttackBonus.t.sol

Command:

forge test --match-path "test/internal_round3/*" -vvv
Ran 4 tests for test/internal_round3/Round3UnderAttackBonus.t.sol:Round3UnderAttackBonusTest
[PASS] testLargeBobUnderAttackSameBlockDilutesAliceBonus()
Logs:
large-bob alice
principal 1000000000000000000
bonus 99900099900099900
payout 1099900099900099900
large-bob bob
principal 1000000000000000000000
bonus 99900099900099900099
payout 1099900099900099900099
[PASS] testPromotionRequestedDepositBlockedButUnderAttackDepositAllowed()
[PASS] testUnderAttackLateDepositLaterTerminalReceivesNearZeroBonus()
Logs:
later-terminal alice
principal 100000000000000000000
bonus 99999999999985115656
payout 199999999999985115656
later-terminal bob
principal 100000000000000000000
bonus 14884343
payout 100000000000014884343
[PASS] testUnderAttackLateDepositSameBlockReceivesAmountWeightedBonus()
Logs:
same-block alice
principal 100000000000000000000
bonus 50000000000000000000
payout 150000000000000000000
same-block bob
principal 100000000000000000000
bonus 50000000000000000000
payout 150000000000000000000
Suite result: ok. 4 passed; 0 failed; 0 skipped.
With equal stakes, Alice and Bob each receive 50 bonus tokens, even though Bob entered only after UNDER_ATTACK was already active.
With a much larger late stake, Bob receives approximately 99.9 bonus tokens while Alice receives only approximately 0.0999 bonus tokens.
When the risk window has nonzero elapsed time, Bob receives only a near-zero bonus amount. This confirms that the issue is caused by the zero-elapsed risk-window fallback path.
# Recommended Mitigation
The reward logic should preserve the distinction between stake that was already exposed before the active-risk phase and stake that entered after the system had already moved into an active-risk condition.
A safe fix should ensure that fallback reward distribution cannot collapse different risk-exposure phases into the same amount-weighted cohort.
Any zero-elapsed or boundary-case reward path should still respect the participant's effective risk phase and should not allow a late entrant to be treated as economically equivalent to earlier exposed stakers.
```diff
- Treat all stake in a zero-elapsed risk window as one amount-weighted cohort.
+ Preserve risk-exposure phase when applying zero-elapsed or boundary-case reward logic.
+ Prevent late active-risk entrants from receiving a meaningful risk bonus as if they were exposed earlier.
```
The mitigation should be applied at the reward-accounting boundary so that all resolution routes preserve the same risk-exposure invariant.

Support

FAQs

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

Give us feedback!