diff --git a/contracts/staking/IStaking.sol b/contracts/staking/IStaking.sol index fa5d60da..8857b5ce 100644 --- a/contracts/staking/IStaking.sol +++ b/contracts/staking/IStaking.sol @@ -27,12 +27,6 @@ pragma solidity ^0.8.9; /// delegation optimizes the network throughput without compromising the /// security of the owners’ stake. interface IStaking { - enum StakeType { - NU, - KEEP, - T - } - // // // Delegating a stake @@ -176,39 +170,11 @@ interface IStaking { /// @notice Reduces the liquid T stake amount by the provided amount and /// withdraws T to the owner. Reverts if there is at least one - /// authorization higher than the sum of the legacy stake and - /// remaining liquid T stake or if the unstake amount is higher than - /// the liquid T stake amount. Can be called only by the delegation - /// owner or the staking provider. - function unstakeT(address stakingProvider, uint96 amount) external; - - /// @notice Sets the legacy KEEP staking contract active stake amount cached - /// in T staking contract to 0. Reverts if the amount of liquid T - /// staked in T staking contract is lower than the highest - /// application authorization. This function allows to unstake from - /// KEEP staking contract and still being able to operate in T - /// network and earning rewards based on the liquid T staked. Can be - /// called only by the delegation owner or the staking provider. - function unstakeKeep(address stakingProvider) external; - - /// @notice Sets to 0 the amount of T that is cached from the legacy - /// NU staking contract. Reverts if there is at least one - /// authorization higher than the sum of remaining legacy NU stake - /// and native T stake for that staking provider or if the unstaked - /// amount is higher than the cached legacy stake amount. If succeeded, - /// the legacy NU stake can be partially or fully undelegated on - /// the legacy NU staking contract. This function allows to unstake - /// from NU staking contract while still being able to operate in - /// T network and earning rewards based on the native T staked. - /// Can be called only by the stake owner or the staking provider. - function unstakeNu(address stakingProvider) external; - - /// @notice Sets cached legacy stake amount to 0, sets the liquid T stake - /// amount to 0 and withdraws all liquid T from the stake to the - /// owner. Reverts if there is at least one non-zero authorization. + /// authorization higher than the remaining liquid T stake or + /// if the unstake amount is higher than the liquid T stake amount. /// Can be called only by the delegation owner or the staking /// provider. - function unstakeAll(address stakingProvider) external; + function unstakeT(address stakingProvider, uint96 amount) external; // // @@ -265,17 +231,8 @@ interface IStaking { view returns (uint96); - /// @notice Returns staked amount of T, Keep and Nu for the specified - /// staking provider. - /// @dev All values are in T denomination - function stakes(address stakingProvider) - external - view - returns ( - uint96 tStake, - uint96 keepInTStake, - uint96 nuInTStake - ); + /// @notice Returns staked amount of T for the specified staking provider. + function tStake(address stakingProvider) external view returns (uint96); /// @notice Returns start staking timestamp. /// @dev This value is set at most once. @@ -290,9 +247,6 @@ interface IStaking { view returns (bool); - /// @notice Returns staked amount of NU for the specified staking provider. - function stakedNu(address stakingProvider) external view returns (uint256); - /// @notice Gets the stake owner, the beneficiary and the authorizer /// for the specified staking provider address. /// @return owner Stake owner address. @@ -313,23 +267,8 @@ interface IStaking { /// @notice Returns length of slashing queue function getSlashingQueueLength() external view returns (uint256); - /// @notice Returns minimum possible stake for T, KEEP or NU in T - /// denomination. - /// @dev For example, suppose the given staking provider has 10 T, 20 T worth - /// of KEEP, and 30 T worth of NU all staked, and the maximum - /// application authorization is 40 T, then `getMinStaked` for - /// that staking provider returns: - /// * 0 T if KEEP stake type specified i.e. - /// min = 40 T max - (10 T) = 30 T - /// * 10 T if NU stake type specified i.e. - /// min = 40 T max - (10 T) = 30 T - /// * 0 T if T stake type specified i.e. - /// min = 40 T max = 40 T - /// In other words, the minimum stake amount for the specified - /// stake type is the minimum amount of stake of the given type - /// needed to satisfy the maximum application authorization given - /// the staked amounts of the T stake types for that staking provider. - function getMinStaked(address stakingProvider, StakeType stakeTypes) + /// @notice Returns the maximum application authorization + function getMaxAuthorization(address stakingProvider) external view returns (uint96); diff --git a/contracts/staking/TokenStaking.sol b/contracts/staking/TokenStaking.sol index 29f50cd3..f7aefc4a 100644 --- a/contracts/staking/TokenStaking.sol +++ b/contracts/staking/TokenStaking.sol @@ -28,12 +28,8 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; /// @notice TokenStaking is the main staking contract of the Threshold Network. -/// Apart from the basic usage of enabling T stakes, it also acts as a -/// sort of "meta-staking" contract, accepting existing legacy NU/KEEP -/// stakes. Additionally, it serves as application manager for the apps -/// that run on the Threshold Network. Note that legacy NU/KEEP staking -/// contracts see TokenStaking as an application (e.g., slashing is -/// requested by TokenStaking and performed by the legacy contracts). +/// Additionally, it serves as application manager for the apps +/// that run on the Threshold Network. /// @dev TokenStaking is upgradeable, using OpenZeppelin's Upgradeability /// framework. As such, it is required to satisfy OZ's guidelines, like /// restrictions on constructors, immutable variables, base contracts and @@ -51,9 +47,9 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { } struct StakingProviderInfo { - uint96 nuInTStake; + uint96 legacyNuInTStake; address owner; - uint96 keepInTStake; + uint96 legacyKeepInTStake; address payable beneficiary; uint96 tStake; address authorizer; @@ -86,9 +82,6 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { /// @custom:oz-upgrades-unsafe-allow state-variable-immutable T internal immutable token; - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - uint256 internal immutable nucypherRatio; - address public governance; uint96 public minTStakeAmount; uint256 public authorizationCeiling; @@ -108,7 +101,6 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { uint256 public slashingQueueIndex; event Staked( - StakeType indexed stakeType, address indexed owner, address indexed stakingProvider, address beneficiary, @@ -215,14 +207,11 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { } /// @param _token Address of T token contract - /// @param _nucypherVendingMachine Address of NuCypher vending machine /// @custom:oz-upgrades-unsafe-allow constructor - constructor(T _token, VendingMachine _nucypherVendingMachine) { + constructor(T _token) { // calls to check contracts are working require(_token.totalSupply() > 0, "Wrong input parameters"); token = _token; - - nucypherRatio = _nucypherVendingMachine.ratio(); } function initialize() external initializer { @@ -274,7 +263,6 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { increaseStakeCheckpoint(stakingProvider, amount); emit Staked( - StakeType.T, msg.sender, stakingProvider, beneficiary, @@ -655,11 +643,10 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { /// @notice Reduces the liquid T stake amount by the provided amount and /// withdraws T to the owner. Reverts if there is at least one - /// authorization higher than the sum of the legacy stake and - /// remaining liquid T stake or if the unstake amount is higher than - /// the liquid T stake amount. Can be called only by the owner or - /// the staking provider. Can only be called when 24h passed since - /// the stake has been delegated. + /// authorization higher than the remaining liquid T stake or + /// if the unstake amount is higher than the liquid T stake amount. + /// Can be called only by the delegation owner or the staking + /// provider. function unstakeT(address stakingProvider, uint96 amount) external override @@ -670,7 +657,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { ]; require( amount > 0 && - amount + getMinStaked(stakingProvider, StakeType.T) <= + amount + getMaxAuthorization(stakingProvider) <= stakingProviderStruct.tStake, "Too much to unstake" ); @@ -687,121 +674,6 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { token.safeTransfer(stakingProviderStruct.owner, amount); } - /// @notice Sets the legacy KEEP staking contract active stake amount cached - /// in T staking contract to 0. Reverts if the amount of liquid T - /// staked in T staking contract is lower than the highest - /// application authorization. This function allows to unstake from - /// KEEP staking contract and still being able to operate in T - /// network and earning rewards based on the liquid T staked. Can be - /// called only by the delegation owner or the staking provider. - /// Can only be called when 24h passed since the stake has been - /// delegated. - /// @dev This function (or `unstakeAll`) must be called before - /// `undelegate`/`undelegateAt` in Keep staking contract. Otherwise - /// provider can be slashed by `notifyKeepStakeDiscrepancy` method. - function unstakeKeep(address stakingProvider) - external - override - onlyOwnerOrStakingProvider(stakingProvider) - { - StakingProviderInfo storage stakingProviderStruct = stakingProviders[ - stakingProvider - ]; - uint96 keepInTStake = stakingProviderStruct.keepInTStake; - require(keepInTStake != 0, "Nothing to unstake"); - require( - getMinStaked(stakingProvider, StakeType.KEEP) == 0, - "Keep stake still authorized" - ); - - emit Unstaked(stakingProvider, keepInTStake); - stakingProviderStruct.keepInTStake = 0; - decreaseStakeCheckpoint(stakingProvider, keepInTStake); - } - - /// @notice Sets to 0 the amount of T that is cached from the legacy - /// NU staking contract. Reverts if there is at least one - /// authorization higher than the sum of remaining legacy NU stake - /// and native T stake for that staking provider or if the unstaked - /// amount is higher than the cached legacy stake amount. If succeeded, - /// the legacy NU stake can be partially or fully undelegated on - /// the legacy NU staking contract. This function allows to unstake - /// from NU staking contract while still being able to operate in - /// T network and earning rewards based on the native T staked. - /// Can be called only by the stake owner or the staking provider. - /// @dev This function (or `unstakeAll`) must be called before `withdraw` - /// in NuCypher staking contract. Otherwise NU tokens can't be - /// unlocked. - /// @param stakingProvider Staking provider address - function unstakeNu(address stakingProvider) - external - override - onlyOwnerOrStakingProvider(stakingProvider) - { - StakingProviderInfo storage stakingProviderStruct = stakingProviders[ - stakingProvider - ]; - uint96 nuInTStake = stakingProviderStruct.nuInTStake; - require(nuInTStake != 0, "Nothing to unstake"); - require( - getMinStaked(stakingProvider, StakeType.NU) == 0, - "NU stake still authorized" - ); - - stakingProviderStruct.nuInTStake = 0; - decreaseStakeCheckpoint(stakingProvider, nuInTStake); - emit Unstaked(stakingProvider, nuInTStake); - } - - /// @notice Sets cached legacy stake amount to 0, sets the liquid T stake - /// amount to 0 and withdraws all liquid T from the stake to the - /// owner. Reverts if there is at least one non-zero authorization. - /// Can be called only by the delegation owner or the staking - /// provider. Can only be called when 24h passed since the stake - /// has been delegated. - function unstakeAll(address stakingProvider) - external - override - onlyOwnerOrStakingProvider(stakingProvider) - { - StakingProviderInfo storage stakingProviderStruct = stakingProviders[ - stakingProvider - ]; - require( - stakingProviderStruct.authorizedApplications.length == 0, - "Stake still authorized" - ); - require( - stakingProviderStruct.startStakingTimestamp + MIN_STAKE_TIME <= - /* solhint-disable-next-line not-rely-on-time */ - block.timestamp, - "Can't unstake earlier than 24h" - ); - - uint96 unstaked = stakingProviderStruct.tStake + - stakingProviderStruct.keepInTStake + - stakingProviderStruct.nuInTStake; - emit Unstaked(stakingProvider, unstaked); - uint96 amount = stakingProviderStruct.tStake; - stakingProviderStruct.tStake = 0; - stakingProviderStruct.keepInTStake = 0; - stakingProviderStruct.nuInTStake = 0; - decreaseStakeCheckpoint(stakingProvider, unstaked); - - if (amount > 0) { - token.safeTransfer(stakingProviderStruct.owner, amount); - } - } - - /// @notice Involuntary decrease authorization for all application up to T - /// stake amount for all staking providers in the list. - /// Sets cached legacy stake amount to 0. Can be called by anyone - function forceUnstakeLegacy(address[] memory _stakingProviders) external { - for (uint256 i = 0; i < _stakingProviders.length; i++) { - forceUnstakeLegacy(_stakingProviders[i]); - } - } - // // // Keeping information in sync @@ -941,25 +813,14 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { .authorized; } - /// @notice Returns staked amount of T, Keep and Nu for the specified - /// staking provider. - /// @dev All values are in T denomination - function stakes(address stakingProvider) + /// @notice Returns staked amount of T for the specified staking provider. + function tStake(address stakingProvider) external view override - returns ( - uint96 tStake, - uint96 keepInTStake, - uint96 nuInTStake - ) + returns (uint96) { - StakingProviderInfo storage stakingProviderStruct = stakingProviders[ - stakingProvider - ]; - tStake = stakingProviderStruct.tStake; - keepInTStake = stakingProviderStruct.keepInTStake; - nuInTStake = stakingProviderStruct.nuInTStake; + return stakingProviders[stakingProvider].tStake; } /// @notice Returns start staking timestamp. @@ -983,19 +844,6 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { return stakingProviders[stakingProvider].autoIncrease; } - /// @notice Returns staked amount of NU for the specified staking provider. - function stakedNu(address stakingProvider) - external - view - override - returns (uint256 nuAmount) - { - (nuAmount, ) = convertFromT( - stakingProviders[stakingProvider].nuInTStake, - nucypherRatio - ); - } - /// @notice Gets the stake owner, the beneficiary and the authorizer /// for the specified staking provider address. /// @return owner Stake owner address. @@ -1078,88 +926,8 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { ); } - /// @notice Involuntary decrease authorization for all application up to T - /// stake amount. Sets cached legacy stake amount to 0. - /// Can be called by anyone - function forceUnstakeLegacy(address stakingProvider) public { - StakingProviderInfo storage stakingProviderStruct = stakingProviders[ - stakingProvider - ]; - uint96 legacyStake = stakingProviderStruct.keepInTStake + - stakingProviderStruct.nuInTStake; - require(legacyStake > 0, "No legacy stake"); - - // similar to authorizationDecrease method - uint256 applicationsToDelete = 0; - for ( - uint256 i = 0; - i < stakingProviderStruct.authorizedApplications.length; - i++ - ) { - address authorizedApplication = stakingProviderStruct - .authorizedApplications[i]; - AppAuthorization storage authorization = stakingProviderStruct - .authorizations[authorizedApplication]; - uint96 fromAmount = authorization.authorized; - - if (fromAmount <= stakingProviderStruct.tStake) { - continue; - } - authorization.authorized = stakingProviderStruct.tStake; - - bool successful = true; - //slither-disable-next-line calls-loop - try - IApplication(authorizedApplication) - .involuntaryAuthorizationDecrease{ - gas: GAS_LIMIT_AUTHORIZATION_DECREASE - }(stakingProvider, fromAmount, authorization.authorized) - {} catch { - successful = false; - } - if (authorization.deauthorizing > authorization.authorized) { - authorization.deauthorizing = authorization.authorized; - } - emit AuthorizationInvoluntaryDecreased( - stakingProvider, - authorizedApplication, - fromAmount, - authorization.authorized, - successful - ); - if (authorization.authorized == 0) { - applicationsToDelete++; - } - } - if (applicationsToDelete > 0) { - cleanAuthorizedApplications( - stakingProviderStruct, - applicationsToDelete - ); - } - - emit Unstaked(stakingProvider, legacyStake); - stakingProviderStruct.keepInTStake = 0; - stakingProviderStruct.nuInTStake = 0; - decreaseStakeCheckpoint(stakingProvider, legacyStake); - } - - /// @notice Returns minimum possible stake for T, KEEP or NU in T denomination - /// @dev For example, suppose the given staking provider has 10 T, 20 T worth - /// of KEEP, and 30 T worth of NU all staked, and the maximum - /// application authorization is 40 T, then `getMinStaked` for - /// that staking provider returns: - /// * 0 T if KEEP stake type specified i.e. - /// min = 40 T max - (10 T) = 30 T - /// * 10 T if NU stake type specified i.e. - /// min = 40 T max - (10 T) = 30 T - /// * 0 T if T stake type specified i.e. - /// min = 40 T max = 40 T - /// In other words, the minimum stake amount for the specified - /// stake type is the minimum amount of stake of the given type - /// needed to satisfy the maximum application authorization given - /// the staked amounts of the T stake types for that staking provider. - function getMinStaked(address stakingProvider, StakeType stakeTypes) + /// @notice Returns the maximum application authorization + function getMaxAuthorization(address stakingProvider) public view override @@ -1182,16 +950,6 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { stakingProviderStruct.authorizations[application].authorized ); } - - if (maxAuthorization == 0) { - return 0; - } - if (stakeTypes != StakeType.T) { - maxAuthorization -= MathUpgradeable.min( - maxAuthorization, - stakingProviderStruct.tStake - ); - } return maxAuthorization.toUint96(); } @@ -1231,9 +989,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { StakingProviderInfo storage stakingProviderStruct = stakingProviders[ stakingProvider ]; - uint96 stakingProviderBalance = stakingProviderStruct.tStake + - stakingProviderStruct.keepInTStake + - stakingProviderStruct.nuInTStake; + uint96 stakingProviderBalance = stakingProviderStruct.tStake; address oldDelegatee = delegates(stakingProvider); _delegates[stakingProvider] = delegatee; emit DelegateChanged(stakingProvider, oldDelegatee, delegatee); @@ -1305,9 +1061,7 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { slashing.stakingProvider ]; uint96 tAmountToSlash = slashing.amount; - uint96 oldStake = stakingProviderStruct.tStake + - stakingProviderStruct.keepInTStake + - stakingProviderStruct.nuInTStake; + uint96 oldStake = stakingProviderStruct.tStake; // slash T tAmountToBurn = MathUpgradeable .min(tAmountToSlash, stakingProviderStruct.tStake) @@ -1322,21 +1076,18 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { stakingProviderStruct, slashedAmount ); - uint96 newStake = stakingProviderStruct.tStake + - stakingProviderStruct.keepInTStake + - stakingProviderStruct.nuInTStake; + uint96 newStake = stakingProviderStruct.tStake; decreaseStakeCheckpoint(slashing.stakingProvider, oldStake - newStake); } /// @notice Synchronize authorizations (if needed) after slashing stake + //slither-disable-next-line dead-code function authorizationDecrease( address stakingProvider, StakingProviderInfo storage stakingProviderStruct, uint96 slashedAmount ) internal { - uint96 totalStake = stakingProviderStruct.tStake + - stakingProviderStruct.nuInTStake + - stakingProviderStruct.keepInTStake; + uint96 totalStake = stakingProviderStruct.tStake; uint256 applicationsToDelete = 0; for ( uint256 i = 0; @@ -1479,18 +1230,4 @@ contract TokenStaking is Initializable, IStaking, Checkpoints { governance = newGuvnor; emit GovernanceTransferred(oldGuvnor, newGuvnor); } - - /// @notice Returns the amount of legacy tokens that's obtained from - /// `tAmount` T tokens for the given `ratio`, and the T remainder - /// that can't be converted. - function convertFromT(uint96 tAmount, uint256 ratio) - internal - pure - returns (uint256 amount, uint96 tRemainder) - { - //slither-disable-next-line weak-prng - tRemainder = (tAmount % ratio).toUint96(); - uint256 convertibleAmount = tAmount - tRemainder; - amount = (convertibleAmount * CONVERSION_DIVISOR) / ratio; - } } diff --git a/contracts/test/TokenStakingTestSet.sol b/contracts/test/TokenStakingTestSet.sol index abdadc2c..eb54e0b9 100644 --- a/contracts/test/TokenStakingTestSet.sol +++ b/contracts/test/TokenStakingTestSet.sol @@ -149,9 +149,7 @@ contract ManagedGrantMock { } contract ExtendedTokenStaking is TokenStaking { - constructor(T _token, VendingMachine _nucypherVendingMachine) - TokenStaking(_token, _nucypherVendingMachine) - {} + constructor(T _token) TokenStaking(_token) {} function cleanAuthorizedApplications( address stakingProvider, @@ -192,72 +190,3 @@ contract ExtendedTokenStaking is TokenStaking { return stakingProviders[stakingProvider].authorizedApplications; } } - -contract LegacyTokenStaking is TokenStaking { - /// @custom:oz-upgrades-unsafe-allow constructor - constructor(T _token, VendingMachine _nucypherVendingMachine) - TokenStaking(_token, _nucypherVendingMachine) - {} - - function setLegacyStakingProviderDefault(address stakingProvider) external { - setLegacyStakingProvider( - stakingProvider, - stakingProvider, - payable(stakingProvider), - stakingProvider - ); - } - - function addLegacyStake( - address stakingProvider, - uint96 keepInTStake, - uint96 nuInTStake - ) external { - StakingProviderInfo storage stakingProviderStruct = stakingProviders[ - stakingProvider - ]; - stakingProviderStruct.keepInTStake += keepInTStake; - stakingProviderStruct.nuInTStake += nuInTStake; - if (stakingProviderStruct.startStakingTimestamp == 0) { - /* solhint-disable-next-line not-rely-on-time */ - stakingProviderStruct.startStakingTimestamp = block.timestamp; - } - increaseStakeCheckpoint(stakingProvider, keepInTStake + nuInTStake); - } - - function forceIncreaseAuthorization( - address stakingProvider, - address application, - uint96 amount - ) external { - StakingProviderInfo storage stakingProviderStruct = stakingProviders[ - stakingProvider - ]; - AppAuthorization storage authorization = stakingProviderStruct - .authorizations[application]; - uint96 fromAmount = authorization.authorized; - if (fromAmount == 0) { - stakingProviderStruct.authorizedApplications.push(application); - } - authorization.authorized += amount; - IApplication(application).authorizationIncreased( - stakingProvider, - fromAmount, - authorization.authorized - ); - } - - function setLegacyStakingProvider( - address stakingProvider, - address owner, - address payable beneficiary, - address authorizer - ) public { - StakingProviderInfo storage stakingProviderStruct = stakingProviders[ - stakingProvider - ]; - stakingProviderStruct.owner = owner; - stakingProviderStruct.authorizer = authorizer; - stakingProviderStruct.beneficiary = beneficiary; - } -} diff --git a/deploy/07_deploy_token_staking.ts b/deploy/07_deploy_token_staking.ts index 2820f162..bb5c5b26 100644 --- a/deploy/07_deploy_token_staking.ts +++ b/deploy/07_deploy_token_staking.ts @@ -9,12 +9,8 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { deployer } = await getNamedAccounts() const T = await deployments.get("T") - const VendingMachineNuCypher = await deployments.get("VendingMachineNuCypher") - const tokenStakingConstructorArgs = [ - T.address, - VendingMachineNuCypher.address, - ] + const tokenStakingConstructorArgs = [T.address] const tokenStakingInitializerArgs = [] // TODO: Consider upgradable deployment also for goerli/sepolia. diff --git a/docs/rfc-1-staking-contract.adoc b/docs/rfc-1-staking-contract.adoc index 6a244fa9..e1b944c7 100644 --- a/docs/rfc-1-staking-contract.adoc +++ b/docs/rfc-1-staking-contract.adoc @@ -297,36 +297,9 @@ will be added to already authorized applications. ==== `unstakeT(address stakingProvider, uint96 amount) external onlyOwnerOrStakingProvider(stakingProvider)` Reduces the liquid T stake amount by `amount` and withdraws `amount` of T -to the owner. Reverts if there is at least one authorization higher than the sum -of a legacy stake and remaining liquid T stake or if the `amount` is higher than -the liquid T stake amount. Can be called only by the owner or the staking provider. - -==== `unstakeKeep(address stakingProvider) external onlyOwnerOrStakingProvider(stakingProvider)` - -Sets the legacy staking contract active stake amount cached in T staking -contract to 0. Reverts if the amount of liquid T staked in T staking contract is -lower than the highest application authorization. This function allows to -unstake from Keep staking contract and sill being able to operate in T network -and earning rewards based on the liquid T staked. Can be called only by the -delegation owner or the staking provider. - -==== `unstakeNu(address stakingProvider, uint96 amount) external onlyOwnerOrStakingProvider(stakingProvider)` - -Sets to 0 the amount of T that is cached from the legacy NU staking contract. -Reverts if there is at least one authorization higher than the sum of remaining -legacy NU stake and native T stake for that staking provider or if the unstaked -amount is higher than the cached legacy stake amount. If succeeded, the legacy -NU stake can be partially or fully undelegated on the legacy NU staking contract. -This function allows to unstake from NU staking contract while still being able -to operate in T network and earning rewards based on the native T staked. -Can be called only by the stake owner or the staking provider. - -==== `unstakeAll(address stakingProvider) external onlyOwnerOrStakingProvider(stakingProvider)` - -Sets cached legacy stake amount to 0, sets the liquid T stake amount to 0 and -withdraws all liquid T from the stake to the owner. Reverts if there is at least one -non-zero authorization. Can be called only by the delegation owner or the -staking provider. +to the owner. Reverts if there is at least one authorization higher than the +remaining liquid T stake or if the `amount` is higher than the liquid T stake amount. +Can be called only by the owner or the staking provider. === Keeping information in sync @@ -369,16 +342,14 @@ each affected application. Returns the authorized stake amount of the staking provider for the application. -==== `stakes(address stakingProvider) external view returns (uint96 tStake, uint96 keepInTStake, uint96 nuInTStake)` +==== `tStake(address stakingProvider) external view returns (uint96)` -Returns staked amount of T, Keep and Nu for the specified staking provider. All values -are in T denomination. +Returns staked amount of T for the specified staking provider. ==== `getStartStakingTimestamp(address stakingProvider) external view returns (uint256)` -Returns start staking timestamp for T/NU stake. This value is set at most once, -and only when a stake is created with T or NU tokens. If a stake is created -from a legacy KEEP stake, this value will remain as zero. +Returns start staking timestamp for T stake. This value is set at most once, +and only when a stake is created with. ==== `getAutoIncreaseFlag(address stakingProvider) external view returns (bool)` @@ -386,10 +357,6 @@ from a legacy KEEP stake, this value will remain as zero. Returns auto-increase flag. If flag is true then any topped up amount will be added to existing authorizations. -==== `stakedNu(address stakingProvider) external view returns (uint256)` - -Returns staked amount of NU for the specified staking provider - ==== `rolesOf(address stakingProvider) external view returns (address owner, address payable beneficiary, address authorizer)` Gets the stake owner, the beneficiary and the authorizer for the specified @@ -403,21 +370,9 @@ Returns length of application array Returns length of slashing queue -==== `getMinStaked(address stakingProvider, StakeType stakeTypes) external view returns (uint96)` - -Returns minimum possible stake for T, KEEP or NU (stake type) in T denomination. -For example, suppose the given staking provider has 10 T, 20 T worth of KEEP, -and 30 T worth of NU all staked, and the maximum application authorization is -40 T, then `getMinStaked` for that staking provider returns: - -* 0 T if KEEP stake type specified i.e. min = 40 T max - (10 T) = 30 T -* 10 T if NU stake type specified i.e. min = 40 T max - (10 T) = 30 T -* 0 T if T stake type specified i.e. min = 40 T max = 40 T +==== `getMaxAuthorization(address stakingProvider) external view returns (uint96)` -In other words, the minimum stake amount for -the specified stake type is the minimum amount of stake of the given type needed -to satisfy the maximum application authorization given the staked amounts of the -T stake type for that staking provider. +Returns the maximum application authorization ==== `getAvailableToAuthorize(address stakingProvider, address application) external view returns (uint96)` diff --git a/test/staking/TokenStaking.test.js b/test/staking/TokenStaking.test.js index 080907b6..abfe7891 100644 --- a/test/staking/TokenStaking.test.js +++ b/test/staking/TokenStaking.test.js @@ -2,15 +2,10 @@ const { expect } = require("chai") const { helpers } = require("hardhat") const { lastBlockTime, mineBlocks, increaseTime } = helpers.time -const { to1e18, to1ePrecision } = helpers.number +const { to1e18 } = helpers.number const { AddressZero, Zero } = ethers.constants -const StakeTypes = { - NU: 0, - KEEP: 1, - T: 2, -} const ApplicationStatus = { NOT_APPROVED: 0, APPROVED: 1, @@ -21,28 +16,10 @@ const { upgrades } = require("hardhat") describe("TokenStaking", () => { let tToken - let nucypherVendingMachine let application1Mock let application2Mock - const floatingPointDivisor = to1ePrecision(1, 15) const tAllocation = to1e18("4500000000") // 4.5 Billion - const maxKeepWrappedTokens = to1e18("1100000000") // 1.1 Billion - const maxNuWrappedTokens = to1e18("900000000") // 0.9 Billion - const keepRatio = floatingPointDivisor - .mul(tAllocation) - .div(maxKeepWrappedTokens) - const nuRatio = floatingPointDivisor.mul(tAllocation).div(maxNuWrappedTokens) - - function convertToT(amount, ratio) { - amount = ethers.BigNumber.from(amount) - const wrappedRemainder = amount.mod(floatingPointDivisor) - amount = amount.sub(wrappedRemainder) - return { - result: amount.mul(ratio).div(floatingPointDivisor), - remainder: wrappedRemainder, - } - } function rewardFromPenalty(penalty, rewardMultiplier) { return penalty.mul(5).div(100).mul(rewardMultiplier).div(100) @@ -88,20 +65,13 @@ describe("TokenStaking", () => { .connect(deployer) .transfer(otherStaker.address, initialStakerBalance) - const VendingMachine = await ethers.getContractFactory("VendingMachineMock") - nucypherVendingMachine = await VendingMachine.deploy( - maxNuWrappedTokens, - tAllocation - ) - await nucypherVendingMachine.deployed() - - const TokenStaking = await ethers.getContractFactory("LegacyTokenStaking") + const TokenStaking = await ethers.getContractFactory("TokenStaking") const tokenStakingInitializerArgs = [] tokenStaking = await upgrades.deployProxy( TokenStaking, tokenStakingInitializerArgs, { - constructorArgs: [tToken.address, nucypherVendingMachine.address], + constructorArgs: [tToken.address], } ) await tokenStaking.deployed() @@ -292,10 +262,7 @@ describe("TokenStaking", () => { }) it("should set value of stakes", async () => { - await assertStakes(stakingProvider.address, amount, Zero, Zero) - expect(await tokenStaking.stakedNu(stakingProvider.address)).to.equal( - 0 - ) + await assertStake(stakingProvider.address, amount) }) it("should start staking timestamp", async () => { @@ -321,7 +288,6 @@ describe("TokenStaking", () => { await expect(tx) .to.emit(tokenStaking, "Staked") .withArgs( - StakeTypes.T, staker.address, stakingProvider.address, beneficiary.address, @@ -643,27 +609,9 @@ describe("TokenStaking", () => { application1Mock.address ) ).to.equal(authorizedAmount) - }) - - it("should increase min staked amount in T", async () => { expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.T - ) + await tokenStaking.getMaxAuthorization(stakingProvider.address) ).to.equal(authorizedAmount) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) }) it("should decrease available amount to authorize for one application", async () => { @@ -756,6 +704,9 @@ describe("TokenStaking", () => { application1Mock.address ) ).to.equal(amount) + expect( + await tokenStaking.getMaxAuthorization(stakingProvider.address) + ).to.equal(amount) }) it("should decrease available amount to authorize for one application", async () => { @@ -773,27 +724,6 @@ describe("TokenStaking", () => { ).to.equal(amount) }) - it("should increase min staked amount in T", async () => { - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.T - ) - ).to.equal(amount) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) - }) - it("should inform application", async () => { await assertApplicationStakingProviders( application1Mock, @@ -871,245 +801,6 @@ describe("TokenStaking", () => { }) } ) - - context( - "when caller is authorizer of staking provider with mixed stake", - () => { - const tStake = initialStakerBalance - const keepStake = initialStakerBalance - const keepInTStake = convertToT(keepStake, keepRatio).result - const nuStake = initialStakerBalance - const nuInTStake = convertToT(nuStake, nuRatio).result - - beforeEach(async () => { - await tokenStaking - .connect(deployer) - .approveApplication(application1Mock.address) - - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - keepInTStake, - nuInTStake - ) - - await tToken.connect(staker).approve(tokenStaking.address, tStake) - await tokenStaking - .connect(staker) - .topUp(stakingProvider.address, tStake) - }) - - context("when authorize more than not legacy staked amount", () => { - it("should revert", async () => { - await expect( - tokenStaking - .connect(authorizer) - .increaseAuthorization( - stakingProvider.address, - application1Mock.address, - tStake.add(1) - ) - ).to.be.revertedWith("Not enough stake to authorize") - }) - }) - - context("when authorize staked tokens in one tx", () => { - let tx - const notAuthorized = tStake.sub(to1e18(1)) - const authorizedAmount = tStake.sub(notAuthorized) - - beforeEach(async () => { - tx = await tokenStaking - .connect(authorizer) - .increaseAuthorization( - stakingProvider.address, - application1Mock.address, - authorizedAmount - ) - }) - - it("should increase authorization", async () => { - expect( - await tokenStaking.authorizedStake( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(authorizedAmount) - }) - - it("should increase min staked amount in T only", async () => { - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.T - ) - ).to.equal(authorizedAmount) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) - }) - - it("should decrease available amount to authorize for one application", async () => { - expect( - await tokenStaking.getAvailableToAuthorize( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(notAuthorized) - expect( - await tokenStaking.getAvailableToAuthorize( - stakingProvider.address, - application2Mock.address - ) - ).to.equal(tStake) - }) - - it("should inform application", async () => { - await assertApplicationStakingProviders( - application1Mock, - stakingProvider.address, - authorizedAmount, - Zero - ) - }) - - it("should emit AuthorizationIncreased", async () => { - await expect(tx) - .to.emit(tokenStaking, "AuthorizationIncreased") - .withArgs( - stakingProvider.address, - application1Mock.address, - 0, - authorizedAmount - ) - }) - - context("when authorize to the second application", () => { - let tx2 - - beforeEach(async () => { - await tokenStaking - .connect(deployer) - .approveApplication(application2Mock.address) - - tx2 = await tokenStaking - .connect(authorizer) - .increaseAuthorization( - stakingProvider.address, - application2Mock.address, - tStake - ) - }) - - it("should increase only one authorization", async () => { - expect( - await tokenStaking.authorizedStake( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(authorizedAmount) - expect( - await tokenStaking.authorizedStake( - stakingProvider.address, - application2Mock.address - ) - ).to.equal(tStake) - }) - - it("should set min staked amount equal to T stake", async () => { - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.T - ) - ).to.equal(tStake) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) - }) - - it("should decrease available amount to authorize for the second application", async () => { - expect( - await tokenStaking.getAvailableToAuthorize( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(notAuthorized) - expect( - await tokenStaking.getAvailableToAuthorize( - stakingProvider.address, - application2Mock.address - ) - ).to.equal(0) - }) - - it("should inform second application", async () => { - await assertApplicationStakingProviders( - application2Mock, - stakingProvider.address, - tStake, - Zero - ) - }) - - it("should emit AuthorizationIncreased", async () => { - await expect(tx2) - .to.emit(tokenStaking, "AuthorizationIncreased") - .withArgs( - stakingProvider.address, - application2Mock.address, - 0, - tStake - ) - }) - }) - }) - - context("when authorize more than staked amount in several txs", () => { - it("should revert", async () => { - await tokenStaking - .connect(authorizer) - .increaseAuthorization( - stakingProvider.address, - application1Mock.address, - tStake.sub(1) - ) - await expect( - tokenStaking - .connect(authorizer) - .increaseAuthorization( - stakingProvider.address, - application1Mock.address, - 2 - ) - ).to.be.revertedWith("Not enough stake to authorize") - }) - }) - } - ) }) describe("requestAuthorizationDecrease", () => { @@ -1520,24 +1211,9 @@ describe("TokenStaking", () => { application1Mock.address ) ).to.equal(expectedToAmount) - }) - - it("should decrease min staked amount in T", async () => { expect( - await tokenStaking.getMinStaked(stakingProvider.address, StakeTypes.T) + await tokenStaking.getMaxAuthorization(stakingProvider.address) ).to.equal(expectedToAmount) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) }) it("should emit AuthorizationDecreaseApproved", async () => { @@ -1594,27 +1270,9 @@ describe("TokenStaking", () => { application2Mock.address ) ).to.equal(otherAmount) - }) - - it("should decrease min staked amount in T", async () => { expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.T - ) + await tokenStaking.getMaxAuthorization(stakingProvider.address) ).to.equal(otherAmount) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) }) it("should emit AuthorizationDecreaseApproved", async () => { @@ -2166,7 +1824,7 @@ describe("TokenStaking", () => { }) it("should update T staked amount", async () => { - await assertStakes(stakingProvider.address, expectedAmount, Zero, Zero) + await assertStake(stakingProvider.address, expectedAmount) }) it("should not update roles", async () => { @@ -2196,22 +1854,10 @@ describe("TokenStaking", () => { ).to.equal(topUpAmount) }) - it("should not increase min staked amount", async () => { + it("should not increase authorized amount", async () => { expect( - await tokenStaking.getMinStaked(stakingProvider.address, StakeTypes.T) + await tokenStaking.getMaxAuthorization(stakingProvider.address) ).to.equal(amount) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) }) it("should emit ToppedUp event", async () => { @@ -2258,14 +1904,16 @@ describe("TokenStaking", () => { await increaseTime(86400) // +24h - await tokenStaking.connect(staker).unstakeAll(stakingProvider.address) + await tokenStaking + .connect(staker) + .unstakeT(stakingProvider.address, amount) tx = await tokenStaking .connect(staker) .topUp(stakingProvider.address, amount) }) it("should update T staked amount", async () => { - await assertStakes(stakingProvider.address, amount, Zero, Zero) + await assertStake(stakingProvider.address, amount) }) it("should not update start staking timestamp", async () => { @@ -2281,206 +1929,16 @@ describe("TokenStaking", () => { }) }) - context("when staking provider has Keep stake", () => { - const keepAmount = initialStakerBalance - const keepInTAmount = convertToT(keepAmount, keepRatio).result - const topUpAmount = initialStakerBalance.mul(2) - const expectedAmount = keepInTAmount.add(topUpAmount) + context("when auto increase flag is enabled", () => { + const amount = initialStakerBalance.div(2) + const topUpAmount = initialStakerBalance + const expectedAmount = amount.add(topUpAmount) + const authorized1 = amount + const authorized2 = amount.div(2) let tx - let blockTimestamp beforeEach(async () => { - await tokenStaking - .connect(deployer) - .setMinimumStakeAmount(topUpAmount.add(1)) - - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - keepInTAmount, - 0 - ) - blockTimestamp = await lastBlockTime() - - await tokenStaking - .connect(staker) - .delegateVoting(stakingProvider.address, delegatee.address) - await tToken.connect(deployer).transfer(authorizer.address, topUpAmount) - await tToken - .connect(authorizer) - .approve(tokenStaking.address, topUpAmount) - tx = await tokenStaking - .connect(authorizer) - .topUp(stakingProvider.address, topUpAmount) - }) - - it("should update only T staked amount", async () => { - await assertStakes( - stakingProvider.address, - topUpAmount, - keepInTAmount, - Zero - ) - }) - - it("should not update roles", async () => { - expect( - await tokenStaking.rolesOf(stakingProvider.address) - ).to.deep.equal([ - staker.address, - beneficiary.address, - authorizer.address, - ]) - }) - - it("should not update start staking timestamp", async () => { - expect( - await tokenStaking.getStartStakingTimestamp(stakingProvider.address) - ).to.equal(blockTimestamp) - }) - - it("should increase available amount to authorize", async () => { - expect( - await tokenStaking.getAvailableToAuthorize( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(topUpAmount) - }) - - it("should transfer tokens to the staking contract", async () => { - expect(await tToken.balanceOf(tokenStaking.address)).to.equal( - topUpAmount - ) - }) - - it("should emit ToppedUp event", async () => { - await expect(tx) - .to.emit(tokenStaking, "ToppedUp") - .withArgs(stakingProvider.address, topUpAmount) - }) - - it("should increase the delegatee voting power", async () => { - expect(await tokenStaking.getVotes(delegatee.address)).to.equal( - expectedAmount - ) - }) - - it("should increase the total voting power", async () => { - const lastBlock = await mineBlocks(1) - expect(await tokenStaking.getPastTotalSupply(lastBlock - 1)).to.equal( - expectedAmount - ) - }) - }) - - context("when staking provider has NuCypher stake", () => { - const nuAmount = initialStakerBalance - const nuInTAmount = convertToT(nuAmount, nuRatio).result - const topUpAmount = initialStakerBalance.mul(2) - const expectedAmount = nuInTAmount.add(topUpAmount) - let tx - let blockTimestamp - - beforeEach(async () => { - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - staker.address, - staker.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - 0, - nuInTAmount - ) - blockTimestamp = await lastBlockTime() - - await tokenStaking - .connect(staker) - .delegateVoting(stakingProvider.address, delegatee.address) - await tToken - .connect(deployer) - .transfer(stakingProvider.address, topUpAmount) - await tToken - .connect(stakingProvider) - .approve(tokenStaking.address, topUpAmount) - tx = await tokenStaking - .connect(stakingProvider) - .topUp(stakingProvider.address, topUpAmount) - }) - - it("should update only T staked amount", async () => { - await assertStakes( - stakingProvider.address, - topUpAmount, - Zero, - nuInTAmount - ) - }) - - it("should not update roles", async () => { - expect( - await tokenStaking.rolesOf(stakingProvider.address) - ).to.deep.equal([staker.address, staker.address, staker.address]) - }) - - it("should not update start staking timestamp", async () => { - expect( - await tokenStaking.getStartStakingTimestamp(stakingProvider.address) - ).to.equal(blockTimestamp) - }) - - it("should increase available amount to authorize", async () => { - expect( - await tokenStaking.getAvailableToAuthorize( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(topUpAmount) - }) - - it("should transfer tokens to the staking contract", async () => { - expect(await tToken.balanceOf(tokenStaking.address)).to.equal( - topUpAmount - ) - }) - - it("should emit ToppedUp event", async () => { - await expect(tx) - .to.emit(tokenStaking, "ToppedUp") - .withArgs(stakingProvider.address, topUpAmount) - }) - - it("should increase the delegatee voting power", async () => { - expect(await tokenStaking.getVotes(delegatee.address)).to.equal( - expectedAmount - ) - }) - - it("should increase the total voting power", async () => { - const lastBlock = await mineBlocks(1) - expect(await tokenStaking.getPastTotalSupply(lastBlock - 1)).to.equal( - expectedAmount - ) - }) - }) - - context("when auto increase flag is enabled", () => { - const amount = initialStakerBalance.div(2) - const topUpAmount = initialStakerBalance - const expectedAmount = amount.add(topUpAmount) - const authorized1 = amount - const authorized2 = amount.div(2) - let tx - - beforeEach(async () => { - await tToken.connect(staker).approve(tokenStaking.address, amount) + await tToken.connect(staker).approve(tokenStaking.address, amount) await tokenStaking .connect(staker) .stake( @@ -2525,7 +1983,7 @@ describe("TokenStaking", () => { }) it("should update T staked amount", async () => { - await assertStakes(stakingProvider.address, expectedAmount, Zero, Zero) + await assertStake(stakingProvider.address, expectedAmount) }) it("should not increase available amount to authorize", async () => { @@ -2543,22 +2001,10 @@ describe("TokenStaking", () => { ).to.equal(amount.sub(authorized2)) }) - it("should increase min staked amount", async () => { + it("should increase authorized amount", async () => { expect( - await tokenStaking.getMinStaked(stakingProvider.address, StakeTypes.T) + await tokenStaking.getMaxAuthorization(stakingProvider.address) ).to.equal(expectedAmount) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) }) it("should emit ToppedUp event", async () => { @@ -2738,29 +2184,6 @@ describe("TokenStaking", () => { }) }) - context("when stake is only in Keep and Nu", () => { - it("should revert", async () => { - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - initialStakerBalance, - initialStakerBalance - ) - - const amountToUnstake = 1 - await expect( - tokenStaking - .connect(stakingProvider) - .unstakeT(stakingProvider.address, amountToUnstake) - ).to.be.revertedWith("Too much to unstake") - }) - }) - context("when amount to unstake is more than not authorized", () => { it("should revert", async () => { const amount = initialStakerBalance @@ -2843,24 +2266,6 @@ describe("TokenStaking", () => { ).to.be.revertedWith("Can't unstake earlier than 24h") }) }) - - context("when another stake type was topped-up", () => { - it("should revert", async () => { - const nuAmount = initialStakerBalance - await tokenStaking.addLegacyStake( - stakingProvider.address, - 0, - nuAmount - ) - - const amountToUnstake = amount - await expect( - tokenStaking - .connect(staker) - .unstakeT(stakingProvider.address, amountToUnstake) - ).to.be.revertedWith("Can't unstake earlier than 24h") - }) - }) }) context("when unstake after minimum staking time passes", () => { @@ -2890,7 +2295,7 @@ describe("TokenStaking", () => { }) it("should update T staked amount", async () => { - await assertStakes(stakingProvider.address, Zero, Zero, Zero) + await assertStake(stakingProvider.address, Zero) }) it("should not update roles", async () => { @@ -2901,746 +2306,25 @@ describe("TokenStaking", () => { beneficiary.address, authorizer.address, ]) - }) - - it("should not update start staking timestamp", async () => { - expect( - await tokenStaking.getStartStakingTimestamp(stakingProvider.address) - ).to.equal(blockTimestamp) - }) - - it("should transfer tokens to the staker address", async () => { - expect(await tToken.balanceOf(tokenStaking.address)).to.equal(0) - expect(await tToken.balanceOf(staker.address)).to.equal(amount) - }) - - it("should emit Unstaked", async () => { - await expect(tx) - .to.emit(tokenStaking, "Unstaked") - .withArgs(stakingProvider.address, amount) - }) - }) - }) - - describe("unstakeKeep", () => { - context("when staking provider has no stake", () => { - it("should revert", async () => { - await expect( - tokenStaking.unstakeKeep(deployer.address) - ).to.be.revertedWith("Not owner or provider") - }) - }) - - context("when caller is not owner or staking provider", () => { - it("should revert", async () => { - await tToken - .connect(staker) - .approve(tokenStaking.address, initialStakerBalance) - await tokenStaking - .connect(staker) - .stake( - stakingProvider.address, - beneficiary.address, - authorizer.address, - initialStakerBalance - ) - await expect( - tokenStaking.connect(authorizer).unstakeKeep(stakingProvider.address) - ).to.be.revertedWith("Not owner or provider") - }) - }) - - context("when stake is only in T and Nu", () => { - it("should revert", async () => { - await tToken - .connect(staker) - .approve(tokenStaking.address, initialStakerBalance) - await tokenStaking - .connect(staker) - .stake( - stakingProvider.address, - beneficiary.address, - authorizer.address, - initialStakerBalance - ) - - await tokenStaking.addLegacyStake( - stakingProvider.address, - 0, - initialStakerBalance - ) - - await expect( - tokenStaking - .connect(stakingProvider) - .unstakeKeep(stakingProvider.address) - ).to.be.revertedWith("Nothing to unstake") - }) - }) - - context("when authorized amount is more than non-Keep stake", () => { - it("should revert", async () => { - const tAmount = initialStakerBalance - const keepAmount = initialStakerBalance - - await tokenStaking - .connect(deployer) - .approveApplication(application1Mock.address) - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - keepAmount, - 0 - ) - - await tToken.connect(staker).approve(tokenStaking.address, tAmount) - await tokenStaking - .connect(staker) - .topUp(stakingProvider.address, tAmount) - - const authorized = tAmount.add(1) - await tokenStaking - .connect(authorizer) - .forceIncreaseAuthorization( - stakingProvider.address, - application1Mock.address, - authorized - ) - - await expect( - tokenStaking.connect(staker).unstakeKeep(stakingProvider.address) - ).to.be.revertedWith("Keep stake still authorized") - }) - }) - - context("when authorized amount is less than non-Keep stake", () => { - const tAmount = initialStakerBalance - const keepAmount = initialStakerBalance - const keepInTAmount = convertToT(keepAmount, keepRatio).result - const authorized = tAmount - let tx - let blockTimestamp - - beforeEach(async () => { - await tokenStaking - .connect(deployer) - .approveApplication(application1Mock.address) - - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - keepInTAmount, - 0 - ) - blockTimestamp = await lastBlockTime() - - await tokenStaking - .connect(staker) - .delegateVoting(stakingProvider.address, delegatee.address) - - await tToken.connect(staker).approve(tokenStaking.address, tAmount) - await tokenStaking - .connect(staker) - .topUp(stakingProvider.address, tAmount) - - await tokenStaking - .connect(authorizer) - .increaseAuthorization( - stakingProvider.address, - application1Mock.address, - authorized - ) - - tx = await tokenStaking - .connect(stakingProvider) - .unstakeKeep(stakingProvider.address) - }) - - it("should set Keep staked amount to zero", async () => { - await assertStakes(stakingProvider.address, tAmount, Zero, Zero) - }) - - it("should not update roles", async () => { - expect( - await tokenStaking.rolesOf(stakingProvider.address) - ).to.deep.equal([ - staker.address, - beneficiary.address, - authorizer.address, - ]) - }) - - it("should not update start staking timestamp", async () => { - expect( - await tokenStaking.getStartStakingTimestamp(stakingProvider.address) - ).to.equal(blockTimestamp) - }) - - it("should decrease available amount to authorize", async () => { - expect( - await tokenStaking.getAvailableToAuthorize( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(tAmount.sub(authorized)) - }) - - it("should update min staked amount", async () => { - expect( - await tokenStaking.getMinStaked(stakingProvider.address, StakeTypes.T) - ).to.equal(tAmount) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) - }) - - it("should emit Unstaked", async () => { - await expect(tx) - .to.emit(tokenStaking, "Unstaked") - .withArgs(stakingProvider.address, keepInTAmount) - }) - - it("should decrease the delegatee voting power", async () => { - expect(await tokenStaking.getVotes(delegatee.address)).to.equal(tAmount) - }) - - it("should decrease the total voting power", async () => { - const lastBlock = await mineBlocks(1) - expect(await tokenStaking.getPastTotalSupply(lastBlock - 1)).to.equal( - tAmount - ) - }) - }) - }) - - describe("unstakeNu", () => { - context("when staking provider has no stake", () => { - it("should revert", async () => { - await expect( - tokenStaking.unstakeNu(deployer.address) - ).to.be.revertedWith("Not owner or provider") - }) - }) - - context("when caller is not owner or staking provider", () => { - it("should revert", async () => { - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - 0, - initialStakerBalance - ) - await expect( - tokenStaking.connect(authorizer).unstakeNu(stakingProvider.address) - ).to.be.revertedWith("Not owner or provider") - }) - }) - - context("when stake is only in Keep and T", () => { - it("should revert", async () => { - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - initialStakerBalance, - 0 - ) - - await tToken - .connect(staker) - .approve(tokenStaking.address, initialStakerBalance) - await tokenStaking - .connect(staker) - .topUp(stakingProvider.address, initialStakerBalance) - - await expect( - tokenStaking - .connect(stakingProvider) - .unstakeNu(stakingProvider.address) - ).to.be.revertedWith("Nothing to unstake") - }) - }) - - context("when amount to unstake is more than not authorized", () => { - it("should revert", async () => { - const nuAmount = initialStakerBalance - const nuInTAmount = convertToT(nuAmount, nuRatio).result - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - 0, - nuInTAmount - ) - - const authorized = nuInTAmount.div(3) - await tokenStaking - .connect(authorizer) - .forceIncreaseAuthorization( - stakingProvider.address, - application1Mock.address, - authorized - ) - - await expect( - tokenStaking - .connect(stakingProvider) - .unstakeNu(stakingProvider.address) - ).to.be.revertedWith("NU stake still authorized") - }) - }) - - context("when amount to unstake is less than not authorized", () => { - const tAmount = initialStakerBalance - const nuAmount = initialStakerBalance - const nuInTAmount = convertToT(nuAmount, nuRatio).result - const authorized = tAmount - const expectedNuAmount = 0 - const expectedNuInTAmount = 0 - const expectedUnstaked = nuInTAmount - let tx - let blockTimestamp - - beforeEach(async () => { - await tokenStaking.connect(deployer).setMinimumStakeAmount(1) - await tokenStaking - .connect(deployer) - .approveApplication(application1Mock.address) - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - 0, - nuInTAmount - ) - blockTimestamp = await lastBlockTime() - - await tokenStaking - .connect(staker) - .delegateVoting(stakingProvider.address, delegatee.address) - await tToken.connect(staker).approve(tokenStaking.address, tAmount) - await tokenStaking - .connect(staker) - .topUp(stakingProvider.address, tAmount) - - await tokenStaking - .connect(authorizer) - .increaseAuthorization( - stakingProvider.address, - application1Mock.address, - authorized - ) - - await increaseTime(86400) // +24h - tx = await tokenStaking - .connect(stakingProvider) - .unstakeNu(stakingProvider.address) - }) - - it("should update Nu staked amount", async () => { - await assertStakes( - stakingProvider.address, - tAmount, - Zero, - expectedNuInTAmount - ) - expect(await tokenStaking.stakedNu(stakingProvider.address)).to.equal( - expectedNuAmount - ) - }) - - it("should not update roles", async () => { - expect( - await tokenStaking.rolesOf(stakingProvider.address) - ).to.deep.equal([ - staker.address, - beneficiary.address, - authorizer.address, - ]) - }) - - it("should start staking timestamp", async () => { - expect( - await tokenStaking.getStartStakingTimestamp(stakingProvider.address) - ).to.equal(blockTimestamp) - }) - - it("should decrease available amount to authorize", async () => { - expect( - await tokenStaking.getAvailableToAuthorize( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(0) - }) - - it("should update min staked amount", async () => { - expect( - await tokenStaking.getMinStaked(stakingProvider.address, StakeTypes.T) - ).to.equal(tAmount) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) - }) - - it("should emit Unstaked", async () => { - await expect(tx) - .to.emit(tokenStaking, "Unstaked") - .withArgs(stakingProvider.address, expectedUnstaked) - }) - - it("should decrease the delegatee voting power", async () => { - expect(await tokenStaking.getVotes(delegatee.address)).to.equal(tAmount) - }) - - it("should decrease the total voting power", async () => { - const lastBlock = await mineBlocks(1) - expect(await tokenStaking.getPastTotalSupply(lastBlock - 1)).to.equal( - tAmount - ) - }) - }) - }) - - describe("unstakeAll", () => { - context("when staking provider has no stake", () => { - it("should revert", async () => { - await expect( - tokenStaking.unstakeAll(deployer.address) - ).to.be.revertedWith("Not owner or provider") - }) - }) - - context("when caller is not owner or staking provider", () => { - it("should revert", async () => { - await tToken - .connect(staker) - .approve(tokenStaking.address, initialStakerBalance) - await tokenStaking - .connect(staker) - .stake( - stakingProvider.address, - beneficiary.address, - authorizer.address, - initialStakerBalance - ) - await expect( - tokenStaking.connect(authorizer).unstakeAll(stakingProvider.address) - ).to.be.revertedWith("Not owner or provider") - }) - }) - - context("when authorized amount is not zero", () => { - it("should revert", async () => { - const amount = initialStakerBalance - await tokenStaking - .connect(deployer) - .approveApplication(application1Mock.address) - await tToken.connect(staker).approve(tokenStaking.address, amount) - await tokenStaking - .connect(staker) - .stake( - stakingProvider.address, - beneficiary.address, - authorizer.address, - amount - ) - const authorized = 1 - await tokenStaking - .connect(authorizer) - .increaseAuthorization( - stakingProvider.address, - application1Mock.address, - authorized - ) - - await expect( - tokenStaking - .connect(stakingProvider) - .unstakeAll(stakingProvider.address) - ).to.be.revertedWith("Stake still authorized") - }) - }) - - context("when unstake T before minimum staking time passes", () => { - it("should revert", async () => { - const amount = initialStakerBalance - const minAmount = 1 - await tToken.connect(staker).approve(tokenStaking.address, amount) - await tokenStaking - .connect(staker) - .stake( - stakingProvider.address, - beneficiary.address, - authorizer.address, - amount - ) - await tokenStaking.connect(deployer).setMinimumStakeAmount(minAmount) - - await expect( - tokenStaking.connect(staker).unstakeAll(stakingProvider.address) - ).to.be.revertedWith("Can't unstake earlier than 24h") - }) - }) - - context("when unstake Nu before minimum staking time passes", () => { - it("should revert", async () => { - const nuAmount = initialStakerBalance - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake(stakingProvider.address, 0, nuAmount) - - await expect( - tokenStaking.connect(staker).unstakeAll(stakingProvider.address) - ).to.be.revertedWith("Can't unstake earlier than 24h") - }) - }) - - context("when unstake Keep before minimum time passes", () => { - it("should revert", async () => { - const keepAmount = initialStakerBalance - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - keepAmount, - 0 - ) - - await expect( - tokenStaking.connect(staker).unstakeAll(stakingProvider.address) - ).to.be.revertedWith("Can't unstake earlier than 24h") - }) - }) - - const contextUnstakeAll = (preparation, tAmount, nuAmount, keepAmount) => { - const nuInTAmount = convertToT(nuAmount, nuRatio).result - const keepInTAmount = convertToT(keepAmount, keepRatio).result - let tx - let blockTimestamp - - beforeEach(async () => { - blockTimestamp = await preparation() - - tx = await tokenStaking - .connect(stakingProvider) - .unstakeAll(stakingProvider.address) - }) - - it("should update staked amount", async () => { - await assertStakes(stakingProvider.address, Zero, Zero, Zero) - }) - - it("should not update roles", async () => { - expect( - await tokenStaking.rolesOf(stakingProvider.address) - ).to.deep.equal([ - staker.address, - beneficiary.address, - authorizer.address, - ]) - }) - - it("should not update start staking timestamp", async () => { - expect( - await tokenStaking.getStartStakingTimestamp(stakingProvider.address) - ).to.equal(blockTimestamp) - }) - - it("should transfer tokens to the staker address", async () => { - expect(await tToken.balanceOf(tokenStaking.address)).to.equal(0) - expect(await tToken.balanceOf(staker.address)).to.equal( - initialStakerBalance - ) - }) - - it("should decrease available amount to authorize", async () => { - expect( - await tokenStaking.getAvailableToAuthorize( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(0) - }) - - it("should update min staked amount", async () => { - expect( - await tokenStaking.getMinStaked(stakingProvider.address, StakeTypes.T) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.NU - ) - ).to.equal(0) - expect( - await tokenStaking.getMinStaked( - stakingProvider.address, - StakeTypes.KEEP - ) - ).to.equal(0) - }) - - it("should emit Unstaked", async () => { - await expect(tx) - .to.emit(tokenStaking, "Unstaked") - .withArgs( - stakingProvider.address, - nuInTAmount.add(keepInTAmount).add(tAmount) - ) - }) - } - - context( - "when unstake after minimum staking time passes for T stake", - () => { - // subtracting arbitrary values just to keep them different - const tAmount = initialStakerBalance.sub(1) - const nuAmount = initialStakerBalance.sub(2) - const keepAmount = initialStakerBalance.sub(3) - const nuInTAmount = convertToT(nuAmount, nuRatio).result - const keepInTAmount = convertToT(keepAmount, keepRatio).result - - contextUnstakeAll( - async () => { - await tokenStaking - .connect(deployer) - .approveApplication(application1Mock.address) - await tokenStaking.connect(deployer).setMinimumStakeAmount(1) - - // - // stake T - // - await tToken.connect(staker).approve(tokenStaking.address, tAmount) - await tokenStaking - .connect(staker) - .stake( - stakingProvider.address, - beneficiary.address, - authorizer.address, - tAmount - ) - const blockTimestamp = await lastBlockTime() - - await tokenStaking.addLegacyStake( - stakingProvider.address, - keepInTAmount, - nuInTAmount - ) - - await increaseTime(86400) // +24h - return blockTimestamp - }, - tAmount, - nuAmount, - keepAmount - ) - } - ) - - context( - "when unstake after minimum staking time passes for NU and KEEP stake", - () => { - // subtracting arbitrary values just to keep them different - const tAmount = initialStakerBalance.sub(3) - const nuAmount = initialStakerBalance.sub(1) - const keepAmount = initialStakerBalance.sub(2) - const nuInTAmount = convertToT(nuAmount, nuRatio).result - const keepInTAmount = convertToT(keepAmount, keepRatio).result - - contextUnstakeAll( - async () => { - await tokenStaking - .connect(deployer) - .approveApplication(application1Mock.address) - await tokenStaking.connect(deployer).setMinimumStakeAmount(1) + }) - // - // legacy stake NU and KEEP - // - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake( - stakingProvider.address, - keepInTAmount, - nuInTAmount - ) - const blockTimestamp = await lastBlockTime() + it("should not update start staking timestamp", async () => { + expect( + await tokenStaking.getStartStakingTimestamp(stakingProvider.address) + ).to.equal(blockTimestamp) + }) - // - // top-up T - // - await tToken.connect(staker).approve(tokenStaking.address, tAmount) - await tokenStaking - .connect(staker) - .topUp(stakingProvider.address, tAmount) - - await increaseTime(86400) // +24h - return blockTimestamp - }, - tAmount, - nuAmount, - keepAmount - ) - } - ) + it("should transfer tokens to the staker address", async () => { + expect(await tToken.balanceOf(tokenStaking.address)).to.equal(0) + expect(await tToken.balanceOf(staker.address)).to.equal(amount) + }) + + it("should emit Unstaked", async () => { + await expect(tx) + .to.emit(tokenStaking, "Unstaked") + .withArgs(stakingProvider.address, amount) + }) + }) }) describe("setNotificationReward", () => { @@ -4336,12 +3020,7 @@ describe("TokenStaking", () => { }) it("should update staked amount", async () => { - await assertStakes( - stakingProvider.address, - expectedAmount, - Zero, - Zero - ) + await assertStake(stakingProvider.address, expectedAmount) }) it("should decrease the delegatee voting power", async () => { @@ -4462,7 +3141,7 @@ describe("TokenStaking", () => { }) it("should update staked amount", async () => { - await assertStakes(otherStaker.address, Zero, Zero, Zero) + await assertStake(otherStaker.address, Zero) }) it("should update index of queue", async () => { @@ -4587,12 +3266,12 @@ describe("TokenStaking", () => { await increaseTime(86400) // +24h await tokenStaking .connect(stakingProvider) - .unstakeAll(stakingProvider.address) + .unstakeT(stakingProvider.address, tAmount) tx = await tokenStaking.connect(auxiliaryAccount).processSlashing(1) }) it("should not update staked amount", async () => { - await assertStakes(stakingProvider.address, Zero, Zero, Zero) + await assertStake(stakingProvider.address, Zero) }) it("should update index of queue", async () => { @@ -4723,10 +3402,7 @@ describe("TokenStaking", () => { const ExtendedTokenStaking = await ethers.getContractFactory( "ExtendedTokenStaking" ) - extendedTokenStaking = await ExtendedTokenStaking.deploy( - tToken.address, - nucypherVendingMachine.address - ) + extendedTokenStaking = await ExtendedTokenStaking.deploy(tToken.address) await extendedTokenStaking.deployed() }) @@ -4843,324 +3519,10 @@ describe("TokenStaking", () => { ) }) - describe("forceUnstakeLegacy", () => { - const tAmount = initialStakerBalance - const keepInTStake = convertToT(initialStakerBalance, keepRatio).result - const nuInTStake = convertToT(initialStakerBalance, nuRatio).result - - beforeEach(async () => { - await tokenStaking - .connect(deployer) - .approveApplication(application1Mock.address) - await tokenStaking - .connect(deployer) - .approveApplication(application2Mock.address) - await tToken - .connect(staker) - .approve(tokenStaking.address, initialStakerBalance) - await tokenStaking - .connect(staker) - .stake(stakingProvider.address, staker.address, staker.address, tAmount) - }) - - context("when no legacy stake", () => { - it("should revert", async () => { - await tokenStaking - .connect(staker) - .increaseAuthorization( - stakingProvider.address, - application1Mock.address, - tAmount - ) - await expect( - tokenStaking["forceUnstakeLegacy(address)"](stakingProvider.address) - ).to.be.revertedWith("No legacy stake") - }) - }) - - context("when authorized only T stake", () => { - let tx - - beforeEach(async () => { - await tokenStaking.addLegacyStake( - stakingProvider.address, - keepInTStake, - nuInTStake - ) - await tokenStaking - .connect(staker) - .increaseAuthorization( - stakingProvider.address, - application1Mock.address, - tAmount - ) - await tokenStaking - .connect(staker) - .delegateVoting(stakingProvider.address, delegatee.address) - - tx = await tokenStaking["forceUnstakeLegacy(address)"]( - stakingProvider.address - ) - }) - - it("should update staked amount", async () => { - await assertStakes(stakingProvider.address, tAmount, Zero, Zero) - }) - - it("should decrease the delegatee voting power", async () => { - expect(await tokenStaking.getVotes(delegatee.address)).to.equal(tAmount) - }) - - it("should not decrease authorized amounts", async () => { - expect( - await tokenStaking.authorizedStake( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(tAmount) - }) - - it("should not inform application", async () => { - await assertApplicationStakingProviders( - application1Mock, - stakingProvider.address, - tAmount, - Zero - ) - }) - - it("should emit Unstaked event", async () => { - await expect(tx) - .to.emit(tokenStaking, "Unstaked") - .withArgs(stakingProvider.address, nuInTStake.add(keepInTStake)) - }) - }) - - context("when authorized T, KEEP and NU stakes", () => { - const authorized1 = tAmount.add(keepInTStake).add(nuInTStake) - const authorized2 = nuInTStake.add(keepInTStake) - const deauth = authorized1.sub(tAmount).sub(1) - let tx - - beforeEach(async () => { - await tokenStaking.addLegacyStake( - stakingProvider.address, - keepInTStake, - nuInTStake - ) - await tokenStaking - .connect(staker) - .forceIncreaseAuthorization( - stakingProvider.address, - application1Mock.address, - authorized1 - ) - await tokenStaking - .connect(staker) - .forceIncreaseAuthorization( - stakingProvider.address, - application2Mock.address, - authorized2 - ) - await tokenStaking - .connect(staker) - ["requestAuthorizationDecrease(address,address,uint96)"]( - stakingProvider.address, - application1Mock.address, - deauth - ) - await tokenStaking - .connect(staker) - .delegateVoting(stakingProvider.address, delegatee.address) - - tx = await tokenStaking["forceUnstakeLegacy(address)"]( - stakingProvider.address - ) - }) - - it("should update staked amount", async () => { - await assertStakes(stakingProvider.address, tAmount, Zero, Zero) - }) - - it("should decrease the delegatee voting power", async () => { - expect(await tokenStaking.getVotes(delegatee.address)).to.equal(tAmount) - }) - - it("should decrease authorized amounts", async () => { - expect( - await tokenStaking.authorizedStake( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(tAmount) - expect( - await tokenStaking.authorizedStake( - stakingProvider.address, - application2Mock.address - ) - ).to.equal(tAmount) - }) - - it("should inform application", async () => { - await assertApplicationStakingProviders( - application1Mock, - stakingProvider.address, - tAmount, - Zero - ) - await assertApplicationStakingProviders( - application2Mock, - stakingProvider.address, - tAmount, - Zero - ) - }) - - it("should emit Unstaked and AuthorizationInvoluntaryDecreased event", async () => { - await expect(tx) - .to.emit(tokenStaking, "Unstaked") - .withArgs(stakingProvider.address, nuInTStake.add(keepInTStake)) - await expect(tx) - .to.emit(tokenStaking, "AuthorizationInvoluntaryDecreased") - .withArgs( - stakingProvider.address, - application1Mock.address, - authorized1, - tAmount, - true - ) - await expect(tx) - .to.emit(tokenStaking, "AuthorizationInvoluntaryDecreased") - .withArgs( - stakingProvider.address, - application2Mock.address, - authorized2, - tAmount, - true - ) - }) - }) - - context("when unstake multiple legacy stakes", () => { - let tx - - beforeEach(async () => { - await tokenStaking.addLegacyStake( - stakingProvider.address, - keepInTStake, - 0 - ) - await tokenStaking.setLegacyStakingProvider( - stakingProvider.address, - staker.address, - beneficiary.address, - authorizer.address - ) - await tokenStaking.addLegacyStake(otherStaker.address, 0, nuInTStake) - - await tokenStaking - .connect(staker) - .forceIncreaseAuthorization( - stakingProvider.address, - application1Mock.address, - tAmount.add(keepInTStake) - ) - await tokenStaking - .connect(staker) - .forceIncreaseAuthorization( - otherStaker.address, - application1Mock.address, - nuInTStake - ) - - tx = await tokenStaking["forceUnstakeLegacy(address[])"]([ - stakingProvider.address, - otherStaker.address, - ]) - }) - - it("should update staked amount", async () => { - await assertStakes(stakingProvider.address, tAmount, Zero, Zero) - await assertStakes(otherStaker.address, Zero, Zero, Zero) - }) - - it("should decrease authorized amounts", async () => { - expect( - await tokenStaking.authorizedStake( - stakingProvider.address, - application1Mock.address - ) - ).to.equal(tAmount) - expect( - await tokenStaking.authorizedStake( - otherStaker.address, - application1Mock.address - ) - ).to.equal(Zero) - }) - - it("should inform application", async () => { - await assertApplicationStakingProviders( - application1Mock, - stakingProvider.address, - tAmount, - Zero - ) - await assertApplicationStakingProviders( - application1Mock, - otherStaker.address, - Zero, - Zero - ) - }) - - it("should emit Unstaked and AuthorizationInvoluntaryDecreased event", async () => { - await expect(tx) - .to.emit(tokenStaking, "Unstaked") - .withArgs(stakingProvider.address, keepInTStake) - await expect(tx) - .to.emit(tokenStaking, "Unstaked") - .withArgs(otherStaker.address, nuInTStake) - await expect(tx) - .to.emit(tokenStaking, "AuthorizationInvoluntaryDecreased") - .withArgs( - stakingProvider.address, - application1Mock.address, - tAmount.add(keepInTStake), - tAmount, - true - ) - await expect(tx) - .to.emit(tokenStaking, "AuthorizationInvoluntaryDecreased") - .withArgs( - otherStaker.address, - application1Mock.address, - nuInTStake, - Zero, - true - ) - }) - }) - }) - - async function assertStakes( - address, - expectedTStake, - expectedKeepInTStake, - expectedNuInTStake - ) { - expect( - (await tokenStaking.stakes(address)).tStake, - "invalid tStake" - ).to.equal(expectedTStake) - expect( - (await tokenStaking.stakes(address)).keepInTStake, - "invalid keepInTStake" - ).to.equal(expectedKeepInTStake) - expect( - (await tokenStaking.stakes(address)).nuInTStake, - "invalid nuInTStake" - ).to.equal(expectedNuInTStake) + async function assertStake(address, expectedTStake) { + expect(await tokenStaking.tStake(address), "invalid tStake").to.equal( + expectedTStake + ) } async function assertApplicationStakingProviders(