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

Blacklisted claimant is permanently denied self-claim with no redirect

Author Revealed upon completion

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

  • Explain the specific issue or problem in one or more sentences

`claimSurvived()` pays the caller directly. `claimAttackerBounty()` pays the moderator-fixed `attacker`. Neither exposes a redirect target.
```solidity
if (!claimsStarted) claimsStarted = true;
stakeToken.safeTransfer(msg.sender, payout);
emit ClaimSurvived(msg.sender, userEligible, bonusShare);
```
```solidity
if (payout > 0) {
corruptedReserve -= payout;
if (!claimsStarted) claimsStarted = true;
stakeToken.safeTransfer(attacker, payout);
}
```
If the stake token uses a per-recipient denylist, a blacklisted staker or whitehat makes `safeTransfer` revert on every claim attempt. USDC is a plausible allowlisted stake token. The recipient can never receive their owed funds.
The pool itself recovers. After the 180-day windows, `sweepUnclaimedBonus` and `sweepUnclaimedCorrupted` route the stranded funds to `recoveryAddress`. The legitimate recipient loses the full value to the sponsor.
The factory allowlist and DESIGN.md only exclude tokens that freeze the pool's own balance and only warn about fee-on-transfer and rebasing. Per-recipient denylists are not covered, so this is not a design-accepted limitation.

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • Reason 2

Impact:

  • Impact 1

  • Impact 2

Proof of Concept

Recommended Mitigation

Add a claimant-controlled redirect so a blacklisted recipient can route their payout to a clean address.
```diff
- function claimSurvived() external nonReentrant {
+ function claimSurvived(address recipient) external nonReentrant {
+ if (recipient == address(0)) revert InvalidAmount();
if (outcome != PoolStates.Outcome.SURVIVED) revert OutcomeNotSet();
if (hasClaimed[msg.sender]) revert InvalidAmount();
...
- stakeToken.safeTransfer(msg.sender, payout);
+ stakeToken.safeTransfer(recipient, payout);
emit ClaimSurvived(msg.sender, userEligible, bonusShare);
}
```
Apply the same redirect parameter to `claimAttackerBounty`, still gating eligibility on `msg.sender`. If a redirect is not added, document that stakers and whitehats must verify their address is not blacklisted by the stake token before depositing.

Support

FAQs

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

Give us feedback!