@@ -203,12 +203,74 @@ abstract contract StakeControllerBase is IStakeController, Initializable, UUPSPr
203203 // ************************************* //
204204
205205 /// @inheritdoc IStakeController
206- function setStake (
206+ function validateStake (
207207 address _account ,
208208 uint96 _courtID ,
209209 uint256 _newStake
210- ) public override onlyByCore returns (uint256 pnkDeposit , uint256 pnkWithdrawal , StakingResult stakingResult ) {
211- return _setStake (_account, _courtID, _newStake);
210+ ) external view override returns (uint256 pnkDeposit , uint256 pnkWithdrawal , StakingResult stakingResult ) {
211+ JurorStake storage currentJurorStake = jurorStakes[_account];
212+ uint256 currentStakeInCourt = currentJurorStake.stakes[_courtID];
213+
214+ if (_newStake > currentStakeInCourt) {
215+ pnkDeposit = _newStake - currentStakeInCourt;
216+ } else if (_newStake < currentStakeInCourt) {
217+ pnkWithdrawal = currentStakeInCourt - _newStake;
218+ }
219+
220+ if (phase != Phase.staking) {
221+ return (pnkDeposit, pnkWithdrawal, StakingResult.Delayed);
222+ } else {
223+ if (currentStakeInCourt == 0 ) {
224+ if (_newStake == 0 ) revert StakingZeroWhenNoStake ();
225+ else if (_newStake > 0 && currentJurorStake.stakedCourtIDs.length >= MAX_STAKE_PATHS)
226+ revert StakingInTooManyCourts ();
227+ }
228+ return (pnkDeposit, pnkWithdrawal, StakingResult.Successful);
229+ }
230+ }
231+
232+ /// @inheritdoc IStakeController
233+ function setStake (
234+ address _account ,
235+ uint96 _courtID ,
236+ uint256 _newStake ,
237+ uint256 _pnkDeposit ,
238+ uint256 _pnkWithdrawal
239+ ) public override onlyByCore {
240+ JurorStake storage currentJurorStake = jurorStakes[_account];
241+ uint256 currentStakeInCourt = currentJurorStake.stakes[_courtID];
242+
243+ if (phase != Phase.staking) {
244+ revert NotInStakingPhase ();
245+ }
246+
247+ // Update jurorStakes
248+ currentJurorStake.stakes[_courtID] = _newStake;
249+ if (_newStake > currentStakeInCourt) {
250+ currentJurorStake.totalStake += _pnkDeposit;
251+ } else if (_newStake < currentStakeInCourt) {
252+ currentJurorStake.totalStake -= _pnkWithdrawal;
253+ }
254+
255+ // Manage stakedCourtIDs
256+ if (currentStakeInCourt == 0 && _newStake > 0 ) {
257+ currentJurorStake.stakedCourtIDs.push (_courtID);
258+ } else if (currentStakeInCourt > 0 && _newStake == 0 ) {
259+ _removeCourt (currentJurorStake.stakedCourtIDs, _courtID);
260+ }
261+
262+ // Update sortition tree
263+ sortition.setStake (_account, _courtID, _newStake);
264+
265+ emit StakeSet (_account, _courtID, _newStake, currentJurorStake.totalStake);
266+ }
267+
268+ /// @inheritdoc IStakeController
269+ function setStakeDelayed (address _account , uint96 _courtID , uint256 _newStake ) public override {
270+ DelayedStake storage delayedStake = delayedStakes[++ delayedStakeWriteIndex];
271+ delayedStake.account = _account;
272+ delayedStake.courtID = _courtID;
273+ delayedStake.stake = _newStake;
212274 }
213275
214276 /// @inheritdoc IStakeController
@@ -235,14 +297,24 @@ abstract contract StakeControllerBase is IStakeController, Initializable, UUPSPr
235297
236298 /// @inheritdoc IStakeController
237299 function setJurorInactive (address _account ) external override onlyByCore returns (uint256 pnkToWithdraw ) {
238- uint96 [] storage courtIDsForJuror = jurorStakes[_account].stakedCourtIDs;
239- while (courtIDsForJuror.length > 0 ) {
240- uint96 courtID = courtIDsForJuror[0 ];
241- _setStake (_account, courtID, 0 );
300+ JurorStake storage currentJurorStake = jurorStakes[_account];
301+ uint96 [] storage stakedCourtIDs = currentJurorStake.stakedCourtIDs;
302+ while (stakedCourtIDs.length > 0 ) {
303+ uint96 courtID = stakedCourtIDs[0 ];
304+ uint256 currentStakeInCourt = currentJurorStake.stakes[courtID];
305+ if (phase == Phase.staking) {
306+ setStake (_account, courtID, 0 , 0 , currentStakeInCourt);
307+ } else {
308+ setStakeDelayed (_account, courtID, 0 );
309+ }
310+ }
311+ if (phase == Phase.staking) {
312+ pnkToWithdraw = vault.getAvailableBalance (_account);
313+ emit JurorSetInactive (_account, false );
314+ } else {
315+ pnkToWithdraw = 0 ;
316+ emit JurorSetInactive (_account, true );
242317 }
243- jurorStakes[_account].totalStake = 0 ;
244- pnkToWithdraw = vault.getAvailableBalance (_account);
245- emit JurorSetInactive (_account);
246318 }
247319
248320 // ************************************* //
@@ -324,64 +396,6 @@ abstract contract StakeControllerBase is IStakeController, Initializable, UUPSPr
324396 // * Internal * //
325397 // ************************************* //
326398
327- /// @dev Internal implementation of setStake with phase-aware delayed stake logic
328- /// @param _account The account to set the stake for.
329- /// @param _courtID The ID of the court to set the stake for.
330- /// @param _newStake The new stake.
331- /// @return pnkDeposit The amount of PNK to deposit.
332- /// @return pnkWithdrawal The amount of PNK to withdraw.
333- /// @return stakingResult The result of the staking operation.
334- function _setStake (
335- address _account ,
336- uint96 _courtID ,
337- uint256 _newStake
338- ) internal virtual returns (uint256 pnkDeposit , uint256 pnkWithdrawal , StakingResult stakingResult ) {
339- JurorStake storage currentJurorStake = jurorStakes[_account];
340- uint256 currentStakeInCourt = currentJurorStake.stakes[_courtID];
341-
342- if (phase == Phase.staking) {
343- if (currentStakeInCourt == 0 ) {
344- if (_newStake == 0 ) revert StakingZeroWhenNoStake ();
345- else if (_newStake > 0 && currentJurorStake.stakedCourtIDs.length >= MAX_STAKE_PATHS)
346- revert StakingInTooManyCourts ();
347- }
348-
349- currentJurorStake.stakes[_courtID] = _newStake;
350-
351- if (_newStake > currentStakeInCourt) {
352- pnkDeposit = _newStake - currentStakeInCourt;
353- currentJurorStake.totalStake += pnkDeposit;
354- } else if (_newStake < currentStakeInCourt) {
355- pnkWithdrawal = currentStakeInCourt - _newStake;
356- currentJurorStake.totalStake -= pnkWithdrawal;
357- }
358-
359- // Manage stakedCourtIDs
360- if (currentStakeInCourt == 0 && _newStake > 0 ) {
361- currentJurorStake.stakedCourtIDs.push (_courtID);
362- } else if (currentStakeInCourt > 0 && _newStake == 0 ) {
363- _removeCourt (currentJurorStake.stakedCourtIDs, _courtID);
364- }
365-
366- sortition.setStake (_account, _courtID, _newStake);
367-
368- emit StakeSet (_account, _courtID, _newStake, currentJurorStake.totalStake);
369- return (pnkDeposit, pnkWithdrawal, StakingResult.Successful);
370- }
371-
372- if (_newStake > currentStakeInCourt) {
373- pnkDeposit = _newStake - currentStakeInCourt;
374- } else if (_newStake < currentStakeInCourt) {
375- pnkWithdrawal = currentStakeInCourt - _newStake;
376- }
377-
378- DelayedStake storage delayedStake = delayedStakes[++ delayedStakeWriteIndex];
379- delayedStake.account = _account;
380- delayedStake.courtID = _courtID;
381- delayedStake.stake = _newStake;
382- return (pnkDeposit, pnkWithdrawal, StakingResult.Delayed);
383- }
384-
385399 /// @dev Removes a court from a juror's list of staked courts.
386400 /// @param _stakedCourts Storage pointer to the juror's array of staked court IDs.
387401 /// @param _courtID The ID of the court to remove.
@@ -410,101 +424,6 @@ abstract contract StakeControllerBase is IStakeController, Initializable, UUPSPr
410424 }
411425 }
412426
413- // ************************************* //
414- // * Migration Utilities * //
415- // ************************************* //
416-
417- /// @dev Import existing stakes from old sortition module for migration
418- /// @param _accounts Array of juror accounts
419- /// @param _courtIDs Array of court IDs
420- /// @param _stakes Array of stake amounts
421- function importExistingStakes (
422- address [] calldata _accounts ,
423- uint96 [] calldata _courtIDs ,
424- uint256 [] calldata _stakes
425- ) external onlyByGovernor {
426- if (_accounts.length != _courtIDs.length || _accounts.length != _stakes.length ) {
427- revert InvalidMigrationData ();
428- }
429-
430- uint256 totalImportedSuccess = 0 ;
431- for (uint256 i = 0 ; i < _accounts.length ; i++ ) {
432- if (_stakes[i] > 0 ) {
433- address account = _accounts[i];
434- uint96 courtID = _courtIDs[i];
435- uint256 stakeToImport = _stakes[i];
436-
437- // Ensure no prior stake exists for this specific account/courtID combination in this contract's state for a clean import.
438- // This check assumes importExistingStakes is for a fresh population or controlled append.
439- // If overwriting/updating was intended, this check might differ.
440- if (jurorStakes[account].stakes[courtID] > 0 ) {
441- // Skip or revert, depending on desired import semantics. For now, skip and log.
442- // emit ImportSkippedDuplicate(account, courtID, stakeToImport);
443- continue ;
444- }
445-
446- // _setStake will update local juror stake mappings (jurorStakes) AND call sortition.setStake.
447- // The pnkDeposit/pnkWithdrawal are calculated but not used by this import function.
448- (, , StakingResult stakingResult ) = _setStake (account, courtID, stakeToImport);
449-
450- if (stakingResult == StakingResult.Successful) {
451- totalImportedSuccess++ ;
452- emit StakeImported (account, courtID, stakeToImport);
453- } else {
454- // Log or handle failed import for a specific entry
455- // emit StakeImportFailed(account, courtID, stakeToImport, stakingResult);
456- }
457- }
458- }
459-
460- emit MigrationCompleted (_accounts.length , totalImportedSuccess);
461- }
462-
463- /// @dev Import delayed stakes from old system for migration
464- /// @param _delayedStakes Array of delayed stake data
465- function importDelayedStakes (DelayedStake[] calldata _delayedStakes ) external onlyByGovernor {
466- for (uint256 i = 0 ; i < _delayedStakes.length ; i++ ) {
467- DelayedStake memory delayedStake = _delayedStakes[i];
468- if (delayedStake.account != address (0 )) {
469- delayedStakeWriteIndex++ ;
470- delayedStakes[delayedStakeWriteIndex] = delayedStake;
471-
472- emit DelayedStakeImported (
473- delayedStake.account,
474- delayedStake.courtID,
475- delayedStake.stake,
476- delayedStakeWriteIndex
477- );
478- }
479- }
480- }
481-
482- /// @dev Migrate phase state from old sortition module
483- /// @param _phase The phase to set
484- /// @param _lastPhaseChange The last phase change timestamp
485- /// @param _disputesWithoutJurors Number of disputes without jurors
486- function migratePhaseState (
487- Phase _phase ,
488- uint256 _lastPhaseChange ,
489- uint256 _disputesWithoutJurors
490- ) external onlyByGovernor {
491- phase = _phase;
492- lastPhaseChange = _lastPhaseChange;
493- disputesWithoutJurors = _disputesWithoutJurors;
494-
495- emit PhaseStateMigrated (_phase, _lastPhaseChange, _disputesWithoutJurors);
496- }
497-
498- /// @dev Emergency coordination reset for critical issues
499- function emergencyCoordinationReset () external onlyByGovernor {
500- phase = Phase.staking;
501- lastPhaseChange = block .timestamp ;
502- disputesWithoutJurors = 0 ;
503- randomNumber = 0 ;
504-
505- emit EmergencyReset (block .timestamp );
506- }
507-
508427 // ************************************* //
509428 // * Errors * //
510429 // ************************************* //
0 commit comments