The Classic Dispute Kit (DisputeKitClassic
) is the default dispute resolution mechanism in Kleros V2. It implements four core features that define how jurors are selected, how votes are counted, how rewards are distributed, and how appeals work.
Other dispute kits may implement these features differently to support various dispute resolution mechanisms.
- π― Core Features
- π’ Events
- π§ Important Methods
- π Implementation Notes
- π Security Considerations
The drawing system determines how jurors are selected for a dispute.
- Probability of being drawn is proportional to staked PNK
- Each juror's chance = (Juror's Staked PNK) / (Total Court's Staked PNK)
- Multiple draws per juror are possible in the same dispute
- Drawing is delegated to the
SortitionModule
contract which:- Maintains sortition trees for efficient random selection
- Tracks staked PNK values for each court
- Handles the actual juror selection algorithm
- Ensures proper stake accounting and locking
- Drawing happens during the evidence period
- Drawn jurors must lock additional PNK as collateral
- Failed draws (inactive jurors) are skipped and redrawn
KlerosCore
initiates the drawing process- For each draw:
- DisputeKit calls
SortitionModule.draw()
with:key
: Court ID as bytes32coreDisputeID
: Dispute identifiernonce
: Current draw iteration
- SortitionModule:
- Uses RNG to select a random position in the tree
- Returns the drawn juror's address
- DisputeKit:
- Validates the drawn juror (stake, not drawn before if required)
- Creates a new vote instance if valid
- Requests a redraw if invalid
- DisputeKit calls
- DisputeKit performs additional checks after each draw:
- Verifies juror has sufficient stake (
totalStaked >= totalLocked + lockedAmountPerJuror
) - Ensures juror hasn't been drawn before if
singleDrawPerJuror
is enabled - Skips and redraws if validation fails
- Verifies juror has sufficient stake (
The vote aggregation system determines how individual votes are combined into a final ruling.
- Each juror gets one vote per draw
- The choice with the most votes wins
- In case of a tie:
- If it's the first round: refuse to arbitrate (choice 0)
- If it's an appeal round: maintain the previous round's winning choice
- All votes have equal weight
- No quadratic voting or other weighted schemes
- Multiple draws = multiple equal votes
The incentive system determines how rewards (both fees and PNK) are distributed.
- Arbitration Fees: Paid by the arbitrable contract
- PNK Penalties: Collected from incoherent voters
- Only coherent jurors receive rewards
- Equal split among all coherent votes
- Two types of rewards:
- Arbitration Fees (ETH/ERC20)
- Split proportionally to coherence
jurorReward = (totalFees / numberOfCoherentVotes) * degreeOfCoherence
- PNK Redistribution
- Penalties from incoherent votes are redistributed
pnkReward = (totalPenalties / numberOfCoherentVotes) * degreeOfCoherence
- Arbitration Fees (ETH/ERC20)
- Full coherence (100%): Voted for winning choice
- Partial coherence: Based on vote's relationship to final outcome
- Zero coherence: Voted for losing choice or didn't vote
degreeOfCoherence
ranges from 0 to 10000 (basis points)
The appeal system determines how disputes can be appealed and funded.
- Only two choices can be funded for appeal
- Any choice can be voted on in the next round
- Appeal period starts after voting ends
- Requires funding for:
- The losing choice
- The winning choice (counter-funding)
- Funding must cover fees for next round:
- Number of jurors doubles in each appeal
- Fee per juror remains constant
- Both sides must be fully funded for appeal to proceed
- If both sides fully funded:
- Appeal proceeds
- New round starts with double the jurors
- If funding incomplete:
- Current round's outcome becomes final
- Partial funding is refunded
All dispute kits must implement these standard events defined in IDisputeKit
:
Emitted when a juror casts their vote, providing transparency about voting choices and their justification.
event VoteCast(
uint256 indexed _coreDisputeID,
address indexed _juror,
uint256[] _voteIDs,
uint256 indexed _choice,
string _justification
);
Parameters:
_coreDisputeID
: Dispute identifier in the Arbitrator contract_juror
: Address of the voting juror_voteIDs
: Array of vote IDs being cast_choice
: Selected choice (0 = refuse to arbitrate, 1+ = ruling options)_justification
: Text explaining the juror's decision
Events specific to the Classic implementation, supporting its unique features:
Emitted when a new dispute is created in the dispute kit.
event DisputeCreation(uint256 indexed _coreDisputeID, uint256 _numberOfChoices, bytes _extraData);
Parameters:
_coreDisputeID
: Dispute identifier in the Arbitrator contract_numberOfChoices
: Number of available ruling choices_extraData
: Additional dispute configuration data
Emitted during the commit phase when a juror submits their vote commitment.
event CommitCast(uint256 indexed _coreDisputeID, address indexed _juror, uint256[] _voteIDs, bytes32 _commit);
Parameters:
_coreDisputeID
: Dispute identifier_juror
: Address of the committing juror_voteIDs
: Array of vote IDs being committed_commit
: Hash of the committed vote (choice + salt)
Emitted when someone contributes ETH to fund an appeal for a specific choice.
event Contribution(
uint256 indexed _coreDisputeID,
uint256 indexed _coreRoundID,
uint256 _choice,
address indexed _contributor,
uint256 _amount
);
Parameters:
_coreDisputeID
: Dispute identifier_coreRoundID
: Round number in the dispute_choice
: Choice being funded (0 = refuse to arbitrate, 1+ = ruling options)_contributor
: Address making the contribution_amount
: Amount of ETH contributed
Emitted when a choice receives full funding required for appeal.
event ChoiceFunded(uint256 indexed _coreDisputeID, uint256 indexed _coreRoundID, uint256 indexed _choice);
Parameters:
_coreDisputeID
: Dispute identifier_coreRoundID
: Round number in the dispute_choice
: Choice that has been fully funded
Emitted when a contributor withdraws their appeal funding contribution.
event Withdrawal(
uint256 indexed _coreDisputeID,
uint256 indexed _coreRoundID,
uint256 _choice,
address indexed _contributor,
uint256 _amount
);
Parameters:
_coreDisputeID
: Dispute identifier_coreRoundID
: Round number in the dispute_choice
: Choice the contribution was made for_contributor
: Address receiving the withdrawal_amount
: Amount of ETH withdrawn
-
Dispute Lifecycle Events
DisputeCreation
: Start of a new disputeCommitCast
: Commit phase votingVoteCast
: Reveal phase or direct voting
-
Appeal Funding Flow
Contribution
: During appeal funding periodChoiceFunded
: When a choice reaches full fundingWithdrawal
: After dispute resolution or failed appeal
-
Event Indexing
_coreDisputeID
: Always indexed for efficient dispute filtering_juror
/_contributor
: Indexed for user-specific queries_choice
/_coreRoundID
: Indexed where relevant for specific filtering
These functions can be called by external actors according to their roles in the dispute resolution process.
function castCommit(
uint256 _coreDisputeID,
uint256[] calldata _voteIDs,
bytes32 _commit
) external
- Called by jurors during the commit period
- Allows jurors to submit their vote commitments
- Parameters:
_coreDisputeID
: Dispute identifier_voteIDs
: Array of vote IDs to commit for_commit
: Hash of the vote choice and salt
- Requirements:
- Must be in commit period
- Caller must own the votes
- Commit must not be empty
- Can be called multiple times to update commits
function castVote(
uint256 _coreDisputeID,
uint256[] calldata _voteIDs,
uint256 _choice,
uint256 _salt,
string memory _justification
) external
- Called by jurors during the vote period
- Reveals votes for courts with hidden votes or casts direct votes
- Parameters:
_coreDisputeID
: Dispute identifier_voteIDs
: Array of vote IDs to cast_choice
: Selected ruling option_salt
: Salt used in commit (for hidden votes)_justification
: Explanation of the decision
- Requirements:
- Must be in vote period
- Caller must own the votes
- Choice must be valid
- For hidden votes, commit must match
function fundAppeal(uint256 _coreDisputeID, uint256 _choice) external payable
- Called by anyone to fund an appeal
- Manages appeal funding contributions in ETH
- Parameters:
_coreDisputeID
: Dispute identifier_choice
: Ruling option to fund
- Key features:
- Winners pay 1x appeal cost
- Losers pay 2x appeal cost
- Losers have half the funding period
- Appeal proceeds when two choices are funded
- Excess contributions are reimbursed
function withdrawFeesAndRewards(
uint256 _coreDisputeID,
address payable _beneficiary,
uint256 _coreRoundID,
uint256 _choice
) external returns (uint256 amount)
- Maintenance function to withdraw appeal fees and rewards
- Called after dispute resolution
- Parameters:
_coreDisputeID
: Dispute identifier_beneficiary
: Address to receive the withdrawal_coreRoundID
: Round to withdraw from_choice
: Ruling option funded
- Returns amount withdrawn
- Handles various scenarios:
- Refunds for unsuccessful funding
- Rewards for funding winning choice
- Refunds when winning choice wasn't funded
These functions are restricted to core arbitrator components.
function createDispute(
uint256 _coreDisputeID,
uint256 _numberOfChoices,
bytes calldata _extraData,
uint256 _nbVotes
) external
- Called only by KlerosCore
- Creates local dispute instance
- Sets up initial round
- Parameters:
_coreDisputeID
: Dispute identifier_numberOfChoices
: Available ruling options_extraData
: Additional configuration_nbVotes
: Number of votes for first round
function draw(
uint256 _coreDisputeID,
uint256 _nonce
) external returns (address drawnAddress)
- Called only by KlerosCore
- Draws jurors using sortition tree
- Parameters:
_coreDisputeID
: Dispute identifier_nonce
: Drawing iteration
- Returns drawn juror's address
- Validates juror eligibility:
- Sufficient stake
- Not already drawn (if enabled)
- Active status
-
Efficiency
- Uses batch operations where possible
- Optimizes gas usage in reward distribution
- Implements efficient vote counting
-
Upgradeability
- Follows UUPS proxy pattern
- Maintains clean upgrade path
- Preserves dispute state across upgrades
-
Integration
- Works with any ERC20 token for fees
- Compatible with all court configurations
- Supports both commit-reveal and direct voting
-
Drawing Fairness
- Random number generation must be secure
- Stake changes during drawing must be prevented
- Tree updates must maintain proportionality
-
Vote Integrity
- Votes must be properly counted
- Ties must be handled consistently
- Vote weights must be accurately tracked
-
Reward Distribution
- All rewards must be accounted for
- No double-claiming of rewards
- Proper handling of edge cases (zero coherent votes)
-
Appeal Safety
- Appeal funding must be atomic
- Refunds must be guaranteed
- Deadlines must be enforced