@@ -3,7 +3,7 @@ pragma solidity ^0.8.28;
33
44import {ProverRegistry} from "./libraries/ProverRegistry.sol " ;
55import {StakedSuccinct} from "./tokens/StakedSuccinct.sol " ;
6- import {ISuccinctStaking} from "./interfaces/ISuccinctStaking.sol " ;
6+ import {UnstakePeriod, ISuccinctStaking} from "./interfaces/ISuccinctStaking.sol " ;
77import {IIntermediateSuccinct} from "./interfaces/IIntermediateSuccinct.sol " ;
88import {IProver} from "./interfaces/IProver.sol " ;
99import {SuccinctGovernor} from "./SuccinctGovernor.sol " ;
@@ -48,8 +48,8 @@ contract SuccinctStaking is
4848 /// @inheritdoc ISuccinctStaking
4949 uint256 public override maxUnstakeRequests;
5050
51- /// @inheritdoc ISuccinctStaking
52- uint256 public override unstakePeriod ;
51+ /// @dev The old global unstake period.
52+ uint256 private deprecatedUnstakePeriod ;
5353
5454 /// @inheritdoc ISuccinctStaking
5555 uint256 public override slashCancellationPeriod;
@@ -81,6 +81,9 @@ contract SuccinctStaking is
8181 /// @dev A mapping from prover to their unstaking escrow pool.
8282 mapping (address => EscrowPool) internal escrowPools;
8383
84+ /// @inheritdoc ISuccinctStaking
85+ mapping (address => UnstakePeriod) public override unstakePeriodChoice;
86+
8487 /*//////////////////////////////////////////////////////////////
8588 MODIFIER
8689 //////////////////////////////////////////////////////////////*/
@@ -110,7 +113,6 @@ contract SuccinctStaking is
110113 address _dispenser ,
111114 uint256 _minStakeAmount ,
112115 uint256 _maxUnstakeRequests ,
113- uint256 _unstakePeriod ,
114116 uint256 _slashCancellationPeriod
115117 ) external initializer {
116118 // Ensure that parameters critical for functionality are non-zero.
@@ -121,7 +123,6 @@ contract SuccinctStaking is
121123 _requireNonZeroAddress (_intermediateProve);
122124 _requireNonZeroAddress (_dispenser);
123125 _requireNonZeroNumber (_maxUnstakeRequests);
124- _requireNonZeroNumber (_unstakePeriod);
125126 _requireNonZeroNumber (_slashCancellationPeriod);
126127
127128 // Setup the initial state.
@@ -132,7 +133,6 @@ contract SuccinctStaking is
132133 dispenser = _dispenser;
133134 minStakeAmount = _minStakeAmount;
134135 maxUnstakeRequests = _maxUnstakeRequests;
135- unstakePeriod = _unstakePeriod;
136136 slashCancellationPeriod = _slashCancellationPeriod;
137137
138138 // Approve the $iPROVE contract to transfer $PROVE from this contract during stake().
@@ -241,7 +241,7 @@ contract SuccinctStaking is
241241 //////////////////////////////////////////////////////////////*/
242242
243243 /// @inheritdoc ISuccinctStaking
244- function stake (address _prover , uint256 _PROVE )
244+ function stake (address _prover , uint256 _PROVE , UnstakePeriod _unstakePeriod )
245245 external
246246 override
247247 onlyForProver (_prover)
@@ -250,7 +250,7 @@ contract SuccinctStaking is
250250 // Transfer $PROVE from the staker to this contract.
251251 IERC20 (prove).safeTransferFrom (msg .sender , address (this ), _PROVE);
252252
253- return _stake (msg .sender , _prover, _PROVE);
253+ return _stake (msg .sender , _prover, _PROVE, _unstakePeriod );
254254 }
255255
256256 /// @inheritdoc ISuccinctStaking
@@ -261,7 +261,8 @@ contract SuccinctStaking is
261261 uint256 _deadline ,
262262 uint8 _v ,
263263 bytes32 _r ,
264- bytes32 _s
264+ bytes32 _s ,
265+ UnstakePeriod _unstakePeriod
265266 ) external override onlyForProver (_prover) returns (uint256 ) {
266267 // If the $PROVE allowance is not equal to the amount being staked, permit the prover to
267268 // spend the $PROVE from the staker.
@@ -273,7 +274,7 @@ contract SuccinctStaking is
273274 // spender.
274275 IProver (_prover).transferProveToStaking (_from, _PROVE);
275276
276- return _stake (_from, _prover, _PROVE);
277+ return _stake (_from, _prover, _PROVE, _unstakePeriod );
277278 }
278279
279280 /// @inheritdoc ISuccinctStaking
@@ -335,8 +336,11 @@ contract SuccinctStaking is
335336 // Check that this prover is not in the process of being slashed.
336337 _requireProverWithoutSlashRequests (prover);
337338
339+ // Get the staker's unstake period choice.
340+ UnstakePeriod unstakePeriod = unstakePeriodChoice[_staker];
341+
338342 // Process the available unstake claims.
339- PROVE += _finishUnstake (_staker, prover, claims);
343+ PROVE += _finishUnstake (_staker, prover, claims, unstakePeriod );
340344
341345 // Reset the slash factor if all $iPROVE has been removed.
342346 EscrowPool storage pool = escrowPools[prover];
@@ -345,11 +349,15 @@ contract SuccinctStaking is
345349 }
346350
347351 // If the staker has no remaining balance and no pending unstakes, remove the staker's
348- // delegate. This allows them to choose a different prover if they stake again.
352+ // delegate and reset their unstake period choice. This allows them to choose a different
353+ // prover if they stake again.
349354 if (balanceOf (_staker) == 0 && claims.length == 0 ) {
350355 // Remove the staker's prover delegation.
351356 stakerToProver[_staker] = address (0 );
352357
358+ // Remove the staker's unstake period choice.
359+ unstakePeriodChoice[_staker] = UnstakePeriod.UNSPECIFIED;
360+
353361 emit ProverUnbound (_staker, prover);
354362 }
355363 }
@@ -526,7 +534,7 @@ contract SuccinctStaking is
526534
527535 /// @dev Deposit a staker's $PROVE to mint $iPROVE, then deposit $iPROVE to mint $PROVER-N, and
528536 /// then directly mint $stPROVE to the staker, which acts as the receipt token for staking.
529- function _stake (address _staker , address _prover , uint256 _PROVE )
537+ function _stake (address _staker , address _prover , uint256 _PROVE , UnstakePeriod _unstakePeriod )
530538 internal
531539 stakingOperation
532540 returns (uint256 stPROVE )
@@ -543,6 +551,15 @@ contract SuccinctStaking is
543551 // Check that this prover is not in the process of being slashed.
544552 _requireProverWithoutSlashRequests (_prover);
545553
554+ // If the staker has not set a unstake period choice, set it to their choice. If they have
555+ // already set a choice, ensure it matches their choice.
556+ UnstakePeriod existingUnstakePeriod = unstakePeriodChoice[_staker];
557+ if (existingUnstakePeriod == UnstakePeriod.UNSPECIFIED) {
558+ unstakePeriodChoice[_staker] = _unstakePeriod;
559+ } else if (existingUnstakePeriod != _unstakePeriod) {
560+ revert AlreadySetUnstakePeriod (existingUnstakePeriod);
561+ }
562+
546563 // Ensure the staker is not already staked with a different prover.
547564 address existingProver = stakerToProver[_staker];
548565 if (existingProver != address (0 ) && existingProver != _prover) {
@@ -553,7 +570,7 @@ contract SuccinctStaking is
553570 if (existingProver == address (0 )) {
554571 stakerToProver[_staker] = _prover;
555572
556- emit ProverBound (_staker, _prover);
573+ emit ProverBound (_staker, _prover, _unstakePeriod );
557574 }
558575
559576 // Deposit $PROVE to mint $iPROVE, sending it to this contract.
@@ -620,13 +637,15 @@ contract SuccinctStaking is
620637
621638 /// @dev Iterate over the unstake claims, processing each one that has passed the unstake
622639 /// period.
623- function _finishUnstake (address _staker , address _prover , UnstakeClaim[] storage _claims )
624- internal
625- returns (uint256 PROVE )
626- {
640+ function _finishUnstake (
641+ address _staker ,
642+ address _prover ,
643+ UnstakeClaim[] storage _claims ,
644+ UnstakePeriod _unstakePeriod
645+ ) internal returns (uint256 PROVE ) {
627646 uint256 i = 0 ;
628647 while (i < _claims.length ) {
629- if (block .timestamp >= _claims[i].timestamp + unstakePeriod ) {
648+ if (block .timestamp >= _claims[i].timestamp + _unstakePeriodToSeconds (_unstakePeriod) ) {
630649 // Store claim before modifying the array.
631650 UnstakeClaim memory claim = _claims[i];
632651
@@ -678,6 +697,20 @@ contract SuccinctStaking is
678697 if (slashClaimCount[_prover] > 0 ) revert ProverHasSlashRequest ();
679698 }
680699
700+ /// @dev Converts a UnstakePeriod enum to a number of seconds. Defaults to 14 days if the
701+ /// unstake period is unspecified, which is relevant for backward compatibility.
702+ function _unstakePeriodToSeconds (UnstakePeriod _unstakePeriod )
703+ internal
704+ pure
705+ returns (uint256 )
706+ {
707+ if (_unstakePeriod == UnstakePeriod.TWENTY_FOUR_MONTHS) return 730 days ;
708+ if (_unstakePeriod == UnstakePeriod.TWELVE_MONTHS) return 365 days ;
709+ if (_unstakePeriod == UnstakePeriod.SIX_MONTHS) return 183 days ;
710+ if (_unstakePeriod == UnstakePeriod.FOURTEEN_DAYS) return 14 days ;
711+ return 14 days ;
712+ }
713+
681714 /// @dev Authorizes an ERC1967 proxy upgrade to a new implementation contract.
682715 function _authorizeUpgrade (address _newImplementation ) internal override onlyOwner {}
683716}
0 commit comments