@@ -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,21 +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 StakeDelayedNotTransferred (address indexed _address , uint256 _courtID , uint256 _amount );
119
- event StakeDelayedAlreadyTransferred (address indexed _address , uint256 _courtID , uint256 _amount );
120
- event StakeDelayedAlreadyTransferredWithdrawn (
121
- uint96 indexed _courtID ,
122
- address indexed _account ,
123
- uint256 _withdrawnAmount
124
- );
125
109
event NewPeriod (uint256 indexed _disputeID , Period _period );
126
110
event AppealPossible (uint256 indexed _disputeID , IArbitrableV2 indexed _arbitrable );
127
111
event AppealDecision (uint256 indexed _disputeID , IArbitrableV2 indexed _arbitrable );
@@ -464,7 +448,7 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
464
448
/// @param _newStake The new stake.
465
449
/// Note that the existing delayed stake will be nullified as non-relevant.
466
450
function setStake (uint96 _courtID , uint256 _newStake ) external {
467
- if ( ! _setStakeForAccount ( msg .sender , _courtID, _newStake, false )) revert StakingFailed ( );
451
+ _setStake ( msg .sender , _courtID, _newStake, false );
468
452
}
469
453
470
454
function setStakeBySortitionModule (
@@ -474,31 +458,7 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
474
458
bool _alreadyTransferred
475
459
) external {
476
460
if (msg .sender != address (sortitionModule)) revert SortitionModuleOnly ();
477
- _setStakeForAccount (_account, _courtID, _newStake, _alreadyTransferred);
478
- }
479
-
480
- function withdrawPartiallyDelayedStake (uint96 _courtID , address _juror , uint256 _amountToWithdraw ) external {
481
- if (msg .sender != address (sortitionModule)) revert SortitionModuleOnly ();
482
- uint256 actualAmount = _amountToWithdraw;
483
- Juror storage juror = jurors[_juror];
484
- if (juror.stakedPnk <= actualAmount) {
485
- actualAmount = juror.stakedPnk;
486
- }
487
- require (pinakion.safeTransfer (_juror, actualAmount));
488
- // StakePnk can become lower because of penalty, thus we adjust the amount for it. stakedPnkByCourt can't be penalized so subtract the default amount.
489
- juror.stakedPnk -= actualAmount;
490
- juror.stakedPnkByCourt[_courtID] -= _amountToWithdraw;
491
- emit StakeDelayedAlreadyTransferredWithdrawn (_courtID, _juror, _amountToWithdraw);
492
- // Note that if we don't delete court here it'll be duplicated after staking.
493
- if (juror.stakedPnkByCourt[_courtID] == 0 ) {
494
- for (uint256 i = juror.courtIDs.length ; i > 0 ; i-- ) {
495
- if (juror.courtIDs[i - 1 ] == _courtID) {
496
- juror.courtIDs[i - 1 ] = juror.courtIDs[juror.courtIDs.length - 1 ];
497
- juror.courtIDs.pop ();
498
- break ;
499
- }
500
- }
501
- }
461
+ _setStake (_account, _courtID, _newStake, _alreadyTransferred);
502
462
}
503
463
504
464
/// @inheritdoc IArbitratorV2
@@ -625,7 +585,7 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
625
585
if (drawnAddress == address (0 )) {
626
586
continue ;
627
587
}
628
- jurors[drawnAddress].lockedPnk += round.pnkAtStakePerJuror;
588
+ sortitionModule. lockStake (drawnAddress, round.pnkAtStakePerJuror) ;
629
589
emit Draw (drawnAddress, _disputeID, currentRound, round.drawnJurors.length );
630
590
round.drawnJurors.push (drawnAddress);
631
591
if (round.drawnJurors.length == round.nbVotes) {
@@ -764,15 +724,10 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
764
724
765
725
// Unlock the PNKs affected by the penalty
766
726
address account = round.drawnJurors[_params.repartition];
767
- jurors[account].lockedPnk -= penalty;
727
+ sortitionModule. unlockStake (account, penalty) ;
768
728
769
729
// Apply the penalty to the staked PNKs.
770
- // Note that lockedPnk will always cover penalty while stakedPnk can become lower after manual unstaking.
771
- if (jurors[account].stakedPnk >= penalty) {
772
- jurors[account].stakedPnk -= penalty;
773
- } else {
774
- jurors[account].stakedPnk = 0 ;
775
- }
730
+ sortitionModule.penalizeStake (account, penalty);
776
731
emit TokenAndETHShift (
777
732
account,
778
733
_params.disputeID,
@@ -831,10 +786,10 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
831
786
uint256 pnkLocked = (round.pnkAtStakePerJuror * degreeOfCoherence) / ALPHA_DIVISOR;
832
787
833
788
// Release the rest of the PNKs of the juror for this round.
834
- jurors[account].lockedPnk -= pnkLocked;
789
+ sortitionModule. unlockStake (account, pnkLocked) ;
835
790
836
791
// Give back the locked PNKs in case the juror fully unstaked earlier.
837
- if (jurors[account].stakedPnk == 0 ) {
792
+ if (! sortitionModule. isJurorStaked (account) ) {
838
793
pinakion.safeTransfer (account, pnkLocked);
839
794
}
840
795
@@ -980,17 +935,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
980
935
return disputes[_disputeID].rounds.length ;
981
936
}
982
937
983
- function getJurorBalance (
984
- address _juror ,
985
- uint96 _courtID
986
- ) external view returns (uint256 totalStaked , uint256 totalLocked , uint256 stakedInCourt , uint256 nbCourts ) {
987
- Juror storage juror = jurors[_juror];
988
- totalStaked = juror.stakedPnk;
989
- totalLocked = juror.lockedPnk;
990
- stakedInCourt = juror.stakedPnkByCourt[_courtID];
991
- nbCourts = juror.courtIDs.length ;
992
- }
993
-
994
938
function isSupported (uint96 _courtID , uint256 _disputeKitID ) external view returns (bool ) {
995
939
return courts[_courtID].supportedDisputeKits[_disputeKitID];
996
940
}
@@ -1033,12 +977,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
1033
977
return disputeKits.length ;
1034
978
}
1035
979
1036
- /// @dev Gets the court identifiers where a specific `_juror` has staked.
1037
- /// @param _juror The address of the juror.
1038
- function getJurorCourtIDs (address _juror ) public view returns (uint96 [] memory ) {
1039
- return jurors[_juror].courtIDs;
1040
- }
1041
-
1042
980
function convertEthToTokenAmount (IERC20 _toToken , uint256 _amountInEth ) public view returns (uint256 ) {
1043
981
return (_amountInEth * 10 ** currencyRates[_toToken].rateDecimals) / currencyRates[_toToken].rateInEth;
1044
982
}
@@ -1056,104 +994,34 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
1056
994
emit DisputeKitEnabled (_courtID, _disputeKitID, _enable);
1057
995
}
1058
996
1059
- /// @dev Sets the specified juror's stake in a court.
1060
- /// `O(n + p * log_k(j))` where
1061
- /// `n` is the number of courts the juror has staked in,
1062
- /// `p` is the depth of the court tree,
1063
- /// `k` is the minimum number of children per node of one of these courts' sortition sum tree,
1064
- /// and `j` is the maximum number of jurors that ever staked in one of these courts simultaneously.
1065
- /// @param _account The address of the juror.
1066
- /// @param _courtID The ID of the court.
1067
- /// @param _newStake The new stake.
1068
- /// @param _alreadyTransferred True if the tokens were already transferred from juror. Only relevant for delayed stakes.
1069
- /// @return succeeded True if the call succeeded, false otherwise.
1070
- function _setStakeForAccount (
997
+ function _setStake (
1071
998
address _account ,
1072
999
uint96 _courtID ,
1073
1000
uint256 _newStake ,
1074
1001
bool _alreadyTransferred
1075
- ) internal returns (bool succeeded ) {
1076
- if (_courtID == Constants.FORKING_COURT || _courtID > courts.length ) return false ;
1077
-
1078
- Juror storage juror = jurors[_account];
1079
- uint256 currentStake = juror.stakedPnkByCourt[_courtID];
1080
-
1081
- if (_newStake != 0 ) {
1082
- if (_newStake < courts[_courtID].minStake) return false ;
1083
- } else if (currentStake == 0 ) {
1084
- return false ;
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.
1085
1005
}
1086
-
1087
- ISortitionModule.PreStakeHookResult result = sortitionModule.preStakeHook (_account, _courtID, _newStake);
1088
- if (result == ISortitionModule.PreStakeHookResult.failed) {
1089
- return false ;
1090
- } else if (result == ISortitionModule.PreStakeHookResult.stakeDelayedNotTransferred) {
1091
- emit StakeDelayedNotTransferred (_account, _courtID, _newStake);
1092
- return true ;
1006
+ if (_newStake != 0 && _newStake < courts[_courtID].minStake) {
1007
+ return false ; // Staking less than the minimum stake is not allowed.
1093
1008
}
1094
-
1095
- uint256 transferredAmount;
1096
- if (_newStake >= currentStake) {
1097
- if (! _alreadyTransferred) {
1098
- // Stake increase
1099
- // When stakedPnk becomes lower than lockedPnk count the locked tokens in when transferring tokens from juror.
1100
- // (E.g. stakedPnk = 0, lockedPnk = 150) which can happen if the juror unstaked fully while having some tokens locked.
1101
- uint256 previouslyLocked = (juror.lockedPnk >= juror.stakedPnk) ? juror.lockedPnk - juror.stakedPnk : 0 ; // underflow guard
1102
- transferredAmount = (_newStake >= currentStake + previouslyLocked) // underflow guard
1103
- ? _newStake - currentStake - previouslyLocked
1104
- : 0 ;
1105
- if (transferredAmount > 0 ) {
1106
- // 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.
1107
- pinakion.safeTransferFrom (_account, address (this ), transferredAmount);
1108
- }
1109
- if (currentStake == 0 ) {
1110
- juror.courtIDs.push (_courtID);
1111
- }
1112
- }
1113
- } else {
1114
- // Note that stakes can be partially delayed only when stake is increased.
1115
- // Stake decrease: make sure locked tokens always stay in the contract. They can only be released during Execution.
1116
- if (juror.stakedPnk >= currentStake - _newStake + juror.lockedPnk) {
1117
- // We have enough pnk staked to afford withdrawal while keeping locked tokens.
1118
- transferredAmount = currentStake - _newStake;
1119
- } else if (juror.stakedPnk >= juror.lockedPnk) {
1120
- // Can't afford withdrawing the current stake fully. Take whatever is available while keeping locked tokens.
1121
- transferredAmount = juror.stakedPnk - juror.lockedPnk;
1122
- }
1123
- if (transferredAmount > 0 ) {
1124
- if (! pinakion.safeTransfer (_account, transferredAmount)) {
1125
- return false ;
1126
- }
1127
- }
1128
- if (_newStake == 0 ) {
1129
- for (uint256 i = juror.courtIDs.length ; i > 0 ; i-- ) {
1130
- if (juror.courtIDs[i - 1 ] == _courtID) {
1131
- juror.courtIDs[i - 1 ] = juror.courtIDs[juror.courtIDs.length - 1 ];
1132
- juror.courtIDs.pop ();
1133
- break ;
1134
- }
1135
- }
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 ;
1136
1022
}
1137
1023
}
1138
-
1139
- // Note that stakedPnk can become async with currentStake (e.g. after penalty).
1140
- // Also note that these values were already updated if the stake was only partially delayed.
1141
- if (! _alreadyTransferred) {
1142
- juror.stakedPnk = (juror.stakedPnk >= currentStake)
1143
- ? juror.stakedPnk - currentStake + _newStake
1144
- : _newStake;
1145
- juror.stakedPnkByCourt[_courtID] = _newStake;
1146
- }
1147
-
1148
- // Transfer the tokens but don't update sortition module.
1149
- if (result == ISortitionModule.PreStakeHookResult.stakeDelayedAlreadyTransferred) {
1150
- emit StakeDelayedAlreadyTransferred (_account, _courtID, _newStake);
1151
- return true ;
1152
- }
1153
-
1154
- sortitionModule.setStake (_account, _courtID, _newStake);
1155
- emit StakeSet (_account, _courtID, _newStake);
1156
- return true ;
1024
+ return sortitionSuccess;
1157
1025
}
1158
1026
1159
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.
0 commit comments