@@ -70,13 +70,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
7070 uint256 drawIterations; // The number of iterations passed drawing the jurors for this round.
7171 }
7272
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-
8073 // Workaround "stack too deep" errors
8174 struct ExecuteParams {
8275 uint256 disputeID; // The ID of the dispute to execute.
@@ -107,15 +100,12 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
107100 Court[] public courts; // The courts.
108101 IDisputeKit[] public disputeKits; // Array of dispute kits.
109102 Dispute[] public disputes; // The disputes.
110- mapping (address => Juror) internal jurors; // The jurors.
111103 mapping (IERC20 => CurrencyRate) public currencyRates; // The price of each token in ETH.
112104
113105 // ************************************* //
114106 // * Events * //
115107 // ************************************* //
116108
117- event StakeSet (address indexed _address , uint256 _courtID , uint256 _amount );
118- event StakeDelayed (address indexed _address , uint256 _courtID , uint256 _amount );
119109 event NewPeriod (uint256 indexed _disputeID , Period _period );
120110 event AppealPossible (uint256 indexed _disputeID , IArbitrableV2 indexed _arbitrable );
121111 event AppealDecision (uint256 indexed _disputeID , IArbitrableV2 indexed _arbitrable );
@@ -456,13 +446,19 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
456446 /// @dev Sets the caller's stake in a court.
457447 /// @param _courtID The ID of the court.
458448 /// @param _newStake The new stake.
449+ /// Note that the existing delayed stake will be nullified as non-relevant.
459450 function setStake (uint96 _courtID , uint256 _newStake ) external {
460- if ( ! _setStakeForAccount ( msg .sender , _courtID, _newStake)) revert StakingFailed ( );
451+ _setStake ( msg .sender , _courtID, _newStake, false );
461452 }
462453
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);
466462 }
467463
468464 /// @inheritdoc IArbitratorV2
@@ -589,7 +585,7 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
589585 if (drawnAddress == address (0 )) {
590586 continue ;
591587 }
592- jurors[drawnAddress].lockedPnk += round.pnkAtStakePerJuror;
588+ sortitionModule. lockStake (drawnAddress, round.pnkAtStakePerJuror) ;
593589 emit Draw (drawnAddress, _disputeID, currentRound, round.drawnJurors.length );
594590 round.drawnJurors.push (drawnAddress);
595591 if (round.drawnJurors.length == round.nbVotes) {
@@ -728,15 +724,10 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
728724
729725 // Unlock the PNKs affected by the penalty
730726 address account = round.drawnJurors[_params.repartition];
731- jurors[account].lockedPnk -= penalty;
727+ sortitionModule. unlockStake (account, penalty) ;
732728
733729 // 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);
740731 emit TokenAndETHShift (
741732 account,
742733 _params.disputeID,
@@ -795,10 +786,10 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
795786 uint256 pnkLocked = (round.pnkAtStakePerJuror * degreeOfCoherence) / ALPHA_DIVISOR;
796787
797788 // Release the rest of the PNKs of the juror for this round.
798- jurors[account].lockedPnk -= pnkLocked;
789+ sortitionModule. unlockStake (account, pnkLocked) ;
799790
800791 // Give back the locked PNKs in case the juror fully unstaked earlier.
801- if (jurors[account].stakedPnk == 0 ) {
792+ if (! sortitionModule. isJurorStaked (account) ) {
802793 pinakion.safeTransfer (account, pnkLocked);
803794 }
804795
@@ -944,17 +935,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
944935 return disputes[_disputeID].rounds.length ;
945936 }
946937
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-
958938 function isSupported (uint96 _courtID , uint256 _disputeKitID ) external view returns (bool ) {
959939 return courts[_courtID].supportedDisputeKits[_disputeKitID];
960940 }
@@ -997,12 +977,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
997977 return disputeKits.length ;
998978 }
999979
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-
1006980 function convertEthToTokenAmount (IERC20 _toToken , uint256 _amountInEth ) public view returns (uint256 ) {
1007981 return (_amountInEth * 10 ** currencyRates[_toToken].rateDecimals) / currencyRates[_toToken].rateInEth;
1008982 }
@@ -1020,89 +994,34 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
1020994 emit DisputeKitEnabled (_courtID, _disputeKitID, _enable);
1021995 }
1022996
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 (
1034998 address _account ,
1035999 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.
10471005 }
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.
10551008 }
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 ;
10961022 }
10971023 }
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;
11061025 }
11071026
11081027 /// @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 {
11431062 // ************************************* //
11441063
11451064 error GovernorOnly ();
1065+ error DisputeKitOnly ();
1066+ error SortitionModuleOnly ();
11461067 error UnsuccessfulCall ();
11471068 error InvalidDisputKitParent ();
11481069 error DepthLevelMax ();
@@ -1153,7 +1074,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
11531074 error CannotDisableClassicDK ();
11541075 error ArraysLengthMismatch ();
11551076 error StakingFailed ();
1156- error WrongCaller ();
11571077 error ArbitrationFeesNotEnough ();
11581078 error DisputeKitNotSupportedByCourt ();
11591079 error MustSupportDisputeKitClassic ();
@@ -1166,7 +1086,6 @@ contract KlerosCore is IArbitratorV2, UUPSProxiable, Initializable {
11661086 error NotEvidencePeriod ();
11671087 error AppealFeesNotEnough ();
11681088 error DisputeNotAppealable ();
1169- error DisputeKitOnly ();
11701089 error NotExecutionPeriod ();
11711090 error RulingAlreadyExecuted ();
11721091 error DisputePeriodIsFinal ();
0 commit comments