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

[L-1] Moderator is not restricted from staking into the pool they adjudicate

Author Revealed upon completion

Nothing in stake() prevents outcomeModerator from depositing into the same pool they're responsible for resolving. Since the moderator has genuine discretion in at least one documented case deciding whether a CORRUPTED registry state falls inside or outside the pool's committed scope, which determines SURVIVED versus CORRUPTED , giving that same address a financial stake in the outcome introduces a conflict of interest the contract doesn't disclose or guard against.

Description

  • There is no msg.sender != outcomeModerator check here or anywhere else gating deposits. The moderator role is set once at initialize() and never changes per-pool, but it's never excluded from the staker set either.

    Per the design documentation, the SURVIVED vscORRUPTED call on an in-scope breach is explicitly a judgment call made off-chain by the moderator, not a mechanical registry read:
    "the moderator's off-chain judgement is the source of truth on what triggered corruption"

  • A moderator holding a stake in the pool has a direct financial incentive pulling on exactly that judgment favoring SURVIVED (which pays them their stake back plus a bonus share) over CORRUPTED (which, in the bad-faith case, sends their own stake to recoveryAddress along with everyone else's).

function stake(uint256 amount) external nonReentrant whenPoolNotPaused {
>
if (amount == 0) revert InvalidAmount();
if (amount < minStake) revert BelowMinStake();
if (outcome != PoolStates.Outcome.UNRESOLVED) revert OutcomeAlreadySet();
if (block.timestamp >= expiry) revert StakingClosed();
_assertDepositsAllowed(_observePoolState());
...
}

Risk

Impact:

  • This doesn't force an incorrect outcome on its own , the moderator still has to act through flagOutcome, and their judgment call is inherently unverifiable on-chain either way, staked or not. The impact is a disclosed-but-unaddressed conflict of interest: a role trusted with discretionary judgment is not prevented from having personal financial exposure to that judgment. We're rating this Low because it doesn't create a new capability the moderator didn't already have(they can already flag whatever outcome they want within the registry-state constraints).

Recommended Mitigation:

  • Block the moderator address from calling stake() (and, for consistency, contributeBonus()), or

  • If some moderators are expected to also be legitimate community stakers, document this as an accepted trust assumption in docs/DESIGN.md alongside the other sponsor/moderator trust-surface disclosures in that document, since it currently isn't mentioned there.

+ msg.sender != outcomeModerator

Support

FAQs

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

Give us feedback!