Skip to content

Commit 392e1c0

Browse files
authored
Merge pull request #2004 from kleros/fix/staking-fix
fix(SortitionModule): fix staking logic and remove instant staking
2 parents 4449e04 + d6b7495 commit 392e1c0

24 files changed

+877
-878
lines changed

contracts/src/arbitration/KlerosCoreBase.sol

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -463,22 +463,25 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
463463
/// @param _newStake The new stake.
464464
/// Note that the existing delayed stake will be nullified as non-relevant.
465465
function setStake(uint96 _courtID, uint256 _newStake) external virtual whenNotPaused {
466-
_setStake(msg.sender, _courtID, _newStake, false, OnError.Revert);
466+
_setStake(msg.sender, _courtID, _newStake, OnError.Revert);
467467
}
468468

469469
/// @dev Sets the stake of a specified account in a court, typically to apply a delayed stake or unstake inactive jurors.
470470
/// @param _account The account whose stake is being set.
471471
/// @param _courtID The ID of the court.
472472
/// @param _newStake The new stake.
473-
/// @param _alreadyTransferred Whether the PNKs have already been transferred to the contract.
474-
function setStakeBySortitionModule(
475-
address _account,
476-
uint96 _courtID,
477-
uint256 _newStake,
478-
bool _alreadyTransferred
479-
) external {
473+
function setStakeBySortitionModule(address _account, uint96 _courtID, uint256 _newStake) external {
480474
if (msg.sender != address(sortitionModule)) revert SortitionModuleOnly();
481-
_setStake(_account, _courtID, _newStake, _alreadyTransferred, OnError.Return);
475+
_setStake(_account, _courtID, _newStake, OnError.Return);
476+
}
477+
478+
/// @dev Transfers PNK to the juror by SortitionModule.
479+
/// @param _account The account of the juror whose PNK to transfer.
480+
/// @param _amount The amount to transfer.
481+
function transferBySortitionModule(address _account, uint256 _amount) external {
482+
if (msg.sender != address(sortitionModule)) revert SortitionModuleOnly();
483+
// Note eligibility is checked in SortitionModule.
484+
pinakion.safeTransfer(_account, _amount);
482485
}
483486

484487
/// @inheritdoc IArbitratorV2
@@ -774,26 +777,25 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
774777

775778
// Fully coherent jurors won't be penalized.
776779
uint256 penalty = (round.pnkAtStakePerJuror * (ALPHA_DIVISOR - degreeOfCoherence)) / ALPHA_DIVISOR;
777-
_params.pnkPenaltiesInRound += penalty;
778780

779781
// Unlock the PNKs affected by the penalty
780782
address account = round.drawnJurors[_params.repartition];
781783
sortitionModule.unlockStake(account, penalty);
782784

783785
// Apply the penalty to the staked PNKs.
784-
sortitionModule.penalizeStake(account, penalty);
786+
(uint256 pnkBalance, uint256 availablePenalty) = sortitionModule.penalizeStake(account, penalty);
787+
_params.pnkPenaltiesInRound += availablePenalty;
785788
emit TokenAndETHShift(
786789
account,
787790
_params.disputeID,
788791
_params.round,
789792
degreeOfCoherence,
790-
-int256(penalty),
793+
-int256(availablePenalty),
791794
0,
792795
round.feeToken
793796
);
794-
795-
if (!disputeKit.isVoteActive(_params.disputeID, _params.round, _params.repartition)) {
796-
// The juror is inactive, unstake them.
797+
// Unstake the juror from all courts if he was inactive or his balance can't cover penalties anymore.
798+
if (pnkBalance == 0 || !disputeKit.isVoteActive(_params.disputeID, _params.round, _params.repartition)) {
797799
sortitionModule.setJurorInactive(account);
798800
}
799801
if (_params.repartition == _params.numberOfVotesInRound - 1 && _params.coherentCount == 0) {
@@ -844,11 +846,6 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
844846
// Release the rest of the PNKs of the juror for this round.
845847
sortitionModule.unlockStake(account, pnkLocked);
846848

847-
// Give back the locked PNKs in case the juror fully unstaked earlier.
848-
if (!sortitionModule.isJurorStaked(account)) {
849-
pinakion.safeTransfer(account, pnkLocked);
850-
}
851-
852849
// Transfer the rewards
853850
uint256 pnkReward = ((_params.pnkPenaltiesInRound / _params.coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
854851
round.sumPnkRewardPaid += pnkReward;
@@ -1074,16 +1071,9 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
10741071
/// @param _account The account to set the stake for.
10751072
/// @param _courtID The ID of the court to set the stake for.
10761073
/// @param _newStake The new stake.
1077-
/// @param _alreadyTransferred Whether the PNKs were already transferred to/from the staking contract.
10781074
/// @param _onError Whether to revert or return false on error.
10791075
/// @return Whether the stake was successfully set or not.
1080-
function _setStake(
1081-
address _account,
1082-
uint96 _courtID,
1083-
uint256 _newStake,
1084-
bool _alreadyTransferred,
1085-
OnError _onError
1086-
) internal returns (bool) {
1076+
function _setStake(address _account, uint96 _courtID, uint256 _newStake, OnError _onError) internal returns (bool) {
10871077
if (_courtID == FORKING_COURT || _courtID >= courts.length) {
10881078
_stakingFailed(_onError, StakingResult.CannotStakeInThisCourt); // Staking directly into the forking court is not allowed.
10891079
return false;
@@ -1092,15 +1082,16 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
10921082
_stakingFailed(_onError, StakingResult.CannotStakeLessThanMinStake); // Staking less than the minimum stake is not allowed.
10931083
return false;
10941084
}
1095-
(uint256 pnkDeposit, uint256 pnkWithdrawal, StakingResult stakingResult) = sortitionModule.setStake(
1085+
(uint256 pnkDeposit, uint256 pnkWithdrawal, StakingResult stakingResult) = sortitionModule.validateStake(
10961086
_account,
10971087
_courtID,
1098-
_newStake,
1099-
_alreadyTransferred
1088+
_newStake
11001089
);
1101-
if (stakingResult != StakingResult.Successful) {
1090+
if (stakingResult != StakingResult.Successful && stakingResult != StakingResult.Delayed) {
11021091
_stakingFailed(_onError, stakingResult);
11031092
return false;
1093+
} else if (stakingResult == StakingResult.Delayed) {
1094+
return true;
11041095
}
11051096
if (pnkDeposit > 0) {
11061097
if (!pinakion.safeTransferFrom(_account, address(this), pnkDeposit)) {
@@ -1114,6 +1105,8 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
11141105
return false;
11151106
}
11161107
}
1108+
sortitionModule.setStake(_account, _courtID, pnkDeposit, pnkWithdrawal, _newStake);
1109+
11171110
return true;
11181111
}
11191112

contracts/src/arbitration/KlerosCoreNeo.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ contract KlerosCoreNeo is KlerosCoreBase {
105105
/// Note that the existing delayed stake will be nullified as non-relevant.
106106
function setStake(uint96 _courtID, uint256 _newStake) external override whenNotPaused {
107107
if (jurorNft.balanceOf(msg.sender) == 0) revert NotEligibleForStaking();
108-
super._setStake(msg.sender, _courtID, _newStake, false, OnError.Revert);
108+
super._setStake(msg.sender, _courtID, _newStake, OnError.Revert);
109109
}
110110

111111
// ************************************* //

contracts/src/arbitration/SortitionModule.sol

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
// SPDX-License-Identifier: MIT
22

3-
/**
4-
* @custom:authors: [@epiqueras, @unknownunknown1, @jaybuidl, @shotaronowhere]
5-
* @custom:reviewers: []
6-
* @custom:auditors: []
7-
* @custom:bounties: []
8-
* @custom:deployments: []
9-
*/
10-
113
pragma solidity 0.8.24;
124

135
import {SortitionModuleBase, KlerosCore, RNG} from "./SortitionModuleBase.sol";

0 commit comments

Comments
 (0)