@@ -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];
@@ -526,7 +530,7 @@ contract SuccinctStaking is
526530
527531 /// @dev Deposit a staker's $PROVE to mint $iPROVE, then deposit $iPROVE to mint $PROVER-N, and
528532 /// then directly mint $stPROVE to the staker, which acts as the receipt token for staking.
529- function _stake (address _staker , address _prover , uint256 _PROVE )
533+ function _stake (address _staker , address _prover , uint256 _PROVE , UnstakePeriod _unstakePeriod )
530534 internal
531535 stakingOperation
532536 returns (uint256 stPROVE )
@@ -556,6 +560,15 @@ contract SuccinctStaking is
556560 emit ProverBound (_staker, _prover);
557561 }
558562
563+ // If the staker has not set a unstake period choice, set it to their choice. If they have
564+ // already set a choice, ensure it matches their choice.
565+ UnstakePeriod existingUnstakePeriod = unstakePeriodChoice[_staker];
566+ if (existingUnstakePeriod == UnstakePeriod.UNSPECIFIED) {
567+ unstakePeriodChoice[_staker] = _unstakePeriod;
568+ } else if (existingUnstakePeriod != _unstakePeriod) {
569+ revert AlreadySetUnstakePeriod (existingUnstakePeriod);
570+ }
571+
559572 // Deposit $PROVE to mint $iPROVE, sending it to this contract.
560573 uint256 iPROVE = IERC4626 (iProve).deposit (_PROVE, address (this ));
561574
@@ -620,13 +633,15 @@ contract SuccinctStaking is
620633
621634 /// @dev Iterate over the unstake claims, processing each one that has passed the unstake
622635 /// period.
623- function _finishUnstake (address _staker , address _prover , UnstakeClaim[] storage _claims )
624- internal
625- returns (uint256 PROVE )
626- {
636+ function _finishUnstake (
637+ address _staker ,
638+ address _prover ,
639+ UnstakeClaim[] storage _claims ,
640+ UnstakePeriod _unstakePeriod
641+ ) internal returns (uint256 PROVE ) {
627642 uint256 i = 0 ;
628643 while (i < _claims.length ) {
629- if (block .timestamp >= _claims[i].timestamp + unstakePeriod ) {
644+ if (block .timestamp >= _claims[i].timestamp + _unstakePeriodToSeconds (_unstakePeriod) ) {
630645 // Store claim before modifying the array.
631646 UnstakeClaim memory claim = _claims[i];
632647
@@ -678,6 +693,20 @@ contract SuccinctStaking is
678693 if (slashClaimCount[_prover] > 0 ) revert ProverHasSlashRequest ();
679694 }
680695
696+ /// @dev Converts a UnstakePeriod enum to a number of seconds. Defaults to 14 days if the
697+ /// unstake period is unspecified, which is relevant for backward compatibility.
698+ function _unstakePeriodToSeconds (UnstakePeriod _unstakePeriod )
699+ internal
700+ pure
701+ returns (uint256 )
702+ {
703+ if (_unstakePeriod == UnstakePeriod.TWENTY_FOUR_MONTHS) return 730 days ;
704+ if (_unstakePeriod == UnstakePeriod.TWELVE_MONTHS) return 365 days ;
705+ if (_unstakePeriod == UnstakePeriod.SIX_MONTHS) return 183 days ;
706+ if (_unstakePeriod == UnstakePeriod.FOURTEEN_DAYS) return 14 days ;
707+ return 14 days ;
708+ }
709+
681710 /// @dev Authorizes an ERC1967 proxy upgrade to a new implementation contract.
682711 function _authorizeUpgrade (address _newImplementation ) internal override onlyOwner {}
683712}
0 commit comments