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

Recovery recipient can be changed after CORRUPTED settlement finalization

Author Revealed upon completion

Root + Impact

Description

The corrupted settlement path finalizes the pool outcome and corrupted reserve accounting before the funds are transferred, but the final recovery recipient remains mutable until the sweep happens.

After the pool is resolved as CORRUPTED, the recovery destination can still be changed before claimCorrupted() or sweepUnclaimedCorrupted() transfers the corrupted-path funds.

This creates a settlement-finality gap: the outcome and accounting are fixed, but the final recipient of the funds is still read live from storage at transfer time.

The affected route is:

  • flagOutcome()

  • setRecoveryAddress()

  • claimCorrupted()

  • sweepUnclaimedCorrupted()

At the time flagOutcome(CORRUPTED, ...) is called, the pool finalizes the corrupted outcome and reserve accounting. However, the recovery recipient used by the later transfer route is not bound to that finalized settlement state.

A later setRecoveryAddress() call can change where the corrupted-path funds are sent.

Risk

Likelihood

Medium.

The issue requires the corrupted outcome to be finalized before the corrupted-path funds are swept. This is a normal multi-step settlement flow and does not require a non-standard ERC20 token, malicious token behavior, or direct modification of the pool contract.

The behavior is confirmed by a Foundry test.

Impact

High.

Funds that are already committed to the CORRUPTED recovery path can be redirected after the corrupted outcome and reserve accounting are already finalized.

This can affect systems or participants that rely on the recovery destination visible at the time of corrupted outcome finalization, including treasury, custody, insurance, recovery, or agreement-specific recovery flows.

The issue creates a mismatch between:

  • finalized corrupted outcome/accounting state; and

  • the final address receiving the corrupted-path funds.

Proof of Concept

A Foundry test was added under:

test/internal_round2/RecoveryAddressFinality.t.sol

Relevant test:

testRecoveryAddressChangeAfterCorruptedOutcomeRedirectsSweep()

Command:

forge test --match-path "test/internal_round2/*" -vvv

Observed output:

Ran 1 test for test/internal_round2/RecoveryAddressFinality.t.sol:RecoveryAddressFinalityTest
[PASS] testRecoveryAddressChangeAfterCorruptedOutcomeRedirectsSweep()
Suite result: ok. 1 passed; 0 failed; 0 skipped.

Minimal scenario:

  1. Alice stakes into the pool.

  2. A bonus is contributed.

  3. The registry reaches an active-risk state.

  4. The registry later reaches CORRUPTED.

  5. The moderator flags the pool as CORRUPTED.

  6. The corrupted outcome and corrupted reserve are finalized.

  7. The recovery address visible at finalization is recorded as flaggedRecovery.

  8. The recovery address is changed before the sweep.

  9. claimCorrupted() is called.

  10. flaggedRecovery receives 0.

  11. The later recovery address receives the corrupted-path funds.

This proves that the final recipient of the corrupted settlement can differ from the recovery address visible when the corrupted outcome was finalized.

Recommended Mitigation

The corrupted settlement flow should preserve consistency between the finalized outcome/accounting state and the recipient used for the final recovery transfer.

A safe fix should ensure that post-resolution configuration changes cannot alter the effective recipient of funds that have already entered a finalized corrupted settlement path.

The mitigation should be applied at the corrupted-settlement boundary, so that all corrupted resolution routes preserve the same recipient-consistency invariant.

- Read the recovery recipient live after corrupted settlement has already been finalized.
+ Preserve recipient consistency once funds enter a finalized corrupted settlement path.
+ Ensure post-resolution configuration changes cannot redirect already-finalized corrupted-path funds.

A regression test should cover a pool that is resolved as CORRUPTED, then has its recovery destination changed before the sweep. The final transfer should preserve the recipient consistency of the finalized settlement state.

Support

FAQs

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

Give us feedback!