@@ -70,13 +70,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
70
70
uint256 drawIterations; // The number of iterations passed drawing the jurors for this round.
71
71
}
72
72
73
- struct Juror {
74
- uint96 [] courtIDs; // The IDs of courts where the juror's stake path ends. A stake path is a path from the general court to a court the juror directly staked in using `_setStake`.
75
- uint256 stakedPnk; // The juror's total amount of tokens staked in subcourts. Reflects actual pnk balance.
76
- uint256 lockedPnk; // The juror's total amount of tokens locked in disputes. Can reflect actual pnk balance when stakedPnk are fully withdrawn.
77
- mapping (uint96 => uint256 ) stakedPnkByCourt; // The amount of PNKs the juror has staked in the court in the form `stakedPnkByCourt[courtID]`.
78
- }
79
-
80
73
// Workaround "stack too deep" errors
81
74
struct ExecuteParams {
82
75
uint256 disputeID; // The ID of the dispute to execute.
@@ -107,15 +100,12 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
107
100
Court[] public courts; // The courts.
108
101
IDisputeKit[] public disputeKits; // Array of dispute kits.
109
102
Dispute[] public disputes; // The disputes.
110
- mapping (address => Juror) internal jurors; // The jurors.
111
103
mapping (IERC20 => CurrencyRate) public currencyRates; // The price of each token in ETH.
112
104
113
105
// ************************************* //
114
106
// * Events * //
115
107
// ************************************* //
116
108
117
- event StakeSet (address indexed _address , uint256 _courtID , uint256 _amount );
118
- event StakeDelayed (address indexed _address , uint256 _courtID , uint256 _amount );
119
109
event NewPeriod (uint256 indexed _disputeID , Period _period );
120
110
event AppealPossible (uint256 indexed _disputeID , IArbitrableV2 indexed _arbitrable );
121
111
event AppealDecision (uint256 indexed _disputeID , IArbitrableV2 indexed _arbitrable );
@@ -456,13 +446,19 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
456
446
/// @dev Sets the caller's stake in a court.
457
447
/// @param _courtID The ID of the court.
458
448
/// @param _newStake The new stake.
449
+ /// Note that the existing delayed stake will be nullified as non-relevant.
459
450
function setStake (uint96 _courtID , uint256 _newStake ) external {
460
- if ( ! _setStakeForAccount ( msg .sender , _courtID, _newStake)) revert StakingFailed ( );
451
+ _setStake ( msg .sender , _courtID, _newStake, false );
461
452
}
462
453
463
- function setStakeBySortitionModule (address _account , uint96 _courtID , uint256 _newStake ) external {
464
- if (msg .sender != address (sortitionModule)) revert WrongCaller ();
465
- _setStakeForAccount (_account, _courtID, _newStake);
454
+ function setStakeBySortitionModule (
455
+ address _account ,
456
+ uint96 _courtID ,
457
+ uint256 _newStake ,
458
+ bool _alreadyTransferred
459
+ ) external {
460
+ if (msg .sender != address (sortitionModule)) revert SortitionModuleOnly ();
461
+ _setStake (_account, _courtID, _newStake, _alreadyTransferred);
466
462
}
467
463
468
464
/// @inheritdoc IArbitratorV2
@@ -589,7 +585,7 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
589
585
if (drawnAddress == address (0 )) {
590
586
continue ;
591
587
}
592
- jurors[drawnAddress].lockedPnk += round.pnkAtStakePerJuror;
588
+ sortitionModule. lockStake (drawnAddress, round.pnkAtStakePerJuror) ;
593
589
emit Draw (drawnAddress, _disputeID, currentRound, round.drawnJurors.length );
594
590
round.drawnJurors.push (drawnAddress);
595
591
if (round.drawnJurors.length == round.nbVotes) {
@@ -728,15 +724,10 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
728
724
729
725
// Unlock the PNKs affected by the penalty
730
726
address account = round.drawnJurors[_params.repartition];
731
- jurors[account].lockedPnk -= penalty;
727
+ sortitionModule. unlockStake (account, penalty) ;
732
728
733
729
// Apply the penalty to the staked PNKs.
734
- // Note that lockedPnk will always cover penalty while stakedPnk can become lower after manual unstaking.
735
- if (jurors[account].stakedPnk >= penalty) {
736
- jurors[account].stakedPnk -= penalty;
737
- } else {
738
- jurors[account].stakedPnk = 0 ;
739
- }
730
+ sortitionModule.penalizeStake (account, penalty);
740
731
emit TokenAndETHShift (
741
732
account,
742
733
_params.disputeID,
@@ -795,10 +786,10 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
795
786
uint256 pnkLocked = (round.pnkAtStakePerJuror * degreeOfCoherence) / ALPHA_DIVISOR;
796
787
797
788
// Release the rest of the PNKs of the juror for this round.
798
- jurors[account].lockedPnk -= pnkLocked;
789
+ sortitionModule. unlockStake (account, pnkLocked) ;
799
790
800
791
// Give back the locked PNKs in case the juror fully unstaked earlier.
801
- if (jurors[account].stakedPnk == 0 ) {
792
+ if (! sortitionModule. isJurorStaked (account) ) {
802
793
pinakion.safeTransfer (account, pnkLocked);
803
794
}
804
795
@@ -944,17 +935,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
944
935
return disputes[_disputeID].rounds.length ;
945
936
}
946
937
947
- function getJurorBalance (
948
- address _juror ,
949
- uint96 _courtID
950
- ) external view returns (uint256 totalStaked , uint256 totalLocked , uint256 stakedInCourt , uint256 nbCourts ) {
951
- Juror storage juror = jurors[_juror];
952
- totalStaked = juror.stakedPnk;
953
- totalLocked = juror.lockedPnk;
954
- stakedInCourt = juror.stakedPnkByCourt[_courtID];
955
- nbCourts = juror.courtIDs.length ;
956
- }
957
-
958
938
function isSupported (uint96 _courtID , uint256 _disputeKitID ) external view returns (bool ) {
959
939
return courts[_courtID].supportedDisputeKits[_disputeKitID];
960
940
}
@@ -997,12 +977,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
997
977
return disputeKits.length ;
998
978
}
999
979
1000
- /// @dev Gets the court identifiers where a specific `_juror` has staked.
1001
- /// @param _juror The address of the juror.
1002
- function getJurorCourtIDs (address _juror ) public view returns (uint96 [] memory ) {
1003
- return jurors[_juror].courtIDs;
1004
- }
1005
-
1006
980
function convertEthToTokenAmount (IERC20 _toToken , uint256 _amountInEth ) public view returns (uint256 ) {
1007
981
return (_amountInEth * 10 ** currencyRates[_toToken].rateDecimals) / currencyRates[_toToken].rateInEth;
1008
982
}
@@ -1020,89 +994,34 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
1020
994
emit DisputeKitEnabled (_courtID, _disputeKitID, _enable);
1021
995
}
1022
996
1023
- /// @dev Sets the specified juror's stake in a court.
1024
- /// `O(n + p * log_k(j))` where
1025
- /// `n` is the number of courts the juror has staked in,
1026
- /// `p` is the depth of the court tree,
1027
- /// `k` is the minimum number of children per node of one of these courts' sortition sum tree,
1028
- /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously.
1029
- /// @param _account The address of the juror.
1030
- /// @param _courtID The ID of the court.
1031
- /// @param _newStake The new stake.
1032
- /// @return succeeded True if the call succeeded, false otherwise.
1033
- function _setStakeForAccount (
997
+ function _setStake (
1034
998
address _account ,
1035
999
uint96 _courtID ,
1036
- uint256 _newStake
1037
- ) internal returns (bool succeeded ) {
1038
- if (_courtID == Constants.FORKING_COURT || _courtID > courts.length ) return false ;
1039
-
1040
- Juror storage juror = jurors[_account];
1041
- uint256 currentStake = juror.stakedPnkByCourt[_courtID];
1042
-
1043
- if (_newStake != 0 ) {
1044
- if (_newStake < courts[_courtID].minStake) return false ;
1045
- } else if (currentStake == 0 ) {
1046
- return false ;
1000
+ uint256 _newStake ,
1001
+ bool _alreadyTransferred
1002
+ ) internal returns (bool success ) {
1003
+ if (_courtID == Constants.FORKING_COURT || _courtID > courts.length ) {
1004
+ return false ; // Staking directly into the forking court is not allowed.
1047
1005
}
1048
-
1049
- ISortitionModule.preStakeHookResult result = sortitionModule.preStakeHook (_account, _courtID, _newStake);
1050
- if (result == ISortitionModule.preStakeHookResult.failed) {
1051
- return false ;
1052
- } else if (result == ISortitionModule.preStakeHookResult.delayed) {
1053
- emit StakeDelayed (_account, _courtID, _newStake);
1054
- return true ;
1006
+ if (_newStake != 0 && _newStake < courts[_courtID].minStake) {
1007
+ return false ; // Staking less than the minimum stake is not allowed.
1055
1008
}
1056
-
1057
- uint256 transferredAmount;
1058
- if (_newStake >= currentStake) {
1059
- // Stake increase
1060
- // When stakedPnk becomes lower than lockedPnk count the locked tokens in when transferring tokens from juror.
1061
- // (E.g. stakedPnk = 0, lockedPnk = 150) which can happen if the juror unstaked fully while having some tokens locked.
1062
- uint256 previouslyLocked = (juror.lockedPnk >= juror.stakedPnk) ? juror.lockedPnk - juror.stakedPnk : 0 ; // underflow guard
1063
- transferredAmount = (_newStake >= currentStake + previouslyLocked) // underflow guard
1064
- ? _newStake - currentStake - previouslyLocked
1065
- : 0 ;
1066
- if (transferredAmount > 0 ) {
1067
- if (! pinakion.safeTransferFrom (_account, address (this ), transferredAmount)) {
1068
- return false ;
1069
- }
1070
- }
1071
- if (currentStake == 0 ) {
1072
- juror.courtIDs.push (_courtID);
1073
- }
1074
- } else {
1075
- // Stake decrease: make sure locked tokens always stay in the contract. They can only be released during Execution.
1076
- if (juror.stakedPnk >= currentStake - _newStake + juror.lockedPnk) {
1077
- // We have enough pnk staked to afford withdrawal while keeping locked tokens.
1078
- transferredAmount = currentStake - _newStake;
1079
- } else if (juror.stakedPnk >= juror.lockedPnk) {
1080
- // Can't afford withdrawing the current stake fully. Take whatever is available while keeping locked tokens.
1081
- transferredAmount = juror.stakedPnk - juror.lockedPnk;
1082
- }
1083
- if (transferredAmount > 0 ) {
1084
- if (! pinakion.safeTransfer (_account, transferredAmount)) {
1085
- return false ;
1086
- }
1087
- }
1088
- if (_newStake == 0 ) {
1089
- for (uint256 i = juror.courtIDs.length ; i > 0 ; i-- ) {
1090
- if (juror.courtIDs[i - 1 ] == _courtID) {
1091
- juror.courtIDs[i - 1 ] = juror.courtIDs[juror.courtIDs.length - 1 ];
1092
- juror.courtIDs.pop ();
1093
- break ;
1094
- }
1095
- }
1009
+ (uint256 pnkDeposit , uint256 pnkWithdrawal , bool sortitionSuccess ) = sortitionModule.setStake (
1010
+ _account,
1011
+ _courtID,
1012
+ _newStake,
1013
+ _alreadyTransferred
1014
+ );
1015
+ if (pnkDeposit > 0 && pnkWithdrawal > 0 ) revert StakingFailed ();
1016
+ if (pnkDeposit > 0 ) {
1017
+ // Note we don't return false after incorrect transfer because when stake is increased the transfer is done immediately, thus it can't disrupt delayed stakes' queue.
1018
+ pinakion.safeTransferFrom (_account, address (this ), pnkDeposit);
1019
+ } else if (pnkWithdrawal > 0 ) {
1020
+ if (! pinakion.safeTransfer (_account, pnkWithdrawal)) {
1021
+ return false ;
1096
1022
}
1097
1023
}
1098
-
1099
- // Note that stakedPnk can become async with currentStake (e.g. after penalty).
1100
- juror.stakedPnk = (juror.stakedPnk >= currentStake) ? juror.stakedPnk - currentStake + _newStake : _newStake;
1101
- juror.stakedPnkByCourt[_courtID] = _newStake;
1102
-
1103
- sortitionModule.setStake (_account, _courtID, _newStake);
1104
- emit StakeSet (_account, _courtID, _newStake);
1105
- return true ;
1024
+ return sortitionSuccess;
1106
1025
}
1107
1026
1108
1027
/// @dev Gets a court ID, the minimum number of jurors and an ID of a dispute kit from a specified extra data bytes array.
@@ -1143,6 +1062,8 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
1143
1062
// ************************************* //
1144
1063
1145
1064
error GovernorOnly ();
1065
+ error DisputeKitOnly ();
1066
+ error SortitionModuleOnly ();
1146
1067
error UnsuccessfulCall ();
1147
1068
error InvalidDisputKitParent ();
1148
1069
error DepthLevelMax ();
@@ -1153,7 +1074,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
1153
1074
error CannotDisableClassicDK ();
1154
1075
error ArraysLengthMismatch ();
1155
1076
error StakingFailed ();
1156
- error WrongCaller ();
1157
1077
error ArbitrationFeesNotEnough ();
1158
1078
error DisputeKitNotSupportedByCourt ();
1159
1079
error MustSupportDisputeKitClassic ();
@@ -1166,7 +1086,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
1166
1086
error NotEvidencePeriod ();
1167
1087
error AppealFeesNotEnough ();
1168
1088
error DisputeNotAppealable ();
1169
- error DisputeKitOnly ();
1170
1089
error NotExecutionPeriod ();
1171
1090
error RulingAlreadyExecuted ();
1172
1091
error DisputePeriodIsFinal ();
0 commit comments