diff --git a/.gas-report b/.gas-report index de53918..3ffba47 100644 --- a/.gas-report +++ b/.gas-report @@ -1,34 +1,34 @@ | contracts/StakeManager.sol:StakeManager contract | | | | | | |--------------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 2931383 | 14953 | | | | | +| 2469345 | 12797 | | | | | | Function Name | min | avg | median | max | # calls | -| EPOCH_SIZE | 307 | 307 | 307 | 307 | 816 | +| EPOCH_SIZE | 307 | 307 | 307 | 307 | 840 | | MAX_BOOST | 285 | 285 | 285 | 285 | 1 | | MAX_LOCKUP_PERIOD | 361 | 361 | 361 | 361 | 4 | | MIN_LOCKUP_PERIOD | 287 | 287 | 287 | 287 | 12 | | YEAR | 263 | 263 | 263 | 263 | 1 | -| accounts | 1597 | 1597 | 1597 | 1597 | 12704 | +| accounts | 1561 | 1561 | 1561 | 1561 | 11747 | | calculateMPToMint | 740 | 740 | 740 | 740 | 4 | -| currentEpoch | 406 | 1691 | 2406 | 2406 | 28 | -| epochEnd | 627 | 643 | 627 | 4627 | 480 | -| epochReward | 1403 | 2903 | 1403 | 5903 | 3 | -| executeAccount | 28897 | 95856 | 95004 | 171987 | 10265 | -| executeEpoch | 23436 | 170888 | 187183 | 204283 | 419 | -| isVault | 540 | 572 | 540 | 2540 | 7762 | +| currentEpoch | 406 | 1188 | 406 | 2406 | 46 | +| epochEnd | 627 | 642 | 627 | 4627 | 516 | +| epochReward | 1391 | 2891 | 1391 | 5891 | 3 | +| executeAccount | 30958 | 96113 | 95053 | 240555 | 9326 | +| executeEpoch | 23424 | 168331 | 163724 | 203524 | 442 | +| isVault | 540 | 578 | 540 | 2540 | 6810 | | lock | 23840 | 23840 | 23840 | 23840 | 1 | | migrateTo | 23869 | 23875 | 23875 | 23881 | 2 | -| migration | 439 | 1439 | 1439 | 2439 | 4 | -| migrationInitialize | 24602 | 24602 | 24602 | 24602 | 1 | -| owner | 2432 | 2432 | 2432 | 2432 | 13 | +| migration | 415 | 1415 | 1415 | 2415 | 4 | +| migrationInitialize | 24578 | 24578 | 24578 | 24578 | 1 | +| owner | 2408 | 2408 | 2408 | 2408 | 13 | | pendingMPToBeMinted | 364 | 364 | 364 | 364 | 202 | | pendingReward | 364 | 1398 | 2364 | 2364 | 29 | -| previousManager | 275 | 275 | 275 | 275 | 13 | -| setVault | 46239 | 46239 | 46239 | 46239 | 125 | +| previousManager | 263 | 263 | 263 | 263 | 13 | +| setVault | 46227 | 46227 | 46227 | 46227 | 130 | | stake | 23983 | 23983 | 23983 | 23983 | 1 | -| stakeRewardEstimate | 436 | 2269 | 2436 | 2436 | 12 | -| stakedToken | 273 | 273 | 273 | 273 | 7778 | -| startMigration | 108037 | 108045 | 108049 | 108049 | 3 | +| stakeRewardEstimate | 412 | 2294 | 2412 | 2412 | 17 | +| stakedToken | 261 | 261 | 261 | 261 | 6826 | +| startMigration | 107990 | 107998 | 108002 | 108002 | 3 | | totalSupply | 740 | 1921 | 2740 | 2740 | 22 | | totalSupplyBalance | 385 | 1785 | 2385 | 2385 | 20 | | totalSupplyMP | 385 | 510 | 385 | 2385 | 223 | @@ -40,7 +40,7 @@ | Deployment Cost | Deployment Size | | | | | | 0 | 0 | | | | | | Function Name | min | avg | median | max | # calls | -| getExpiredMP | 2427 | 2427 | 2427 | 2427 | 475 | +| getExpiredMP | 2427 | 2427 | 2427 | 2427 | 513 | | transferOwnership | 28533 | 28533 | 28533 | 28533 | 1 | @@ -49,13 +49,13 @@ | Deployment Cost | Deployment Size | | | | | | 0 | 0 | | | | | | Function Name | min | avg | median | max | # calls | -| acceptMigration | 35258 | 35258 | 35258 | 35258 | 2 | -| leave | 35244 | 35244 | 35244 | 35244 | 1 | -| lock | 45192 | 73900 | 66395 | 158442 | 7 | -| owner | 362 | 362 | 362 | 362 | 7761 | -| stake | 27265 | 268815 | 267631 | 353862 | 7766 | +| acceptMigration | 35246 | 35246 | 35246 | 35246 | 2 | +| leave | 35232 | 35232 | 35232 | 35232 | 1 | +| lock | 45376 | 99625 | 66468 | 193557 | 7 | +| owner | 362 | 362 | 362 | 362 | 6809 | +| stake | 27265 | 269080 | 267668 | 353899 | 6814 | | stakedToken | 212 | 212 | 212 | 212 | 2 | -| unstake | 42163 | 92598 | 80285 | 207456 | 11 | +| unstake | 42248 | 92540 | 80334 | 206828 | 11 | | contracts/VaultFactory.sol:VaultFactory contract | | | | | | @@ -63,7 +63,7 @@ | Deployment Cost | Deployment Size | | | | | | 0 | 0 | | | | | | Function Name | min | avg | median | max | # calls | -| createVault | 696531 | 696531 | 696531 | 696531 | 7765 | +| createVault | 696519 | 696519 | 696519 | 696519 | 6813 | | setStakeManager | 23710 | 26669 | 26076 | 30222 | 3 | | stakeManager | 368 | 1868 | 2368 | 2368 | 4 | @@ -73,24 +73,24 @@ | Deployment Cost | Deployment Size | | | | | | 0 | 0 | | | | | | Function Name | min | avg | median | max | # calls | -| approve | 46175 | 46198 | 46199 | 46211 | 7761 | -| balanceOf | 561 | 617 | 561 | 2561 | 21747 | +| approve | 46175 | 46198 | 46199 | 46211 | 6809 | +| balanceOf | 561 | 627 | 561 | 2561 | 19886 | | script/Deploy.s.sol:Deploy contract | | | | | | |-------------------------------------|-----------------|---------|---------|---------|---------| | Deployment Cost | Deployment Size | | | | | -| 6532373 | 31450 | | | | | +| 6070229 | 29294 | | | | | | Function Name | min | avg | median | max | # calls | -| run | 5752636 | 5752636 | 5752636 | 5752636 | 56 | +| run | 5320405 | 5320405 | 5320405 | 5320405 | 61 | | script/DeployMigrationStakeManager.s.sol:DeployMigrationStakeManager contract | | | | | | |-------------------------------------------------------------------------------|-----------------|---------|---------|---------|---------| | Deployment Cost | Deployment Size | | | | | -| 3695815 | 18218 | | | | | +| 3233781 | 16062 | | | | | | Function Name | min | avg | median | max | # calls | -| run | 2738306 | 2738306 | 2738306 | 2738306 | 9 | +| run | 2306051 | 2306051 | 2306051 | 2306051 | 14 | | script/DeploymentConfig.s.sol:DeploymentConfig contract | | | | | | @@ -98,7 +98,7 @@ | Deployment Cost | Deployment Size | | | | | | 0 | 0 | | | | | | Function Name | min | avg | median | max | # calls | -| activeNetworkConfig | 455 | 455 | 455 | 455 | 112 | +| activeNetworkConfig | 455 | 455 | 455 | 455 | 122 | | test/mocks/BrokenERC20.s.sol:BrokenERC20 contract | | | | | | @@ -113,9 +113,9 @@ | test/script/DeployBroken.s.sol:DeployBroken contract | | | | | | |------------------------------------------------------|-----------------|---------|---------|---------|---------| | Deployment Cost | Deployment Size | | | | | -| 5217091 | 25248 | | | | | +| 4754997 | 23092 | | | | | | Function Name | min | avg | median | max | # calls | -| run | 4592458 | 4592458 | 4592458 | 4592458 | 1 | +| run | 4160227 | 4160227 | 4160227 | 4160227 | 1 | diff --git a/.gas-snapshot b/.gas-snapshot index 359d366..950fb2d 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,57 +1,62 @@ CreateVaultTest:testDeployment() (gas: 9774) -CreateVaultTest:test_createVault() (gas: 692936) -ExecuteAccountTest:testDeployment() (gas: 28742) -ExecuteAccountTest:test_ExecuteAccountLimit() (gas: 1318328) -ExecuteAccountTest:test_ExecuteAccountMintMP() (gas: 3861122) -ExecuteAccountTest:test_RevertWhen_InvalidLimitEpoch() (gas: 1154383) -ExecuteAccountTest:test_ShouldNotMintMoreThanCap() (gas: 110701476) -ExecuteEpochTest:testDeployment() (gas: 28720) -ExecuteEpochTest:testNewDeployment() (gas: 30815) -ExecuteEpochTest:test_ExecuteEpochShouldIncreaseEpoch() (gas: 94810) -ExecuteEpochTest:test_ExecuteEpochShouldIncreasePendingReward() (gas: 253041) -ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochBeforeEnd() (gas: 17972) -ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 105698) -LeaveTest:testDeployment() (gas: 28720) -LeaveTest:test_RevertWhen_NoPendingMigration() (gas: 1154639) +CreateVaultTest:test_createVault() (gas: 692924) +ExecuteAccountTest:testDeployment() (gas: 28694) +ExecuteAccountTest:test_ExecuteAccountLimit() (gas: 1316714) +ExecuteAccountTest:test_ExecuteAccountMintMP() (gas: 3859099) +ExecuteAccountTest:test_RevertWhen_InvalidLimitEpoch() (gas: 1154457) +ExecuteAccountTest:test_ShouldNotMintMoreThanCap() (gas: 110444756) +ExecuteEpochTest:testDeployment() (gas: 28650) +ExecuteEpochTest:testNewDeployment() (gas: 30812) +ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterEpochEnd() (gas: 1345318) +ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() (gas: 1736988) +ExecuteEpochTest:test_ExecuteEpochExecuteEpochAfterEnd() (gas: 1370481) +ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochs() (gas: 1721590) +ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() (gas: 1729713) +ExecuteEpochTest:test_ExecuteEpochShouldIncreaseEpoch() (gas: 94029) +ExecuteEpochTest:test_ExecuteEpochShouldIncreasePendingReward() (gas: 252458) +ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochBeforeEnd() (gas: 18013) +ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 105614) +LeaveTest:testDeployment() (gas: 28672) +LeaveTest:test_RevertWhen_NoPendingMigration() (gas: 1154628) LeaveTest:test_RevertWhen_SenderIsNotVault() (gas: 10750) -LockTest:testDeployment() (gas: 28720) -LockTest:test_NewLockupPeriod() (gas: 1143197) -LockTest:test_RevertWhen_InvalidNewLockupPeriod() (gas: 1134814) -LockTest:test_RevertWhen_InvalidUpdateLockupPeriod() (gas: 1229677) +LockTest:testDeployment() (gas: 28672) +LockTest:test_NewLockupPeriod() (gas: 1143247) +LockTest:test_RevertWhen_InvalidNewLockupPeriod() (gas: 1134900) +LockTest:test_RevertWhen_InvalidUpdateLockupPeriod() (gas: 1329649) LockTest:test_RevertWhen_SenderIsNotVault() (gas: 10630) -LockTest:test_ShouldIncreaseBonusMP() (gas: 1123297) -LockTest:test_UpdateLockupPeriod() (gas: 1260010) -MigrateTest:testDeployment() (gas: 28720) -MigrateTest:test_RevertWhen_NoPendingMigration() (gas: 1152278) +LockTest:test_ShouldIncreaseBonusMP() (gas: 1123311) +LockTest:test_UpdateLockupPeriod() (gas: 1337133) +MigrateTest:testDeployment() (gas: 28672) +MigrateTest:test_RevertWhen_NoPendingMigration() (gas: 1152279) MigrateTest:test_RevertWhen_SenderIsNotVault() (gas: 10750) -MigrationInitializeTest:testDeployment() (gas: 28720) -MigrationInitializeTest:test_RevertWhen_MigrationPending() (gas: 5506950) -MigrationStakeManagerTest:testDeployment() (gas: 28720) -MigrationStakeManagerTest:testNewDeployment() (gas: 30859) -MigrationStakeManagerTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 105686) +MigrationInitializeTest:testDeployment() (gas: 28672) +MigrationInitializeTest:test_RevertWhen_MigrationPending() (gas: 4642666) +MigrationStakeManagerTest:testDeployment() (gas: 28672) +MigrationStakeManagerTest:testNewDeployment() (gas: 30811) +MigrationStakeManagerTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 105579) SetStakeManagerTest:testDeployment() (gas: 9774) SetStakeManagerTest:test_RevertWhen_InvalidStakeManagerAddress() (gas: 20481) SetStakeManagerTest:test_SetStakeManager() (gas: 19869) -StakeManagerTest:testDeployment() (gas: 28492) -StakeTest:testDeployment() (gas: 28698) -StakeTest:test_RevertWhen_InvalidLockupPeriod() (gas: 892168) -StakeTest:test_RevertWhen_Restake() (gas: 1157929) -StakeTest:test_RevertWhen_RestakeWithLock() (gas: 1159442) +StakeManagerTest:testDeployment() (gas: 28444) +StakeTest:testDeployment() (gas: 28650) +StakeTest:test_RevertWhen_InvalidLockupPeriod() (gas: 892314) +StakeTest:test_RevertWhen_Restake() (gas: 1158027) +StakeTest:test_RevertWhen_RestakeWithLock() (gas: 1159540) StakeTest:test_RevertWhen_SenderIsNotVault() (gas: 10674) -StakeTest:test_RevertWhen_StakeIsTooLow() (gas: 745276) +StakeTest:test_RevertWhen_StakeIsTooLow() (gas: 745337) StakeTest:test_RevertWhen_StakeTokenTransferFails() (gas: 175040) -StakeTest:test_StakeWithLockBonusMP() (gas: 2023009) -StakeTest:test_StakeWithoutLockUpTimeMintsMultiplierPoints() (gas: 1028803) +StakeTest:test_StakeWithLockBonusMP() (gas: 2023012) +StakeTest:test_StakeWithoutLockUpTimeMintsMultiplierPoints() (gas: 1028805) StakedTokenTest:testStakeToken() (gas: 7616) -UnstakeTest:testDeployment() (gas: 28742) -UnstakeTest:test_RevertWhen_AmountMoreThanBalance() (gas: 1132893) -UnstakeTest:test_RevertWhen_FundsLocked() (gas: 1158045) +UnstakeTest:testDeployment() (gas: 28694) +UnstakeTest:test_RevertWhen_AmountMoreThanBalance() (gas: 1132991) +UnstakeTest:test_RevertWhen_FundsLocked() (gas: 1158228) UnstakeTest:test_RevertWhen_SenderIsNotVault() (gas: 10653) -UnstakeTest:test_UnstakeShouldBurnMultiplierPoints() (gas: 5535329) -UnstakeTest:test_UnstakeShouldReturnFund_NoLockUp() (gas: 1026305) -UnstakeTest:test_UnstakeShouldReturnFund_WithLockUp() (gas: 1113953) -UserFlowsTest:testDeployment() (gas: 28720) -UserFlowsTest:test_PendingMPToBeMintedCannotBeGreaterThanTotalSupplyMP(uint8) (runs: 101, μ: 65588077, ~: 26417145) -UserFlowsTest:test_StakeWithLockUpTimeLocksStake() (gas: 1114841) -UserFlowsTest:test_StakedSupplyShouldIncreaseAndDecreaseAgain() (gas: 1950367) +UnstakeTest:test_UnstakeShouldBurnMultiplierPoints() (gas: 5501580) +UnstakeTest:test_UnstakeShouldReturnFund_NoLockUp() (gas: 1026379) +UnstakeTest:test_UnstakeShouldReturnFund_WithLockUp() (gas: 1113182) +UserFlowsTest:testDeployment() (gas: 28672) +UserFlowsTest:test_PendingMPToBeMintedCannotBeGreaterThanTotalSupplyMP(uint8) (runs: 101, μ: 56960836, ~: 26417438) +UserFlowsTest:test_StakeWithLockUpTimeLocksStake() (gas: 1114155) +UserFlowsTest:test_StakedSupplyShouldIncreaseAndDecreaseAgain() (gas: 1950527) VaultFactoryTest:testDeployment() (gas: 9774) \ No newline at end of file diff --git a/contracts/StakeManager.sol b/contracts/StakeManager.sol index d6201d8..5544067 100644 --- a/contracts/StakeManager.sol +++ b/contracts/StakeManager.sol @@ -138,27 +138,30 @@ contract StakeManager is Ownable { /** * @notice Process epoch if it has ended */ - modifier finalizeEpoch() { - if (block.timestamp >= epochEnd() && address(migration) == address(0)) { + function finalizeEpoch() private { + if (address(migration) != address(0)) { + return; + } + Epoch storage thisEpoch = epochs[currentEpoch]; + if (block.timestamp >= thisEpoch.startTime + EPOCH_SIZE) { uint256 expiredMP = stakeRewardEstimate.getExpiredMP(currentEpoch); if (expiredMP > 0) { totalMPPerEpoch -= expiredMP; stakeRewardEstimate.deleteExpiredMP(currentEpoch); } - epochs[currentEpoch].estimatedMP = totalMPPerEpoch - currentEpochTotalExpiredMP; + thisEpoch.estimatedMP = totalMPPerEpoch - currentEpochTotalExpiredMP; delete currentEpochTotalExpiredMP; - pendingMPToBeMinted += epochs[currentEpoch].estimatedMP; + pendingMPToBeMinted += thisEpoch.estimatedMP; //finalize current epoch - epochs[currentEpoch].epochReward = epochReward(); - epochs[currentEpoch].totalSupply = totalSupply(); - pendingReward += epochs[currentEpoch].epochReward; + thisEpoch.epochReward = epochReward(); + thisEpoch.totalSupply = totalSupply(); + pendingReward += thisEpoch.epochReward; //create new epoch currentEpoch++; - epochs[currentEpoch].startTime = block.timestamp; + epochs[currentEpoch].startTime = thisEpoch.startTime + EPOCH_SIZE; } - _; } constructor(address _stakedToken, address _previousManager) { @@ -181,7 +184,8 @@ contract StakeManager is Ownable { * @dev Reverts when account has already staked funds. * @dev Reverts when amount staked results in less than 1 MP per epoch. */ - function stake(uint256 _amount, uint256 _secondsToLock) external onlyVault noPendingMigration finalizeEpoch { + function stake(uint256 _amount, uint256 _secondsToLock) external onlyVault noPendingMigration { + finalizeEpoch(); Account storage account = accounts[msg.sender]; if (account.balance > 0 || account.lockUntil != 0) { revert StakeManager__AlreadyStaked(); @@ -220,13 +224,8 @@ contract StakeManager is Ownable { /** * leaves the staking pool and withdraws all funds; */ - function unstake(uint256 _amount) - external - onlyVault - onlyAccountInitialized(msg.sender) - noPendingMigration - finalizeEpoch - { + function unstake(uint256 _amount) external onlyVault onlyAccountInitialized(msg.sender) noPendingMigration { + finalizeEpoch(); Account storage account = accounts[msg.sender]; if (_amount > account.balance) { revert StakeManager__InsufficientFunds(); @@ -266,8 +265,8 @@ contract StakeManager is Ownable { onlyVault onlyAccountInitialized(msg.sender) noPendingMigration - finalizeEpoch { + finalizeEpoch(); Account storage account = accounts[msg.sender]; _processAccount(account, currentEpoch); uint256 lockUntil = account.lockUntil; @@ -291,8 +290,8 @@ contract StakeManager is Ownable { * @notice Release rewards for current epoch and increase epoch. * @dev only executes the prerequisite modifier finalizeEpoch */ - function executeEpoch() external noPendingMigration finalizeEpoch { - return; //see modifier finalizeEpoch + function executeEpoch() external noPendingMigration { + finalizeEpoch(); } /** @@ -300,14 +299,8 @@ contract StakeManager is Ownable { * @param _vault Referred account * @param _limitEpoch Until what epoch it should be executed */ - function executeAccount( - address _vault, - uint256 _limitEpoch - ) - external - onlyAccountInitialized(_vault) - finalizeEpoch - { + function executeAccount(address _vault, uint256 _limitEpoch) external onlyAccountInitialized(_vault) { + finalizeEpoch(); _processAccount(accounts[_vault], _limitEpoch); } @@ -323,7 +316,8 @@ contract StakeManager is Ownable { * @notice starts migration to new StakeManager * @param _migration new StakeManager */ - function startMigration(StakeManager _migration) external onlyOwner noPendingMigration finalizeEpoch { + function startMigration(StakeManager _migration) external onlyOwner noPendingMigration { + finalizeEpoch(); if (_migration == this || address(_migration) == address(0)) { revert StakeManager__InvalidMigration(); } diff --git a/test/StakeManager.t.sol b/test/StakeManager.t.sol index 060c3b4..8531b9d 100644 --- a/test/StakeManager.t.sol +++ b/test/StakeManager.t.sol @@ -728,6 +728,64 @@ contract MigrationStakeManagerTest is StakeManagerTest { } contract ExecuteEpochTest is MigrationStakeManagerTest { + function test_ExecuteEpochExecuteEpochAfterEnd() public { + StakeVault userVault = _createStakingAccount(makeAddr("testUser"), 100_000, 0); + + vm.warp(stakeManager.epochEnd() + (stakeManager.EPOCH_SIZE() / 2)); + stakeManager.executeEpoch(); + stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch()); + vm.warp(stakeManager.epochEnd()); + + stakeManager.executeEpoch(); + stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch()); + + vm.warp(stakeManager.epochEnd() + (stakeManager.EPOCH_SIZE() * 2)); + stakeManager.executeEpoch(); + stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch()); + } + + function test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochs() public { + StakeVault userVault = _createStakingAccount(makeAddr("testUser"), 100_000, 0); + + for (uint256 i = 0; i < 10; i++) { + vm.warp(stakeManager.epochEnd()); + stakeManager.executeEpoch(); + } + stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch()); + } + + function test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() public { + StakeVault userVault = _createStakingAccount(makeAddr("testUser"), 100_000, 0); + + for (uint256 i = 0; i < 10; i++) { + vm.warp(stakeManager.epochEnd() + (stakeManager.EPOCH_SIZE() / 10 - i)); + stakeManager.executeEpoch(); + } + stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch()); + } + + function test_ExecuteEpochExecuteAccountAfterEpochEnd() public { + StakeVault userVault = _createStakingAccount(makeAddr("testUser"), 100_000, 0); + + vm.warp(stakeManager.epochEnd() + (stakeManager.EPOCH_SIZE() / 2)); + stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch()); + + vm.warp(stakeManager.epochEnd()); + stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch()); + + vm.warp(stakeManager.epochEnd() + (stakeManager.EPOCH_SIZE() * 2)); + stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch()); + } + + function test_ExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() public { + StakeVault userVault = _createStakingAccount(makeAddr("testUser"), 100_000, 0); + + for (uint256 i = 0; i < 10; i++) { + vm.warp(stakeManager.epochEnd() + (stakeManager.EPOCH_SIZE() / 10 - i)); + stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch()); + } + } + function test_ExecuteEpochShouldNotIncreaseEpochBeforeEnd() public { assertEq(stakeManager.currentEpoch(), 0);