outcomeModerator is set exactly once in initialize() and there is no setOutcomeModerator() function anywhere in the contract. If the DAO rotates its moderator address (e.g., post-incident key rotation), all existing live pools remain permanently tied to the old — potentially compromised — key. A compromised moderator key forces stakers into a 210+ day forced lockup before permissionless resolution is available.
The outcomeModerator is the DAO-controlled address that holds the authority to call flagOutcome() — the sole mechanism for setting SURVIVED or CORRUPTED outcomes on a pool. The protocol design assumes the DAO can always act through this address to resolve pools correctly.
The factory correctly supports key rotation for new pools via setDefaultOutcomeModerator. But there is no corresponding setter on the pool contract itself. Once deployed, each pool is permanently bound to the moderator address recorded at init time.
The real-world risk is a DAO key rotation event — common after security incidents, multisig threshold changes, or governance migrations. After such an event:
All newly created pools use the new, secure moderator address.
All existing live pools retain the old address — which may be a compromised or deprecated key.
If the old moderator key is compromised, the attacker controlling it can call flagOutcome(SURVIVED) on a CORRUPTED pool (returning principal to stakers, denying the attacker their bounty) or call flagOutcome(CORRUPTED, goodFaith=true, attacker=attacker_wallet) (redirecting the entire pool to themselves, if the registry is in CORRUPTED state). If the key is simply lost (not compromised), the only resolution path is the permissionless claimExpired() backstop, available only after expiry + MODERATOR_CORRUPTED_GRACE = up to 211+ days.
Pool is deployed and the moderator key is later rotated, lost, or compromised at the DAO level.
The pool is in an active state (UNRESOLVED, with riskWindowStart != 0 and registry in CORRUPTED state).
The DAO performs a key rotation on their outcomeModerator address after pool deployment — a routine operational event.
Alternatively: the old moderator key is compromised by an external attacker.
Scenario A — Key Loss (staker lockup):
Pool created with outcomeModerator = DAOv1_key.
DAO rotates to DAOv2_key via factory.setDefaultOutcomeModerator(DAOv2_key).
Existing pool's outcomeModerator remains DAOv1_key.
Agreement is CORRUPTED; moderator MUST flag outcome within MODERATOR_CORRUPTED_GRACE.
DAO holds DAOv2_key only — DAOv1_key is discarded. No call to flagOutcome() is possible.
claimExpired() before expiry + 180 days reverts with AgreementCorruptedAwaitingModerator.
After expiry + 180 days, permissionless bad-faith CORRUPTED resolution fires — stakers lose all principal to recoveryAddress.
Scenario B — Key Compromise (attacker misflags outcome):
Pool has 1.5M ONE staked. Agreement is CORRUPTED (breach confirmed, attacker named in good-faith path).
Attacker compromises DAOv1_key.
Attacker calls flagOutcome(SURVIVED, false, address(0)) — legal because registry is CORRUPTED and SURVIVED accepts CORRUPTED registry state.
Stakers reclaim their principal (technically beneficial for stakers, but breach was real).
Actual attacker (who performed the breach) receives no bounty — subverts the protocol's accountability mechanism.
Key loss: Stakers' capital locked for expiry duration + 180 days (typically 211+ days for a 31-day pool). After the lockup, funds sweep to recoveryAddress as bad-faith CORRUPTED — stakers lose all principal.
Key compromise: Attacker can misflag outcomes (SURVIVED when CORRUPTED or vice versa), subverting accountability payouts and potentially redirecting funds.
Operational: The DAO has no on-chain recourse for existing pools after a key rotation.
Add a setOutcomeModerator() function, gated to prevent abuse by the pool owner (sponsor). Since the outcomeModerator is a DAO-controlled role, the setter should ideally be restricted to the current moderator (a self-rotate) or require a two-step handoff rather than being onlyOwner:
[!IMPORTANT]
IfonlyOwneris used instead, the sponsor gains the ability to replace the DAO moderator and self-flag outcomes. The fix must restrict this setter to the currentoutcomeModeratoror implement a factory-governed two-step handoff to maintain the trust boundary.
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.