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.
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.
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.
A Foundry test was added under:
test/internal_round2/RecoveryAddressFinality.t.sol
Relevant test:
testRecoveryAddressChangeAfterCorruptedOutcomeRedirectsSweep()
Command:
Observed output:
Minimal scenario:
Alice stakes into the pool.
A bonus is contributed.
The registry reaches an active-risk state.
The registry later reaches CORRUPTED.
The moderator flags the pool as CORRUPTED.
The corrupted outcome and corrupted reserve are finalized.
The recovery address visible at finalization is recorded as flaggedRecovery.
The recovery address is changed before the sweep.
claimCorrupted() is called.
flaggedRecovery receives 0.
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.
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.
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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.