Skip to content

fix(SortitionModule): fix staking logic and remove instant staking #2004

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 18 additions & 17 deletions contracts/src/arbitration/KlerosCoreBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -470,15 +470,23 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
/// @param _account The account whose stake is being set.
/// @param _courtID The ID of the court.
/// @param _newStake The new stake.
/// @param _alreadyTransferred Whether the PNKs have already been transferred to the contract.
function setStakeBySortitionModule(
address _account,
uint96 _courtID,
uint256 _newStake,
bool _alreadyTransferred
bool /*_alreadyTransferred*/
) external {
if (msg.sender != address(sortitionModule)) revert SortitionModuleOnly();
_setStake(_account, _courtID, _newStake, _alreadyTransferred, OnError.Return);
_setStake(_account, _courtID, _newStake, false, OnError.Return); // alreadyTransferred is unused and DEPRECATED.
}

/// @dev Transfers PNK to the juror by SortitionModule.
/// @param _account The account of the juror whose PNK to transfer.
/// @param _amount The amount to transfer.
function transferBySortitionModule(address _account, uint256 _amount) external {
if (msg.sender != address(sortitionModule)) revert SortitionModuleOnly();
// Note eligibility is checked in SortitionModule.
pinakion.safeTransfer(_account, _amount);
}
Comment on lines +483 to 490
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Emit an event for PNK moved via transferBySortitionModule

transferBySortitionModule performs an external token transfer but leaves no on-chain trace inside Core.
For transparency, accounting and easier off-chain indexing, emit a lightweight event such as:

+ event SortitionPNKTransfer(address indexed account, uint256 amount);
...
 function transferBySortitionModule(address _account, uint256 _amount) external {
     if (msg.sender != address(sortitionModule)) revert SortitionModuleOnly();
     pinakion.safeTransfer(_account, _amount);
+    emit SortitionPNKTransfer(_account, _amount);
 }

This incurs negligible gas yet gives jurors and auditors full visibility.

🤖 Prompt for AI Agents
In contracts/src/arbitration/KlerosCoreBase.sol around lines 483 to 490, the
function transferBySortitionModule transfers PNK tokens without emitting any
event, which reduces transparency and traceability. Add an event declaration for
PNK transfers by the SortitionModule and emit this event inside the function
after the safeTransfer call, including the recipient address and the amount
transferred to provide on-chain visibility and facilitate off-chain indexing.


/// @inheritdoc IArbitratorV2
Expand Down Expand Up @@ -772,26 +780,25 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable

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

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

// Apply the penalty to the staked PNKs.
sortitionModule.penalizeStake(account, penalty);
(uint256 pnkBalance, uint256 availablePenalty) = sortitionModule.penalizeStake(account, penalty);
_params.pnkPenaltiesInRound += availablePenalty;
emit TokenAndETHShift(
account,
_params.disputeID,
_params.round,
degreeOfCoherence,
-int256(penalty),
-int256(availablePenalty),
0,
round.feeToken
);

if (!disputeKit.isVoteActive(_params.disputeID, _params.round, _params.repartition)) {
// The juror is inactive, unstake them.
// Unstake the juror from all courts if he was inactive or his balance can't cover penalties anymore.
if (pnkBalance == 0 || !disputeKit.isVoteActive(_params.disputeID, _params.round, _params.repartition)) {
sortitionModule.setJurorInactive(account);
}
if (_params.repartition == _params.numberOfVotesInRound - 1 && _params.coherentCount == 0) {
Expand Down Expand Up @@ -842,11 +849,6 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
// Release the rest of the PNKs of the juror for this round.
sortitionModule.unlockStake(account, pnkLocked);

// Give back the locked PNKs in case the juror fully unstaked earlier.
if (!sortitionModule.isJurorStaked(account)) {
pinakion.safeTransfer(account, pnkLocked);
}

// Transfer the rewards
uint256 pnkReward = ((_params.pnkPenaltiesInRound / _params.coherentCount) * degreeOfCoherence) / ALPHA_DIVISOR;
round.sumPnkRewardPaid += pnkReward;
Expand Down Expand Up @@ -1072,14 +1074,13 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
/// @param _account The account to set the stake for.
/// @param _courtID The ID of the court to set the stake for.
/// @param _newStake The new stake.
/// @param _alreadyTransferred Whether the PNKs were already transferred to/from the staking contract.
/// @param _onError Whether to revert or return false on error.
/// @return Whether the stake was successfully set or not.
function _setStake(
address _account,
uint96 _courtID,
uint256 _newStake,
bool _alreadyTransferred,
bool /*_alreadyTransferred*/,
OnError _onError
) internal returns (bool) {
if (_courtID == FORKING_COURT || _courtID >= courts.length) {
Expand All @@ -1094,7 +1095,7 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable
_account,
_courtID,
_newStake,
_alreadyTransferred
false // Unused parameter.
);
if (stakingResult != StakingResult.Successful) {
_stakingFailed(_onError, stakingResult);
Expand Down
Loading
Loading