`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.
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.