Skip to content

Commit 6a509ff

Browse files
authored
Merge pull request #175 from threshold-network/touchups
Touchups
2 parents 97c3f98 + 414437b commit 6a509ff

File tree

5 files changed

+460
-75
lines changed

5 files changed

+460
-75
lines changed

.github/workflows/format.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,3 @@ jobs:
2121
- name: Install dependencies
2222
run: yarn install
2323

24-
- name: Check formatting
25-
run: yarn format

contracts/staking/IStaking.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,13 @@ interface IStaking {
148148
address[] memory stakingProviders
149149
) external;
150150

151+
/// @notice Called by the application at its discretion to approve the
152+
/// migration of stake to the application. Rest of stake is
153+
/// released to the owner.
154+
function migrateAndRelease(address stakingProvider, uint96 amount)
155+
external
156+
returns (bool stakeless);
157+
151158
//
152159
//
153160
// Auxiliary functions

contracts/staking/TokenStaking.sol

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,6 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
291291

292292
uint96 fromAmount = authorization.authorized;
293293
authorization.authorized -= authorization.deauthorizing;
294-
authorization.deauthorizing = 0;
295294
emit AuthorizationDecreaseApproved(
296295
stakingProvider,
297296
msg.sender,
@@ -304,6 +303,16 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
304303
cleanAuthorizedApplications(stakingProviderStruct, 1);
305304
}
306305

306+
// Unstake
307+
stakingProviderStruct.tStake -= authorization.deauthorizing;
308+
decreaseStakeCheckpoint(stakingProvider, authorization.deauthorizing);
309+
emit Unstaked(stakingProvider, authorization.deauthorizing);
310+
token.safeTransfer(
311+
stakingProviderStruct.owner,
312+
authorization.deauthorizing
313+
);
314+
315+
authorization.deauthorizing = 0;
307316
return authorization.authorized;
308317
}
309318

@@ -488,6 +497,59 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
488497
);
489498
}
490499

500+
/// Migration
501+
function migrateAndRelease(address stakingProvider, uint96 amount)
502+
external
503+
override
504+
returns (bool stakeless)
505+
{
506+
ApplicationInfo storage applicationStruct = applicationInfo[msg.sender];
507+
require(
508+
applicationStruct.status == ApplicationStatus.APPROVED,
509+
"Application is not approved"
510+
);
511+
512+
require(
513+
!skipApplication(msg.sender),
514+
"Only TACo app can call this method"
515+
);
516+
517+
StakingProviderInfo storage stakingProviderStruct = stakingProviders[
518+
stakingProvider
519+
];
520+
require(
521+
stakingProviderStruct.owner != address(0),
522+
"Wrong staking provider"
523+
);
524+
uint96 toUnstake = stakingProviderStruct.tStake;
525+
stakingProviderStruct.tStake = 0;
526+
decreaseStakeCheckpoint(stakingProvider, toUnstake);
527+
emit Unstaked(stakingProvider, toUnstake);
528+
529+
AppAuthorization storage authorization = stakingProviderStruct
530+
.authorizations[msg.sender];
531+
532+
// stakeless
533+
if (authorization.authorized == 0) {
534+
stakeless = true;
535+
} else {
536+
require(
537+
authorization.authorized >= amount,
538+
"Not enough authorization"
539+
);
540+
toUnstake -= amount;
541+
authorization.authorized = 0;
542+
if (amount > 0) {
543+
token.safeTransfer(msg.sender, amount);
544+
}
545+
stakeless = false;
546+
}
547+
548+
if (toUnstake > 0) {
549+
token.safeTransfer(stakingProviderStruct.owner, toUnstake);
550+
}
551+
}
552+
491553
/// @notice Delegate voting power from the stake associated to the
492554
/// `stakingProvider` to a `delegatee` address. Caller must be the
493555
/// owner of this stake.
@@ -520,6 +582,9 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
520582
override
521583
returns (uint96)
522584
{
585+
if (skipApplication(application)) {
586+
return 0;
587+
}
523588
return
524589
stakingProviders[stakingProvider]
525590
.authorizations[application]
@@ -818,61 +883,10 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
818883
emit GovernanceTransferred(oldGuvnor, newGuvnor);
819884
}
820885

821-
function forceDecreaseAuthorization(
822-
address stakingProvider,
823-
uint96 amountTo
824-
) internal {
825-
StakingProviderInfo storage stakingProviderStruct = stakingProviders[
826-
stakingProvider
827-
];
828-
uint96 deauthorized = 0;
829-
for (
830-
uint256 i = 0;
831-
i < stakingProviderStruct.authorizedApplications.length;
832-
i++
833-
) {
834-
address application = stakingProviderStruct.authorizedApplications[
835-
i
836-
];
837-
if (skipApplication(application)) {
838-
continue;
839-
}
840-
AppAuthorization storage authorization = stakingProviderStruct
841-
.authorizations[application];
842-
uint96 authorized = authorization.authorized;
843-
if (authorized > amountTo) {
844-
IApplication(application).involuntaryAuthorizationDecrease(
845-
stakingProvider,
846-
authorized,
847-
amountTo
848-
);
849-
uint96 decrease = authorized - amountTo;
850-
851-
if (authorization.deauthorizing >= decrease) {
852-
authorization.deauthorizing -= decrease;
853-
} else {
854-
authorization.deauthorizing = 0;
855-
}
856-
857-
authorization.authorized = amountTo;
858-
deauthorized += decrease;
859-
860-
emit AuthorizationDecreaseApproved(
861-
stakingProvider,
862-
application,
863-
authorized,
864-
amountTo
865-
);
866-
}
867-
}
868-
869-
require(deauthorized > 0, "Nothing to deauthorize");
870-
}
871-
872886
// slither-disable-next-line dead-code
873887
function skipApplication(address application)
874888
internal
875-
pure
889+
view
876890
virtual
877891
returns (bool)
878892
{

contracts/test/TokenStakingTestSet.sol

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ contract ApplicationMock is IApplication {
2525

2626
TokenStaking internal immutable tokenStaking;
2727
mapping(address => StakingProviderStruct) public stakingProviders;
28+
mapping(address => bool) public stakeless;
2829

2930
constructor(TokenStaking _tokenStaking) {
3031
tokenStaking = _tokenStaking;
@@ -58,6 +59,15 @@ contract ApplicationMock is IApplication {
5859
.approveAuthorizationDecrease(stakingProvider);
5960
}
6061

62+
function migrateAndRelease(address stakingProvider, uint96 amount)
63+
external
64+
{
65+
stakeless[stakingProvider] = tokenStaking.migrateAndRelease(
66+
stakingProvider,
67+
amount
68+
);
69+
}
70+
6171
function availableRewards(address) external pure returns (uint96) {
6272
return 0;
6373
}
@@ -130,6 +140,8 @@ contract ManagedGrantMock {
130140
contract ExtendedTokenStaking is TokenStaking {
131141
using SafeTUpgradeable for T;
132142

143+
mapping(address => bool) public skipList;
144+
133145
/// @custom:oz-upgrades-unsafe-allow constructor
134146
constructor(T _token) TokenStaking(_token) {}
135147

@@ -161,6 +173,10 @@ contract ExtendedTokenStaking is TokenStaking {
161173
.authorizedApplications = _applications;
162174
}
163175

176+
function addToSkipList(address application) external {
177+
skipList[application] = true;
178+
}
179+
164180
/// @notice Creates a delegation with `msg.sender` owner with the given
165181
/// staking provider, beneficiary, and authorizer. Transfers the
166182
/// given amount of T to the staking contract.
@@ -308,6 +324,43 @@ contract ExtendedTokenStaking is TokenStaking {
308324
require(deauthorizing > 0, "Nothing was authorized");
309325
}
310326

327+
function legacyApproveAuthorizationDecrease(
328+
address stakingProvider,
329+
address application
330+
) external returns (uint96) {
331+
ApplicationInfo storage applicationStruct = applicationInfo[
332+
application
333+
];
334+
require(
335+
applicationStruct.status == ApplicationStatus.APPROVED,
336+
"Application is not approved"
337+
);
338+
339+
StakingProviderInfo storage stakingProviderStruct = stakingProviders[
340+
stakingProvider
341+
];
342+
AppAuthorization storage authorization = stakingProviderStruct
343+
.authorizations[application];
344+
require(authorization.deauthorizing > 0, "No deauthorizing in process");
345+
346+
uint96 fromAmount = authorization.authorized;
347+
authorization.authorized -= authorization.deauthorizing;
348+
emit AuthorizationDecreaseApproved(
349+
stakingProvider,
350+
application,
351+
fromAmount,
352+
authorization.authorized
353+
);
354+
355+
// remove application from an array
356+
if (authorization.authorized == 0) {
357+
cleanAuthorizedApplications(stakingProviderStruct, 1);
358+
}
359+
360+
authorization.deauthorizing = 0;
361+
return authorization.authorized;
362+
}
363+
311364
function getAuthorizedApplications(address stakingProvider)
312365
external
313366
view
@@ -373,7 +426,12 @@ contract ExtendedTokenStaking is TokenStaking {
373426
newStakeCheckpoint(_delegator, _amount, true);
374427
}
375428

376-
function skipApplication(address) internal pure override returns (bool) {
377-
return false;
429+
function skipApplication(address application)
430+
internal
431+
view
432+
override
433+
returns (bool)
434+
{
435+
return skipList[application];
378436
}
379437
}

0 commit comments

Comments
 (0)