refactor(StakeManager): optimize epoch finalization and execution of accounts and epochs

This commit refactors the StakeManager contract to optimize the finalization and execution of epochs. It updates the function finalizeEpoch() to new function finalizeEpoch(uint256 _limitEpoch) that releases rewards for the current epoch and increases the epoch up to the specified limit. This allows for more efficient processing of epochs.

The changes also include updates to the executeEpoch() and executeAccount() functions to utilize the new finalizeEpoch() function. Additionally, a new function newEpoch() is introduced to calculate the last epoch that can be processed based on the current time. Now the executeAccount(account,limit) will also process epochs up to the specified limit.

Added a overload of the executeAccount to process the account up to the newEpoch()

Added a overload of the executeEpoch to allow the process of epochs up to a certain limit.

All methods now that finalizeEpoch will process the epochs up to the newEpoch().

These changes improve the overall performance, user expirience and reliability of the StakeManager contract.

chore(StakeManagerStartMigration.spec): add new function executeEpoch(uint256) to blockedWhenMigrating

fix(StakeManager.sol): Replace the check for pending migration in migrationInitialize with noPendingMigration modifier to avoid code duplication
This commit is contained in:
Ricardo Guilherme Schmidt 2024-09-25 12:53:59 -03:00
parent 142137dee7
commit 4a261e8b48
5 changed files with 273 additions and 125 deletions

View File

@ -1,38 +1,41 @@
| contracts/StakeManager.sol:StakeManager contract | | | | | |
|--------------------------------------------------|-----------------|--------|--------|--------|---------|
| Deployment Cost | Deployment Size | | | | |
| 2469345 | 12797 | | | | |
| Function Name | min | avg | median | max | # calls |
| EPOCH_SIZE | 307 | 307 | 307 | 307 | 1476 |
| MAX_BOOST | 285 | 285 | 285 | 285 | 637 |
| MAX_LOCKUP_PERIOD | 361 | 361 | 361 | 361 | 4 |
| MIN_LOCKUP_PERIOD | 287 | 287 | 287 | 287 | 12 |
| YEAR | 263 | 263 | 263 | 263 | 637 |
| accounts | 1561 | 1561 | 1561 | 1561 | 144375 |
| calculateMPToMint | 740 | 740 | 740 | 740 | 1276 |
| currentEpoch | 406 | 1188 | 406 | 2406 | 46 |
| epochEnd | 627 | 627 | 627 | 4627 | 23648 |
| epochReward | 1391 | 2891 | 1391 | 5891 | 3 |
| executeAccount | 30958 | 76171 | 77953 | 240555 | 141954 |
| executeEpoch | 23424 | 145441 | 146624 | 203524 | 23574 |
| isVault | 540 | 939 | 540 | 2540 | 676 |
| lock | 23840 | 23840 | 23840 | 23840 | 1 |
| migrateTo | 23869 | 23875 | 23875 | 23881 | 2 |
| migration | 415 | 1415 | 1415 | 2415 | 4 |
| migrationInitialize | 24578 | 24578 | 24578 | 24578 | 1 |
| owner | 2408 | 2408 | 2408 | 2408 | 13 |
| pendingMPToBeMinted | 364 | 364 | 364 | 364 | 46466 |
| pendingReward | 364 | 1398 | 2364 | 2364 | 29 |
| previousManager | 263 | 263 | 263 | 263 | 13 |
| setVault | 46227 | 46227 | 46227 | 46227 | 135 |
| stake | 23983 | 23983 | 23983 | 23983 | 1 |
| stakeRewardEstimate | 412 | 2294 | 2412 | 2412 | 17 |
| stakedToken | 261 | 261 | 261 | 261 | 692 |
| startMigration | 107990 | 107998 | 108002 | 108002 | 3 |
| totalSupply | 740 | 1921 | 2740 | 2740 | 22 |
| totalSupplyBalance | 385 | 1785 | 2385 | 2385 | 20 |
| totalSupplyMP | 385 | 385 | 385 | 2385 | 46487 |
| unstake | 23819 | 23819 | 23819 | 23819 | 1 |
| contracts/StakeManager.sol:StakeManager contract | | | | | |
|--------------------------------------------------|-----------------|--------|--------|---------|---------|
| Deployment Cost | Deployment Size | | | | |
| 2447267 | 12701 | | | | |
| Function Name | min | avg | median | max | # calls |
| EPOCH_SIZE | 263 | 263 | 263 | 263 | 1498 |
| MAX_BOOST | 307 | 307 | 307 | 307 | 637 |
| MAX_LOCKUP_PERIOD | 361 | 361 | 361 | 361 | 4 |
| MIN_LOCKUP_PERIOD | 264 | 264 | 264 | 264 | 12 |
| YEAR | 263 | 263 | 263 | 263 | 637 |
| accounts | 1619 | 1619 | 1619 | 1619 | 144243 |
| calculateMPToMint | 740 | 740 | 740 | 740 | 1276 |
| currentEpoch | 406 | 1072 | 406 | 2406 | 54 |
| epochEnd | 649 | 649 | 649 | 4649 | 23670 |
| epochReward | 1425 | 2925 | 1425 | 5925 | 3 |
| executeAccount(address) | 171751 | 171751 | 171751 | 171751 | 2 |
| executeAccount(address,uint256) | 30909 | 76511 | 78308 | 240839 | 141830 |
| executeEpoch() | 23458 | 146010 | 147059 | 3562421 | 23559 |
| executeEpoch(uint256) | 28208 | 28242 | 28208 | 28330 | 7 |
| isVault | 562 | 970 | 562 | 2562 | 680 |
| lock | 23862 | 23862 | 23862 | 23862 | 1 |
| migrateTo | 23891 | 23897 | 23897 | 23903 | 2 |
| migration | 395 | 1395 | 1395 | 2395 | 4 |
| migrationInitialize | 24602 | 24602 | 24602 | 24602 | 1 |
| newEpoch | 788 | 788 | 788 | 788 | 5 |
| owner | 2432 | 2432 | 2432 | 2432 | 13 |
| pendingMPToBeMinted | 386 | 386 | 386 | 386 | 46422 |
| pendingReward | 386 | 1420 | 2386 | 2386 | 29 |
| previousManager | 275 | 275 | 275 | 275 | 13 |
| setVault | 46216 | 46216 | 46216 | 46216 | 139 |
| stake | 23983 | 23983 | 23983 | 23983 | 1 |
| stakeRewardEstimate | 436 | 2345 | 2436 | 2436 | 22 |
| stakedToken | 295 | 295 | 295 | 295 | 696 |
| startMigration | 108281 | 108289 | 108293 | 108293 | 3 |
| totalSupply | 762 | 1943 | 2762 | 2762 | 22 |
| totalSupplyBalance | 407 | 1807 | 2407 | 2407 | 20 |
| totalSupplyMP | 362 | 362 | 362 | 2362 | 46443 |
| unstake | 23841 | 23841 | 23841 | 23841 | 1 |
| contracts/StakeManager.sol:StakeRewardEstimate contract | | | | | |
@ -40,7 +43,7 @@
| Deployment Cost | Deployment Size | | | | |
| 0 | 0 | | | | |
| Function Name | min | avg | median | max | # calls |
| getExpiredMP | 2427 | 2427 | 2427 | 2427 | 23645 |
| getExpiredMP | 2427 | 2427 | 2427 | 2427 | 23720 |
| transferOwnership | 28533 | 28533 | 28533 | 28533 | 1 |
@ -49,13 +52,13 @@
| Deployment Cost | Deployment Size | | | | |
| 0 | 0 | | | | |
| Function Name | min | avg | median | max | # calls |
| acceptMigration | 35246 | 35246 | 35246 | 35246 | 2 |
| leave | 35232 | 35232 | 35232 | 35232 | 1 |
| lock | 45376 | 99625 | 66468 | 193557 | 7 |
| owner | 362 | 362 | 362 | 362 | 675 |
| stake | 27265 | 283876 | 267740 | 353899 | 680 |
| acceptMigration | 35280 | 35280 | 35280 | 35280 | 2 |
| leave | 35266 | 35266 | 35266 | 35266 | 1 |
| lock | 45569 | 108497 | 66661 | 254253 | 7 |
| owner | 362 | 362 | 362 | 362 | 679 |
| stake | 27265 | 284241 | 267959 | 354106 | 684 |
| stakedToken | 212 | 212 | 212 | 212 | 2 |
| unstake | 42248 | 92540 | 80334 | 206828 | 11 |
| unstake | 42429 | 104547 | 80498 | 272053 | 11 |
| contracts/VaultFactory.sol:VaultFactory contract | | | | | |
@ -63,7 +66,7 @@
| Deployment Cost | Deployment Size | | | | |
| 0 | 0 | | | | |
| Function Name | min | avg | median | max | # calls |
| createVault | 696519 | 696519 | 696519 | 696519 | 679 |
| createVault | 696553 | 696553 | 696553 | 696553 | 683 |
| setStakeManager | 23710 | 26669 | 26076 | 30222 | 3 |
| stakeManager | 368 | 1868 | 2368 | 2368 | 4 |
@ -73,24 +76,24 @@
| Deployment Cost | Deployment Size | | | | |
| 0 | 0 | | | | |
| Function Name | min | avg | median | max | # calls |
| approve | 46175 | 46236 | 46199 | 46367 | 675 |
| balanceOf | 561 | 2108 | 2561 | 2561 | 30755 |
| approve | 46175 | 46240 | 46199 | 46367 | 679 |
| balanceOf | 561 | 2102 | 2561 | 2561 | 30842 |
| script/Deploy.s.sol:Deploy contract | | | | | |
|-------------------------------------|-----------------|---------|---------|---------|---------|
| Deployment Cost | Deployment Size | | | | |
| 6070229 | 29294 | | | | |
| 6048121 | 29198 | | | | |
| Function Name | min | avg | median | max | # calls |
| run | 5320405 | 5320405 | 5320405 | 5320405 | 61 |
| run | 5301161 | 5301161 | 5301161 | 5301161 | 66 |
| script/DeployMigrationStakeManager.s.sol:DeployMigrationStakeManager contract | | | | | |
|-------------------------------------------------------------------------------|-----------------|---------|---------|---------|---------|
| Deployment Cost | Deployment Size | | | | |
| 3233781 | 16062 | | | | |
| 3211677 | 15966 | | | | |
| Function Name | min | avg | median | max | # calls |
| run | 2306051 | 2306051 | 2306051 | 2306051 | 14 |
| run | 2286831 | 2286831 | 2286831 | 2286831 | 19 |
| script/DeploymentConfig.s.sol:DeploymentConfig contract | | | | | |
@ -98,7 +101,7 @@
| Deployment Cost | Deployment Size | | | | |
| 0 | 0 | | | | |
| Function Name | min | avg | median | max | # calls |
| activeNetworkConfig | 455 | 455 | 455 | 455 | 122 |
| activeNetworkConfig | 455 | 455 | 455 | 455 | 132 |
| test/mocks/BrokenERC20.s.sol:BrokenERC20 contract | | | | | |
@ -113,9 +116,9 @@
| test/script/DeployBroken.s.sol:DeployBroken contract | | | | | |
|------------------------------------------------------|-----------------|---------|---------|---------|---------|
| Deployment Cost | Deployment Size | | | | |
| 4754997 | 23092 | | | | |
| 4732891 | 22996 | | | | |
| Function Name | min | avg | median | max | # calls |
| run | 4160227 | 4160227 | 4160227 | 4160227 | 1 |
| run | 4140983 | 4140983 | 4140983 | 4140983 | 1 |

View File

@ -1,62 +1,67 @@
CreateVaultTest:testDeployment() (gas: 9774)
CreateVaultTest:test_createVault() (gas: 713988)
ExecuteAccountTest:testDeployment() (gas: 28694)
ExecuteAccountTest:test_ExecuteAccountLimit() (gas: 1623530)
ExecuteAccountTest:test_ExecuteAccountMintMP() (gas: 5407775)
ExecuteAccountTest:test_RevertWhen_InvalidLimitEpoch() (gas: 1294709)
ExecuteAccountTest:test_ShouldNotMintMoreThanCap() (gas: 337956933)
ExecuteEpochTest:testDeployment() (gas: 28650)
ExecuteEpochTest:testNewDeployment() (gas: 30812)
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterEpochEnd() (gas: 1740114)
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() (gas: 2763201)
ExecuteEpochTest:test_ExecuteEpochExecuteEpochAfterEnd() (gas: 1980675)
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochs() (gas: 2789261)
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() (gas: 2799415)
ExecuteEpochTest:test_ExecuteEpochShouldIncreaseEpoch() (gas: 119081)
ExecuteEpochTest:test_ExecuteEpochShouldIncreasePendingReward() (gas: 277700)
ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochBeforeEnd() (gas: 43065)
ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 154098)
LeaveTest:testDeployment() (gas: 28672)
LeaveTest:test_RevertWhen_NoPendingMigration() (gas: 1329912)
LeaveTest:test_RevertWhen_SenderIsNotVault() (gas: 31942)
LockTest:testDeployment() (gas: 28672)
LockTest:test_NewLockupPeriod() (gas: 1333043)
LockTest:test_RevertWhen_InvalidNewLockupPeriod() (gas: 1305408)
LockTest:test_RevertWhen_InvalidUpdateLockupPeriod() (gas: 1588267)
LockTest:test_RevertWhen_SenderIsNotVault() (gas: 31834)
LockTest:test_ShouldIncreaseBonusMP() (gas: 1315583)
LockTest:test_UpdateLockupPeriod() (gas: 1662367)
MigrateTest:testDeployment() (gas: 28672)
MigrateTest:test_RevertWhen_NoPendingMigration() (gas: 1293999)
MigrateTest:test_RevertWhen_SenderIsNotVault() (gas: 31954)
MigrationInitializeTest:testDeployment() (gas: 28672)
MigrationInitializeTest:test_RevertWhen_MigrationPending() (gas: 5192974)
MigrationStakeManagerTest:testDeployment() (gas: 28672)
MigrationStakeManagerTest:testNewDeployment() (gas: 30811)
MigrationStakeManagerTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 154063)
CreateVaultTest:test_createVault() (gas: 714022)
ExecuteAccountTest:testDeployment() (gas: 28807)
ExecuteAccountTest:test_ExecuteAccountLimit() (gas: 1624700)
ExecuteAccountTest:test_ExecuteAccountMintMP() (gas: 5413915)
ExecuteAccountTest:test_RevertWhen_InvalidLimitEpoch() (gas: 1869543)
ExecuteAccountTest:test_ShouldNotMintMoreThanCap() (gas: 339213337)
ExecuteEpochTest:testDeployment() (gas: 28808)
ExecuteEpochTest:testNewDeployment() (gas: 30880)
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterEpochEnd() (gas: 1375214)
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsJumoMany() (gas: 1402953)
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() (gas: 1654146)
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTimeJumpMany() (gas: 1412668)
ExecuteEpochTest:test_ExecuteEpochExecuteEpochAfterEnd() (gas: 2068294)
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochs() (gas: 2794284)
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsJumoMany() (gas: 1502361)
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() (gas: 2804020)
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTimeJumpMany() (gas: 1512053)
ExecuteEpochTest:test_ExecuteEpochNewEpoch() (gas: 3781443)
ExecuteEpochTest:test_ExecuteEpochShouldIncreaseEpoch() (gas: 119582)
ExecuteEpochTest:test_ExecuteEpochShouldIncreasePendingReward() (gas: 278146)
ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochBeforeEnd() (gas: 43290)
ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 154405)
LeaveTest:testDeployment() (gas: 28785)
LeaveTest:test_RevertWhen_NoPendingMigration() (gas: 1330232)
LeaveTest:test_RevertWhen_SenderIsNotVault() (gas: 31964)
LockTest:testDeployment() (gas: 28785)
LockTest:test_NewLockupPeriod() (gas: 1333546)
LockTest:test_RevertWhen_InvalidNewLockupPeriod() (gas: 1305853)
LockTest:test_RevertWhen_InvalidUpdateLockupPeriod() (gas: 1589343)
LockTest:test_RevertWhen_SenderIsNotVault() (gas: 31856)
LockTest:test_ShouldIncreaseBonusMP() (gas: 1316098)
LockTest:test_UpdateLockupPeriod() (gas: 1704020)
MigrateTest:testDeployment() (gas: 28785)
MigrateTest:test_RevertWhen_NoPendingMigration() (gas: 1294285)
MigrateTest:test_RevertWhen_SenderIsNotVault() (gas: 31976)
MigrationInitializeTest:testDeployment() (gas: 28785)
MigrationInitializeTest:test_RevertWhen_MigrationPending() (gas: 5149125)
MigrationStakeManagerTest:testDeployment() (gas: 28785)
MigrationStakeManagerTest:testNewDeployment() (gas: 30924)
MigrationStakeManagerTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 154370)
SetStakeManagerTest:testDeployment() (gas: 9774)
SetStakeManagerTest:test_RevertWhen_InvalidStakeManagerAddress() (gas: 63105)
SetStakeManagerTest:test_SetStakeManager() (gas: 41301)
StakeManagerTest:testDeployment() (gas: 28444)
StakeTest:testDeployment() (gas: 28650)
StakeTest:test_RevertWhen_InvalidLockupPeriod() (gas: 1084774)
StakeTest:test_RevertWhen_Restake() (gas: 1320915)
StakeTest:test_RevertWhen_RestakeWithLock() (gas: 1324952)
StakeManagerTest:testDeployment() (gas: 28557)
StakeTest:testDeployment() (gas: 28763)
StakeTest:test_RevertWhen_InvalidLockupPeriod() (gas: 1085114)
StakeTest:test_RevertWhen_Restake() (gas: 1321326)
StakeTest:test_RevertWhen_RestakeWithLock() (gas: 1325340)
StakeTest:test_RevertWhen_SenderIsNotVault() (gas: 32018)
StakeTest:test_RevertWhen_StakeIsTooLow() (gas: 819797)
StakeTest:test_RevertWhen_StakeIsTooLow() (gas: 820001)
StakeTest:test_RevertWhen_StakeTokenTransferFails() (gas: 211363)
StakeTest:test_StakeWithLockBonusMP() (gas: 2363512)
StakeTest:test_StakeWithoutLockUpTimeMintsMultiplierPoints() (gas: 1325199)
StakeTest:test_StakeWithLockBonusMP() (gas: 2364371)
StakeTest:test_StakeWithoutLockUpTimeMintsMultiplierPoints() (gas: 1325644)
StakedTokenTest:testStakeToken() (gas: 7616)
UnstakeTest:testDeployment() (gas: 28694)
UnstakeTest:test_RevertWhen_AmountMoreThanBalance() (gas: 1301475)
UnstakeTest:test_RevertWhen_FundsLocked() (gas: 1348316)
UnstakeTest:test_RevertWhen_SenderIsNotVault() (gas: 31857)
UnstakeTest:test_UnstakeShouldBurnMultiplierPoints() (gas: 7382927)
UnstakeTest:test_UnstakeShouldReturnFund_NoLockUp() (gas: 1322773)
UnstakeTest:test_UnstakeShouldReturnFund_WithLockUp() (gas: 1422152)
UserFlowsTest:testDeployment() (gas: 28672)
UserFlowsTest:test_PendingMPToBeMintedCannotBeGreaterThanTotalSupplyMP(uint8,uint128) (runs: 106, μ: 140983431, ~: 140305109)
UserFlowsTest:test_StakeWithLockUpTimeLocksStake() (gas: 1465329)
UserFlowsTest:test_StakedSupplyShouldIncreaseAndDecreaseAgain() (gas: 2501983)
UnstakeTest:testDeployment() (gas: 28807)
UnstakeTest:test_RevertWhen_AmountMoreThanBalance() (gas: 1301908)
UnstakeTest:test_RevertWhen_FundsLocked() (gas: 1348907)
UnstakeTest:test_RevertWhen_SenderIsNotVault() (gas: 31879)
UnstakeTest:test_UnstakeShouldBurnMultiplierPoints() (gas: 7404912)
UnstakeTest:test_UnstakeShouldReturnFund_NoLockUp() (gas: 1323170)
UnstakeTest:test_UnstakeShouldReturnFund_WithLockUp() (gas: 1471322)
UserFlowsTest:testDeployment() (gas: 28785)
UserFlowsTest:test_PendingMPToBeMintedCannotBeGreaterThanTotalSupplyMP(uint8,uint128) (runs: 106, μ: 141478753, ~: 140916642)
UserFlowsTest:test_StakeWithLockUpTimeLocksStake() (gas: 1514680)
UserFlowsTest:test_StakedSupplyShouldIncreaseAndDecreaseAgain() (gas: 2502892)
VaultFactoryTest:testDeployment() (gas: 9774)

View File

@ -19,6 +19,7 @@ definition blockedWhenMigrating(method f) returns bool = (
f.selector == sig:unstake(uint256).selector ||
f.selector == sig:lock(uint256).selector ||
f.selector == sig:executeEpoch().selector ||
f.selector == sig:executeEpoch(uint256).selector ||
f.selector == sig:startMigration(address).selector ||
f.selector == sig:migrationInitialize(uint256,uint256,uint256,uint256,uint256,uint256,uint256).selector
);

View File

@ -136,14 +136,12 @@ contract StakeManager is Ownable {
}
/**
* @notice Process epoch if it has ended
* @notice Release rewards for current epoch and increase epoch up to _limitEpoch
* @param _limitEpoch Until what epoch it should be executed
*/
function finalizeEpoch() private {
if (address(migration) != address(0)) {
return;
}
Epoch storage thisEpoch = epochs[currentEpoch];
if (block.timestamp >= thisEpoch.startTime + EPOCH_SIZE) {
function finalizeEpoch(uint256 _limitEpoch) private {
while (currentEpoch < _limitEpoch) {
Epoch storage thisEpoch = epochs[currentEpoch];
uint256 expiredMP = stakeRewardEstimate.getExpiredMP(currentEpoch);
if (expiredMP > 0) {
totalMPPerEpoch -= expiredMP;
@ -185,7 +183,7 @@ contract StakeManager is Ownable {
* @dev Reverts when amount staked results in less than 1 MP per epoch.
*/
function stake(uint256 _amount, uint256 _secondsToLock) external onlyVault noPendingMigration {
finalizeEpoch();
finalizeEpoch(newEpoch());
Account storage account = accounts[msg.sender];
if (account.balance > 0 || account.lockUntil != 0) {
revert StakeManager__AlreadyStaked();
@ -225,7 +223,7 @@ contract StakeManager is Ownable {
* leaves the staking pool and withdraws all funds;
*/
function unstake(uint256 _amount) external onlyVault onlyAccountInitialized(msg.sender) noPendingMigration {
finalizeEpoch();
finalizeEpoch(newEpoch());
Account storage account = accounts[msg.sender];
if (_amount > account.balance) {
revert StakeManager__InsufficientFunds();
@ -239,7 +237,6 @@ contract StakeManager is Ownable {
uint256 reducedInitialMP = Math.mulDiv(_amount, account.bonusMP, account.balance);
uint256 mpPerEpoch = _getMPToMint(account.balance, EPOCH_SIZE);
stakeRewardEstimate.decrementExpiredMP(account.mpLimitEpoch, mpPerEpoch);
if (account.mpLimitEpoch < currentEpoch) {
totalMPPerEpoch -= mpPerEpoch;
@ -266,7 +263,7 @@ contract StakeManager is Ownable {
onlyAccountInitialized(msg.sender)
noPendingMigration
{
finalizeEpoch();
finalizeEpoch(newEpoch());
Account storage account = accounts[msg.sender];
_processAccount(account, currentEpoch);
uint256 lockUntil = account.lockUntil;
@ -287,11 +284,32 @@ contract StakeManager is Ownable {
}
/**
* @notice Release rewards for current epoch and increase epoch.
* @dev only executes the prerequisite modifier finalizeEpoch
* @notice Release rewards for current epoch and increase epoch to latest epoch.
*/
function executeEpoch() external noPendingMigration {
finalizeEpoch();
finalizeEpoch(newEpoch());
}
/**
* @notice Release rewards for current epoch and increase epoch up to _limitEpoch
* @param _limitEpoch Until what epoch it should be executed
*/
function executeEpoch(uint256 _limitEpoch) external noPendingMigration {
if (newEpoch() < _limitEpoch) {
revert StakeManager__InvalidLimitEpoch();
}
finalizeEpoch(_limitEpoch);
}
/**
* @notice Execute rewards for account until last possible epoch reached
* @param _vault Referred account
*/
function executeAccount(address _vault) external onlyAccountInitialized(_vault) {
if (address(migration) == address(0)) {
finalizeEpoch(newEpoch());
}
_processAccount(accounts[_vault], currentEpoch);
}
/**
@ -300,7 +318,12 @@ contract StakeManager is Ownable {
* @param _limitEpoch Until what epoch it should be executed
*/
function executeAccount(address _vault, uint256 _limitEpoch) external onlyAccountInitialized(_vault) {
finalizeEpoch();
if (address(migration) == address(0)) {
if (newEpoch() < _limitEpoch) {
revert StakeManager__InvalidLimitEpoch();
}
finalizeEpoch(_limitEpoch);
}
_processAccount(accounts[_vault], _limitEpoch);
}
@ -317,7 +340,7 @@ contract StakeManager is Ownable {
* @param _migration new StakeManager
*/
function startMigration(StakeManager _migration) external onlyOwner noPendingMigration {
finalizeEpoch();
finalizeEpoch(newEpoch());
if (_migration == this || address(_migration) == address(0)) {
revert StakeManager__InvalidMigration();
}
@ -354,10 +377,8 @@ contract StakeManager is Ownable {
)
external
onlyPreviousManager
noPendingMigration
{
if (address(migration) != address(0)) {
revert StakeManager__PendingMigration();
}
if (currentEpoch > 0) {
revert StakeManager__AlreadyProcessedEpochs();
}
@ -589,4 +610,13 @@ contract StakeManager is Ownable {
function epochEnd() public view returns (uint256 _epochEnd) {
return epochs[currentEpoch].startTime + EPOCH_SIZE;
}
/**
* @notice Returns the last epoch that can be processed on current time
* @return _newEpoch the number of the epoch after all epochs that can be processed
*/
function newEpoch() public view returns (uint256 _newEpoch) {
_newEpoch = currentEpoch;
_newEpoch = _newEpoch + ((block.timestamp - epochs[_newEpoch].startTime) / EPOCH_SIZE);
}
}

View File

@ -454,6 +454,49 @@ contract ExecuteAccountTest is StakeManagerTest {
uint256 currentEpoch = stakeManager.currentEpoch();
vm.expectRevert(StakeManager.StakeManager__InvalidLimitEpoch.selector);
stakeManager.executeEpoch(currentEpoch + 1);
vm.expectRevert(StakeManager.StakeManager__InvalidLimitEpoch.selector);
stakeManager.executeAccount(address(userVault), currentEpoch + 1);
vm.warp(stakeManager.epochEnd() - 1);
vm.expectRevert(StakeManager.StakeManager__InvalidLimitEpoch.selector);
stakeManager.executeEpoch(currentEpoch + 1);
vm.expectRevert(StakeManager.StakeManager__InvalidLimitEpoch.selector);
stakeManager.executeAccount(address(userVault), currentEpoch + 1);
vm.warp(stakeManager.epochEnd());
stakeManager.executeAccount(address(userVault), currentEpoch + 1);
stakeManager.executeEpoch(currentEpoch + 1);
currentEpoch++;
vm.expectRevert(StakeManager.StakeManager__InvalidLimitEpoch.selector);
stakeManager.executeEpoch(currentEpoch + 1);
vm.expectRevert(StakeManager.StakeManager__InvalidLimitEpoch.selector);
stakeManager.executeAccount(address(userVault), currentEpoch + 1);
vm.warp(stakeManager.epochEnd() + stakeManager.EPOCH_SIZE() - 1);
vm.expectRevert(StakeManager.StakeManager__InvalidLimitEpoch.selector);
stakeManager.executeEpoch(currentEpoch + 2);
vm.expectRevert(StakeManager.StakeManager__InvalidLimitEpoch.selector);
stakeManager.executeAccount(address(userVault), currentEpoch + 2);
stakeManager.executeAccount(address(userVault), currentEpoch + 1);
stakeManager.executeEpoch(currentEpoch + 1);
currentEpoch++;
vm.expectRevert(StakeManager.StakeManager__InvalidLimitEpoch.selector);
stakeManager.executeEpoch(currentEpoch + 1);
vm.expectRevert(StakeManager.StakeManager__InvalidLimitEpoch.selector);
stakeManager.executeAccount(address(userVault), currentEpoch + 1);
}
@ -744,6 +787,34 @@ contract MigrationStakeManagerTest is StakeManagerTest {
}
contract ExecuteEpochTest is MigrationStakeManagerTest {
function test_ExecuteEpochNewEpoch() public {
uint256 firstEpochEnd = stakeManager.epochEnd();
assertEq(stakeManager.currentEpoch(), 0, "Epoch not 0 at start of test");
assertEq(stakeManager.newEpoch(), 0, "New epoch not 0 at start of test");
stakeManager.executeEpoch();
assertEq(stakeManager.currentEpoch(), 0, "Epoch should not increase if no time passed since start");
vm.warp(firstEpochEnd - 1);
assertEq(stakeManager.newEpoch(), 0, "New epoch not 0 if 1 second before epoch end");
stakeManager.executeEpoch();
assertEq(stakeManager.currentEpoch(), 0, "Epoch should not increase if 1 second before epoch end");
vm.warp(firstEpochEnd);
assertEq(stakeManager.newEpoch(), 1, "New epoch should be 1 if exactly at epochEnd");
stakeManager.executeEpoch();
assertEq(stakeManager.currentEpoch(), 1, "Current epoch should increased to 1 if exactly at epochEnd of 0");
vm.warp(firstEpochEnd + 1);
assertEq(stakeManager.newEpoch(), 1, "New epoch should be 1 if 1 second after epochend of 1");
stakeManager.executeEpoch();
assertEq(stakeManager.currentEpoch(), 1, "Current epoch should increase if 1 second after epochend of 1");
vm.warp(firstEpochEnd + (stakeManager.EPOCH_SIZE() * 99));
assertEq(stakeManager.newEpoch(), 100);
stakeManager.executeEpoch();
assertEq(stakeManager.currentEpoch(), 100);
}
function test_ExecuteEpochExecuteEpochAfterEnd() public {
StakeVault userVault = _createStakingAccount(makeAddr("testUser"), 100_000, 0);
@ -770,6 +841,25 @@ contract ExecuteEpochTest is MigrationStakeManagerTest {
stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch());
}
function test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsJumoMany() 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_ExecuteEpochExecuteAccountAfterManyEpochsJumoMany() public {
StakeVault userVault = _createStakingAccount(makeAddr("testUser"), 100_000, 0);
for (uint256 i = 0; i < 10; i++) {
vm.warp(stakeManager.epochEnd());
}
stakeManager.executeAccount(address(userVault));
}
function test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() public {
StakeVault userVault = _createStakingAccount(makeAddr("testUser"), 100_000, 0);
@ -780,6 +870,25 @@ contract ExecuteEpochTest is MigrationStakeManagerTest {
stakeManager.executeAccount(address(userVault), stakeManager.currentEpoch());
}
function test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTimeJumpMany() 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_ExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTimeJumpMany() 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));
}
function test_ExecuteEpochExecuteAccountAfterEpochEnd() public {
StakeVault userVault = _createStakingAccount(makeAddr("testUser"), 100_000, 0);