Dispute lifecycle
The full juror path: stake to qualify, get drawn onto a panel, commit and reveal a sealed vote, resolve the plurality outcome, and earn rewards (or get slashed for a no-show).
A dispute moves through four phases. Jurors stake to qualify, a panel is drawn, votes are committed then revealed so no one can copy another’s ruling, and the plurality outcome is executed back on the app that raised it.
Phases and outcomes
- Phase enum:
Commit(0) →Reveal(1) →Resolved(2) →Executed(3). The live phase is derived from deadlines, not stored. - Outcome enum:
Unresolved(0),P1(1),P2(2),P3(3),P4(4).P1/P2are the two sides,P3is a split, andP4is undecided, also the result of any tie.
1. Become a juror (stake)
Stake JURY to qualify. Approve the protocol for the JURY first, then:
protocol.stake(amount);
- You are registered once
stakeOf ≥ minStake. - New stake becomes effective next epoch (7-day epochs). Your
effectiveStakeis your reward and slash weight. - Selection is not epoch-gated: a freshly-staked juror can be drawn immediately.
See the Juror & staking guide for the full staking, cooldown, and rewards flow.
2. A dispute opens
Any contract opens a dispute, usually an arbitrable app like Jury Escrow, not an end user directly:
protocol.createDispute(arbitrable, PolicyKind.Sortition, title, description);
- Open policy: any registered juror may vote. Sortition: a random panel is drawn.
- Charges a USDC creation fee and reserves the JURY reward from the pre-funded pool (reverts if the reserve can’t cover it).
- Sets
commitDeadline = now + 3 daysandrevealDeadline = commitDeadline + 2 days.
3. The panel is drawn (Sortition)
A panel of 7 (targetPanelSize) is selected on-chain with a partial Fisher–Yates shuffle seeded by block.prevrandao. Each member’s vote weight is snapshotted at draw time. Check eligibility with isSelected(disputeId, juror); read the roster with getPanel(disputeId).
4. Commit a sealed vote
Each eligible juror submits a hash of their vote, never the vote itself:
commitment = keccak256(abi.encodePacked(uint8 vote, bytes32 salt, address juror));
protocol.commitVote(disputeId, commitment);
Binding juror into the hash stops anyone from replaying your commitment. Derive the salt deterministically from a wallet signature so it’s recoverable on any device:
const sig = await signer.signMessage(`Jury vote salt for dispute ${disputeId}`);
const salt = keccak256(sig); // bytes32
5. Reveal the vote
In the reveal window, reveal the same vote and salt:
protocol.revealVote(disputeId, vote, salt);
The contract checks keccak256(vote, salt, msg.sender) matches your commitment, then adds +1 to the tally for that outcome. Vote weight is always 1; stake never multiplies it.
6. Resolve and execute
After the reveal deadline, anyone can finalize:
protocol.resolve(disputeId);
The plurality outcome wins. Any tie, or a dispute where nobody revealed, resolves to P4. If the arbitrable is a contract, the protocol immediately calls rule(disputeId, outcome) back on it, atomically. A reverting callback can’t block resolution; it can be retried with execute(disputeId).
Rewards and slashing
- Rewards (pull model, JURY). Jurors who voted the winning outcome share the dispute’s reward pool, weighted by their snapshotted stake. Claim across many disputes in one transfer with
claimAllRewards(max). A wrong vote earns nothing but is never slashed. - Slashing (no-show only, JURY). A juror who owed a vote and never revealed is slashed 1% (
slashBps) of effective stake, routed to the protocol treasury. Settlement is lazy: it happens on your nextstake/requestUnstake/unstake/claimAllRewards, or when anyone callssettle(juror, max).