Skip to content

Commit 50df19c

Browse files
committed
Method to opt-out 50% of authorization amount
1 parent cb4b228 commit 50df19c

File tree

2 files changed

+431
-86
lines changed

2 files changed

+431
-86
lines changed

contracts/staking/TokenStaking.sol

Lines changed: 145 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)