The IArbitratorV2
interface defines the standard interface for arbitration in the Kleros V2 protocol. Unlike its predecessor ERC-792, this standard is not concerned with appeals, allowing each arbitrator to implement an appeal system that best suits its needs.
- 💫 Typical Flow
- 🔄 Court and Dispute Kit Jumps
- 📦 Extra Data Format
- 💰 Fee Token Support
- 🔄 Events
- 🔧 Core Methods
- 🛡️ Emergency Controls
- 🔗 Related Components
- 🔒 Security Considerations
-
Dispute Creation
- Arbitrable contract calls
createDispute
- Pays arbitration fees (ETH or ERC20)
- Dispute is created with specified court and parameters
- Jurors are drawn based on court configuration
- Arbitrable contract calls
-
Dispute Resolution
- Follows court-specific periods:
- Evidence submission
- Commit (if hidden votes)
- Vote
- Appeal
- Execution
- Follows court-specific periods:
-
Court and Dispute Kit Jumps
- During appeals, disputes can:
- Move to parent courts (Court Jump)
- Switch dispute resolution mechanisms (Dispute Kit Jump)
- Triggered when:
- Number of jurors reaches
jurorsForCourtJump
- Parent court doesn't support current dispute kit
- Number of jurors reaches
- During appeals, disputes can:
-
Ruling Execution
- Final ruling determined through
currentRuling
- Ruling executed on arbitrable contract
- Final ruling determined through
sequenceDiagram
participant Arbitrable
participant Juror
participant KlerosCore
participant DisputeKit
participant SortitionModule
Note over Arbitrable,SortitionModule: 1. Dispute Creation
Arbitrable->>KlerosCore: createDispute(choices, extraData)
KlerosCore->>SortitionModule: createDisputeHook()
KlerosCore->>DisputeKit: createDispute()
KlerosCore-->>KlerosCore: emit DisputeCreation
Note over Arbitrable,SortitionModule: 2. Evidence Period
loop Drawing until nbVotes reached
KlerosCore->>DisputeKit: draw()
DisputeKit-->>KlerosCore: drawnAddress
KlerosCore->>SortitionModule: lockStake()
KlerosCore-->>KlerosCore: emit Draw
end
Note over KlerosCore: Check: All jurors drawn & (round > 0 || evidence period passed)
KlerosCore->>KlerosCore: passPeriod()
KlerosCore-->>KlerosCore: emit NewPeriod(commit/vote)
Note over Arbitrable,SortitionModule: 3. Commit Period (if hidden votes)
loop Until deadline
Juror->>DisputeKit: castCommit()
DisputeKit-->>DisputeKit: emit CommitCast
end
Note over KlerosCore: Check: Deadline passed || all commits cast
KlerosCore->>KlerosCore: passPeriod()
KlerosCore-->>KlerosCore: emit NewPeriod(vote)
Note over Arbitrable,SortitionModule: 4. Vote Period
loop Until deadline
Juror->>DisputeKit: castVote()
DisputeKit-->>DisputeKit: emit VoteCast
end
Note over KlerosCore: Check: Deadline passed || all votes cast
KlerosCore->>KlerosCore: passPeriod()
KlerosCore-->>KlerosCore: emit NewPeriod(appeal)
KlerosCore-->>KlerosCore: emit AppealPossible
Note over Arbitrable,SortitionModule: 5. Appeal Period
KlerosCore->>KlerosCore: passPeriod()
alt Appeal Filed & Fully Funded
DisputeKit->>KlerosCore: appeal()
KlerosCore-->>KlerosCore: emit AppealDecision
opt Court Jump (nbVotes >= jurorsForCourtJump)
KlerosCore-->>KlerosCore: Switch to parent court
KlerosCore-->>KlerosCore: emit CourtJump
end
opt Dispute Kit Jump (parent court incompatible)
KlerosCore->>DisputeKit: createDispute() in new DK
KlerosCore-->>KlerosCore: emit DisputeKitJump
end
KlerosCore-->>KlerosCore: emit NewPeriod(evidence)
Note over Arbitrable,SortitionModule: Return to Evidence Period
else No Appeal or Appeal Failed
Note over KlerosCore: Check: Appeal period deadline passed
KlerosCore->>KlerosCore: passPeriod()
KlerosCore-->>KlerosCore: emit NewPeriod(execution)
end
Note over Arbitrable,SortitionModule: 6. Execution Period
loop Execute Rewards
KlerosCore->>DisputeKit: getCoherentCount()
KlerosCore->>DisputeKit: getDegreeOfCoherence()
KlerosCore->>SortitionModule: unlockStake()
KlerosCore-->>KlerosCore: emit TokenAndETHShift
opt If no coherent votes or residual rewards
KlerosCore-->>KlerosCore: emit LeftoverRewardSent
end
end
KlerosCore->>KlerosCore: executeRuling()
KlerosCore-->>KlerosCore: emit Ruling
KlerosCore->>Arbitrable: rule()
The diagram shows:
- Dispute Creation: Arbitrable contract initiates the dispute
- Evidence Period: Jurors are drawn through the dispute kit
- Requires all jurors to be drawn
- For first round: evidence period must also have passed
- Commit Period: Hidden votes phase (if enabled)
- Transitions when deadline passed or all commits cast
- Vote Period: Jurors cast their votes
- Transitions when deadline passed or all votes cast
- Appeal Period: Possible court/dispute kit jumps if appealed
- Transitions to execution if deadline passed without successful appeal
- Returns to evidence if appeal successful
- Execution Period: Rewards distribution and final ruling
Key interactions:
- KlerosCore orchestrates the overall process and emits state change events
- DisputeKit handles voting mechanics and vote-related events
- SortitionModule manages stake operations
- Jurors interact directly with DisputeKit for voting
- Arbitrable contract initiates and receives the final ruling
When a dispute is appealed, it may move to a parent court and/or switch dispute kits. The Classic Dispute Kit (ID: 1) serves as a universal fallback mechanism, being mandatorily supported by all courts to ensure disputes can always be resolved.
graph TD
A[Dispute in Court N<br/>with Dispute Kit X] -->|Appeal| B{Check Jurors Count}
B -->|nbVotes >= jurorsForCourtJump| C[Move to Parent Court]
B -->|nbVotes < jurorsForCourtJump| D[Stay in Current Court]
C -->|Check DK Support| E{Parent Court<br/>Supports DK X?}
E -->|Yes| F[Keep Dispute Kit X]
E -->|No| G[Switch to Classic DK]
F --> H[New Round in<br/>Parent Court<br/>with DK X]
G --> I[New Round in<br/>Parent Court<br/>with Classic DK]
D --> J[New Round in<br/>Court N with DK X]
style A fill:#fff,stroke:#333
style B fill:#ff9,stroke:#333
style C fill:#9cf,stroke:#333
style D fill:#9cf,stroke:#333
style E fill:#ff9,stroke:#333
style F fill:#9cf,stroke:#333
style G fill:#f99,stroke:#333
style H fill:#9f9,stroke:#333
style I fill:#9f9,stroke:#333
style J fill:#9f9,stroke:#333
The diagram shows:
- Initial dispute state and appeal trigger
- Court jump decision based on juror count
- Dispute kit compatibility check
- Final state after jumps (if any)
When a dispute is appealed and the number of jurors reaches or exceeds the court's jurorsForCourtJump
threshold:
-
Trigger Conditions
if (round.nbVotes >= courts[newCourtID].jurorsForCourtJump) { newCourtID = courts[newCourtID].parent; }
-
Jump Process
- Dispute moves to parent court
- New round created with parent court parameters
- Fees and stakes recalculated based on parent court
- Emits
CourtJump
event with:event CourtJump( uint256 indexed _disputeID, uint256 indexed _roundID, uint96 indexed _fromCourtID, uint96 _toCourtID );
-
Special Cases
- General Court appeals: Reserved for future forking mechanism
- Forking Court: Cannot be directly used for disputes
A dispute kit jump occurs only during a court jump when the parent court doesn't support the current dispute kit:
-
Trigger Condition
if (!courts[newCourtID].supportedDisputeKits[newDisputeKitID]) { newDisputeKitID = DISPUTE_KIT_CLASSIC; }
- Always defaults to Classic Dispute Kit (ID: 1)
- Classic Dispute Kit must be supported by all courts
-
Jump Process
- New dispute created in Classic Dispute Kit
- State transferred to new dispute kit
- Emits
DisputeKitJump
event:event DisputeKitJump( uint256 indexed _disputeID, uint256 indexed _roundID, uint256 indexed _fromDisputeKitID, uint256 _toDisputeKitID );
-
Jump Sequence
- Court jump evaluated first
- Dispute kit jump follows if needed
- Both can occur in same appeal
- Classic Dispute Kit ensures resolution continuity
-
State Management
- Dispute parameters updated for new court/kit
- Round information preserved
- Appeal periods reset
- Drawing process restarts
-
Fee Handling
extraRound.nbVotes = msg.value / court.feeForJuror; extraRound.pnkAtStakePerJuror = (court.minStake * court.alpha) / ALPHA_DIVISOR; extraRound.totalFeesForJurors = msg.value;
-
Security Considerations
- Validates court existence
- Ensures dispute kit compatibility
- Maintains coherent state during transitions
- Preserves appeal funding
The extraData
parameter is a crucial component used in dispute creation and cost calculation. It encodes three key parameters that determine how a dispute will be handled.
bytes extraData = abi.encode(
uint96 courtID, // Court handling the dispute
uint256 minJurors, // Minimum number of jurors
uint256 disputeKitID // Specific dispute resolution mechanism
);
Court ID (first 32 bytes)
- Type:
uint96
- Purpose: Identifies which court will handle the dispute
- Validation:
- If
courtID == FORKING_COURT
→ defaults toGENERAL_COURT
- If
courtID >= courts.length
→ defaults toGENERAL_COURT
- Must be a valid court that supports the specified dispute kit
- If
Minimum Jurors (next 32 bytes)
- Type:
uint256
- Purpose: Specifies minimum number of jurors required
- Validation:
- If
minJurors == 0
→ defaults toDEFAULT_NB_OF_JURORS
- If
- Impact: Directly affects arbitration costs (
feeForJuror * minJurors
)
Dispute Kit ID (last 32 bytes)
- Type:
uint256
- Purpose: Specifies which dispute resolution mechanism to use
- Validation:
- If
disputeKitID == NULL_DISPUTE_KIT (0)
→ defaults toDISPUTE_KIT_CLASSIC (1)
- If
disputeKitID >= disputeKits.length
→ defaults toDISPUTE_KIT_CLASSIC (1)
- Must be supported by the selected court
- If
- Encoding: Always use
abi.encode()
to ensure proper padding and alignment - Length Validation: Implementation handles both complete and incomplete data
- Default Behavior:
- If
extraData
is shorter than expected → all parameters get default values - If any parameter is invalid → that parameter gets a default value
- Other valid parameters are still used
- If
- Gas Efficiency: Uses assembly for efficient decoding
- Safety: All invalid inputs are handled gracefully with defaults
The arbitrator supports both native currency (ETH) and ERC20 token payments for arbitration fees.
-
Native currency (ETH):
- Always supported as the default payment method
- Direct value transfer through payable functions
- Required for appeal fees: Appeals must be paid in ETH due to complexity of handling token conversions during court jumps
-
ERC20 tokens:
- Must be explicitly enabled by the governor
- Acceptance tracked through
AcceptedFeeToken
events - Requires approval before dispute creation
- Not supported for appeal fees
-
Rate Management:
- Maintained by the governor
- Defined as
(rateInEth, rateDecimals)
pairs - Updates tracked through
NewCurrencyRate
events
-
Cost Conversion:
tokenAmount = (ethAmount * 10^rateDecimals) / rateInEth
-
Rate Maintenance:
- Regular updates to reflect market prices
- Balance between accuracy and gas costs
- Consider price oracle integration
-
Token Integration:
- Proper decimal handling in conversions
- SafeERC20 usage for transfers
- Clear documentation of supported tokens
-
Security:
- Rate manipulation protection
- Token approval safety
- Reentrance protection in fee payments
event DisputeCreation(uint256 indexed _disputeID, IArbitrableV2 indexed _arbitrable)
- Emitted when a new dispute is created
- Parameters:
_disputeID
: Unique identifier for the dispute_arbitrable
: Contract which created the dispute
event Ruling(IArbitrableV2 indexed _arbitrable, uint256 indexed _disputeID, uint256 _ruling)
- Emitted when a ruling is given
- Parameters:
_arbitrable
: Contract receiving the ruling_disputeID
: Identifier of the dispute_ruling
: The ruling value
event AcceptedFeeToken(IERC20 indexed _token, bool indexed _accepted)
- Emitted when an ERC20 token is added/removed as a payment method
- Parameters:
_token
: The ERC20 token_accepted
: Whether the token is accepted
event NewCurrencyRate(IERC20 indexed _feeToken, uint64 _rateInEth, uint8 _rateDecimals)
- Emitted when fee rates for an ERC20 token are updated
- Parameters:
_feeToken
: The ERC20 token_rateInEth
: New rate in ETH_rateDecimals
: Decimals for the rate
function createDispute(
uint256 _numberOfChoices,
bytes calldata _extraData
) external payable returns (uint256 disputeID)
- Creates a dispute with native currency payment (typically ETH)
- Parameters:
_numberOfChoices
: Number of ruling options_extraData
: Additional dispute data containing:- Court ID (first 32 bytes)
- Minimum jurors required (next 32 bytes)
- Dispute kit ID (last 32 bytes)
- Returns: Unique identifier for the created dispute
- Requirements:
- Must be called by the arbitrable contract
- Payment must be >=
arbitrationCost(_extraData)
function createDispute(
uint256 _numberOfChoices,
bytes calldata _extraData,
IERC20 _feeToken,
uint256 _feeAmount
) external returns (uint256 disputeID)
- Creates a dispute with ERC20 token payment
- Additional Parameters:
_feeToken
: ERC20 token used for payment_feeAmount
: Amount of tokens to pay
- Requirements:
- Token must be accepted for fee payment
- Amount must be >=
arbitrationCost(_extraData, _feeToken)
function arbitrationCost(bytes calldata _extraData) external view returns (uint256 cost)
- Computes arbitration cost in native currency
- In KlerosCoreBase: Cost =
feeForJuror * minJurors
- Note: Changes should be infrequent due to gas costs for arbitrable contracts
function arbitrationCost(
bytes calldata _extraData,
IERC20 _feeToken
) external view returns (uint256 cost)
- Computes arbitration cost in specified ERC20 token
- Uses currency rates to convert from native currency cost
function appealCost(uint256 _disputeID) public view returns (uint256 cost)
- Gets the cost of appealing a specified dispute
- Cost calculation:
- If staying in current court:
feeForJuror * ((nbVotes * 2) + 1)
- If jumping to parent court: uses parent court's
feeForJuror
- If appealing in General Court: returns non-payable amount (reserved for future forking mechanism)
- If staying in current court:
- Cost increases exponentially with each appeal to discourage frivolous appeals
- Important: Appeal fees must always be paid in ETH (native currency) due to complexity of handling token conversions during court jumps
function setStake(uint96 _courtID, uint256 _newStake) external whenNotPaused
- Allows jurors to stake/unstake PNK in courts
- Delegated to
SortitionModule
which:- Manages the sortition trees for each court
- Handles stake transitions and delayed stakes
- Tracks total staked amounts
- Ensures proper stake accounting
- Requirements:
- System must not be paused
- Stake amount must meet court's minimum requirement
- Court must be valid (not Forking Court)
function draw(uint256 _disputeID, uint256 _iterations) external
- Draws jurors for a dispute during evidence period
- Delegated to the dispute kit associated with the dispute
- Can be called in parts through
_iterations
parameter - For each successful draw:
- Locks juror's PNK stake as collateral
- Emits
Draw
event - Updates round information
function passPeriod(uint256 _disputeID) external
- Advances dispute to next period when conditions are met
- Period sequence: evidence → commit → vote → appeal → execution
- Each transition has specific requirements:
- Evidence: All jurors must be drawn
- Commit: All commits must be cast (if hidden votes)
- Vote: All votes must be cast
- Appeal: Appeal period must have passed
- Execution: Final state
function appeal(uint256 _disputeID, uint256 _numberOfChoices, bytes memory _extraData) external payable
- Handles appeals of dispute rulings
- Delegated to the dispute kit for appeal validation
- Manages:
- Court jumps when juror count threshold is reached
- Dispute kit jumps when parent court compatibility requires
- Creation of new rounds
- Fee payments and stake calculations
function execute(uint256 _disputeID, uint256 _round, uint256 _iterations) external whenNotPaused
- Distributes PNK stakes and dispute fees to jurors
- Can be called in parts through
_iterations
- Handles:
- PNK penalties for incoherent votes
- Fee distribution to coherent jurors
- Reward calculations based on vote coherence
- Leftover reward distribution
function executeRuling(uint256 _disputeID) external
- Finalizes dispute by executing the ruling
- Can only be called in execution period
- Ensures:
- Dispute is in execution period
- Ruling hasn't been executed before
- Emits final ruling and calls arbitrable contract
function currentRuling(
uint256 _disputeID
) external view returns (uint256 ruling, bool tied, bool overridden)
- Gets current ruling for a dispute
- Returns:
ruling
: Current ruling valuetied
: Whether there's a tieoverridden
: Whether ruling was overridden by appeal funding
These methods work together to enable:
- Juror selection through secure stake-weighted randomization
- Multi-round dispute resolution with appeals
- Economic incentives through coherence-based rewards
- Seamless transitions between courts and dispute kits
The Kleros V2 protocol implements an emergency control system that allows rapid response to potential security threats while maintaining a balance between security and decentralization.
-
Guardian
- Focused security role with limited permissions
- Can pause the system in emergencies
- Currently set to a multisig contract for quick response
- Cannot unpause the system (requires governor)
- Cannot modify system parameters
-
Governor
- Administrative role with broader permissions
- Can both pause and unpause the system
- Can change system parameters
- Can execute governance proposals
- Can change both guardian and governor addresses
- Currently set to a multisig contract distinct from the guardian
- Planned to transition to DAO control via the KlerosGovernor contract, following the governance model of Kleros v1
The pause mechanism is implemented through two key functions:
function pause() external onlyByGuardianOrGovernor whenNotPaused
function unpause() external onlyByGovernor whenPaused
Key characteristics:
- Pausing can be triggered by either guardian or governor
- Only the governor can unpause the system
- State changes emit corresponding events:
event Paused(); event Unpaused();
- Prevents duplicate pause/unpause calls through state checks
When the system is paused:
-
Blocked Operations
- Staking operations (
setStake
) - Reward execution and distribution (
execute
) - Any operation marked with
whenNotPaused
modifier
- Staking operations (
-
Allowed Operations
- Dispute creation remains active
- Voting continues to function
- Core dispute resolution remains operational
- Appeal mechanisms stay active
This selective pausing ensures that while potentially vulnerable economic operations can be halted, the core arbitration functionality remains available to users.
The pause mechanism serves as a critical security control that:
- Provides rapid response capability to security events
- Protects user assets during emergencies
- Maintains essential dispute resolution services
- Ensures controlled recovery through governor-only unpause
IArbitrableV2
: Interface for contracts that can be arbitratedKlerosCoreBase
: Reference implementation of the arbitrator interface- Dispute Kits:
DisputeKitClassic
: Default implementation with proportional drawing to staked PNK and plurality voting. Mandatorily supported by all courts as a fallback mechanism.DisputeKitSybilResistant
: Variant requiring Proof of Humanity registration for drawingDisputeKitGated
: Variant requiring token holdings (ERC20/721/1155) for drawing- More dispute kits can be implemented to support different voting mechanisms, but courts must always support the Classic Dispute Kit
EvidenceModule
: Handles submission and tracking of evidence for disputesSortitionModule
: Manages juror selection and stake tracking using sortition trees
-
Fee Management
- Arbitration costs should change infrequently:
- Frequent changes force arbitrable contracts to update their stored fees
- Each update costs significant gas for arbitrable contracts
- Changes can make pending transactions invalid if fees increase
- Cost calculation must be deterministic and consistent
- ERC20 rates must be carefully maintained to ensure fair fee conversion
- Arbitration costs should change infrequently:
-
Dispute Creation
- Only arbitrable contracts can create disputes
- Fees must be paid upfront
- Extra data validation is critical
-
Ruling Integrity
- Rulings are final once executed
- Appeal system must be robust
- Tied votes must be handled consistently
The KlerosCore contract delegates key functionality to specialized modules while maintaining the orchestration of the dispute lifecycle.