Skip to content

Commit d63d000

Browse files
committed
Merge branch 'dev' into feat/rng-fallback2
2 parents 31efb24 + 0f31e0e commit d63d000

File tree

14 files changed

+183
-126
lines changed

14 files changed

+183
-126
lines changed

contracts/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@ The format is based on [Common Changelog](https://common-changelog.org/).
1212
- Set the Hardhat Solidity version to v0.8.30 and enable the IR pipeline ([#2069](https://github.com/kleros/kleros-v2/issues/2069))
1313
- Set the Foundry Solidity version to v0.8.30 and enable the IR pipeline ([#2073](https://github.com/kleros/kleros-v2/issues/2073))
1414
- Widen the allowed solc version to any v0.8.x for the interfaces only ([#2083](https://github.com/kleros/kleros-v2/issues/2083))
15+
- Make `IDisputeKit.getDegreeOfCoherenceReward()` multi-dimensional so different calculations may be applied to PNK rewards, fee rewards and PNK penalties (future-proofing) ([#2090](https://github.com/kleros/kleros-v2/issues/2090))
16+
- Consolidate the constant `ALPHA_DIVISOR` with `ONE_BASIS_POINTS` ([#2090](https://github.com/kleros/kleros-v2/issues/2090))
1517
- Bump `hardhat` to v2.26.2 ([#2069](https://github.com/kleros/kleros-v2/issues/2069))
1618
- Bump `@kleros/vea-contracts` to v0.7.0 ([#2073](https://github.com/kleros/kleros-v2/issues/2073))
1719

20+
### Fixed
21+
22+
- Do not pass to Voting period if all the commits are cast because it breaks the current Shutter auto-reveal process. ([#2085](https://github.com/kleros/kleros-v2/issues/2085))
23+
1824
## [0.12.0] - 2025-08-05
1925

2026
### Changed

contracts/src/arbitration/KlerosCoreBase.sol

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
8888
// * Storage * //
8989
// ************************************* //
9090

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

9493
address public governor; // The governor of the contract.
@@ -566,10 +565,8 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
566565
if (round.drawnJurors.length != round.nbVotes) revert DisputeStillDrawing();
567566
dispute.period = court.hiddenVotes ? Period.commit : Period.vote;
568567
} else if (dispute.period == Period.commit) {
569-
if (
570-
block.timestamp - dispute.lastPeriodChange < court.timesPerPeriod[uint256(dispute.period)] &&
571-
!disputeKits[round.disputeKitID].areCommitsAllCast(_disputeID)
572-
) {
568+
// Note that we do not want to pass to Voting period if all the commits are cast because it breaks the Shutter auto-reveal currently.
569+
if (block.timestamp - dispute.lastPeriodChange < court.timesPerPeriod[uint256(dispute.period)]) {
573570
revert CommitPeriodNotPassed();
574571
}
575572
dispute.period = Period.vote;
@@ -768,20 +765,21 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
768765
IDisputeKit disputeKit = disputeKits[round.disputeKitID];
769766

770767
// [0, 1] value that determines how coherent the juror was in this round, in basis points.
771-
uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence(
768+
uint256 coherence = disputeKit.getDegreeOfCoherencePenalty(
772769
_params.disputeID,
773770
_params.round,
774771
_params.repartition,
775772
_params.feePerJurorInRound,
776773
_params.pnkAtStakePerJurorInRound
777774
);
778-
if (degreeOfCoherence > ALPHA_DIVISOR) {
779-
// Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
780-
degreeOfCoherence = ALPHA_DIVISOR;
775+
776+
// Guard against degree exceeding 1, though it should be ensured by the dispute kit.
777+
if (coherence > ONE_BASIS_POINT) {
778+
coherence = ONE_BASIS_POINT;
781779
}
782780

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

786784
// Unlock the PNKs affected by the penalty
787785
address account = round.drawnJurors[_params.repartition];
@@ -794,7 +792,7 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
794792
account,
795793
_params.disputeID,
796794
_params.round,
797-
degreeOfCoherence,
795+
coherence,
798796
-int256(availablePenalty),
799797
0,
800798
round.feeToken
@@ -826,37 +824,40 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
826824
IDisputeKit disputeKit = disputeKits[round.disputeKitID];
827825

828826
// [0, 1] value that determines how coherent the juror was in this round, in basis points.
829-
uint256 degreeOfCoherence = disputeKit.getDegreeOfCoherence(
827+
(uint256 pnkCoherence, uint256 feeCoherence) = disputeKit.getDegreeOfCoherenceReward(
830828
_params.disputeID,
831829
_params.round,
832830
_params.repartition % _params.numberOfVotesInRound,
833831
_params.feePerJurorInRound,
834832
_params.pnkAtStakePerJurorInRound
835833
);
836834

837-
// Make sure the degree doesn't exceed 1, though it should be ensured by the dispute kit.
838-
if (degreeOfCoherence > ALPHA_DIVISOR) {
839-
degreeOfCoherence = ALPHA_DIVISOR;
835+
// Guard against degree exceeding 1, though it should be ensured by the dispute kit.
836+
if (pnkCoherence > ONE_BASIS_POINT) {
837+
pnkCoherence = ONE_BASIS_POINT;
838+
}
839+
if (feeCoherence > ONE_BASIS_POINT) {
840+
feeCoherence = ONE_BASIS_POINT;
840841
}
841842

842843
address account = round.drawnJurors[_params.repartition % _params.numberOfVotesInRound];
843-
uint256 pnkLocked = _applyCoherence(round.pnkAtStakePerJuror, degreeOfCoherence);
844+
uint256 pnkLocked = _applyCoherence(round.pnkAtStakePerJuror, pnkCoherence);
844845

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

848849
// Transfer the rewards
849-
uint256 pnkReward = _applyCoherence(_params.pnkPenaltiesInRound / _params.coherentCount, degreeOfCoherence);
850+
uint256 pnkReward = _applyCoherence(_params.pnkPenaltiesInRound / _params.coherentCount, pnkCoherence);
850851
round.sumPnkRewardPaid += pnkReward;
851-
uint256 feeReward = _applyCoherence(round.totalFeesForJurors / _params.coherentCount, degreeOfCoherence);
852+
uint256 feeReward = _applyCoherence(round.totalFeesForJurors / _params.coherentCount, feeCoherence);
852853
round.sumFeeRewardPaid += feeReward;
853854
pinakion.safeTransfer(account, pnkReward);
854855
_transferFeeToken(round.feeToken, payable(account), feeReward);
855856
emit TokenAndETHShift(
856857
account,
857858
_params.disputeID,
858859
_params.round,
859-
degreeOfCoherence,
860+
pnkCoherence,
860861
int256(pnkReward),
861862
int256(feeReward),
862863
round.feeToken
@@ -1059,18 +1060,18 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
10591060

10601061
/// @dev Applies degree of coherence to an amount
10611062
/// @param _amount The base amount to apply coherence to.
1062-
/// @param _degreeOfCoherence The degree of coherence in basis points.
1063+
/// @param _coherence The degree of coherence in basis points.
10631064
/// @return The amount after applying the degree of coherence.
1064-
function _applyCoherence(uint256 _amount, uint256 _degreeOfCoherence) internal pure returns (uint256) {
1065-
return (_amount * _degreeOfCoherence) / ALPHA_DIVISOR;
1065+
function _applyCoherence(uint256 _amount, uint256 _coherence) internal pure returns (uint256) {
1066+
return (_amount * _coherence) / ONE_BASIS_POINT;
10661067
}
10671068

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

10761077
/// @dev Toggles the dispute kit support for a given court.

contracts/src/arbitration/SortitionModuleBase.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,14 +338,14 @@ abstract contract SortitionModuleBase is ISortitionModule, Initializable, UUPSPr
338338
// Update the sortition sum tree.
339339
bytes32 stakePathID = _accountAndCourtIDToStakePathID(_account, _courtID);
340340
bool finished = false;
341-
uint96 currenCourtID = _courtID;
341+
uint96 currentCourtID = _courtID;
342342
while (!finished) {
343343
// Tokens are also implicitly staked in parent courts through sortition module to increase the chance of being drawn.
344-
_set(bytes32(uint256(currenCourtID)), _newStake, stakePathID);
345-
if (currenCourtID == GENERAL_COURT) {
344+
_set(bytes32(uint256(currentCourtID)), _newStake, stakePathID);
345+
if (currentCourtID == GENERAL_COURT) {
346346
finished = true;
347347
} else {
348-
(currenCourtID, , , , , , ) = core.courts(currenCourtID); // Get the parent court.
348+
(currentCourtID, , , , , , ) = core.courts(currentCourtID); // Get the parent court.
349349
}
350350
}
351351
emit StakeSet(_account, _courtID, _newStake, juror.stakedPnk);

contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {KlerosCore, KlerosCoreBase, IDisputeKit, ISortitionModule} from "../Kler
66
import {Initializable} from "../../proxy/Initializable.sol";
77
import {UUPSProxiable} from "../../proxy/UUPSProxiable.sol";
88
import {SafeSend} from "../../libraries/SafeSend.sol";
9+
import {ONE_BASIS_POINT} from "../../libraries/Constants.sol";
910

1011
/// @title DisputeKitClassicBase
1112
/// Abstract Dispute kit classic implementation of the Kleros v1 features including:
@@ -57,7 +58,6 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
5758
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.
5859
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.
5960
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.
60-
uint256 public constant ONE_BASIS_POINT = 10000; // One basis point, for scaling.
6161

6262
address public governor; // The governor of the contract.
6363
KlerosCore public core; // The Kleros Core arbitrator
@@ -526,14 +526,39 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi
526526
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
527527
/// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit.
528528
/// @param _voteID The ID of the vote.
529-
/// @return The degree of coherence in basis points.
530-
function getDegreeOfCoherence(
529+
/// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward.
530+
/// @return feeCoherence The degree of coherence in basis points for the dispute fee reward.
531+
function getDegreeOfCoherenceReward(
531532
uint256 _coreDisputeID,
532533
uint256 _coreRoundID,
533534
uint256 _voteID,
534535
uint256 /* _feePerJuror */,
535536
uint256 /* _pnkAtStakePerJuror */
536-
) external view override returns (uint256) {
537+
) external view override returns (uint256 pnkCoherence, uint256 feeCoherence) {
538+
uint256 coherence = _getDegreeOfCoherence(_coreDisputeID, _coreRoundID, _voteID);
539+
return (coherence, coherence);
540+
}
541+
542+
/// @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.
543+
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
544+
/// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit.
545+
/// @param _voteID The ID of the vote.
546+
/// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward.
547+
function getDegreeOfCoherencePenalty(
548+
uint256 _coreDisputeID,
549+
uint256 _coreRoundID,
550+
uint256 _voteID,
551+
uint256 /* _feePerJuror */,
552+
uint256 /* _pnkAtStakePerJuror */
553+
) external view override returns (uint256 pnkCoherence) {
554+
return _getDegreeOfCoherence(_coreDisputeID, _coreRoundID, _voteID);
555+
}
556+
557+
function _getDegreeOfCoherence(
558+
uint256 _coreDisputeID,
559+
uint256 _coreRoundID,
560+
uint256 _voteID
561+
) internal view returns (uint256 coherence) {
537562
// In this contract this degree can be either 0 or 1, but in other dispute kits this value can be something in between.
538563
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
539564
Vote storage vote = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]].votes[_voteID];

contracts/src/arbitration/evidence/ModeratedEvidenceModule.sol

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@ pragma solidity ^0.8.24;
55
// TODO: standard interfaces should be placed in a separated repo (?)
66
import {IArbitrableV2, IArbitratorV2} from "../interfaces/IArbitrableV2.sol";
77
import "../interfaces/IDisputeTemplateRegistry.sol";
8-
import "../../libraries/CappedMath.sol";
98

109
/// @title Implementation of the Evidence Standard with Moderated Submissions
1110
contract ModeratedEvidenceModule is IArbitrableV2 {
12-
using CappedMath for uint256;
13-
1411
// ************************************* //
1512
// * Enums / Structs * //
1613
// ************************************* //
@@ -205,8 +202,8 @@ contract ModeratedEvidenceModule is IArbitrableV2 {
205202
ArbitratorData storage arbitratorData = arbitratorDataList[arbitratorDataList.length - 1];
206203

207204
uint256 arbitrationCost = arbitrator.arbitrationCost(arbitratorData.arbitratorExtraData);
208-
uint256 totalCost = arbitrationCost.mulCap(totalCostMultiplier) / MULTIPLIER_DIVISOR;
209-
uint256 depositRequired = totalCost.mulCap(initialDepositMultiplier) / MULTIPLIER_DIVISOR;
205+
uint256 totalCost = (arbitrationCost * totalCostMultiplier) / MULTIPLIER_DIVISOR;
206+
uint256 depositRequired = (totalCost * initialDepositMultiplier) / MULTIPLIER_DIVISOR;
210207

211208
Moderation storage moderation = evidenceData.moderations.push();
212209
// Overpaying is allowed.
@@ -245,12 +242,12 @@ contract ModeratedEvidenceModule is IArbitrableV2 {
245242
ArbitratorData storage arbitratorData = arbitratorDataList[moderation.arbitratorDataID];
246243

247244
uint256 arbitrationCost = arbitrator.arbitrationCost(arbitratorData.arbitratorExtraData);
248-
uint256 totalCost = arbitrationCost.mulCap(totalCostMultiplier) / MULTIPLIER_DIVISOR;
245+
uint256 totalCost = (arbitrationCost * totalCostMultiplier) / MULTIPLIER_DIVISOR;
249246

250247
uint256 opposition = 3 - uint256(_side);
251248
uint256 depositRequired = moderation.paidFees[opposition] * 2;
252249
if (depositRequired == 0) {
253-
depositRequired = totalCost.mulCap(initialDepositMultiplier) / MULTIPLIER_DIVISOR;
250+
depositRequired = (totalCost * initialDepositMultiplier) / MULTIPLIER_DIVISOR;
254251
} else if (depositRequired > totalCost) {
255252
depositRequired = totalCost;
256253
}
@@ -317,7 +314,9 @@ contract ModeratedEvidenceModule is IArbitrableV2 {
317314
) internal returns (uint256) {
318315
uint256 contribution;
319316
uint256 remainingETH;
320-
uint256 requiredAmount = _totalRequired.subCap(_moderation.paidFees[uint256(_side)]);
317+
uint256 requiredAmount = _moderation.paidFees[uint256(_side)] >= _totalRequired
318+
? 0
319+
: _totalRequired - _moderation.paidFees[uint256(_side)];
321320
(contribution, remainingETH) = calculateContribution(_amount, requiredAmount);
322321
_moderation.contributions[_contributor][uint256(_side)] += contribution;
323322
_moderation.paidFees[uint256(_side)] += contribution;

contracts/src/arbitration/interfaces/IDisputeKit.sol

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,30 @@ interface IDisputeKit {
6767
/// @param _voteID The ID of the vote.
6868
/// @param _feePerJuror The fee per juror.
6969
/// @param _pnkAtStakePerJuror The PNK at stake per juror.
70-
/// @return The degree of coherence in basis points.
71-
function getDegreeOfCoherence(
70+
/// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward.
71+
/// @return feeCoherence The degree of coherence in basis points for the dispute fee reward.
72+
function getDegreeOfCoherenceReward(
7273
uint256 _coreDisputeID,
7374
uint256 _coreRoundID,
7475
uint256 _voteID,
7576
uint256 _feePerJuror,
7677
uint256 _pnkAtStakePerJuror
77-
) external view returns (uint256);
78+
) external view returns (uint256 pnkCoherence, uint256 feeCoherence);
79+
80+
/// @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.
81+
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.
82+
/// @param _coreRoundID The ID of the round in Kleros Core, not in the Dispute Kit.
83+
/// @param _voteID The ID of the vote.
84+
/// @param _feePerJuror The fee per juror.
85+
/// @param _pnkAtStakePerJuror The PNK at stake per juror.
86+
/// @return pnkCoherence The degree of coherence in basis points for the dispute PNK reward.
87+
function getDegreeOfCoherencePenalty(
88+
uint256 _coreDisputeID,
89+
uint256 _coreRoundID,
90+
uint256 _voteID,
91+
uint256 _feePerJuror,
92+
uint256 _pnkAtStakePerJuror
93+
) external view returns (uint256 pnkCoherence);
7894

7995
/// @dev Gets the number of jurors who are eligible to a reward in this round.
8096
/// @param _coreDisputeID The ID of the dispute in Kleros Core, not in the Dispute Kit.

0 commit comments

Comments
 (0)