Early rate$2,400 of senior audit time for $500. Early members keep the rate as it climbs.$2,400 of senior audit time for $500See how →
F-2026-0020·missing-validation

The fee-pool solvency counter can be driven to zero by any caller

Fixedescrowarbitrationdispute-resolution
TL;DR

tally accepts a non-existent case ID and runs the quorum-failure branch, which decrements the active-case counter. Any caller can drive the counter to zero and disable the floor that keeps juror compensation solvent.

Severity
MEDIUM
Impact
MEDIUM
Likelihood
MEDIUM
Method
MManual review
CAT.
Complexity
MEDIUM
Exploitability
MEDIUM
02Section · Description

Description

withdrawFeePool refuses to draw the balance below a floor derived from activeCaseCount:

solidity
uint256 floor = activeCaseCount * PANEL_SIZE_ROUND1 * tierFeeExpertCommercial;

tally(caseId) performs no existence check on its argument and carries no modifiers. For an unused caseId the default Case has phase == Evidence (enum 0) and commitDeadline == 0, so the auto-advance at #L716-L718 fires; the phase gate passes; revealDeadline == 0 clears the reveal-window check; the empty juror loop yields totalRevealed == 0; and the quorum-failure branch executes if (activeCaseCount > 0) activeCaseCount--;. Repeating with fresh bytes32 values drives the counter to zero while genuine cases are live and their panels are seated.

Vulnerable Scenario:

  1. A genuine dispute is opened and a three-juror panel is seated. activeCaseCount == 1.
  2. The owner attempts to withdraw the fee pool; the floor correctly refuses it.
  3. Any address calls tally with an arbitrary unused caseId. The counter falls to zero.
  4. The same withdrawal now succeeds and the fee pool is emptied.
  5. The genuine panel reaches a ruling and each winning juror is paid nothing.
03Section · Impact

Impact

The guard that keeps juror compensation solvent can be disabled by any external caller at negligible cost. Realising the loss additionally requires the owner to withdraw the newly exposed balance, so the counter forgery alone is a griefing precondition rather than a completed theft. Escrow principal, juror stake and appeal deposits are separately accounted and are not reachable by this path, and withdrawal goes to a fixed treasury address, which bounds the severity.

04Section · Recommendation

Recommendation

Reject an absent case: tally should revert when cases[caseId].escrowContract == address(0) rather than falling through its default-value branches, and activeCaseCount should be adjusted only on validated phase transitions.

05Section · Resolution

Resolution

Fixed. A tally against a nonexistent case now reverts instead of decrementing the counter.

06Section · Affected files

Affected files

  • ArbitrationPool.sol#L442-L450 (withdrawFeePool)
  • ArbitrationPool.sol#L713-L796 (tally)
Status
Fixed
F-2026-0020