diff --git a/contracts/dao/staking/interfaces/IStakingGetter.sol b/contracts/dao/staking/interfaces/IStakingGetter.sol index 4dba3c5..76bb695 100644 --- a/contracts/dao/staking/interfaces/IStakingGetter.sol +++ b/contracts/dao/staking/interfaces/IStakingGetter.sol @@ -17,4 +17,6 @@ interface IStakingGetter { function getStream( uint256 streamId ) external view returns (uint256 rewardDepositAmount, uint256 rewardClaimedAmount, uint256 rps, StreamStatus status); + + function isProhibitedLockPosition(uint256 lockId, address account) external view returns (bool); } diff --git a/contracts/dao/staking/packages/RewardsInternals.sol b/contracts/dao/staking/packages/RewardsInternals.sol index 36bfd29..81415f5 100644 --- a/contracts/dao/staking/packages/RewardsInternals.sol +++ b/contracts/dao/staking/packages/RewardsInternals.sol @@ -15,6 +15,7 @@ contract RewardsInternals is StakingStorage, IStakingEvents { error InsufficientRewardsError(); error NoLockError(); error NoSharesError(); + error ClaimingOfRewardsUnfeasibleForLockWithoutEarlyWithdraw(); function _updateStreamsRewardsSchedules(uint256 streamId, uint256 rewardTokenAmount) internal { uint256 streamScheduleRewardLength = streams[streamId].schedule.reward.length; @@ -28,6 +29,11 @@ contract RewardsInternals is StakingStorage, IStakingEvents { revert InactiveStreamError(); } LockedBalance storage lock = locks[account][lockId - 1]; + + if (prohibitedEarlyWithdraw[account][lockId] && lock.end > block.timestamp) { + return; + } + if (lock.amountOfToken == 0) { revert NoStakeError(); } diff --git a/contracts/dao/staking/packages/StakingGetters.sol b/contracts/dao/staking/packages/StakingGetters.sol index 1293549..7e727df 100644 --- a/contracts/dao/staking/packages/StakingGetters.sol +++ b/contracts/dao/staking/packages/StakingGetters.sol @@ -45,4 +45,11 @@ contract StakingGetters is StakingStorage, IStakingGetter, StakingInternals { Stream storage stream = streams[streamId]; return (stream.rewardDepositAmount, stream.rewardClaimedAmount, stream.rps, stream.status); } + + /** + @notice this will be used by frontend to get the actual amount that can be claimed + */ + function isProhibitedLockPosition(uint256 lockId, address account) external view override returns (bool) { + return prohibitedEarlyWithdraw[account][lockId]; + } } diff --git a/coralX-scenarios.js b/coralX-scenarios.js index 1484e74..91734f2 100644 --- a/coralX-scenarios.js +++ b/coralX-scenarios.js @@ -22,6 +22,14 @@ module.exports = { ['execute', '--path', 'scripts/migrations/save-address/3_save_address_prod.js', '--network', 'xdc'] ], + deployUpgradedStakingContractApothem: [ + ['execute', '--path', 'scripts/migrations/deploy-new-staking-package', '--network', 'apothem'], + ], + + deployUpgradedStakingContractXDC: [ + ['execute', '--path', 'scripts/migrations/deploy-new-staking-package', '--network', 'xdc'], + ], + transferTokenFromMultisigApothem: [ ['execute', '--path', 'scripts/units/transfer-tokens.js', '--network', 'apothem'], ], diff --git a/package.json b/package.json index 6667391..5eb70a2 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,13 @@ "migrate-reset-apothem-dev": "export NODE_ENV=dev && rm -rf build/contracts && rm -rf build/job-logs && coralX compile && coralX scenario --run deployApothem", "migrate-reset-apothem-demo": "export NODE_ENV=demo && rm -rf build/contracts && rm -rf build/job-logs && coralX compile && coralX scenario --run deployApothem", "migrate-reset-apothem-qa": "export NODE_ENV=qa && rm -rf build/contracts && rm -rf build/job-logs && coralX compile && coralX scenario --run deployApothem", - "migrate-reset-xdc": "export NODE_ENV=prod && rm -rf build/contracts && rm -rf build/job-logs && coralX compile && coralX scenario --run deployXDC" + "migrate-reset-xdc": "export NODE_ENV=prod && rm -rf build/contracts && rm -rf build/job-logs && coralX compile && coralX scenario --run deployXDC", + "migrate-reset-staking-upgrade-apothem-dev": "export NODE_ENV=dev && rm -rf build/contracts && rm -rf build/job-logs && coralX compile && coralX scenario --run deployUpgradedStakingContractApothem", + "migrate-reset-staking-upgrade-apothem-demo": "export NODE_ENV=demo && rm -rf build/contracts && rm -rf build/job-logs && coralX compile && coralX scenario --run deployUpgradedStakingContractApothem", + "migrate-reset-staking-upgrade-apothem-qa": "export NODE_ENV=qa && rm -rf build/contracts && rm -rf build/job-logs && coralX compile && coralX scenario --run deployUpgradedStakingContractApothem", + "migrate-reset-staking-upgrade-xdc": "export NODE_ENV=prod && rm -rf build/contracts && rm -rf build/job-logs && coralX compile && coralX scenario --run deployUpgradedStakingContractXDC" }, + "author": "fathom.fi", "license": "AGPL 3.0", "dependencies": { diff --git a/scripts/migrations/deploy-new-staking-package/1_deploy_staking_package.js b/scripts/migrations/deploy-new-staking-package/1_deploy_staking_package.js new file mode 100644 index 0000000..bcb59a3 --- /dev/null +++ b/scripts/migrations/deploy-new-staking-package/1_deploy_staking_package.js @@ -0,0 +1,23 @@ +const PackageStakingImplementation = artifacts.require('./dao/staking/packages/StakingPackage.sol'); +const fs = require("fs"); + +module.exports = async function(deployer) { + let promises = [ + deployer.deploy(PackageStakingImplementation, {gas: 8000000}), + ] + await Promise.all(promises); + + let addresses = { + newStakingPackageImplementation: PackageStakingImplementation.address + } + + let env = process.env.NODE_ENV || 'demo'; + let filePath = `./addresses.new-staking-package.${env}.json`; + + let data = JSON.stringify(addresses, null, " "); + fs.writeFileSync(filePath, data, function (err) { + if (err) { + console.log(err); + } + }); +} \ No newline at end of file diff --git a/scripts/migrations/deploy-new-staking-package/2_staking_upgrade.js b/scripts/migrations/deploy-new-staking-package/2_staking_upgrade.js new file mode 100644 index 0000000..d19cd11 --- /dev/null +++ b/scripts/migrations/deploy-new-staking-package/2_staking_upgrade.js @@ -0,0 +1,51 @@ +const constants = require("../../tests/helpers/testConstants"); +const eventsHelper = require("../../tests/helpers/eventsHelper"); + +const PackageStakingImplementation = artifacts.require('./dao/staking/packages/StakingPackage.sol'); +const fs = require("fs"); +const { EMPTY_BYTES } = require("../../tests/helpers/testConstants"); +let env = process.env.NODE_ENV; +let addressesFilePath = `../../../addresses.${env}.json`; +const rawdata = fs.readFileSync(addressesFilePath); +const addresses = JSON.parse(rawdata); +const IMultiSigWallet = artifacts.require("./dao/treasury/interfaces/IMultiSigWallet.sol"); + +const MultisigWalletAddress = addresses.multiSigWallet; +const StakingProxyAddress = addresses.staking; +const StakingProxyAdminAddress = addresses.stakingProxyAdmin; +const SUBMIT_TRANSACTION_EVENT = constants.SUBMIT_TRANSACTION_EVENT + +const _encodeUpgradeFunction = (_proxy, _impl) => { + let toRet = web3.eth.abi.encodeFunctionCall({ + name: 'upgrade', + type: 'function', + inputs: [{ + type: 'address', + name: 'proxy' + },{ + type: 'address', + name: 'implementation' + }] + }, [_proxy, _impl]); + + return toRet; +} + +module.exports = async function(deployer) { + const multiSigWallet = await IMultiSigWallet.at(MultisigWalletAddress) + + let resultUpgrade = await multiSigWallet.submitTransaction( + StakingProxyAdminAddress, + EMPTY_BYTES, + _encodeUpgradeFunction( + StakingProxyAddress, + PackageStakingImplementation.address + ), + 0, + {gas: 8000000} + ) + + let txnIndexUpgrade = eventsHelper.getIndexedEventArgs(resultUpgrade, SUBMIT_TRANSACTION_EVENT)[0]; + await multiSigWallet.confirmTransaction(txnIndexUpgrade, {gas: 8000000}); + await multiSigWallet.executeTransaction(txnIndexUpgrade, {gas: 8000000}); +} \ No newline at end of file diff --git a/scripts/migrations/test/6_init_main_stream.js b/scripts/migrations/test/6_init_main_stream.js index 5978078..7040e8d 100644 --- a/scripts/migrations/test/6_init_main_stream.js +++ b/scripts/migrations/test/6_init_main_stream.js @@ -62,17 +62,11 @@ module.exports = async function(deployer) { const oneYear = 31556926; const scheduleTimes = [ startTime, - startTime + oneYear, - startTime + 2 * oneYear, - startTime + 3 * oneYear, startTime + 4 * oneYear, ]; const scheduleRewards = [ - web3.utils.toWei('20000', 'ether'), - web3.utils.toWei('10000', 'ether'), - web3.utils.toWei('5000', 'ether'), - web3.utils.toWei('2500', 'ether'), + web3.utils.toWei('200000000', 'ether'), web3.utils.toWei("0", 'ether') ]; diff --git a/scripts/tests/dao/staking/staking-criticals.test.js b/scripts/tests/dao/staking/staking-criticals.test.js index 437a38e..0ac4d1c 100644 --- a/scripts/tests/dao/staking/staking-criticals.test.js +++ b/scripts/tests/dao/staking/staking-criticals.test.js @@ -211,6 +211,53 @@ const _encodeProposeStreamFunction = ( return toRet; } +const _encodeCreateLockWithoutEarlyWithdrawal = (_createLockParam) => { + // encoded transfer function call for staking on behalf of someone else from treasury. + let toRet = web3.eth.abi.encodeFunctionCall({ + name:'createFixedLocksOnBehalfOfUserByAdmin', + type:'function', + inputs: [{ + type: 'tuple[]', + name: 'lockPositions', + components: [ + {"type":"uint256", "name":"amount"}, + {"type":"uint256", "name":"lockPeriod"}, + {"type":"address", "name":"account"} + ] + } + ] + },[_createLockParam]) + return toRet +} + + +const _createLockParamObject = ( + _amount, + _lockPeriod, + _account) => { + return { + amount: _amount, + lockPeriod: _lockPeriod, + account: _account + } +} + +const _encodeStakeApproveFunction = (amount, spender) => { + // approve(address spender, uint256 amount) + let toRet = web3.eth.abi.encodeFunctionCall({ + name: 'approve', + type: 'function', + inputs: [{ + type: 'address', + name: 'spender' + },{ + type: 'uint256', + name: 'amount' + }] + }, [spender, amount]); + + return toRet; +} describe("Staking Test, Upgrade Test and Emergency Scenarios", () => { @@ -376,7 +423,7 @@ describe("Staking Test, Upgrade Test and Emergency Scenarios", () => { expectedTotalAmountOfVFTHM = new web3.utils.BN(0) it('Should create a lock possition with lockId = 1 for staker_1', async() => { // So that staker 1 can actually stake the token: - await FTHMToken.approve(stakingService.address, sumToApprove, {from: staker_1}) + await FTHMToken.approve(stakingService.address, sumToApprove, {from: staker_1}) const beforeFTHMBalance = await FTHMToken.balanceOf(staker_1); await blockchain.increaseTime(20); @@ -385,7 +432,7 @@ describe("Staking Test, Upgrade Test and Emergency Scenarios", () => { const unlockTime = lockingPeriod; console.log(".........Creating a Lock Position for staker 1........."); - let result = await stakingService.createLock(sumToDeposit,unlockTime,{from: staker_1}); + let result = await stakingService.createLock(sumToDeposit,unlockTime,{from: staker_1}); // staker_1 locks lock position - 1 // Since block time stamp can change after locking, we record the timestamp, // later to be used in the expectedNVFTHM calculation. // This mitigates an error created from the slight change in block time. @@ -420,7 +467,7 @@ describe("Staking Test, Upgrade Test and Emergency Scenarios", () => { const unlockTime = lockingPeriod; console.log(".........Creating a second Lock Position for staker 1........."); - let result = await stakingService.createLock(sumToDeposit, unlockTime, {from: staker_1, gas:maxGasForTxn}); + let result = await stakingService.createLock(sumToDeposit, unlockTime, {from: staker_1, gas:maxGasForTxn}); //staker_1 creates a lock position - 2 let eventArgs = eventsHelper.getIndexedEventArgs(result, "Staked(address,uint256,uint256,uint256,uint256,uint256)"); const lockInfo = await stakingGetterService.getLockInfo(staker_1,2) @@ -459,9 +506,9 @@ describe("Staking Test, Upgrade Test and Emergency Scenarios", () => { await blockchain.mineBlock(await _getTimeStamp() + 20); console.log(".........Creating a Lock Position for staker 2 and Staker 3......."); - let result1 = await stakingService.createLock(sumToDepositForAll,unlockTime, {from: staker_2}); + let result1 = await stakingService.createLock(sumToDepositForAll,unlockTime, {from: staker_2}); //staker_2 creates a lock position - 1 await blockchain.mineBlock(await _getTimeStamp() + 20); - let result2 = await stakingService.createLock(sumToDepositForAll,unlockTime,{from: staker_3}); + let result2 = await stakingService.createLock(sumToDepositForAll,unlockTime,{from: staker_3}); //staker_3 creates a lock position - 1 await blockchain.mineBlock(await _getTimeStamp() + 20); let eventArgs1 = eventsHelper.getIndexedEventArgs(result1, "Staked(address,uint256,uint256,uint256,uint256,uint256)"); @@ -471,6 +518,7 @@ describe("Staking Test, Upgrade Test and Emergency Scenarios", () => { assert.equal(eventArgs1[1].toString(),expectedLockId) assert.equal(eventArgs2[1].toString(),expectedLockId) }) + it("Should not unlock locked position before the end of the lock position's lock period - staker_1", async() => { @@ -494,6 +542,298 @@ describe("Staking Test, Upgrade Test and Emergency Scenarios", () => { console.log(".........Unlocking All The Lock Positions created till Now..........") }) + it('Approve FATHOM tokens to staking contracy', async() => { + const approveAmount = web3.utils.toWei('800000', 'ether'); + + const _approve = async ( + _amount, + _spender + ) => { + const result = await multiSigWallet.submitTransaction( + FTHMToken.address, + EMPTY_BYTES, + _encodeStakeApproveFunction(_amount, _spender), + 0, + {"from": accounts[0]} + ); + const tx = eventsHelper.getIndexedEventArgs(result, SUBMIT_TRANSACTION_EVENT)[0]; + + await multiSigWallet.confirmTransaction(tx, {"from": accounts[0]}); + await multiSigWallet.confirmTransaction(tx, {"from": accounts[1]}); + + await multiSigWallet.executeTransaction(tx, {"from": accounts[1]}); + } + + await _approve( + approveAmount, + stakingService.address + ) + }) + it('Should create early unwithdrawable lock position for staker_1, staker_2', async() => { + const oneYr = 365 * 24 * 60 * 60; + const amount = web3.utils.toWei('200000', 'ether'); + + const lockPositionForCommityOne = _createLockParamObject(amount,oneYr,staker_2) //staker_2 creates a lock position - 2 + const lockPositionForCommityTwo = _createLockParamObject(amount,oneYr,staker_3) //staker_3 creates a lock position - 2 + const allLockPositions = [lockPositionForCommityOne, lockPositionForCommityTwo] + + const _createLockWithoutEarlyWithdrawal = async( + lockParamObject + ) => { + const result = await multiSigWallet.submitTransaction( + stakingService.address, + EMPTY_BYTES, + _encodeCreateLockWithoutEarlyWithdrawal( + lockParamObject + ), + 0, + {"from": accounts[0]} + ) + + const tx = eventsHelper.getIndexedEventArgs(result, SUBMIT_TRANSACTION_EVENT)[0]; + + await multiSigWallet.confirmTransaction(tx, {"from": accounts[0]}); + await multiSigWallet.confirmTransaction(tx, {"from": accounts[1]}); + + await multiSigWallet.executeTransaction(tx, {"from": accounts[1]}); + } + await blockchain.mineBlock(await _getTimeStamp() + 100) + await _createLockWithoutEarlyWithdrawal(allLockPositions); + await blockchain.mineBlock(await _getTimeStamp() + 100) + }) + + it('Should create another early unwithrawable lock position for staker_1, staker_2', async() => { + const oneYr = 365 * 24 * 60 * 60; + const amount = web3.utils.toWei('200000', 'ether'); + + + const lockPositionForCommityOne = _createLockParamObject(amount,oneYr,staker_2) //staker_2 creates a lock position - 3 + const lockPositionForCommityTwo = _createLockParamObject(amount,oneYr,staker_3) //staker_3 creates a lock position - 3 + const allLockPositions = [lockPositionForCommityOne, lockPositionForCommityTwo] + + const _createLockWithoutEarlyWithdrawal = async( + lockParamObject + ) => { + const result = await multiSigWallet.submitTransaction( + stakingService.address, + EMPTY_BYTES, + _encodeCreateLockWithoutEarlyWithdrawal( + lockParamObject + ), + 0, + {"from": accounts[0]} + ) + + const tx = eventsHelper.getIndexedEventArgs(result, SUBMIT_TRANSACTION_EVENT)[0]; + + await multiSigWallet.confirmTransaction(tx, {"from": accounts[0]}); + await multiSigWallet.confirmTransaction(tx, {"from": accounts[1]}); + + await multiSigWallet.executeTransaction(tx, {"from": accounts[1]}); + } + await blockchain.mineBlock(await _getTimeStamp() + 100) + await _createLockWithoutEarlyWithdrawal(allLockPositions); + await blockchain.mineBlock(await _getTimeStamp() + 100) + }) + + it('Approve FATHOM tokens to staking contract for creating 4 lock positions of 20000 each', async() => { + const approveAmount = web3.utils.toWei('800000', 'ether'); + + const _approve = async ( + _amount, + _spender + ) => { + const result = await multiSigWallet.submitTransaction( + FTHMToken.address, + EMPTY_BYTES, + _encodeStakeApproveFunction(_amount, _spender), + 0, + {"from": accounts[0]} + ); + const tx = eventsHelper.getIndexedEventArgs(result, SUBMIT_TRANSACTION_EVENT)[0]; + + await multiSigWallet.confirmTransaction(tx, {"from": accounts[0]}); + await multiSigWallet.confirmTransaction(tx, {"from": accounts[1]}); + + await multiSigWallet.executeTransaction(tx, {"from": accounts[1]}); + } + + await _approve( + approveAmount, + stakingService.address + ) + }) + + it('Should create early unwithrawable lock position for staker_1 - staker_2 - staker-3 staker -4 all at once', async() => { + const oneYr = 365 * 24 * 60 * 60; + const amount = web3.utils.toWei('200000', 'ether'); + + + const lockPositionForCommityOne = _createLockParamObject(amount,oneYr,staker_1) //staker_1 creates a lock position - 2 + const lockPositionForCommityTwo = _createLockParamObject(amount,oneYr,staker_2) //staker_2 creates a lock position - 4 + const lockPositionForComityThree = _createLockParamObject(amount,oneYr,staker_3) //staker_3 creates a lock position - 4 + const lockPositionForCommityFour = _createLockParamObject(amount,oneYr,staker_4) //staker_4 creates a lock position - 1 + const allLockPositions = [lockPositionForCommityOne, lockPositionForCommityTwo, lockPositionForComityThree, lockPositionForCommityFour] + + const _createLockWithoutEarlyWithdrawal = async( + lockParamObject + ) => { + const result = await multiSigWallet.submitTransaction( + stakingService.address, + EMPTY_BYTES, + _encodeCreateLockWithoutEarlyWithdrawal( + lockParamObject + ), + 0, + {"from": accounts[0]} + ) + + const tx = eventsHelper.getIndexedEventArgs(result, SUBMIT_TRANSACTION_EVENT)[0]; + + await multiSigWallet.confirmTransaction(tx, {"from": accounts[0]}); + await multiSigWallet.confirmTransaction(tx, {"from": accounts[1]}); + + await multiSigWallet.executeTransaction(tx, {"from": accounts[1]}); + } + await blockchain.mineBlock(await _getTimeStamp() + 100) + await _createLockWithoutEarlyWithdrawal(allLockPositions); + await blockchain.mineBlock(await _getTimeStamp() + 100) + }) + + + + it("Should Revert: early unlock not possible staker_2 - lock position -2,3", async() => { + await blockchain.mineBlock(await _getTimeStamp() + 20); + const errorMessage = "revert"; + + await shouldRevertAndHaveSubstring( + stakingService.earlyUnlock(2, {from: staker_2}), + errTypes.revert, + errorMessage + ); + + await blockchain.mineBlock(await _getTimeStamp() + 20); + + await shouldRevertAndHaveSubstring( + stakingService.earlyUnlock(3, {from: staker_2}), + errTypes.revert, + errorMessage + ); + + await blockchain.mineBlock(await _getTimeStamp() + 20); + + await shouldRevertAndHaveSubstring( + stakingService.earlyUnlock(4, {from: staker_2}), + errTypes.revert, + errorMessage + ); + + + }) + + it("Should Revert: early unlock not possible - staker_3 - lock position 2,-3", async() => { + + await blockchain.mineBlock(await _getTimeStamp() + 20); + const errorMessage = "revert"; + + await shouldRevertAndHaveSubstring( + stakingService.earlyUnlock(2, {from: staker_3}), + errTypes.revert, + errorMessage + ); + + await blockchain.mineBlock(await _getTimeStamp() + 20); + + await shouldRevertAndHaveSubstring( + stakingService.earlyUnlock(3, {from: staker_3}), + errTypes.revert, + errorMessage + ); + + await blockchain.mineBlock(await _getTimeStamp() + 20); + + await shouldRevertAndHaveSubstring( + stakingService.earlyUnlock(4, {from: staker_3}), + errTypes.revert, + errorMessage + ); + + }) + + it("Should Revert: early unlock not possible - staker_1 and staker_4 - lock position -3 and lock position - 1, respectively", async() => { + + await blockchain.mineBlock(await _getTimeStamp() + 20); + const errorMessage = "revert"; + + await shouldRevertAndHaveSubstring( + stakingService.earlyUnlock(2, {from: staker_1}), + errTypes.revert, + errorMessage + ); + + await blockchain.mineBlock(await _getTimeStamp() + 20); + + await shouldRevertAndHaveSubstring( + stakingService.earlyUnlock(1, {from: staker_4}), + errTypes.revert, + errorMessage + ); + + }) + + it('Should not claim rewards for early unlock - staker - 2', async() => { + const MAIN_STREAM_ID = 0 + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.withdrawAllStreams({from: staker_2}) + const mineSixMonthsToCheckIfRewardsCouldBeClaimed = 6 * 30 * 24 * 60 * 60; + await blockchain.mineBlock(await _getTimeStamp() + mineSixMonthsToCheckIfRewardsCouldBeClaimed); + await stakingService.claimAllStreamRewardsForLock(2,{from: staker_2}) + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.claimAllStreamRewardsForLock(3,{from: staker_2}) + const userPendingRewardsShouldBeZeroAfterClaim = await stakingService.getUsersPendingRewards(staker_2,MAIN_STREAM_ID) + assert.equal(userPendingRewardsShouldBeZeroAfterClaim.toString(), "0") + }) + + it('Should not claim rewards for early unlock - staker - 3', async() => { + const MAIN_STREAM_ID = 0 + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.withdrawAllStreams({from: staker_3}) + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.claimAllStreamRewardsForLock(2,{from: staker_3}) + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.claimAllStreamRewardsForLock(3,{from: staker_3}) + const userPendingRewardsShouldBeZeroAfterClaim = await stakingService.getUsersPendingRewards(staker_3,MAIN_STREAM_ID) + assert.equal(userPendingRewardsShouldBeZeroAfterClaim.toString(), "0") + }) + + + + it("Should be able to claim after lock period expires - staker_2", async() => { + const MAIN_STREAM_ID = 0 + await blockchain.mineBlock(await _getTimeStamp() + 20); + const mineEightMonthsToCheckIfRewardsCouldBeClaimed = 8 * 30 * 24 * 60 * 60; + await blockchain.mineBlock(await _getTimeStamp() + mineEightMonthsToCheckIfRewardsCouldBeClaimed); + await stakingService.claimAllStreamRewardsForLock(2,{from: staker_2}) + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.claimAllStreamRewardsForLock(3,{from: staker_2}) + const userPendingRewardsShouldBeMoreThanZeroAfterClaim = await stakingService.getUsersPendingRewards(staker_2,MAIN_STREAM_ID) + console.log("usersPendingRewards: ", _convertToEtherBalance(userPendingRewardsShouldBeMoreThanZeroAfterClaim.toString())); + }) + + it("Should be able to claim after lock period expires - staker_3", async() => { + const MAIN_STREAM_ID = 0 + await blockchain.mineBlock(await _getTimeStamp() + 20); + const mineOneMoreMonthToCheckIfRewardsCouldBeClaimed = 1 * 30 * 24 * 60 * 60; + await blockchain.mineBlock(await _getTimeStamp() + mineOneMoreMonthToCheckIfRewardsCouldBeClaimed); + await stakingService.claimAllStreamRewardsForLock(2,{from: staker_3}) + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.claimAllStreamRewardsForLock(3,{from: staker_3}) + const userPendingRewardsShouldBeMoreThanZeroAfterClaim = await stakingService.getUsersPendingRewards(staker_3,MAIN_STREAM_ID) + console.log("usersPendingRewards: ", _convertToEtherBalance(userPendingRewardsShouldBeMoreThanZeroAfterClaim.toString())); + }) + + + it('Should upgrade Staking by mulitsig and call new function getLockInfo', async() => { await blockchain.mineBlock(await _getTimeStamp() + 20); const _proposeUpgrade = async ( @@ -567,17 +907,36 @@ describe("Staking Test, Upgrade Test and Emergency Scenarios", () => { await blockchain.mineBlock(await _getTimeStamp() + 20); await stakingService.unlock(1, {from: staker_2}); await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.unlock(1, {from: staker_2}); + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.unlock(1, {from: staker_2}); + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.unlock(1, {from: staker_2}); + await blockchain.mineBlock(await _getTimeStamp() + 20); }) it("Should unlock completely locked positions for user - staker_3", async() => { await stakingService.unlock(1, {from: staker_3}); await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.unlock(1, {from: staker_3}); + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.unlock(1, {from: staker_3}); + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.unlock(1, {from: staker_3}); }); + + + it("Should unlock completely locked positions for user - staker_4", async() => { + await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.unlock(1, {from: staker_4}); + }) + it("Should unlock completely for locked position 1 - staker_1", async() => { await blockchain.mineBlock(await _getTimeStamp() + 20); await stakingService.unlock(1, {from: staker_1}); await blockchain.mineBlock(await _getTimeStamp() + 20); + await stakingService.unlock(1, {from: staker_1}); const totalAmountOfStakedToken = await stakingService.totalAmountOfStakedToken() const totalAmountOfStreamShares = await stakingService.totalStreamShares() assert.equal(totalAmountOfStakedToken.toString(),"0") @@ -586,6 +945,7 @@ describe("Staking Test, Upgrade Test and Emergency Scenarios", () => { console.log(".........total amount of Staked Protocol Tokens Amount: ", totalAmountOfStakedToken.toString()); console.log(".........total Amount Of Stream Shares: ", totalAmountOfStreamShares.toString()); }); + });