@@ -57,6 +57,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
5757 address [] authorizedApplications;
5858 uint256 startStakingTimestamp;
5959 bool autoIncrease;
60+ uint256 optOutAmount;
6061 }
6162
6263 struct AppAuthorization {
@@ -76,6 +77,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
7677
7778 uint256 internal constant MIN_STAKE_TIME = 24 hours ;
7879 uint96 internal constant MAX_STAKE = 15 * 10 ** (18 + 6 ); // 15m T
80+ uint96 internal constant HALF_MAX_STAKE = MAX_STAKE / 2 ; // 7.5m T
7981
8082 /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
8183 T internal immutable token;
@@ -344,7 +346,8 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
344346 }
345347
346348 // TODO consider rename
347- // @dev decreases to 15m T
349+ /// @notice Forced deauthorization of stake above 15m T.
350+ /// Can be called by anyone.
348351 function forceCapDecreaseAuthorization (address [] memory _stakingProviders )
349352 external
350353 {
@@ -354,94 +357,39 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
354357 }
355358 }
356359
357- /// @notice Forced deauthorization of stake above 15m T.
358- /// Can be called by anyone.
359- // TODO consider rename
360- function forceCapDecreaseAuthorization (address stakingProvider ) public {
361- //override {
362- StakingProviderInfo storage stakingProviderStruct = stakingProviders[
363- stakingProvider
364- ];
365- uint96 deauthorized = 0 ;
366- for (
367- uint256 i = 0 ;
368- i < stakingProviderStruct.authorizedApplications.length ;
369- i++
370- ) {
371- address application = stakingProviderStruct.authorizedApplications[
372- i
373- ];
374- AppAuthorization storage authorization = stakingProviderStruct
375- .authorizations[application];
376- uint96 authorized = authorization.authorized;
377- if (authorized > MAX_STAKE) {
378- IApplication (application).involuntaryAuthorizationDecrease (
379- stakingProvider,
380- authorized,
381- MAX_STAKE
382- );
383- uint96 decrease = authorized - MAX_STAKE;
384-
385- if (authorization.deauthorizing >= decrease) {
386- authorization.deauthorizing -= decrease;
387- } else {
388- authorization.deauthorizing = 0 ;
389- // TODO cancel deauth? how? check tBTC
390- }
391-
392- authorization.authorized = MAX_STAKE;
393- deauthorized += decrease;
394-
395- emit AuthorizationDecreaseApproved (
396- stakingProvider,
397- application,
398- authorized,
399- MAX_STAKE
400- );
401- }
402- }
403-
404- require (deauthorized > 0 , "Nothing was deauthorized " );
405- }
406-
407- /// @notice Forced deauthorization of Beta staker.
408- /// Can be called only by the governance.
409- function forceBetaStakersCapDecreaseAuthorization (address betaStaker )
410- public
411- onlyGovernance
360+ /// @notice Allows to instantly deauthorize up to 50% of max authorization.
361+ /// Can be called only by the delegation owner or the staking
362+ /// provider.
363+ function optOutDecreaseAuthorization (address stakingProvider , uint96 amount )
364+ external
365+ onlyOwnerOrStakingProvider (stakingProvider)
412366 {
367+ require (amount > 0 , "Parameters must be specified " );
413368 StakingProviderInfo storage stakingProviderStruct = stakingProviders[
414- betaStaker
369+ stakingProvider
415370 ];
416- uint256 authorizedApplications = stakingProviderStruct
417- .authorizedApplications
418- .length ;
419-
420- require (authorizedApplications > 0 , "Nothing was authorized " );
421- for (uint256 i = 0 ; i < authorizedApplications; i++ ) {
422- address application = stakingProviderStruct.authorizedApplications[
423- i
424- ];
425- forceDecreaseAuthorization (
426- betaStaker,
427- stakingProviderStruct,
428- application
429- );
371+ (
372+ uint96 availableToOptOut ,
373+ uint96 maxAuthorization
374+ ) = getAvailableOptOutAmount (stakingProvider, stakingProviderStruct);
375+ if (maxAuthorization > MAX_STAKE) {
376+ forceDecreaseAuthorization (stakingProvider, MAX_STAKE);
377+ maxAuthorization = MAX_STAKE;
378+ availableToOptOut = HALF_MAX_STAKE;
430379 }
431- cleanAuthorizedApplications (
432- stakingProviderStruct,
433- authorizedApplications
434- );
380+ require (availableToOptOut >= amount, "Opt-out is not available " ); // TODO rephrase
381+ forceDecreaseAuthorization (stakingProvider, maxAuthorization - amount);
382+ stakingProviderStruct.optOutAmount += amount;
435383 }
436384
437385 /// @notice Forced deauthorization of Beta stakers.
438386 /// Can be called only by the governance.
439- function forceBetaStakersCapDecreaseAuthorization (
440- address [] memory betaStakers
441- ) external {
387+ function forceBetaStakerDecreaseAuthorization ( address [] memory betaStakers )
388+ external
389+ {
442390 require (betaStakers.length > 0 , "Wrong input parameters " );
443391 for (uint256 i = 0 ; i < betaStakers.length ; i++ ) {
444- forceBetaStakersCapDecreaseAuthorization (betaStakers[i]);
392+ forceBetaStakerDecreaseAuthorization (betaStakers[i]);
445393 }
446394 }
447395
@@ -732,6 +680,44 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
732680 );
733681 }
734682
683+ /// @notice Forced deauthorization of Beta staker.
684+ /// Can be called only by the governance.
685+ function forceBetaStakerDecreaseAuthorization (address betaStaker )
686+ public
687+ onlyGovernance
688+ {
689+ StakingProviderInfo storage stakingProviderStruct = stakingProviders[
690+ betaStaker
691+ ];
692+ uint256 authorizedApplications = stakingProviderStruct
693+ .authorizedApplications
694+ .length ;
695+
696+ require (authorizedApplications > 0 , "Nothing was authorized " );
697+ for (uint256 i = 0 ; i < authorizedApplications; i++ ) {
698+ address application = stakingProviderStruct.authorizedApplications[
699+ i
700+ ];
701+ forceDecreaseAuthorization (
702+ betaStaker,
703+ stakingProviderStruct,
704+ application
705+ );
706+ }
707+ cleanAuthorizedApplications (
708+ stakingProviderStruct,
709+ authorizedApplications
710+ );
711+ }
712+
713+ /// @notice Forced deauthorization of stake above 15m T.
714+ /// Can be called by anyone.
715+ // TODO consider rename
716+ function forceCapDecreaseAuthorization (address stakingProvider ) public {
717+ //override {
718+ forceDecreaseAuthorization (stakingProvider, MAX_STAKE);
719+ }
720+
735721 /// @notice Returns the maximum application authorization
736722 function getMaxAuthorization (address stakingProvider )
737723 public
@@ -779,6 +765,21 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
779765 }
780766 }
781767
768+ /// @notice Returns available amount to instantly deauthorize.
769+ function getAvailableOptOutAmount (address stakingProvider )
770+ public
771+ view
772+ returns (uint96 availableToOptOut )
773+ {
774+ StakingProviderInfo storage stakingProviderStruct = stakingProviders[
775+ stakingProvider
776+ ];
777+ (availableToOptOut, ) = getAvailableOptOutAmount (
778+ stakingProvider,
779+ stakingProviderStruct
780+ );
781+ }
782+
782783 /// @notice Delegate voting power from the stake associated to the
783784 /// `stakingProvider` to a `delegatee` address. Caller must be the owner
784785 /// of this stake.
@@ -905,4 +906,69 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
905906 governance = newGuvnor;
906907 emit GovernanceTransferred (oldGuvnor, newGuvnor);
907908 }
909+
910+ function forceDecreaseAuthorization (
911+ address stakingProvider ,
912+ uint96 amountTo
913+ ) internal {
914+ StakingProviderInfo storage stakingProviderStruct = stakingProviders[
915+ stakingProvider
916+ ];
917+ uint96 deauthorized = 0 ;
918+ for (
919+ uint256 i = 0 ;
920+ i < stakingProviderStruct.authorizedApplications.length ;
921+ i++
922+ ) {
923+ address application = stakingProviderStruct.authorizedApplications[
924+ i
925+ ];
926+ AppAuthorization storage authorization = stakingProviderStruct
927+ .authorizations[application];
928+ uint96 authorized = authorization.authorized;
929+ if (authorized > amountTo) {
930+ IApplication (application).involuntaryAuthorizationDecrease (
931+ stakingProvider,
932+ authorized,
933+ amountTo
934+ );
935+ uint96 decrease = authorized - amountTo;
936+
937+ if (authorization.deauthorizing >= decrease) {
938+ authorization.deauthorizing -= decrease;
939+ } else {
940+ authorization.deauthorizing = 0 ;
941+ }
942+
943+ authorization.authorized = amountTo;
944+ deauthorized += decrease;
945+
946+ emit AuthorizationDecreaseApproved (
947+ stakingProvider,
948+ application,
949+ authorized,
950+ amountTo
951+ );
952+ }
953+ }
954+
955+ require (deauthorized > 0 , "Nothing was deauthorized " );
956+ }
957+
958+ function getAvailableOptOutAmount (
959+ address stakingProvider ,
960+ StakingProviderInfo storage stakingProviderStruct
961+ )
962+ internal
963+ view
964+ returns (uint96 availableToOptOut , uint96 maxAuthorization )
965+ {
966+ maxAuthorization = getMaxAuthorization (stakingProvider);
967+ uint96 optOutAmount = stakingProviderStruct.optOutAmount.toUint96 ();
968+ if (maxAuthorization < optOutAmount) {
969+ availableToOptOut = 0 ;
970+ } else {
971+ availableToOptOut = (maxAuthorization - optOutAmount) / 2 ;
972+ }
973+ }
908974}
0 commit comments