Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions contracts/src/arbitration/KlerosCoreBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
// * Storage * //
// ************************************* //

uint256 private constant ALPHA_DIVISOR = 1e4; // The number to divide `Court.alpha` by.
uint256 private constant NON_PAYABLE_AMOUNT = (2 ** 256 - 2) / 2; // An amount higher than the supply of ETH.

address public governor; // The governor of the contract.
Expand Down Expand Up @@ -768,20 +767,21 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
IDisputeKit disputeKit = disputeKits[round.disputeKitID];

// [0, 1] value that determines how coherent the juror was in this round, in basis points.
uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence(
uint256 coherence = disputeKit.getDegreeOfCoherencePenalty(
_params.disputeID,
_params.round,
_params.repartition,
_params.feePerJurorInRound,
_params.pnkAtStakePerJurorInRound
);
if (degreeOfCoherence > ALPHA_DIVISOR) {
// Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
degreeOfCoherence = ALPHA_DIVISOR;

// Guard against degree exceeding 1, though it should be ensured by the dispute kit.
if (coherence > ONE_BASIS_POINT) {
coherence = ONE_BASIS_POINT;
}

// Fully coherent jurors won't be penalized.
uint256 penalty = (round.pnkAtStakePerJuror * (ALPHA_DIVISOR - degreeOfCoherence)) / ALPHA_DIVISOR;
uint256 penalty = (round.pnkAtStakePerJuror * (ONE_BASIS_POINT - coherence)) / ONE_BASIS_POINT;

// Unlock the PNKs affected by the penalty
address account = round.drawnJurors[_params.repartition];
Expand All @@ -794,7 +794,7 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
account,
_params.disputeID,
_params.round,
degreeOfCoherence,
coherence,
-int256(availablePenalty),
0,
round.feeToken
Expand Down Expand Up @@ -826,37 +826,40 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
IDisputeKit disputeKit = disputeKits[round.disputeKitID];

// [0, 1] value that determines how coherent the juror was in this round, in basis points.
uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence(
(uint256 pnkCoherence, uint256 feeCoherence) = disputeKit.getDegreeOfCoherenceReward(
_params.disputeID,
_params.round,
_params.repartition % _params.numberOfVotesInRound,
_params.feePerJurorInRound,
_params.pnkAtStakePerJurorInRound
);

// Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
if (degreeOfCoherence > ALPHA_DIVISOR) {
degreeOfCoherence = ALPHA_DIVISOR;
// Guard against degree exceeding 1, though it should be ensured by the dispute kit.
if (pnkCoherence > ONE_BASIS_POINT) {
pnkCoherence = ONE_BASIS_POINT;
}
if (feeCoherence > ONE_BASIS_POINT) {
feeCoherence = ONE_BASIS_POINT;
}

address account = round.drawnJurors[_params.repartition % _params.numberOfVotesInRound];
uint256 pnkLocked = _applyCoherence(round.pnkAtStakePerJuror, degreeOfCoherence);
uint256 pnkLocked = _applyCoherence(round.pnkAtStakePerJuror, pnkCoherence);

// Release the rest of the PNKs of the juror for this round.
sortitionModule.unlockStake(account, pnkLocked);

// Transfer the rewards
uint256 pnkReward = _applyCoherence(_params.pnkPenaltiesInRound / _params.coherentCount, degreeOfCoherence);
uint256 pnkReward = _applyCoherence(_params.pnkPenaltiesInRound / _params.coherentCount, pnkCoherence);
round.sumPnkRewardPaid += pnkReward;
uint256 feeReward = _applyCoherence(round.totalFeesForJurors / _params.coherentCount, degreeOfCoherence);
uint256 feeReward = _applyCoherence(round.totalFeesForJurors / _params.coherentCount, feeCoherence);
round.sumFeeRewardPaid += feeReward;
pinakion.safeTransfer(account, pnkReward);
_transferFeeToken(round.feeToken, payable(account), feeReward);
emit TokenAndETHShift(
account,
_params.disputeID,
_params.round,
degreeOfCoherence,
pnkCoherence,
int256(pnkReward),
int256(feeReward),
round.feeToken
Expand Down Expand Up @@ -1059,18 +1062,18 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable

/// @dev Applies degree of coherence to an amount
/// @param _amount The base amount to apply coherence to.
/// @param _degreeOfCoherence The degree of coherence in basis points.
/// @param _coherence The degree of coherence in basis points.
/// @return The amount after applying the degree of coherence.
function _applyCoherence(uint256 _amount, uint256 _degreeOfCoherence) internal pure returns (uint256) {
return (_amount * _degreeOfCoherence) / ALPHA_DIVISOR;
function _applyCoherence(uint256 _amount, uint256 _coherence) internal pure returns (uint256) {
return (_amount * _coherence) / ONE_BASIS_POINT;
}

/// @dev Calculates PNK at stake per juror based on court parameters
/// @param _minStake The minimum stake for the court.
/// @param _alpha The alpha parameter for the court in basis points.
/// @return The amount of PNK at stake per juror.
function _calculatePnkAtStake(uint256 _minStake, uint256 _alpha) internal pure returns (uint256) {
return (_minStake * _alpha) / ALPHA_DIVISOR;
return (_minStake * _alpha) / ONE_BASIS_POINT;
}

/// @dev Toggles the dispute kit support for a given court.
Expand Down
8 changes: 4 additions & 4 deletions contracts/src/arbitration/SortitionModuleBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,14 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
// Update the sortition sum tree.
bytes32 stakePathID = _accountAndCourtIDToStakePathID(_account, _courtID);
bool finished = false;
uint96 currenCourtID = _courtID;
uint96 currentCourtID = _courtID;
while (!finished) {
// Tokens are also implicitly staked in parent courts through sortition module to increase the chance of being drawn.
_set(bytes32(uint256(currenCourtID)), _newStake, stakePathID);
if (currenCourtID == GENERAL_COURT) {
_set(bytes32(uint256(currentCourtID)), _newStake, stakePathID);
if (currentCourtID == GENERAL_COURT) {
finished = true;
} else {
(currenCourtID, , , , , , ) = core.courts(currenCourtID); // Get the parent court.
(currentCourtID, , , , , , ) = core.courts(currentCourtID); // Get the parent court.
}
}
emit StakeSet(_account, _courtID, _newStake, juror.stakedPnk);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {KlerosCore, KlerosCoreBase, IDisputeKit, ISortitionModule} from "../Kler
import {Initializable} from "../../proxy/Initializable.sol";
import {UUPSProxiable} from "../../proxy/UUPSProxiable.sol";
import {SafeSend} from "../../libraries/SafeSend.sol";
import {ONE_BASIS_POINT} from "../../libraries/Constants.sol";

/// @title DisputeKitClassicBase
/// Abstract Dispute kit classic implementation of the Kleros v1 features including:
Expand Down Expand Up @@ -57,7 +58,6 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
uint256 public constant WINNER_STAKE_MULTIPLIER = 10000; // Multiplier of the appeal cost that the winner has to pay as fee stake for a round in basis points. Default is 1x of appeal fee.
uint256 public constant LOSER_STAKE_MULTIPLIER = 20000; // Multiplier of the appeal cost that the loser has to pay as fee stake for a round in basis points. Default is 2x of appeal fee.
uint256 public constant LOSER_APPEAL_PERIOD_MULTIPLIER = 5000; // Multiplier of the appeal period for the choice that wasn't voted for in the previous round, in basis points. Default is 1/2 of original appeal period.
uint256 public constant ONE_BASIS_POINT = 10000; // One basis point, for scaling.

address public governor; // The governor of the contract.
KlerosCore public core; // The Kleros Core arbitrator
Expand Down Expand Up @@ -526,14 +526,39 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
/// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit.
/// @param _voteID The ID of the vote.
/// @return The degree of coherence in basis points.
function getDegreeOfCoherence(
/// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward.
/// @return feeCoherence The degree of coherence in basis points for the dispute fee reward.
function getDegreeOfCoherenceReward(
uint256 _coreDisputeID,
uint256 _coreRoundID,
uint256 _voteID,
uint256 /* _feePerJuror */,
uint256 /* _pnkAtStakePerJuror */
) external view override returns (uint256) {
) external view override returns (uint256 pnkCoherence, uint256 feeCoherence) {
uint256 coherence = _getDegreeOfCoherence(_coreDisputeID, _coreRoundID, _voteID);
return (coherence, coherence);
}

/// @dev Gets the degree of coherence of a particular voter. This function is called by Kleros Core in order to determine the amount of the penalty.
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
/// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit.
/// @param _voteID The ID of the vote.
/// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward.
function getDegreeOfCoherencePenalty(
uint256 _coreDisputeID,
uint256 _coreRoundID,
uint256 _voteID,
uint256 /* _feePerJuror */,
uint256 /* _pnkAtStakePerJuror */
) external view override returns (uint256 pnkCoherence) {
return _getDegreeOfCoherence(_coreDisputeID, _coreRoundID, _voteID);
}

function _getDegreeOfCoherence(
uint256 _coreDisputeID,
uint256 _coreRoundID,
uint256 _voteID
) internal view returns (uint256 coherence) {
// In this contract this degree can be either 0 or 1, but in other dispute kits this value can be something in between.
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
Vote storage vote = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]].votes[_voteID];
Expand Down
22 changes: 19 additions & 3 deletions contracts/src/arbitration/interfaces/IDisputeKit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,30 @@ interface IDisputeKit {
/// @param _voteID The ID of the vote.
/// @param _feePerJuror The fee per juror.
/// @param _pnkAtStakePerJuror The PNK at stake per juror.
/// @return The degree of coherence in basis points.
function getDegreeOfCoherence(
/// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward.
/// @return feeCoherence The degree of coherence in basis points for the dispute fee reward.
function getDegreeOfCoherenceReward(
uint256 _coreDisputeID,
uint256 _coreRoundID,
uint256 _voteID,
uint256 _feePerJuror,
uint256 _pnkAtStakePerJuror
) external view returns (uint256);
) external view returns (uint256 pnkCoherence, uint256 feeCoherence);

/// @dev Gets the degree of coherence of a particular voter. This function is called by Kleros Core in order to determine the amount of the penalty.
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
/// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit.
/// @param _voteID The ID of the vote.
/// @param _feePerJuror The fee per juror.
/// @param _pnkAtStakePerJuror The PNK at stake per juror.
/// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward.
function getDegreeOfCoherencePenalty(
uint256 _coreDisputeID,
uint256 _coreRoundID,
uint256 _voteID,
uint256 _feePerJuror,
uint256 _pnkAtStakePerJuror
) external view returns (uint256 pnkCoherence);

/// @dev Gets the number of jurors who are eligible to a reward in this round.
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
Expand Down
39 changes: 21 additions & 18 deletions contracts/src/arbitration/university/KlerosCoreUniversity.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import {IArbitrableV2, IArbitratorV2} from "../interfaces/IArbitratorV2.sol";
import {IDisputeKit} from "../interfaces/IDisputeKit.sol";
import {ISortitionModuleUniversity} from "./ISortitionModuleUniversity.sol";
import {SafeERC20, IERC20} from "../../libraries/SafeERC20.sol";
import "../../libraries/Constants.sol";
import {UUPSProxiable} from "../../proxy/UUPSProxiable.sol";
import {Initializable} from "../../proxy/Initializable.sol";
import "../../libraries/Constants.sol";

/// @title KlerosCoreUniversity
/// Core arbitrator contract for educational purposes.
Expand Down Expand Up @@ -87,7 +87,6 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
// * Storage * //
// ************************************* //

uint256 private constant ALPHA_DIVISOR = 1e4; // The number to divide `Court.alpha` by.
uint256 private constant NON_PAYABLE_AMOUNT = (2 ** 256 - 2) / 2; // An amount higher than the supply of ETH.

address public governor; // The governor of the contract.
Expand Down Expand Up @@ -526,7 +525,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
: convertEthToTokenAmount(_feeToken, court.feeForJuror);
round.nbVotes = _feeAmount / feeForJuror;
round.disputeKitID = disputeKitID;
round.pnkAtStakePerJuror = (court.minStake * court.alpha) / ALPHA_DIVISOR;
round.pnkAtStakePerJuror = (court.minStake * court.alpha) / ONE_BASIS_POINT;
round.totalFeesForJurors = _feeAmount;
round.feeToken = IERC20(_feeToken);

Expand Down Expand Up @@ -655,7 +654,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {

Court storage court = courts[newCourtID];
extraRound.nbVotes = msg.value / court.feeForJuror; // As many votes that can be afforded by the provided funds.
extraRound.pnkAtStakePerJuror = (court.minStake * court.alpha) / ALPHA_DIVISOR;
extraRound.pnkAtStakePerJuror = (court.minStake * court.alpha) / ONE_BASIS_POINT;
extraRound.totalFeesForJurors = msg.value;
extraRound.disputeKitID = newDisputeKitID;

Expand Down Expand Up @@ -754,20 +753,21 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
IDisputeKit disputeKit = disputeKits[round.disputeKitID];

// [0, 1] value that determines how coherent the juror was in this round, in basis points.
uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence(
uint256 coherence = disputeKit.getDegreeOfCoherencePenalty(
_params.disputeID,
_params.round,
_params.repartition,
_params.feePerJurorInRound,
_params.pnkAtStakePerJurorInRound
);
if (degreeOfCoherence > ALPHA_DIVISOR) {
// Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
degreeOfCoherence = ALPHA_DIVISOR;

// Guard against degree exceeding 1, though it should be ensured by the dispute kit.
if (coherence > ONE_BASIS_POINT) {
coherence = ONE_BASIS_POINT;
}

// Fully coherent jurors won't be penalized.
uint256 penalty = (round.pnkAtStakePerJuror * (ALPHA_DIVISOR - degreeOfCoherence)) / ALPHA_DIVISOR;
uint256 penalty = (round.pnkAtStakePerJuror * (ONE_BASIS_POINT - coherence)) / ONE_BASIS_POINT;

// Unlock the PNKs affected by the penalty
address account = round.drawnJurors[_params.repartition];
Expand All @@ -780,7 +780,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
account,
_params.disputeID,
_params.round,
degreeOfCoherence,
coherence,
-int256(availablePenalty),
0,
round.feeToken
Expand Down Expand Up @@ -818,29 +818,32 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
IDisputeKit disputeKit = disputeKits[round.disputeKitID];

// [0, 1] value that determines how coherent the juror was in this round, in basis points.
uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence(
(uint256 pnkCoherence, uint256 feeCoherence) = disputeKit.getDegreeOfCoherenceReward(
_params.disputeID,
_params.round,
_params.repartition % _params.numberOfVotesInRound,
_params.feePerJurorInRound,
_params.pnkAtStakePerJurorInRound
);

// Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
if (degreeOfCoherence > ALPHA_DIVISOR) {
degreeOfCoherence = ALPHA_DIVISOR;
// Guard against degree exceeding 1, though it should be ensured by the dispute kit.
if (pnkCoherence > ONE_BASIS_POINT) {
pnkCoherence = ONE_BASIS_POINT;
}
if (feeCoherence > ONE_BASIS_POINT) {
feeCoherence = ONE_BASIS_POINT;
}

address account = round.drawnJurors[_params.repartition % _params.numberOfVotesInRound];
uint256 pnkLocked = (round.pnkAtStakePerJuror * degreeOfCoherence) / ALPHA_DIVISOR;
uint256 pnkLocked = (round.pnkAtStakePerJuror * pnkCoherence) / ONE_BASIS_POINT;

// Release the rest of the PNKs of the juror for this round.
sortitionModule.unlockStake(account, pnkLocked);

// Transfer the rewards
uint256 pnkReward = ((_params.pnkPenaltiesInRound / _params.coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
uint256 pnkReward = ((_params.pnkPenaltiesInRound / _params.coherentCount) * pnkCoherence) / ONE_BASIS_POINT;
round.sumPnkRewardPaid += pnkReward;
uint256 feeReward = ((round.totalFeesForJurors / _params.coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
uint256 feeReward = ((round.totalFeesForJurors / _params.coherentCount) * feeCoherence) / ONE_BASIS_POINT;
round.sumFeeRewardPaid += feeReward;
pinakion.safeTransfer(account, pnkReward);
if (round.feeToken == NATIVE_CURRENCY) {
Expand All @@ -854,7 +857,7 @@ contract KlerosCoreUniversity is IArbitratorV2, UUPSProxiable, Initializable {
account,
_params.disputeID,
_params.round,
degreeOfCoherence,
pnkCoherence,
int256(pnkReward),
int256(feeReward),
round.feeToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ contract xKlerosLiquidV2 is Initializable, ITokenController, IArbitratorV2 {
uint256 public constant MAX_STAKE_PATHS = 4; // The maximum number of stake paths a juror can have.
uint256 public constant DEFAULT_NB_OF_JURORS = 3; // The default number of jurors in a dispute.
uint256 public constant NON_PAYABLE_AMOUNT = (2 ** 256 - 2) / 2; // An amount higher than the supply of ETH.
uint256 public constant ALPHA_DIVISOR = 1e4; // The number to divide `Court.alpha` by.
// General Contracts
address public governor; // The governor of the contract.
WrappedPinakion public pinakion; // The Pinakion token contract.
Expand Down
3 changes: 3 additions & 0 deletions contracts/src/libraries/Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ uint256 constant DEFAULT_K = 6; // Default number of children per node.
uint256 constant DEFAULT_NB_OF_JURORS = 3; // The default number of jurors in a dispute.
IERC20 constant NATIVE_CURRENCY = IERC20(address(0)); // The native currency, such as ETH on Arbitrum, Optimism and Ethereum L1.

// Units
uint256 constant ONE_BASIS_POINT = 10000;

enum OnError {
Revert,
Return
Expand Down
Loading
Loading