mirror of https://github.com/logos-co/staking.git
refactor(StakeManager): extract interfaces and rename variables to merge functionalty with StakingRewardStreamer
This commit is contained in:
parent
feb6dd1530
commit
365551b18e
83
.gas-report
83
.gas-report
|
@ -1,41 +1,42 @@
|
|||
| contracts/StakeManager.sol:StakeManager contract | | | | | |
|
||||
|--------------------------------------------------|-----------------|--------|--------|--------|---------|
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 2495816 | 13179 | | | | |
|
||||
| 2512529 | 13257 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| EPOCH_SIZE | 263 | 263 | 263 | 263 | 1498 |
|
||||
| MAX_BOOST | 264 | 264 | 264 | 264 | 637 |
|
||||
| MAX_LOCKUP_PERIOD | 383 | 383 | 383 | 383 | 4 |
|
||||
| EPOCH_SIZE | 285 | 285 | 285 | 285 | 1498 |
|
||||
| MAX_LOCKUP_PERIOD | 361 | 361 | 361 | 361 | 4 |
|
||||
| MAX_MULTIPLIER | 307 | 307 | 307 | 307 | 637 |
|
||||
| MIN_LOCKUP_PERIOD | 264 | 264 | 264 | 264 | 12 |
|
||||
| YEAR | 263 | 263 | 263 | 263 | 637 |
|
||||
| accounts | 1616 | 1616 | 1616 | 1616 | 144285 |
|
||||
| calculateMPToMint | 740 | 740 | 740 | 740 | 1276 |
|
||||
| currentEpoch | 384 | 1050 | 384 | 2384 | 54 |
|
||||
| epochEnd | 649 | 649 | 649 | 2649 | 23677 |
|
||||
| YEAR | 307 | 307 | 307 | 307 | 637 |
|
||||
| acceptUpdate | 23632 | 23632 | 23632 | 23632 | 1 |
|
||||
| accounts | 1572 | 1572 | 1572 | 1572 | 144273 |
|
||||
| calculateMP | 738 | 738 | 738 | 738 | 1276 |
|
||||
| currentEpoch | 406 | 1072 | 406 | 2406 | 54 |
|
||||
| epochEnd | 627 | 627 | 627 | 2627 | 23675 |
|
||||
| epochReward | 1381 | 2881 | 1381 | 5881 | 3 |
|
||||
| executeAccount(address) | 149300 | 149300 | 149300 | 149300 | 2 |
|
||||
| executeAccount(address,uint256) | 26562 | 72246 | 74122 | 200087 | 141872 |
|
||||
| executeEpoch() | 23480 | 120708 | 121865 | 900380 | 23566 |
|
||||
| executeEpoch(uint256) | 23861 | 24497 | 23861 | 26090 | 7 |
|
||||
| expiredStakeStorage | 437 | 2346 | 2437 | 2437 | 22 |
|
||||
| executeAccount(address) | 149349 | 149349 | 149349 | 149349 | 2 |
|
||||
| executeAccount(address,uint256) | 26540 | 72264 | 74140 | 200389 | 141860 |
|
||||
| executeEpoch() | 23458 | 120684 | 121843 | 900358 | 23564 |
|
||||
| executeEpoch(uint256) | 23905 | 24541 | 23905 | 26134 | 7 |
|
||||
| expiredStakeStorage | 394 | 2303 | 2394 | 2394 | 22 |
|
||||
| isTrustedCodehash | 541 | 949 | 541 | 2541 | 680 |
|
||||
| lock | 23818 | 23818 | 23818 | 23818 | 1 |
|
||||
| migrateTo | 23922 | 23928 | 23928 | 23934 | 2 |
|
||||
| leave | 23631 | 23631 | 23631 | 23631 | 1 |
|
||||
| lock | 23862 | 23862 | 23862 | 23862 | 1 |
|
||||
| migration | 417 | 1417 | 1417 | 2417 | 4 |
|
||||
| migrationInitialize | 24624 | 24624 | 24624 | 24624 | 1 |
|
||||
| newEpoch | 441 | 441 | 441 | 441 | 5 |
|
||||
| owner | 2432 | 2432 | 2432 | 2432 | 13 |
|
||||
| pendingMPToBeMinted | 363 | 363 | 363 | 363 | 46436 |
|
||||
| owner | 2410 | 2410 | 2410 | 2410 | 13 |
|
||||
| pendingReward | 408 | 1442 | 2408 | 2408 | 29 |
|
||||
| potentialMP | 408 | 408 | 408 | 408 | 46432 |
|
||||
| previousManager | 275 | 275 | 275 | 275 | 13 |
|
||||
| rewardToken | 293 | 293 | 293 | 293 | 696 |
|
||||
| setTrustedCodehash | 47960 | 47960 | 47960 | 47960 | 139 |
|
||||
| stake | 23983 | 23983 | 23983 | 23983 | 1 |
|
||||
| stakedToken | 272 | 272 | 272 | 272 | 696 |
|
||||
| startMigration | 103602 | 103610 | 103614 | 103614 | 3 |
|
||||
| startTime | 306 | 306 | 306 | 306 | 21 |
|
||||
| stake | 24005 | 24005 | 24005 | 24005 | 1 |
|
||||
| startMigration | 103624 | 103632 | 103636 | 103636 | 3 |
|
||||
| startTime | 264 | 264 | 264 | 264 | 21 |
|
||||
| totalMP | 385 | 385 | 385 | 2385 | 46453 |
|
||||
| totalStaked | 385 | 1785 | 2385 | 2385 | 20 |
|
||||
| totalSupply | 784 | 1965 | 2784 | 2784 | 22 |
|
||||
| totalSupplyBalance | 407 | 1807 | 2407 | 2407 | 20 |
|
||||
| totalSupplyMP | 384 | 384 | 384 | 2384 | 46457 |
|
||||
| unstake | 23841 | 23841 | 23841 | 23841 | 1 |
|
||||
|
||||
|
||||
|
@ -44,13 +45,13 @@
|
|||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 0 | 0 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| acceptMigration | 35311 | 35311 | 35311 | 35311 | 2 |
|
||||
| leave | 35297 | 35297 | 35297 | 35297 | 1 |
|
||||
| lock | 43285 | 90487 | 61938 | 180284 | 7 |
|
||||
| owner | 362 | 362 | 362 | 362 | 679 |
|
||||
| stake | 27265 | 282115 | 265681 | 351644 | 684 |
|
||||
| stakedToken | 212 | 212 | 212 | 212 | 2 |
|
||||
| unstake | 40180 | 96354 | 78700 | 229598 | 11 |
|
||||
| acceptMigration | 35140 | 35140 | 35140 | 35140 | 2 |
|
||||
| leave | 35152 | 35152 | 35152 | 35152 | 1 |
|
||||
| lock | 43329 | 90544 | 61982 | 180383 | 7 |
|
||||
| owner | 351 | 351 | 351 | 351 | 679 |
|
||||
| stake | 27265 | 282111 | 265792 | 351743 | 684 |
|
||||
| stakedToken | 215 | 215 | 215 | 215 | 2 |
|
||||
| unstake | 40157 | 96345 | 78682 | 229644 | 11 |
|
||||
|
||||
|
||||
| contracts/VaultFactory.sol:VaultFactory contract | | | | | |
|
||||
|
@ -58,7 +59,7 @@
|
|||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 0 | 0 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| createVault | 696530 | 696530 | 696530 | 696530 | 683 |
|
||||
| createVault | 682103 | 682103 | 682103 | 682103 | 683 |
|
||||
| setStakeManager | 23710 | 26669 | 26076 | 30222 | 3 |
|
||||
| stakeManager | 368 | 1868 | 2368 | 2368 | 4 |
|
||||
|
||||
|
@ -68,7 +69,7 @@
|
|||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 0 | 0 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| getExpiredMP | 2427 | 2427 | 2427 | 2427 | 23727 |
|
||||
| getExpiredMP | 2427 | 2427 | 2427 | 2427 | 23725 |
|
||||
| transferOwnership | 28533 | 28533 | 28533 | 28533 | 1 |
|
||||
|
||||
|
||||
|
@ -77,24 +78,24 @@
|
|||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 0 | 0 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| approve | 46175 | 46239 | 46199 | 46367 | 679 |
|
||||
| balanceOf | 561 | 2107 | 2561 | 2561 | 30746 |
|
||||
| approve | 46175 | 46241 | 46199 | 46367 | 679 |
|
||||
| balanceOf | 561 | 2107 | 2561 | 2561 | 30744 |
|
||||
|
||||
|
||||
| script/Deploy.s.sol:Deploy contract | | | | | |
|
||||
|-------------------------------------|-----------------|---------|---------|---------|---------|
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 6149062 | 29676 | | | | |
|
||||
| 6149710 | 29676 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| run | 5343965 | 5343965 | 5343965 | 5343965 | 66 |
|
||||
| run | 5343984 | 5343984 | 5343984 | 5343984 | 66 |
|
||||
|
||||
|
||||
| script/DeployMigrationStakeManager.s.sol:DeployMigrationStakeManager contract | | | | | |
|
||||
|-------------------------------------------------------------------------------|-----------------|---------|---------|---------|---------|
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 3312594 | 16444 | | | | |
|
||||
| 3329385 | 16522 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| run | 2330294 | 2330294 | 2330294 | 2330294 | 19 |
|
||||
| run | 2345854 | 2345854 | 2345854 | 2345854 | 19 |
|
||||
|
||||
|
||||
| script/DeploymentConfig.s.sol:DeploymentConfig contract | | | | | |
|
||||
|
@ -117,9 +118,9 @@
|
|||
| test/script/DeployBroken.s.sol:DeployBroken contract | | | | | |
|
||||
|------------------------------------------------------|-----------------|---------|---------|---------|---------|
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 4833800 | 23474 | | | | |
|
||||
| 4834448 | 23474 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| run | 4183787 | 4183787 | 4183787 | 4183787 | 1 |
|
||||
| run | 4183805 | 4183805 | 4183805 | 4183805 | 1 |
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,67 +1,67 @@
|
|||
CreateVaultTest:testDeployment() (gas: 9774)
|
||||
CreateVaultTest:test_createVault() (gas: 713999)
|
||||
CreateVaultTest:test_createVault() (gas: 699564)
|
||||
ExecuteAccountTest:testDeployment() (gas: 28828)
|
||||
ExecuteAccountTest:test_ExecuteAccountLimit() (gas: 1579141)
|
||||
ExecuteAccountTest:test_ExecuteAccountMintMP() (gas: 5295554)
|
||||
ExecuteAccountTest:test_RevertWhen_InvalidLimitEpoch() (gas: 1787120)
|
||||
ExecuteAccountTest:test_ShouldNotMintMoreThanCap() (gas: 321023887)
|
||||
ExecuteAccountTest:test_ExecuteAccountLimit() (gas: 1564735)
|
||||
ExecuteAccountTest:test_ExecuteAccountMintMP() (gas: 5252076)
|
||||
ExecuteAccountTest:test_RevertWhen_InvalidLimitEpoch() (gas: 1772992)
|
||||
ExecuteAccountTest:test_ShouldNotMintMoreThanCap() (gas: 320738328)
|
||||
ExecuteEpochTest:testDeployment() (gas: 28829)
|
||||
ExecuteEpochTest:testNewDeployment() (gas: 30901)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterEpochEnd() (gas: 1367865)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsJumoMany() (gas: 1385552)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() (gas: 1630963)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTimeJumpMany() (gas: 1395267)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteEpochAfterEnd() (gas: 1928353)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochs() (gas: 2511195)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsJumoMany() (gas: 1479072)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() (gas: 2520931)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTimeJumpMany() (gas: 1488764)
|
||||
ExecuteEpochTest:test_ExecuteEpochNewEpoch() (gas: 1083687)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterEpochEnd() (gas: 1353515)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsJumoMany() (gas: 1371040)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() (gas: 1616635)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTimeJumpMany() (gas: 1380975)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteEpochAfterEnd() (gas: 1914097)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochs() (gas: 2496675)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsJumoMany() (gas: 1464542)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTime() (gas: 2506631)
|
||||
ExecuteEpochTest:test_ExecuteEpochExecuteEpochExecuteAccountAfterManyEpochsWithBrokenTimeJumpMany() (gas: 1474454)
|
||||
ExecuteEpochTest:test_ExecuteEpochNewEpoch() (gas: 1083709)
|
||||
ExecuteEpochTest:test_ExecuteEpochShouldIncreaseEpoch() (gas: 92344)
|
||||
ExecuteEpochTest:test_ExecuteEpochShouldIncreasePendingReward() (gas: 256338)
|
||||
ExecuteEpochTest:test_ExecuteEpochShouldIncreasePendingReward() (gas: 256303)
|
||||
ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochBeforeEnd() (gas: 39028)
|
||||
ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 149748)
|
||||
ExecuteEpochTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 149770)
|
||||
LeaveTest:testDeployment() (gas: 28806)
|
||||
LeaveTest:test_RevertWhen_NoPendingMigration() (gas: 1329731)
|
||||
LeaveTest:test_RevertWhen_SenderIsNotVault() (gas: 31995)
|
||||
LeaveTest:test_RevertWhen_NoPendingMigration() (gas: 1315087)
|
||||
LeaveTest:test_RevertWhen_SenderIsNotVault() (gas: 31696)
|
||||
LockTest:testDeployment() (gas: 28806)
|
||||
LockTest:test_NewLockupPeriod() (gas: 1328279)
|
||||
LockTest:test_RevertWhen_InvalidNewLockupPeriod() (gas: 1303028)
|
||||
LockTest:test_RevertWhen_InvalidUpdateLockupPeriod() (gas: 1543611)
|
||||
LockTest:test_RevertWhen_SenderIsNotVault() (gas: 31812)
|
||||
LockTest:test_ShouldIncreaseBonusMP() (gas: 1310872)
|
||||
LockTest:test_UpdateLockupPeriod() (gas: 1579753)
|
||||
LockTest:test_NewLockupPeriod() (gas: 1313929)
|
||||
LockTest:test_RevertWhen_InvalidNewLockupPeriod() (gas: 1288722)
|
||||
LockTest:test_RevertWhen_InvalidUpdateLockupPeriod() (gas: 1529335)
|
||||
LockTest:test_RevertWhen_SenderIsNotVault() (gas: 31856)
|
||||
LockTest:test_ShouldIncreaseBonusMP() (gas: 1296480)
|
||||
LockTest:test_UpdateLockupPeriod() (gas: 1565467)
|
||||
MigrateTest:testDeployment() (gas: 28806)
|
||||
MigrateTest:test_RevertWhen_NoPendingMigration() (gas: 1293753)
|
||||
MigrateTest:test_RevertWhen_SenderIsNotVault() (gas: 32007)
|
||||
MigrateTest:test_RevertWhen_NoPendingMigration() (gas: 1279254)
|
||||
MigrateTest:test_RevertWhen_SenderIsNotVault() (gas: 31738)
|
||||
MigrationInitializeTest:testDeployment() (gas: 28806)
|
||||
MigrationInitializeTest:test_RevertWhen_MigrationPending() (gas: 5241726)
|
||||
MigrationInitializeTest:test_RevertWhen_MigrationPending() (gas: 5275163)
|
||||
MigrationStakeManagerTest:testDeployment() (gas: 28806)
|
||||
MigrationStakeManagerTest:testNewDeployment() (gas: 30945)
|
||||
MigrationStakeManagerTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 149713)
|
||||
MigrationStakeManagerTest:test_ExecuteEpochShouldNotIncreaseEpochInMigration() (gas: 149735)
|
||||
SetStakeManagerTest:testDeployment() (gas: 9774)
|
||||
SetStakeManagerTest:test_RevertWhen_InvalidStakeManagerAddress() (gas: 63105)
|
||||
SetStakeManagerTest:test_SetStakeManager() (gas: 41301)
|
||||
StakeManagerTest:testDeployment() (gas: 28578)
|
||||
StakeTest:testDeployment() (gas: 28784)
|
||||
StakeTest:test_RevertWhen_InvalidLockupPeriod() (gas: 1078067)
|
||||
StakeTest:test_RevertWhen_Restake() (gas: 1318488)
|
||||
StakeTest:test_RevertWhen_RestakeWithLock() (gas: 1322318)
|
||||
StakeTest:test_RevertWhen_SenderIsNotVault() (gas: 32018)
|
||||
StakeTest:test_RevertWhen_StakeIsTooLow() (gas: 817324)
|
||||
StakeTest:test_RevertWhen_InvalidLockupPeriod() (gas: 1061162)
|
||||
StakeTest:test_RevertWhen_Restake() (gas: 1304182)
|
||||
StakeTest:test_RevertWhen_RestakeWithLock() (gas: 1308012)
|
||||
StakeTest:test_RevertWhen_SenderIsNotVault() (gas: 32040)
|
||||
StakeTest:test_RevertWhen_StakeIsTooLow() (gas: 802919)
|
||||
StakeTest:test_RevertWhen_StakeTokenTransferFails() (gas: 211363)
|
||||
StakeTest:test_StakeWithLockBonusMP() (gas: 2356570)
|
||||
StakeTest:test_StakeWithoutLockUpTimeMintsMultiplierPoints() (gas: 1314730)
|
||||
StakedTokenTest:testStakeToken() (gas: 7616)
|
||||
StakeTest:test_StakeWithLockBonusMP() (gas: 2327824)
|
||||
StakeTest:test_StakeWithoutLockUpTimeMintsMultiplierPoints() (gas: 1300293)
|
||||
StakedTokenTest:testStakeToken() (gas: 7619)
|
||||
UnstakeTest:testDeployment() (gas: 28828)
|
||||
UnstakeTest:test_RevertWhen_AmountMoreThanBalance() (gas: 1299096)
|
||||
UnstakeTest:test_RevertWhen_FundsLocked() (gas: 1343662)
|
||||
UnstakeTest:test_RevertWhen_AmountMoreThanBalance() (gas: 1284745)
|
||||
UnstakeTest:test_RevertWhen_FundsLocked() (gas: 1329288)
|
||||
UnstakeTest:test_RevertWhen_SenderIsNotVault() (gas: 31879)
|
||||
UnstakeTest:test_UnstakeShouldBurnMultiplierPoints() (gas: 6450026)
|
||||
UnstakeTest:test_UnstakeShouldReturnFund_NoLockUp() (gas: 1321258)
|
||||
UnstakeTest:test_UnstakeShouldReturnFund_WithLockUp() (gas: 1438734)
|
||||
UnstakeTest:test_UnstakeShouldBurnMultiplierPoints() (gas: 6438180)
|
||||
UnstakeTest:test_UnstakeShouldReturnFund_NoLockUp() (gas: 1306895)
|
||||
UnstakeTest:test_UnstakeShouldReturnFund_WithLockUp() (gas: 1424419)
|
||||
UserFlowsTest:testDeployment() (gas: 28806)
|
||||
UserFlowsTest:test_PendingMPToBeMintedCannotBeGreaterThanTotalSupplyMP(uint8,uint128) (runs: 106, μ: 130945931, ~: 130391929)
|
||||
UserFlowsTest:test_StakeWithLockUpTimeLocksStake() (gas: 1479843)
|
||||
UserFlowsTest:test_StakedSupplyShouldIncreaseAndDecreaseAgain() (gas: 2496411)
|
||||
UserFlowsTest:test_PendingMPToBeMintedCannotBeGreaterThanTotalSupplyMP(uint8,uint128) (runs: 106, μ: 130825949, ~: 130282915)
|
||||
UserFlowsTest:test_StakeWithLockUpTimeLocksStake() (gas: 1463005)
|
||||
UserFlowsTest:test_StakedSupplyShouldIncreaseAndDecreaseAgain() (gas: 2467653)
|
||||
VaultFactoryTest:testDeployment() (gas: 9774)
|
|
@ -5,7 +5,7 @@
|
|||
"certora/helpers/ERC20A.sol"
|
||||
],
|
||||
"link" : [
|
||||
"StakeManager:stakedToken=ERC20A",
|
||||
"StakeManager:rewardToken=ERC20A",
|
||||
"StakeManager:expiredStakeStorage=ExpiredStakeStorageA"
|
||||
],
|
||||
"msg": "Verifying StakeManager.sol",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"certora/helpers/ExpiredStakeStorageA.sol"
|
||||
],
|
||||
"link" : [
|
||||
"StakeManager:stakedToken=ERC20A",
|
||||
"StakeManager:rewardToken=ERC20A",
|
||||
"StakeManager:expiredStakeStorage=ExpiredStakeStorageA"
|
||||
],
|
||||
"msg": "Verifying StakeManager ProcessAccount",
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"certora/helpers/ERC20A.sol"
|
||||
],
|
||||
"link" : [
|
||||
"StakeManager:stakedToken=ERC20A",
|
||||
"StakeManager:rewardToken=ERC20A",
|
||||
"StakeManager:expiredStakeStorage=ExpiredStakeStorageA",
|
||||
],
|
||||
"msg": "Verifying StakeManager.sol",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
],
|
||||
"link" : [
|
||||
"StakeVault:STAKED_TOKEN=ERC20A",
|
||||
"StakeManager:stakedToken=ERC20A",
|
||||
"StakeManager:rewardToken=ERC20A",
|
||||
"StakeManager:expiredStakeStorage=ExpiredStakeStorageA",
|
||||
"StakeVault:stakeManager=StakeManager"
|
||||
],
|
||||
|
|
|
@ -4,8 +4,8 @@ using ERC20A as staked;
|
|||
|
||||
methods {
|
||||
function staked.balanceOf(address) external returns (uint256) envfree;
|
||||
function totalSupplyBalance() external returns (uint256) envfree;
|
||||
function totalSupplyMP() external returns (uint256) envfree;
|
||||
function totalStaked() external returns (uint256) envfree;
|
||||
function totalMP() external returns (uint256) envfree;
|
||||
function previousManager() external returns (address) envfree;
|
||||
function _.migrateFrom(address, bool, StakeManager.Account) external => NONDET;
|
||||
function _.increaseTotalMP(uint256) external => NONDET;
|
||||
|
@ -22,7 +22,8 @@ function mulDivSummary(uint256 a, uint256 b, uint256 c) returns uint256 {
|
|||
|
||||
function isMigrationfunction(method f) returns bool {
|
||||
return
|
||||
f.selector == sig:migrateTo(bool).selector ||
|
||||
f.selector == sig:acceptUpdate().selector ||
|
||||
f.selector == sig:leave().selector ||
|
||||
f.selector == sig:transferNonPending().selector;
|
||||
}
|
||||
|
||||
|
@ -65,13 +66,13 @@ hook Sload uint256 newValue accounts[KEY address addr].totalMP {
|
|||
}
|
||||
|
||||
invariant sumOfBalancesIsTotalSupplyBalance()
|
||||
sumOfBalances == to_mathint(totalSupplyBalance())
|
||||
sumOfBalances == to_mathint(totalStaked())
|
||||
filtered {
|
||||
m -> !requiresPreviousManager(m) && !requiresNextManager(m)
|
||||
}
|
||||
|
||||
invariant sumOfMultipliersIsMultiplierSupply()
|
||||
sumOfMultipliers == to_mathint(totalSupplyMP())
|
||||
sumOfMultipliers == to_mathint(totalMP())
|
||||
filtered {
|
||||
m -> !requiresPreviousManager(m) && !requiresNextManager(m)
|
||||
}
|
||||
|
|
|
@ -4,14 +4,14 @@ using ERC20A as staked;
|
|||
|
||||
methods {
|
||||
function staked.balanceOf(address) external returns (uint256) envfree;
|
||||
function totalSupplyBalance() external returns (uint256) envfree;
|
||||
function totalSupplyMP() external returns (uint256) envfree;
|
||||
function totalStaked() external returns (uint256) envfree;
|
||||
function totalMP() external returns (uint256) envfree;
|
||||
function totalMPPerEpoch() external returns (uint256) envfree;
|
||||
function accounts(address) external returns(address, uint256, uint256, uint256, uint256, uint256, uint256, uint256) envfree;
|
||||
|
||||
function _processAccount(StakeManager.Account storage account, uint256 _limitEpoch) internal with(env e) => markAccountProccessed(e.msg.sender, _limitEpoch);
|
||||
function _.migrationInitialize(uint256,uint256,uint256,uint256,uint256,uint256,uint256) external => NONDET;
|
||||
function pendingMPToBeMinted() external returns (uint256) envfree;
|
||||
function potentialMP() external returns (uint256) envfree;
|
||||
}
|
||||
|
||||
// keeps track of the last epoch an account was processed
|
||||
|
|
|
@ -5,13 +5,13 @@ using StakeManagerNew as newStakeManager;
|
|||
|
||||
methods {
|
||||
function staked.balanceOf(address) external returns (uint256) envfree;
|
||||
function totalSupplyBalance() external returns (uint256) envfree;
|
||||
function totalSupplyMP() external returns (uint256) envfree;
|
||||
function totalStaked() external returns (uint256) envfree;
|
||||
function totalMP() external returns (uint256) envfree;
|
||||
function previousManager() external returns (address) envfree;
|
||||
function accounts(address) external returns(address, uint256, uint256, uint256, uint256, uint256, uint256, uint256) envfree;
|
||||
|
||||
function _.migrationInitialize(uint256,uint256,uint256,uint256,uint256,uint256,uint256) external => DISPATCHER(true);
|
||||
function StakeManagerNew.totalSupplyBalance() external returns (uint256) envfree;
|
||||
function StakeManagerNew.totalStaked() external returns (uint256) envfree;
|
||||
}
|
||||
|
||||
definition blockedWhenMigrating(method f) returns bool = (
|
||||
|
@ -25,7 +25,8 @@ definition blockedWhenMigrating(method f) returns bool = (
|
|||
);
|
||||
|
||||
definition blockedWhenNotMigrating(method f) returns bool = (
|
||||
f.selector == sig:migrateTo(bool).selector ||
|
||||
f.selector == sig:acceptUpdate().selector ||
|
||||
f.selector == sig:leave().selector ||
|
||||
f.selector == sig:transferNonPending().selector
|
||||
);
|
||||
|
||||
|
@ -89,7 +90,7 @@ rule startMigrationCorrect {
|
|||
startMigration(e, newContract);
|
||||
|
||||
assert currentContract.migration == newContract;
|
||||
assert newStakeManager.totalSupplyBalance() == currentContract.totalSupplyBalance();
|
||||
assert newStakeManager.totalStaked() == currentContract.totalStaked();
|
||||
}
|
||||
|
||||
rule migrationLockedIn(method f) filtered {
|
||||
|
|
|
@ -7,7 +7,8 @@ definition requiresPreviousManager(method f) returns bool = (
|
|||
);
|
||||
|
||||
definition requiresNextManager(method f) returns bool = (
|
||||
f.selector == sig:_stakeManager.migrateTo(bool).selector ||
|
||||
f.selector == sig:_stakeManager.acceptUpdate().selector ||
|
||||
f.selector == sig:_stakeManager.leave().selector ||
|
||||
f.selector == sig:_stakeManager.transferNonPending().selector
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.18;
|
||||
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import { ITrustedCodehashAccess } from "./access/ITrustedCodehashAccess.sol";
|
||||
|
||||
interface IStakeManager is ITrustedCodehashAccess {
|
||||
error StakeManager__FundsLocked();
|
||||
error StakeManager__InvalidLockTime();
|
||||
error StakeManager__InsufficientFunds();
|
||||
error StakeManager__StakeIsTooLow();
|
||||
|
||||
function MIN_LOCKUP_PERIOD() external pure returns (uint256);
|
||||
function MAX_LOCKUP_PERIOD() external pure returns (uint256);
|
||||
|
||||
function stake(uint256 _amount, uint256 _seconds) external;
|
||||
function unstake(uint256 _amount) external;
|
||||
function lock(uint256 _secondsIncrease) external;
|
||||
|
||||
function acceptUpdate() external returns (IStakeManager _migrated);
|
||||
function leave() external returns (bool _leaveAccepted);
|
||||
|
||||
function totalStaked() external view returns (uint256 _totalStaked);
|
||||
function getStakedBalance(address _vault) external view returns (uint256 _balance);
|
||||
function potentialMP() external view returns (uint256 _potentialMP);
|
||||
function totalMP() external view returns (uint256 _totalMP);
|
||||
|
||||
function totalSupply() external view returns (uint256 _totalSupply);
|
||||
function totalSupplyMinted() external view returns (uint256 _totalSupply);
|
||||
function pendingReward() external view returns (uint256 _pendingReward);
|
||||
|
||||
function calculateMP(uint256 _balance, uint256 _deltaTime) external pure returns (uint256);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// SPDX-License-Identifier: MIT-1.0
|
||||
pragma solidity ^0.8.18;
|
||||
|
||||
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
|
||||
|
||||
abstract contract MultiplierPointMath {
|
||||
uint256 public constant YEAR = 365 days;
|
||||
uint256 public constant MP_APY = 1;
|
||||
uint256 public constant MAX_MULTIPLIER = 4;
|
||||
|
||||
/**
|
||||
* @notice Calculates multiplier points accurred for given `_amount` and `_seconds` time passed
|
||||
* @param _amount quantity of tokens
|
||||
* @param _seconds time in seconds
|
||||
* @return _accuredMP points accured for given `_amount` and `_seconds`
|
||||
*/
|
||||
function _calculateAccuredMP(uint256 _amount, uint256 _seconds) internal pure returns (uint256 _accuredMP) {
|
||||
return Math.mulDiv(_amount, _seconds, YEAR) * MP_APY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates bonus multiplier points for given `_amount` and `_lockedSeconds`
|
||||
* @param _amount quantity of tokens
|
||||
* @param _lockedSeconds time in seconds locked
|
||||
* @return _bonusMP bonus multiplier points for given `_amount` and `_lockedSeconds`
|
||||
*/
|
||||
function _calculateBonusMP(uint256 _amount, uint256 _lockedSeconds) internal pure returns (uint256 _bonusMP) {
|
||||
_bonusMP = _amount;
|
||||
if (_lockedSeconds > 0) {
|
||||
_bonusMP += _calculateAccuredMP(_amount, _lockedSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates minimum stake to genarate 1 multiplier points for given `_seconds`
|
||||
* @param _seconds time in seconds
|
||||
* @return _minimumStake minimum quantity of tokens
|
||||
*/
|
||||
function _calculateMinimumStake(uint256 _seconds) internal pure returns (uint256 _minimumStake) {
|
||||
return YEAR / (_seconds * MP_APY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates maximum stake a given `_amount` can be generated with `MAX_MULTIPLIER`
|
||||
* @param _amount quantity of tokens
|
||||
* @return _maxMPAccured maximum quantity of muliplier points that can be generated for given `_amount`
|
||||
*/
|
||||
function _calculateMaxAccuredMP(uint256 _amount) internal pure returns (uint256 _maxMPAccured) {
|
||||
return _calculateAccuredMP(_amount, MAX_MULTIPLIER * YEAR);
|
||||
}
|
||||
}
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
pragma solidity ^0.8.18;
|
||||
|
||||
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
|
||||
|
||||
import { TrustedCodehashAccess } from "./access/TrustedCodehashAccess.sol";
|
||||
import { ExpiredStakeStorage } from "./storage/ExpiredStakeStorage.sol";
|
||||
import { IStakeManager } from "./IStakeManager.sol";
|
||||
import { MultiplierPointMath } from "./MultiplierPointMath.sol";
|
||||
import { StakeVault } from "./StakeVault.sol";
|
||||
|
||||
contract StakeManager is TrustedCodehashAccess {
|
||||
error StakeManager__FundsLocked();
|
||||
error StakeManager__InvalidLockTime();
|
||||
contract StakeManager is IStakeManager, MultiplierPointMath, TrustedCodehashAccess {
|
||||
error StakeManager__NoPendingMigration();
|
||||
error StakeManager__PendingMigration();
|
||||
error StakeManager__SenderIsNotPreviousStakeManager();
|
||||
|
@ -19,9 +19,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
error StakeManager__AccountNotInitialized();
|
||||
error StakeManager__InvalidMigration();
|
||||
error StakeManager__AlreadyProcessedEpochs();
|
||||
error StakeManager__InsufficientFunds();
|
||||
error StakeManager__AlreadyStaked();
|
||||
error StakeManager__StakeIsTooLow();
|
||||
|
||||
struct Account {
|
||||
address rewardAddress;
|
||||
|
@ -37,15 +35,12 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
struct Epoch {
|
||||
uint256 epochReward;
|
||||
uint256 totalSupply;
|
||||
uint256 estimatedMP;
|
||||
uint256 potentialMP;
|
||||
}
|
||||
|
||||
uint256 public constant EPOCH_SIZE = 1 weeks;
|
||||
uint256 public constant YEAR = 365 days;
|
||||
uint256 public constant MIN_LOCKUP_PERIOD = 2 weeks;
|
||||
uint256 public constant MAX_LOCKUP_PERIOD = 4 * YEAR; // 4 years
|
||||
uint256 public constant MP_APY = 1;
|
||||
uint256 public constant MAX_BOOST = 4;
|
||||
|
||||
mapping(address index => Account value) public accounts;
|
||||
mapping(uint256 index => Epoch value) public epochs;
|
||||
|
@ -54,9 +49,9 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
uint256 public pendingReward;
|
||||
uint256 public immutable startTime;
|
||||
|
||||
uint256 public pendingMPToBeMinted;
|
||||
uint256 public totalSupplyMP;
|
||||
uint256 public totalSupplyBalance;
|
||||
uint256 public potentialMP;
|
||||
uint256 public totalMP;
|
||||
uint256 public totalStaked;
|
||||
uint256 public totalMPPerEpoch;
|
||||
|
||||
ExpiredStakeStorage public expiredStakeStorage;
|
||||
|
@ -65,7 +60,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
|
||||
StakeManager public migration;
|
||||
StakeManager public immutable previousManager;
|
||||
ERC20 public immutable stakedToken;
|
||||
IERC20 public immutable rewardToken;
|
||||
|
||||
modifier onlyAccountInitialized(address account) {
|
||||
if (accounts[account].lockUntil == 0) {
|
||||
|
@ -117,26 +112,26 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
totalMPPerEpoch -= expiredMP;
|
||||
expiredStakeStorage.deleteExpiredMP(tempCurrentEpoch);
|
||||
}
|
||||
uint256 epochEstimatedMP = totalMPPerEpoch;
|
||||
uint256 epochPotentialMP = totalMPPerEpoch;
|
||||
if (tempCurrentEpoch == currentEpoch) {
|
||||
epochEstimatedMP -= currentEpochTotalExpiredMP;
|
||||
epochPotentialMP -= currentEpochTotalExpiredMP;
|
||||
currentEpochTotalExpiredMP = 0;
|
||||
thisEpoch.epochReward = epochReward();
|
||||
pendingReward += thisEpoch.epochReward;
|
||||
}
|
||||
|
||||
pendingMPToBeMinted += epochEstimatedMP;
|
||||
thisEpoch.estimatedMP = epochEstimatedMP;
|
||||
potentialMP += epochPotentialMP;
|
||||
thisEpoch.potentialMP = epochPotentialMP;
|
||||
thisEpoch.totalSupply = totalSupply();
|
||||
tempCurrentEpoch++;
|
||||
}
|
||||
currentEpoch = tempCurrentEpoch;
|
||||
}
|
||||
|
||||
constructor(address _stakedToken, address _previousManager) {
|
||||
constructor(address _REWARD_TOKEN, address _previousManager) {
|
||||
startTime = (_previousManager == address(0)) ? block.timestamp : StakeManager(_previousManager).startTime();
|
||||
previousManager = StakeManager(_previousManager);
|
||||
stakedToken = ERC20(_stakedToken);
|
||||
rewardToken = IERC20(_REWARD_TOKEN);
|
||||
if (address(previousManager) != address(0)) {
|
||||
expiredStakeStorage = previousManager.expiredStakeStorage();
|
||||
} else {
|
||||
|
@ -147,36 +142,32 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
/**
|
||||
* Increases balance of msg.sender;
|
||||
* @param _amount Amount of balance being staked.
|
||||
* @param _secondsToLock Seconds of lockup time. 0 means no lockup.
|
||||
* @param _seconds Seconds of lockup time. 0 means no lockup.
|
||||
*
|
||||
* @dev Reverts when resulting locked time is not in range of [MIN_LOCKUP_PERIOD, MAX_LOCKUP_PERIOD]
|
||||
* @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 onlyTrustedCodehash noPendingMigration {
|
||||
function stake(uint256 _amount, uint256 _seconds) external onlyTrustedCodehash noPendingMigration {
|
||||
finalizeEpoch(newEpoch());
|
||||
if (accounts[msg.sender].balance > 0) {
|
||||
revert StakeManager__AlreadyStaked();
|
||||
}
|
||||
if (_secondsToLock != 0 && (_secondsToLock < MIN_LOCKUP_PERIOD || _secondsToLock > MAX_LOCKUP_PERIOD)) {
|
||||
if (_seconds != 0 && (_seconds < MIN_LOCKUP_PERIOD || _seconds > MAX_LOCKUP_PERIOD)) {
|
||||
revert StakeManager__InvalidLockTime();
|
||||
}
|
||||
|
||||
//mp estimation
|
||||
uint256 mpPerEpoch = _getMPToMint(_amount, EPOCH_SIZE);
|
||||
uint256 mpPerEpoch = _calculateAccuredMP(_amount, EPOCH_SIZE);
|
||||
if (mpPerEpoch < 1) {
|
||||
revert StakeManager__StakeIsTooLow();
|
||||
}
|
||||
uint256 currentEpochExpiredMP = mpPerEpoch - _getMPToMint(_amount, epochEnd() - block.timestamp);
|
||||
uint256 maxMpToMint = _getMPToMint(_amount, MAX_BOOST * YEAR) + currentEpochExpiredMP;
|
||||
uint256 currentEpochExpiredMP = mpPerEpoch - _calculateAccuredMP(_amount, epochEnd() - block.timestamp);
|
||||
uint256 maxMpToMint = _calculateMaxAccuredMP(_amount) + currentEpochExpiredMP;
|
||||
uint256 epochAmountToReachMpLimit = (maxMpToMint) / mpPerEpoch;
|
||||
uint256 mpLimitEpoch = currentEpoch + epochAmountToReachMpLimit;
|
||||
uint256 lastEpochAmountToMint = ((mpPerEpoch * (epochAmountToReachMpLimit + 1)) - maxMpToMint);
|
||||
uint256 bonusMP = _amount;
|
||||
if (_secondsToLock > 0) {
|
||||
//bonus for lock time
|
||||
bonusMP += _getMPToMint(_amount, _secondsToLock);
|
||||
}
|
||||
uint256 bonusMP = _calculateBonusMP(_amount, _seconds);
|
||||
|
||||
// account initialization
|
||||
accounts[msg.sender] = Account({
|
||||
|
@ -185,14 +176,14 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
bonusMP: bonusMP,
|
||||
totalMP: bonusMP,
|
||||
lastMint: block.timestamp,
|
||||
lockUntil: block.timestamp + _secondsToLock,
|
||||
lockUntil: block.timestamp + _seconds,
|
||||
epoch: currentEpoch,
|
||||
mpLimitEpoch: mpLimitEpoch
|
||||
});
|
||||
|
||||
//update global storage
|
||||
totalSupplyMP += bonusMP;
|
||||
totalSupplyBalance += _amount;
|
||||
totalMP += bonusMP;
|
||||
totalStaked += _amount;
|
||||
currentEpochTotalExpiredMP += currentEpochExpiredMP;
|
||||
totalMPPerEpoch += mpPerEpoch;
|
||||
expiredStakeStorage.incrementExpiredMP(mpLimitEpoch, lastEpochAmountToMint);
|
||||
|
@ -221,7 +212,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
uint256 reducedMP = Math.mulDiv(_amount, account.totalMP, account.balance);
|
||||
uint256 reducedInitialMP = Math.mulDiv(_amount, account.bonusMP, account.balance);
|
||||
|
||||
uint256 mpPerEpoch = _getMPToMint(account.balance, EPOCH_SIZE);
|
||||
uint256 mpPerEpoch = _calculateAccuredMP(account.balance, EPOCH_SIZE);
|
||||
expiredStakeStorage.decrementExpiredMP(account.mpLimitEpoch, mpPerEpoch);
|
||||
if (account.mpLimitEpoch < currentEpoch) {
|
||||
totalMPPerEpoch -= mpPerEpoch;
|
||||
|
@ -231,18 +222,18 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
account.balance -= _amount;
|
||||
account.bonusMP -= reducedInitialMP;
|
||||
account.totalMP -= reducedMP;
|
||||
totalSupplyBalance -= _amount;
|
||||
totalSupplyMP -= reducedMP;
|
||||
totalStaked -= _amount;
|
||||
totalMP -= reducedMP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Locks entire balance for more amount of time.
|
||||
* @param _secondsToIncreaseLock Seconds to increase in locked time. If stake is unlocked, increases from
|
||||
* @param _secondsIncrease Seconds to increase in locked time. If stake is unlocked, increases from
|
||||
* block.timestamp.
|
||||
*
|
||||
* @dev Reverts when resulting locked time is not in range of [MIN_LOCKUP_PERIOD, MAX_LOCKUP_PERIOD]
|
||||
*/
|
||||
function lock(uint256 _secondsToIncreaseLock)
|
||||
function lock(uint256 _secondsIncrease)
|
||||
external
|
||||
onlyTrustedCodehash
|
||||
onlyAccountInitialized(msg.sender)
|
||||
|
@ -255,11 +246,11 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
uint256 deltaTime;
|
||||
if (lockUntil < block.timestamp) {
|
||||
//if unlocked, increase from now
|
||||
lockUntil = block.timestamp + _secondsToIncreaseLock;
|
||||
deltaTime = _secondsToIncreaseLock;
|
||||
lockUntil = block.timestamp + _secondsIncrease;
|
||||
deltaTime = _secondsIncrease;
|
||||
} else {
|
||||
//if locked, increase from lock until
|
||||
lockUntil += _secondsToIncreaseLock;
|
||||
lockUntil += _secondsIncrease;
|
||||
deltaTime = lockUntil - block.timestamp;
|
||||
}
|
||||
//checks if the lock time is in range
|
||||
|
@ -267,14 +258,14 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
revert StakeManager__InvalidLockTime();
|
||||
}
|
||||
//mints bonus multiplier points for seconds increased
|
||||
uint256 bonusMP = _getMPToMint(account.balance, _secondsToIncreaseLock);
|
||||
uint256 bonusMP = _calculateAccuredMP(account.balance, _secondsIncrease);
|
||||
|
||||
//update account storage
|
||||
account.lockUntil = lockUntil;
|
||||
account.bonusMP += bonusMP;
|
||||
account.totalMP += bonusMP;
|
||||
//update global storage
|
||||
totalSupplyMP += bonusMP;
|
||||
totalMP += bonusMP;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -331,16 +322,10 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
revert StakeManager__InvalidMigration();
|
||||
}
|
||||
migration = _migration;
|
||||
stakedToken.transfer(address(migration), epochReward());
|
||||
rewardToken.transfer(address(migration), epochReward());
|
||||
expiredStakeStorage.transferOwnership(address(_migration));
|
||||
migration.migrationInitialize(
|
||||
currentEpoch,
|
||||
totalSupplyMP,
|
||||
totalSupplyBalance,
|
||||
startTime,
|
||||
totalMPPerEpoch,
|
||||
pendingMPToBeMinted,
|
||||
currentEpochTotalExpiredMP
|
||||
currentEpoch, totalMP, totalStaked, startTime, totalMPPerEpoch, potentialMP, currentEpochTotalExpiredMP
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -348,17 +333,17 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
* @dev Callable automatically from old StakeManager.startMigration(address)
|
||||
* @notice Initilizes migration process
|
||||
* @param _currentEpoch epoch of old manager
|
||||
* @param _totalSupplyMP MP supply on old manager
|
||||
* @param _totalSupplyBalance stake supply on old manager
|
||||
* @param _totalMP MP supply on old manager
|
||||
* @param _totalStaked stake supply on old manager
|
||||
* @param _startTime start time of old manager
|
||||
*/
|
||||
function migrationInitialize(
|
||||
uint256 _currentEpoch,
|
||||
uint256 _totalSupplyMP,
|
||||
uint256 _totalSupplyBalance,
|
||||
uint256 _totalMP,
|
||||
uint256 _totalStaked,
|
||||
uint256 _startTime,
|
||||
uint256 _totalMPPerEpoch,
|
||||
uint256 _pendingMPToBeMinted,
|
||||
uint256 _potentialMP,
|
||||
uint256 _currentEpochExpiredMP
|
||||
)
|
||||
external
|
||||
|
@ -372,10 +357,10 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
revert StakeManager__InvalidMigration();
|
||||
}
|
||||
currentEpoch = _currentEpoch;
|
||||
totalSupplyMP = _totalSupplyMP;
|
||||
totalSupplyBalance = _totalSupplyBalance;
|
||||
totalMP = _totalMP;
|
||||
totalStaked = _totalStaked;
|
||||
totalMPPerEpoch = _totalMPPerEpoch;
|
||||
pendingMPToBeMinted = _pendingMPToBeMinted;
|
||||
potentialMP = _potentialMP;
|
||||
currentEpochTotalExpiredMP = _currentEpochExpiredMP;
|
||||
}
|
||||
|
||||
|
@ -383,7 +368,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
* @notice Transfer current epoch funds for migrated manager
|
||||
*/
|
||||
function transferNonPending() external onlyPendingMigration {
|
||||
stakedToken.transfer(address(migration), epochReward());
|
||||
rewardToken.transfer(address(migration), epochReward());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -391,7 +376,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
* @param _acceptMigration true if wants to migrate, false if wants to leave
|
||||
*/
|
||||
function migrateTo(bool _acceptMigration)
|
||||
external
|
||||
internal
|
||||
onlyTrustedCodehash
|
||||
onlyAccountInitialized(msg.sender)
|
||||
onlyPendingMigration
|
||||
|
@ -399,13 +384,30 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
{
|
||||
_processAccount(accounts[msg.sender], currentEpoch);
|
||||
Account memory account = accounts[msg.sender];
|
||||
totalSupplyMP -= account.totalMP;
|
||||
totalSupplyBalance -= account.balance;
|
||||
totalMP -= account.totalMP;
|
||||
totalStaked -= account.balance;
|
||||
delete accounts[msg.sender];
|
||||
migration.migrateFrom(msg.sender, _acceptMigration, account);
|
||||
return migration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Account accepts an update to new contract
|
||||
* @return _migrated new manager
|
||||
*/
|
||||
function acceptUpdate() external returns (IStakeManager _migrated) {
|
||||
return migrateTo(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Account leaves contract in case of a contract breach
|
||||
* @return _leaveAccepted true if accepted
|
||||
*/
|
||||
function leave() external returns (bool _leaveAccepted) {
|
||||
migrateTo(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Only callable from old manager.
|
||||
* @notice Migrate account from old manager
|
||||
|
@ -417,8 +419,8 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
if (_acceptMigration) {
|
||||
accounts[_vault] = _account;
|
||||
} else {
|
||||
totalSupplyMP -= _account.totalMP;
|
||||
totalSupplyBalance -= _account.balance;
|
||||
totalMP -= _account.totalMP;
|
||||
totalStaked -= _account.balance;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -428,7 +430,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
* @param _amount amount MP increased on account after migration initialized
|
||||
*/
|
||||
function increaseTotalMP(uint256 _amount) external onlyPreviousManager {
|
||||
totalSupplyMP += _amount;
|
||||
totalMP += _amount;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -461,7 +463,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
account.epoch = userEpoch;
|
||||
if (userReward > 0) {
|
||||
pendingReward -= userReward;
|
||||
stakedToken.transfer(account.rewardAddress, userReward);
|
||||
rewardToken.transfer(account.rewardAddress, userReward);
|
||||
}
|
||||
if (address(migration) != address(0)) {
|
||||
mpDifference = account.totalMP - mpDifference;
|
||||
|
@ -477,7 +479,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
*/
|
||||
function _mintMP(Account storage account, uint256 processTime, Epoch storage epoch) private {
|
||||
uint256 mpToMint = _getMaxMPToMint(
|
||||
_getMPToMint(account.balance, processTime - account.lastMint),
|
||||
_calculateAccuredMP(account.balance, processTime - account.lastMint),
|
||||
account.balance,
|
||||
account.bonusMP,
|
||||
account.totalMP
|
||||
|
@ -486,11 +488,11 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
//update storage
|
||||
account.lastMint = processTime;
|
||||
account.totalMP += mpToMint;
|
||||
totalSupplyMP += mpToMint;
|
||||
totalMP += mpToMint;
|
||||
|
||||
//mp estimation
|
||||
epoch.estimatedMP -= mpToMint;
|
||||
pendingMPToBeMinted -= mpToMint;
|
||||
epoch.potentialMP -= mpToMint;
|
||||
potentialMP -= mpToMint;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -512,7 +514,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
returns (uint256 _maxMpToMint)
|
||||
{
|
||||
// Maximum multiplier point for given balance
|
||||
_maxMpToMint = _getMPToMint(_balance, MAX_BOOST * YEAR) + _bonusMP;
|
||||
_maxMpToMint = _calculateMaxAccuredMP(_balance) + _bonusMP;
|
||||
if (_mpToMint + _totalMP > _maxMpToMint) {
|
||||
//reached cap when increasing MP
|
||||
return _maxMpToMint - _totalMP; //how much left to reach cap
|
||||
|
@ -523,13 +525,12 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
}
|
||||
|
||||
/**
|
||||
* @notice Calculates multiplier points to mint for given balance and time
|
||||
* @param _balance balance of account
|
||||
* @param _deltaTime time difference
|
||||
* @return multiplier points to mint
|
||||
* @notice Returns account balance
|
||||
* @param _vault Account address
|
||||
* @return _balance account balance
|
||||
*/
|
||||
function _getMPToMint(uint256 _balance, uint256 _deltaTime) private pure returns (uint256) {
|
||||
return Math.mulDiv(_balance, _deltaTime, YEAR) * MP_APY;
|
||||
function getStakedBalance(address _vault) external view returns (uint256 _balance) {
|
||||
return accounts[_vault].balance;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -538,8 +539,8 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
* @param _deltaTime time difference
|
||||
* @return multiplier points to mint
|
||||
*/
|
||||
function calculateMPToMint(uint256 _balance, uint256 _deltaTime) public pure returns (uint256) {
|
||||
return _getMPToMint(_balance, _deltaTime);
|
||||
function calculateMP(uint256 _balance, uint256 _deltaTime) public pure returns (uint256) {
|
||||
return _calculateAccuredMP(_balance, _deltaTime);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -548,7 +549,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
* @return _totalSupply current total supply
|
||||
*/
|
||||
function totalSupply() public view returns (uint256 _totalSupply) {
|
||||
return totalSupplyMP + totalSupplyBalance + pendingMPToBeMinted;
|
||||
return totalMP + totalStaked + potentialMP;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -556,7 +557,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
* @return _totalSupply current total supply
|
||||
*/
|
||||
function totalSupplyMinted() public view returns (uint256 _totalSupply) {
|
||||
return totalSupplyMP + totalSupplyBalance;
|
||||
return totalMP + totalStaked;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -564,7 +565,7 @@ contract StakeManager is TrustedCodehashAccess {
|
|||
* @return _epochReward current epoch reward
|
||||
*/
|
||||
function epochReward() public view returns (uint256 _epochReward) {
|
||||
return stakedToken.balanceOf(address(this)) - pendingReward;
|
||||
return rewardToken.balanceOf(address(this)) - pendingReward;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
pragma solidity ^0.8.18;
|
||||
|
||||
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
import { StakeManager } from "./StakeManager.sol";
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import { IStakeManager } from "./IStakeManager.sol";
|
||||
|
||||
/**
|
||||
* @title StakeVault
|
||||
|
@ -18,20 +18,20 @@ contract StakeVault is Ownable {
|
|||
|
||||
error StakeVault__UnstakingFailed();
|
||||
|
||||
StakeManager private stakeManager;
|
||||
IStakeManager private stakeManager;
|
||||
|
||||
ERC20 public immutable STAKED_TOKEN;
|
||||
IERC20 public immutable stakedToken;
|
||||
|
||||
event Staked(address from, address to, uint256 _amount, uint256 time);
|
||||
|
||||
constructor(address _owner, ERC20 _stakedToken, StakeManager _stakeManager) {
|
||||
constructor(address _owner, IERC20 _stakedToken, IStakeManager _stakeManager) {
|
||||
_transferOwnership(_owner);
|
||||
STAKED_TOKEN = _stakedToken;
|
||||
stakedToken = _stakedToken;
|
||||
stakeManager = _stakeManager;
|
||||
}
|
||||
|
||||
function stake(uint256 _amount, uint256 _time) external onlyOwner {
|
||||
bool success = STAKED_TOKEN.transferFrom(msg.sender, address(this), _amount);
|
||||
bool success = stakedToken.transferFrom(msg.sender, address(this), _amount);
|
||||
if (!success) {
|
||||
revert StakeVault__StakingFailed();
|
||||
}
|
||||
|
@ -46,27 +46,24 @@ contract StakeVault is Ownable {
|
|||
|
||||
function unstake(uint256 _amount) external onlyOwner {
|
||||
stakeManager.unstake(_amount);
|
||||
bool success = STAKED_TOKEN.transfer(msg.sender, _amount);
|
||||
bool success = stakedToken.transfer(msg.sender, _amount);
|
||||
if (!success) {
|
||||
revert StakeVault__UnstakingFailed();
|
||||
}
|
||||
}
|
||||
|
||||
function leave() external onlyOwner {
|
||||
stakeManager.migrateTo(false);
|
||||
STAKED_TOKEN.transferFrom(address(this), msg.sender, STAKED_TOKEN.balanceOf(address(this)));
|
||||
if (stakeManager.leave()) {
|
||||
stakedToken.transferFrom(address(this), msg.sender, stakedToken.balanceOf(address(this)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Opt-in migration to a new StakeManager contract.
|
||||
* @notice Opt-in migration to a new IStakeManager contract.
|
||||
*/
|
||||
function acceptMigration() external onlyOwner {
|
||||
StakeManager migrated = stakeManager.migrateTo(true);
|
||||
IStakeManager migrated = stakeManager.acceptUpdate();
|
||||
if (address(migrated) == address(0)) revert StakeVault__MigrationNotAvailable();
|
||||
stakeManager = migrated;
|
||||
}
|
||||
|
||||
function stakedToken() external view returns (ERC20) {
|
||||
return STAKED_TOKEN;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ contract VaultFactory is Ownable2Step {
|
|||
/// @dev Anyone can call this function.
|
||||
/// @dev Emits a {VaultCreated} event.
|
||||
function createVault() external returns (StakeVault) {
|
||||
StakeVault vault = new StakeVault(msg.sender, stakeManager.stakedToken(), stakeManager);
|
||||
StakeVault vault = new StakeVault(msg.sender, stakeManager.rewardToken(), stakeManager);
|
||||
emit VaultCreated(address(vault), msg.sender);
|
||||
return vault;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.18;
|
||||
|
||||
/**
|
||||
* @title TrustedCodehashAccess
|
||||
* @author Ricardo Guilherme Schmidt <ricardo3@status.im>
|
||||
* @notice Ensures that only specific contract bytecode hashes are trusted to
|
||||
* interact with the functions using the `onlyTrustedCodehash` modifier.
|
||||
*/
|
||||
interface ITrustedCodehashAccess {
|
||||
error TrustedCodehashAccess__UnauthorizedCodehash();
|
||||
|
||||
event TrustedCodehashUpdated(bytes32 indexed codehash, bool trusted);
|
||||
|
||||
/**
|
||||
* @notice Allows the owner to set or update the trust status for a contract's codehash.
|
||||
* @dev Emits the `TrustedCodehashUpdated` event whenever a codehash is updated.
|
||||
* @param _codehash The bytecode hash of the contract.
|
||||
* @param _trusted Boolean flag to designate the contract as trusted or not.
|
||||
*/
|
||||
function setTrustedCodehash(bytes32 _codehash, bool _trusted) external;
|
||||
|
||||
/**
|
||||
* @notice Checks if a contract's codehash is trusted to interact with protected functions.
|
||||
* @param _codehash The bytecode hash of the contract.
|
||||
* @return bool True if the codehash is trusted, false otherwise.
|
||||
*/
|
||||
function isTrustedCodehash(bytes32 _codehash) external view returns (bool);
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
pragma solidity ^0.8.18;
|
||||
|
||||
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import { ITrustedCodehashAccess } from "./ITrustedCodehashAccess.sol";
|
||||
|
||||
/**
|
||||
* @title TrustedCodehashAccess
|
||||
|
@ -9,11 +10,7 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
|
|||
* @notice Ensures that only specific contract bytecode hashes are trusted to
|
||||
* interact with the functions using the `onlyTrustedCodehash` modifier.
|
||||
*/
|
||||
contract TrustedCodehashAccess is Ownable {
|
||||
error TrustedCodehashAccess__UnauthorizedCodehash();
|
||||
|
||||
event TrustedCodehashUpdated(bytes32 indexed codehash, bool trusted);
|
||||
|
||||
contract TrustedCodehashAccess is ITrustedCodehashAccess, Ownable {
|
||||
mapping(bytes32 codehash => bool permission) private trustedCodehashes;
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
pragma solidity ^0.8.18;
|
||||
|
||||
/**
|
||||
* @title Singleton Factory (EIP-2470)
|
||||
* @notice Exposes CREATE2 (EIP-1014) to deploy bytecode on deterministic addresses based on initialization code and
|
||||
* salt.
|
||||
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
||||
*/
|
||||
contract ERC2470 {
|
||||
error ERC2470__CREATE2Failed();
|
||||
error ERC2470__CREATE2BadCall();
|
||||
|
||||
fallback(bytes calldata _initCode) external payable returns (bytes memory) {
|
||||
return toBytes(deploy(_initCode, 0));
|
||||
}
|
||||
|
||||
receive() external payable {
|
||||
revert ERC2470__CREATE2BadCall();
|
||||
}
|
||||
|
||||
function deploy(bytes memory _initCode, bytes32 _salt) public payable returns (address payable createdContract) {
|
||||
assembly {
|
||||
createdContract := create2(callvalue(), add(_initCode, 0x20), mload(_initCode), _salt)
|
||||
}
|
||||
if (createdContract == address(0)) {
|
||||
revert ERC2470__CREATE2Failed();
|
||||
}
|
||||
}
|
||||
|
||||
function predict(bytes memory _initCode, bytes32 _salt) public view returns (address payable createdContract) {
|
||||
createdContract = payable(
|
||||
address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, _initCode)))))
|
||||
);
|
||||
}
|
||||
|
||||
function predictFrom(
|
||||
bytes memory _initCode,
|
||||
bytes32 _salt,
|
||||
address _factoryAddress
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (address payable createdContract)
|
||||
{
|
||||
createdContract = payable(
|
||||
address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), _factoryAddress, _salt, _initCode)))))
|
||||
);
|
||||
}
|
||||
|
||||
function toBytes(address a) internal pure returns (bytes memory) {
|
||||
return abi.encodePacked(a);
|
||||
}
|
||||
|
||||
function toAddress(bytes memory b) external pure returns (address addr) {
|
||||
return abi.decode(b, (address));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,390 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
import { Test, console } from "forge-std/Test.sol";
|
||||
import { Deploy } from "../script/Deploy.s.sol";
|
||||
import { DeployMigrationStakeManager } from "../script/DeployMigrationStakeManager.s.sol";
|
||||
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
|
||||
import { TrustedCodehashAccess, StakeManager, ExpiredStakeStorage } from "../contracts/StakeManager.sol";
|
||||
import { MultiplierPointMath } from "../contracts/MultiplierPointMath.sol";
|
||||
import { StakeVault } from "../contracts/StakeVault.sol";
|
||||
import { VaultFactory } from "../contracts/VaultFactory.sol";
|
||||
|
||||
contract DynamicTest is MultiplierPointMath, Test {
|
||||
DeploymentConfig internal deploymentConfig;
|
||||
StakeManager internal stakeManager;
|
||||
VaultFactory internal vaultFactory;
|
||||
|
||||
address internal stakeToken;
|
||||
address internal deployer;
|
||||
address internal testUser = makeAddr("testUser");
|
||||
address internal testUser2 = makeAddr("testUser2");
|
||||
|
||||
function setUp() public virtual {
|
||||
Deploy deployment = new Deploy();
|
||||
(vaultFactory, stakeManager, deploymentConfig) = deployment.run();
|
||||
(deployer, stakeToken) = deploymentConfig.activeNetworkConfig();
|
||||
}
|
||||
|
||||
modifier withPrank(address _prankAddress) {
|
||||
vm.startPrank(_prankAddress);
|
||||
_;
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
modifier fuzz_stake(uint256 _amount) {
|
||||
vm.assume(_amount > _calculateMinimumStake(stakeManager.EPOCH_SIZE()));
|
||||
vm.assume(_amount < 1e20);
|
||||
_;
|
||||
}
|
||||
|
||||
modifier fuzz_lock(uint256 _seconds) {
|
||||
vm.assume(_seconds == 0 || _seconds > stakeManager.MIN_LOCKUP_PERIOD());
|
||||
vm.assume(_seconds == 0 || _seconds < stakeManager.MAX_LOCKUP_PERIOD());
|
||||
_;
|
||||
}
|
||||
|
||||
modifier fuzz_unstake(uint256 _staked, uint256 _unstaked) {
|
||||
vm.assume(_unstaked > 0);
|
||||
vm.assume(_unstaked < _staked);
|
||||
_;
|
||||
}
|
||||
|
||||
function _setTrustedCodehash(StakeVault _vault, bool _trusted) internal withPrank(deployer) {
|
||||
if (stakeManager.isTrustedCodehash(address(_vault).codehash) == _trusted) {
|
||||
stakeManager.setTrustedCodehash(address(_vault).codehash, _trusted);
|
||||
}
|
||||
}
|
||||
|
||||
function _createVault(address _account) internal withPrank(_account) returns (StakeVault vault) {
|
||||
vm.prank(_account);
|
||||
vault = vaultFactory.createVault();
|
||||
}
|
||||
|
||||
function _initializeVault(address _account) internal returns (StakeVault vault) {
|
||||
vault = _createVault(_account);
|
||||
_setTrustedCodehash(vault, true);
|
||||
}
|
||||
|
||||
function _stake(StakeVault _vault, uint256 _amount, uint256 _lockedSeconds) internal withPrank(_vault.owner()) {
|
||||
ERC20(stakeToken).approve(address(_vault), _amount);
|
||||
_vault.stake(_amount, _lockedSeconds);
|
||||
}
|
||||
|
||||
function _unstake(StakeVault _vault, uint256 _amount) internal withPrank(_vault.owner()) {
|
||||
_vault.unstake(_amount);
|
||||
}
|
||||
|
||||
function _lock(StakeVault _vault, uint256 _lockedSeconds) internal withPrank(_vault.owner()) {
|
||||
_vault.unstake(_lockedSeconds);
|
||||
}
|
||||
|
||||
enum VaultMethod {
|
||||
CREATE,
|
||||
STAKE,
|
||||
UNSTAKE,
|
||||
LOCK
|
||||
}
|
||||
enum VMMethod {
|
||||
VM_WARP
|
||||
}
|
||||
|
||||
struct StageActions {
|
||||
VMAction[] vmActions;
|
||||
VaultAction[] vaultActions;
|
||||
}
|
||||
|
||||
struct VaultAction {
|
||||
StakeVault vault;
|
||||
VaultMethod method;
|
||||
uint256[] args;
|
||||
}
|
||||
|
||||
struct VMAction {
|
||||
VMMethod method;
|
||||
uint256[] args;
|
||||
}
|
||||
|
||||
struct StageState {
|
||||
uint256 timestamp;
|
||||
VaultState[] vaultStates;
|
||||
}
|
||||
|
||||
struct VaultState {
|
||||
StakeVault vault;
|
||||
uint256 increasedAccuredMP;
|
||||
uint256 predictedBonusMP;
|
||||
uint256 predictedAccuredMP;
|
||||
uint256 stakeAmount;
|
||||
}
|
||||
|
||||
function _processStage(
|
||||
StageState memory _input,
|
||||
StageActions memory _action
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (StageState memory output)
|
||||
{
|
||||
output = _input;
|
||||
for (uint256 i = 0; i < _action.vmActions.length; i++) {
|
||||
output = _processStage_VMAction_StageResult(output, _action.vmActions[i]);
|
||||
}
|
||||
for (uint256 i = 0; i < _action.vaultActions.length; i++) {
|
||||
output = _processStage_AccountAction_StageResult(output, _action.vaultActions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function _processStage_VMAction_StageResult(
|
||||
StageState memory _input,
|
||||
VMAction memory _action
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (StageState memory output)
|
||||
{
|
||||
if (_action.method == VMMethod.VM_WARP) {
|
||||
output.timestamp = _input.timestamp + _action.args[0];
|
||||
output.vaultStates = new VaultState[](_input.vaultStates.length);
|
||||
for (uint256 i = 0; i < _input.vaultStates.length; i++) {
|
||||
output.vaultStates[i] = _predict_VMAction_AccountState(_input.vaultStates[i], _action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _processStage_AccountAction_StageResult(
|
||||
StageState memory input,
|
||||
VaultAction memory action
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (StageState memory output)
|
||||
{
|
||||
if (action.method == VaultMethod.CREATE) {
|
||||
output.vaultStates = new VaultState[](input.vaultStates.length + 1);
|
||||
} else {
|
||||
output.vaultStates = new VaultState[](input.vaultStates.length);
|
||||
}
|
||||
for (uint256 i = 0; i < input.vaultStates.length; i++) {
|
||||
output.vaultStates[i] = _predict_AccountAction_AccountState(input.vaultStates[i], action);
|
||||
}
|
||||
}
|
||||
|
||||
function _predict_VMAction_AccountState(
|
||||
VaultState memory input,
|
||||
VMAction memory action
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (VaultState memory output)
|
||||
{
|
||||
if (action.method == VMMethod.VM_WARP) {
|
||||
require(action.args.length == 1, "Incorrect number of arguments");
|
||||
output.stakeAmount = input.stakeAmount;
|
||||
output.predictedBonusMP = input.predictedBonusMP;
|
||||
output.increasedAccuredMP = _calculateAccuredMP(input.stakeAmount, action.args[0]);
|
||||
output.predictedAccuredMP = input.predictedAccuredMP + output.increasedAccuredMP;
|
||||
}
|
||||
}
|
||||
|
||||
function _predict_AccountAction_AccountState(
|
||||
VaultState memory input,
|
||||
VaultAction memory action
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (VaultState memory output)
|
||||
{
|
||||
if (
|
||||
action.method != VaultMethod.CREATE && action.vault != input.vault
|
||||
|| action.method == VaultMethod.CREATE && address(input.vault) != address(0)
|
||||
) {
|
||||
return input;
|
||||
}
|
||||
output.vault = input.vault;
|
||||
if (action.method == VaultMethod.CREATE) {
|
||||
//output.vault = _createVault(address(uint160(action.args[0])));
|
||||
output.stakeAmount = 0;
|
||||
output.predictedBonusMP = 0;
|
||||
output.increasedAccuredMP = 0;
|
||||
output.predictedAccuredMP = 0;
|
||||
} else if (action.method == VaultMethod.STAKE) {
|
||||
require(action.args.length == 2, "Incorrect number of arguments");
|
||||
output.stakeAmount = input.stakeAmount + action.args[0];
|
||||
output.predictedBonusMP = _calculateBonusMP(output.stakeAmount, action.args[1]);
|
||||
output.increasedAccuredMP = input.increasedAccuredMP;
|
||||
output.predictedAccuredMP = input.predictedAccuredMP;
|
||||
} else if (action.method == VaultMethod.UNSTAKE) {
|
||||
require(action.args.length == 1, "Incorrect number of arguments");
|
||||
output.stakeAmount = input.stakeAmount - action.args[0];
|
||||
output.predictedBonusMP = (output.stakeAmount * input.predictedBonusMP) / input.stakeAmount;
|
||||
output.increasedAccuredMP = input.increasedAccuredMP;
|
||||
output.predictedAccuredMP = (output.stakeAmount * input.predictedAccuredMP) / input.stakeAmount;
|
||||
} else if (action.method == VaultMethod.LOCK) {
|
||||
require(action.args.length == 1, "Incorrect number of arguments");
|
||||
output.stakeAmount = input.stakeAmount;
|
||||
output.predictedBonusMP = _calculateBonusMP(output.stakeAmount, action.args[0]);
|
||||
output.increasedAccuredMP = input.increasedAccuredMP;
|
||||
output.predictedAccuredMP = input.predictedAccuredMP + output.increasedAccuredMP;
|
||||
}
|
||||
}
|
||||
/*
|
||||
function testFuzz_UnstakeBonusMPAndAccuredMP(
|
||||
uint256 amountStaked,
|
||||
uint256 secondsLocked,
|
||||
uint256 reducedStake,
|
||||
uint256 increasedTime
|
||||
)
|
||||
public
|
||||
fuzz_stake(amountStaked)
|
||||
fuzz_unstake(amountStaked, reducedStake)
|
||||
fuzz_lock(secondsLocked)
|
||||
{
|
||||
|
||||
//initialize memory placehodlders
|
||||
uint totalStages = 4;
|
||||
uint[totalStages] memory timestamp;
|
||||
AccountState[totalStages] memory globalParams;
|
||||
AccountState[totalStages][] memory userParams;
|
||||
StageActions[totalStages] memory actions;
|
||||
address[totalStages][] memory users;
|
||||
|
||||
//stages variables setup
|
||||
uint stage = 0; // first stage = initialization
|
||||
{
|
||||
actions[stage] = StageActions({
|
||||
timeIncrease: 0,
|
||||
userActions: [ UserActions({
|
||||
stakeIncrease: amountStaked,
|
||||
lockupIncrease: secondsLocked,
|
||||
stakeDecrease: 0
|
||||
})]
|
||||
});
|
||||
timestamp[stage] = block.timestamp;
|
||||
users[stage] = [alice];
|
||||
userParams[stage] = new AccountState[](users[stage].length);
|
||||
{
|
||||
UserActions memory userActions = actions[stage].userActions[0];
|
||||
userParams[stage][0].stakeAmount = userActions.stakeIncrease;
|
||||
userParams[stage][0].predictedBonusMP = _calculateBonusMP(userActions.stakeIncrease,
|
||||
userActions.lockupIncrease);
|
||||
userParams[stage][0].increasedAccuredMP = 0; //no increased accured MP in first stage
|
||||
userParams[stage][0].predictedAccuredMP = 0; //no accured MP in first stage
|
||||
}
|
||||
}
|
||||
|
||||
stage++; // second stage = progress in time
|
||||
{
|
||||
actions[stage] = StageActions({
|
||||
timeIncrease: increasedTime,
|
||||
userActions: [UserActions({
|
||||
stakeIncrease: 0,
|
||||
lockupIncrease: 0,
|
||||
stakeDecrease: 0
|
||||
})]
|
||||
});
|
||||
timestamp[stage] = timestamp[stage-1] + actions[stage].timeIncrease;
|
||||
users[stage] = users[stage-1]; //no new users in second stage
|
||||
userParams[stage] = new AccountState[](users[stage].length);
|
||||
{
|
||||
UserActions memory userActions = actions[stage].userActions[0];
|
||||
userParams[stage][0].stakeAmount = userParams[stage-1][0].stakeAmount; //no changes in stake at second stage
|
||||
userParams[stage][0].predictedBonusMP = userParams[stage-1][0].predictedBonusMP; //no changes in bonusMP at
|
||||
second stage
|
||||
userParams[stage][0].increasedAccuredMP = _calculeAccuredMP(amountStaked, timestamp[stage] -
|
||||
timestamp[stage-1]);
|
||||
userParams[stage][0].predictedAccuredMP = userParams[stage-1][0].predictedAccuredMP +
|
||||
userParams[stage][0].increasedAccuredMP;
|
||||
}
|
||||
}
|
||||
|
||||
stage++; //third stage = reduce stake
|
||||
{
|
||||
timestamp[stage] = timestamp[stage-1]; //no time increased in third stage
|
||||
users[stage] = users[stage-1]; //no new users in third stage
|
||||
userParams[stage] = new AccountState[](users[stage].length);
|
||||
{
|
||||
userParams[stage][0].stakeAmount = userParams[stage-1][0].stakeAmount - reducedStake;
|
||||
//bonusMP from this stage is a proportion from the difference of current stakeAmount and past stage stakeAmount
|
||||
//if the account reduced 50% of its stake, the bonusMP should be reduced by 50%
|
||||
userParams[stage][0].predictedBonusMP = (userParams[stage][0].stakeAmount *
|
||||
userParams[stage-1][0].predictedBonusMP) / userParams[stage-1][0].stakeAmount;
|
||||
userParams[stage][0].increasedAccuredMP = 0; //no accuredMP in third stage;
|
||||
//total accuredMP from this stage is a proportion from the difference of current stakeAmount and past stage
|
||||
stakeAmount
|
||||
//if the account reduced 50% of its stake, the accuredMP should be reduced by 50%
|
||||
userParams[stage][0].predictedAccuredMP = (userParams[stage][0].stakeAmount * predictedAccuredMP[stage-1]) /
|
||||
userParams[stage-1][0].stakeAmount;;
|
||||
}
|
||||
}
|
||||
|
||||
// stages execution
|
||||
stage = 0; // first stage = initialization
|
||||
{
|
||||
_stake(amountStaked, secondsLocked);
|
||||
for(uint i = 0; i < users[stage].length; i++) {
|
||||
RewardsStreamerMP.UserInfo memory userInfo = streamer.getUserInfo(users[stage][i]);
|
||||
assertEq(userInfo.stakedBalance, userParams[stage][i].stakeAmount, "wrong user staked balance");
|
||||
assertEq(userInfo.userMP, userParams[stage][i].predictedAccuredMP + userParams[stage][i].predictedBonusMP,
|
||||
"wrong user MP");
|
||||
assertEq(userInfo.maxMP, userParams[stage][i].stakeAmount * MAX_MULTIPLIER
|
||||
+userParams[stage][i].predictedBonusMP, "wrong user max MP");
|
||||
//sum all usersParams to globalParams
|
||||
globalParams[stage].stakeAmount += userParams[stage][i].stakeAmount;
|
||||
globalParams[stage].predictedBonusMP += userParams[stage][i].predictedBonusMP;
|
||||
globalParams[stage].increasedAccuredMP += userParams[stage][i].increasedAccuredMP;
|
||||
globalParams[stage].predictedAccuredMP += userParams[stage][i].predictedAccuredMP;
|
||||
}
|
||||
assertEq(streamer.totalStaked(), globalParams[stage].stakeAmount, "wrong total staked");
|
||||
assertEq(streamer.totalMP(), globalParams[stage].predictedBonusMP, "wrong total MP");
|
||||
assertEq(streamer.totalMaxMP(), globalParams[stage].stakeAmount * MAX_MULTIPLIER +
|
||||
globalParams[stage].predictedBonusMP, "wrong totalMaxMP MP");
|
||||
}
|
||||
|
||||
stage++; // second stage = progress in time
|
||||
{
|
||||
vm.warp(timestamp[stage]);
|
||||
for(uint i = 0; i < users[stage].length; i++) {
|
||||
RewardsStreamerMP.UserInfo memory userInfo = streamer.getUserInfo(users[stage][i]);
|
||||
assertEq(userInfo.stakedBalance, userParams[stage][i].stakeAmount, "wrong user staked balance");
|
||||
assertEq(userInfo.userMP, userParams[stage][i].predictedAccuredMP + userParams[stage][i].predictedBonusMP,
|
||||
"wrong user MP");
|
||||
assertEq(userInfo.maxMP, userParams[stage][i].stakeAmount * MAX_MULTIPLIER
|
||||
+userParams[stage][i].predictedBonusMP, "wrong user max MP");
|
||||
//sum all usersParams to globalParams
|
||||
globalParams[stage].stakeAmount += userParams[stage][i].stakeAmount;
|
||||
globalParams[stage].predictedBonusMP += userParams[stage][i].predictedBonusMP;
|
||||
globalParams[stage].increasedAccuredMP += userParams[stage][i].increasedAccuredMP;
|
||||
globalParams[stage].predictedAccuredMP += userParams[stage][i].predictedAccuredMP;
|
||||
}
|
||||
assertEq(streamer.totalStaked(), globalParams[stage].stakeAmount, "wrong total staked");
|
||||
assertEq(streamer.totalMP(), globalParams[stage].predictedBonusMP, "wrong total MP");
|
||||
assertEq(streamer.totalMaxMP(), globalParams[stage].stakeAmount * MAX_MULTIPLIER +
|
||||
globalParams[stage].predictedBonusMP, "wrong totalMaxMP MP");
|
||||
}
|
||||
|
||||
stage++; // third stage = reduce stake
|
||||
{
|
||||
_unstake(reducedStake);
|
||||
for(uint i = 0; i < users[stage].length; i++) {
|
||||
RewardsStreamerMP.UserInfo memory userInfo = streamer.getUserInfo(users[stage][i]);
|
||||
assertEq(userInfo.stakedBalance, userParams[stage][i].stakeAmount, "wrong user staked balance");
|
||||
assertEq(userInfo.userMP, userParams[stage][i].predictedAccuredMP + userParams[stage][i].predictedBonusMP,
|
||||
"wrong user MP");
|
||||
assertEq(userInfo.maxMP, userParams[stage][i].stakeAmount * MAX_MULTIPLIER +
|
||||
userParams[stage][i].predictedBonusMP, "wrong user max MP");
|
||||
//sum all usersParams to globalParams
|
||||
globalParams[stage].stakeAmount += userParams[stage][i].stakeAmount;
|
||||
globalParams[stage].predictedBonusMP += userParams[stage][i].predictedBonusMP;
|
||||
globalParams[stage].increasedAccuredMP += userParams[stage][i].increasedAccuredMP;
|
||||
globalParams[stage].predictedAccuredMP += userParams[stage][i].predictedAccuredMP;
|
||||
}
|
||||
assertEq(streamer.totalStaked(), globalParams[stage].stakeAmount, "wrong total staked");
|
||||
assertEq(streamer.totalMP(), globalParams[stage].predictedBonusMP, "wrong total MP");
|
||||
assertEq(streamer.totalMaxMP(), globalParams[stage].stakeAmount * MAX_MULTIPLIER +
|
||||
globalParams[stage].predictedBonusMP, "wrong totalMaxMP MP");
|
||||
}
|
||||
}*/
|
||||
}
|
|
@ -7,7 +7,9 @@ import { Test, console } from "forge-std/Test.sol";
|
|||
import { Deploy } from "../script/Deploy.s.sol";
|
||||
import { DeployMigrationStakeManager } from "../script/DeployMigrationStakeManager.s.sol";
|
||||
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
|
||||
import { TrustedCodehashAccess, StakeManager, ExpiredStakeStorage } from "../contracts/StakeManager.sol";
|
||||
import { StakeManager, IStakeManager, ExpiredStakeStorage } from "../contracts/StakeManager.sol";
|
||||
import { ITrustedCodehashAccess } from "../contracts/access/ITrustedCodehashAccess.sol";
|
||||
import { MultiplierPointMath } from "../contracts/MultiplierPointMath.sol";
|
||||
import { StakeVault } from "../contracts/StakeVault.sol";
|
||||
import { VaultFactory } from "../contracts/VaultFactory.sol";
|
||||
|
||||
|
@ -31,9 +33,9 @@ contract StakeManagerTest is Test {
|
|||
assertEq(stakeManager.owner(), deployer);
|
||||
assertEq(stakeManager.currentEpoch(), 0);
|
||||
assertEq(stakeManager.pendingReward(), 0);
|
||||
assertEq(stakeManager.totalSupplyMP(), 0);
|
||||
assertEq(stakeManager.totalSupplyBalance(), 0);
|
||||
assertEq(address(stakeManager.stakedToken()), stakeToken);
|
||||
assertEq(stakeManager.totalMP(), 0);
|
||||
assertEq(stakeManager.totalStaked(), 0);
|
||||
assertEq(address(stakeManager.rewardToken()), stakeToken);
|
||||
assertEq(address(stakeManager.previousManager()), address(0));
|
||||
assertEq(stakeManager.totalSupply(), 0);
|
||||
}
|
||||
|
@ -83,7 +85,7 @@ contract StakeManagerTest is Test {
|
|||
|
||||
contract StakeTest is StakeManagerTest {
|
||||
function test_RevertWhen_SenderIsNotVault() public {
|
||||
vm.expectRevert(TrustedCodehashAccess.TrustedCodehashAccess__UnauthorizedCodehash.selector);
|
||||
vm.expectRevert(ITrustedCodehashAccess.TrustedCodehashAccess__UnauthorizedCodehash.selector);
|
||||
stakeManager.stake(100, 1);
|
||||
}
|
||||
|
||||
|
@ -96,11 +98,13 @@ contract StakeTest is StakeManagerTest {
|
|||
(, uint256 balance, uint256 bonusMP, uint256 totalMP,,,,) = stakeManager.accounts(address(userVault));
|
||||
assertEq(balance, stakeAmount, "balance of user vault should be equal to stake amount after stake");
|
||||
assertEq(bonusMP, stakeAmount, "bonusMP of user vault should be equal to stake amount after stake if no lock");
|
||||
assertEq(totalMP, bonusMP, "totalMP of user vault should be equal to bonusMP after stake if no epochs passed");
|
||||
assertEq(
|
||||
totalMP, stakeAmount, "totalMP of user vault should be equal to stakeAmount after stake if no epochs passed"
|
||||
);
|
||||
|
||||
vm.prank(testUser);
|
||||
userVault.lock(lockTime);
|
||||
uint256 estimatedBonusMp = stakeAmount + stakeManager.calculateMPToMint(stakeAmount, lockTime);
|
||||
uint256 estimatedBonusMp = stakeAmount + stakeManager.calculateMP(stakeAmount, lockTime);
|
||||
|
||||
(, balance, bonusMP, totalMP,,,,) = stakeManager.accounts(address(userVault));
|
||||
assertEq(balance, stakeAmount, "balance of user vault should be equal to stake amount after lock");
|
||||
|
@ -128,18 +132,18 @@ contract StakeTest is StakeManagerTest {
|
|||
ERC20(stakeToken).approve(address(userVault), 100);
|
||||
|
||||
uint256 lockTime = stakeManager.MIN_LOCKUP_PERIOD() - 1;
|
||||
vm.expectRevert(StakeManager.StakeManager__InvalidLockTime.selector);
|
||||
vm.expectRevert(IStakeManager.StakeManager__InvalidLockTime.selector);
|
||||
userVault.stake(100, lockTime);
|
||||
|
||||
lockTime = stakeManager.MAX_LOCKUP_PERIOD() + 1;
|
||||
vm.expectRevert(StakeManager.StakeManager__InvalidLockTime.selector);
|
||||
vm.expectRevert(IStakeManager.StakeManager__InvalidLockTime.selector);
|
||||
userVault.stake(100, lockTime);
|
||||
}
|
||||
|
||||
function test_RevertWhen_StakeIsTooLow() public {
|
||||
StakeVault userVault = _createTestVault(testUser);
|
||||
vm.startPrank(testUser);
|
||||
vm.expectRevert(StakeManager.StakeManager__StakeIsTooLow.selector);
|
||||
vm.expectRevert(IStakeManager.StakeManager__StakeIsTooLow.selector);
|
||||
userVault.stake(0, 0);
|
||||
}
|
||||
|
||||
|
@ -173,21 +177,21 @@ contract StakeTest is StakeManagerTest {
|
|||
StakeVault userVault = _createStakingAccount(testUser, stakeAmount, 0, stakeAmount);
|
||||
|
||||
(,, uint256 totalMP,,,,,) = stakeManager.accounts(address(userVault));
|
||||
assertEq(stakeManager.totalSupplyMP(), stakeAmount, "total multiplier point supply");
|
||||
assertEq(stakeManager.totalMP(), stakeAmount, "total multiplier point supply");
|
||||
assertEq(totalMP, stakeAmount, "user multiplier points");
|
||||
|
||||
vm.prank(testUser);
|
||||
userVault.unstake(stakeAmount);
|
||||
|
||||
(,,, totalMP,,,,) = stakeManager.accounts(address(userVault));
|
||||
assertEq(stakeManager.totalSupplyMP(), 0, "totalSupplyMP burned after unstaking");
|
||||
assertEq(stakeManager.totalMP(), 0, "totalMP burned after unstaking");
|
||||
assertEq(totalMP, 0, "userMP burned after unstaking");
|
||||
}
|
||||
}
|
||||
|
||||
contract UnstakeTest is StakeManagerTest {
|
||||
function test_RevertWhen_SenderIsNotVault() public {
|
||||
vm.expectRevert(TrustedCodehashAccess.TrustedCodehashAccess__UnauthorizedCodehash.selector);
|
||||
vm.expectRevert(ITrustedCodehashAccess.TrustedCodehashAccess__UnauthorizedCodehash.selector);
|
||||
stakeManager.unstake(1);
|
||||
}
|
||||
|
||||
|
@ -198,11 +202,11 @@ contract UnstakeTest is StakeManagerTest {
|
|||
StakeVault userVault = _createStakingAccount(testUser, stakeAmount, lockTime, mintAmount);
|
||||
|
||||
vm.prank(testUser);
|
||||
vm.expectRevert(StakeManager.StakeManager__FundsLocked.selector);
|
||||
vm.expectRevert(IStakeManager.StakeManager__FundsLocked.selector);
|
||||
userVault.unstake(1);
|
||||
|
||||
vm.prank(testUser);
|
||||
vm.expectRevert(StakeManager.StakeManager__FundsLocked.selector);
|
||||
vm.expectRevert(IStakeManager.StakeManager__FundsLocked.selector);
|
||||
userVault.unstake(stakeAmount);
|
||||
}
|
||||
|
||||
|
@ -216,7 +220,7 @@ contract UnstakeTest is StakeManagerTest {
|
|||
vm.prank(testUser);
|
||||
userVault.unstake(100);
|
||||
|
||||
assertEq(stakeManager.totalSupplyBalance(), 0);
|
||||
assertEq(stakeManager.totalStaked(), 0);
|
||||
assertEq(ERC20(stakeToken).balanceOf(address(userVault)), 0);
|
||||
assertEq(ERC20(stakeToken).balanceOf(testUser), 1000);
|
||||
}
|
||||
|
@ -233,7 +237,7 @@ contract UnstakeTest is StakeManagerTest {
|
|||
vm.prank(testUser);
|
||||
userVault.unstake(100);
|
||||
|
||||
assertEq(stakeManager.totalSupplyBalance(), 0);
|
||||
assertEq(stakeManager.totalStaked(), 0);
|
||||
assertEq(ERC20(stakeToken).balanceOf(address(userVault)), 0);
|
||||
assertEq(ERC20(stakeToken).balanceOf(testUser), 1000);
|
||||
}
|
||||
|
@ -245,14 +249,14 @@ contract UnstakeTest is StakeManagerTest {
|
|||
|
||||
vm.startPrank(testUser);
|
||||
|
||||
assertEq(stakeManager.totalSupplyMP(), stakeAmount);
|
||||
assertEq(stakeManager.totalMP(), stakeAmount);
|
||||
for (uint256 i = 0; i < 53; i++) {
|
||||
vm.warp(stakeManager.epochEnd());
|
||||
stakeManager.executeAccount(address(userVault), i + 1);
|
||||
}
|
||||
(, uint256 balanceBefore, uint256 bonusMPBefore, uint256 totalMPBefore,,,,) =
|
||||
stakeManager.accounts(address(userVault));
|
||||
uint256 totalSupplyMPBefore = stakeManager.totalSupplyMP();
|
||||
uint256 totalSupplyMPBefore = stakeManager.totalMP();
|
||||
uint256 unstakeAmount = stakeAmount * percentToBurn / 100;
|
||||
console.log("unstake", unstakeAmount);
|
||||
|
||||
|
@ -261,7 +265,7 @@ contract UnstakeTest is StakeManagerTest {
|
|||
(, uint256 balanceAfter, uint256 bonusMPAfter, uint256 totalMPAfter,,,,) =
|
||||
stakeManager.accounts(address(userVault));
|
||||
|
||||
uint256 totalSupplyMPAfter = stakeManager.totalSupplyMP();
|
||||
uint256 totalSupplyMPAfter = stakeManager.totalMP();
|
||||
console.log("totalSupplyMPBefore", totalSupplyMPBefore);
|
||||
console.log("totalSupplyMPAfter", totalSupplyMPAfter);
|
||||
console.log("balanceBefore", balanceBefore);
|
||||
|
@ -282,14 +286,14 @@ contract UnstakeTest is StakeManagerTest {
|
|||
uint256 stakeAmount = 1000;
|
||||
StakeVault userVault = _createStakingAccount(testUser, stakeAmount);
|
||||
vm.startPrank(testUser);
|
||||
vm.expectRevert(StakeManager.StakeManager__InsufficientFunds.selector);
|
||||
vm.expectRevert(IStakeManager.StakeManager__InsufficientFunds.selector);
|
||||
userVault.unstake(stakeAmount + 1);
|
||||
}
|
||||
}
|
||||
|
||||
contract LockTest is StakeManagerTest {
|
||||
function test_RevertWhen_SenderIsNotVault() public {
|
||||
vm.expectRevert(TrustedCodehashAccess.TrustedCodehashAccess__UnauthorizedCodehash.selector);
|
||||
vm.expectRevert(ITrustedCodehashAccess.TrustedCodehashAccess__UnauthorizedCodehash.selector);
|
||||
stakeManager.lock(100);
|
||||
}
|
||||
|
||||
|
@ -312,7 +316,7 @@ contract LockTest is StakeManagerTest {
|
|||
|
||||
uint256 lockTime = stakeManager.MAX_LOCKUP_PERIOD() + 1;
|
||||
vm.startPrank(testUser);
|
||||
vm.expectRevert(StakeManager.StakeManager__InvalidLockTime.selector);
|
||||
vm.expectRevert(IStakeManager.StakeManager__InvalidLockTime.selector);
|
||||
userVault.lock(lockTime);
|
||||
}
|
||||
|
||||
|
@ -345,7 +349,7 @@ contract LockTest is StakeManagerTest {
|
|||
(,,,,, uint256 lockUntil,,) = stakeManager.accounts(address(userVault));
|
||||
console.log(lockUntil);
|
||||
vm.startPrank(testUser);
|
||||
vm.expectRevert(StakeManager.StakeManager__InvalidLockTime.selector);
|
||||
vm.expectRevert(IStakeManager.StakeManager__InvalidLockTime.selector);
|
||||
userVault.lock(minLockup - 1);
|
||||
}
|
||||
|
||||
|
@ -354,15 +358,15 @@ contract LockTest is StakeManagerTest {
|
|||
uint256 lockTime = stakeManager.MAX_LOCKUP_PERIOD();
|
||||
StakeVault userVault = _createStakingAccount(testUser, stakeAmount);
|
||||
(, uint256 balance, uint256 bonusMP, uint256 totalMP,,,,) = stakeManager.accounts(address(userVault));
|
||||
uint256 totalSupplyMPBefore = stakeManager.totalSupplyMP();
|
||||
uint256 totalSupplyMPBefore = stakeManager.totalMP();
|
||||
|
||||
vm.startPrank(testUser);
|
||||
userVault.lock(lockTime);
|
||||
|
||||
//solhint-disable-next-line max-line-length
|
||||
(, uint256 newBalance, uint256 newBonusMP, uint256 newCurrentMP,,,,) = stakeManager.accounts(address(userVault));
|
||||
uint256 totalSupplyMPAfter = stakeManager.totalSupplyMP();
|
||||
assertGt(totalSupplyMPAfter, totalSupplyMPBefore, "totalSupplyMP");
|
||||
uint256 totalSupplyMPAfter = stakeManager.totalMP();
|
||||
assertGt(totalSupplyMPAfter, totalSupplyMPBefore, "totalMP");
|
||||
assertGt(newBonusMP, bonusMP, "bonusMP");
|
||||
assertGt(newCurrentMP, totalMP, "totalMP");
|
||||
assertEq(newBalance, balance, "balance");
|
||||
|
@ -371,8 +375,8 @@ contract LockTest is StakeManagerTest {
|
|||
|
||||
contract LeaveTest is StakeManagerTest {
|
||||
function test_RevertWhen_SenderIsNotVault() public {
|
||||
vm.expectRevert(TrustedCodehashAccess.TrustedCodehashAccess__UnauthorizedCodehash.selector);
|
||||
stakeManager.migrateTo(false);
|
||||
vm.expectRevert(ITrustedCodehashAccess.TrustedCodehashAccess__UnauthorizedCodehash.selector);
|
||||
stakeManager.leave();
|
||||
}
|
||||
|
||||
function test_RevertWhen_NoPendingMigration() public {
|
||||
|
@ -393,8 +397,8 @@ contract LeaveTest is StakeManagerTest {
|
|||
|
||||
contract MigrateTest is StakeManagerTest {
|
||||
function test_RevertWhen_SenderIsNotVault() public {
|
||||
vm.expectRevert(TrustedCodehashAccess.TrustedCodehashAccess__UnauthorizedCodehash.selector);
|
||||
stakeManager.migrateTo(true);
|
||||
vm.expectRevert(ITrustedCodehashAccess.TrustedCodehashAccess__UnauthorizedCodehash.selector);
|
||||
stakeManager.acceptUpdate();
|
||||
}
|
||||
|
||||
function test_RevertWhen_NoPendingMigration() public {
|
||||
|
@ -431,8 +435,8 @@ contract MigrationInitializeTest is StakeManagerTest {
|
|||
secondStakeManager.startMigration(thirdStakeManager);
|
||||
|
||||
uint256 currentEpoch = stakeManager.currentEpoch();
|
||||
uint256 totalMP = stakeManager.totalSupplyMP();
|
||||
uint256 totalBalance = stakeManager.totalSupplyBalance();
|
||||
uint256 totalMP = stakeManager.totalMP();
|
||||
uint256 totalBalance = stakeManager.totalStaked();
|
||||
|
||||
// `stakeManager` calling `migrationInitialize` while the new stake manager is
|
||||
// in migration itself, should revert
|
||||
|
@ -514,7 +518,7 @@ contract ExecuteAccountTest is StakeManagerTest {
|
|||
|
||||
//expected MP is, the starting totalMP + the calculatedMPToMint of user balance for one EPOCH_SIZE multiplied by
|
||||
// 2.
|
||||
uint256 expectedMP = totalMP + (stakeManager.calculateMPToMint(stakeAmount, stakeManager.EPOCH_SIZE()) * 2);
|
||||
uint256 expectedMP = totalMP + (stakeManager.calculateMP(stakeAmount, stakeManager.EPOCH_SIZE()) * 2);
|
||||
stakeManager.executeAccount(address(userVaults[0]), stakeManager.currentEpoch() + 1);
|
||||
(,,, totalMP, lastMint,, epoch,) = stakeManager.accounts(address(userVaults[0]));
|
||||
|
||||
|
@ -579,9 +583,9 @@ contract ExecuteAccountTest is StakeManagerTest {
|
|||
function test_ShouldNotMintMoreThanCap() public {
|
||||
uint256 stakeAmount = 10_000_000_000;
|
||||
|
||||
uint256 epochsAmountToReachCap = stakeManager.calculateMPToMint(
|
||||
stakeAmount, stakeManager.MAX_BOOST() * stakeManager.YEAR()
|
||||
) / stakeManager.calculateMPToMint(stakeAmount, stakeManager.EPOCH_SIZE());
|
||||
uint256 epochsAmountToReachCap = stakeManager.calculateMP(
|
||||
stakeAmount, stakeManager.MAX_MULTIPLIER() * stakeManager.YEAR()
|
||||
) / stakeManager.calculateMP(stakeAmount, stakeManager.EPOCH_SIZE());
|
||||
|
||||
deal(stakeToken, testUser, stakeAmount);
|
||||
|
||||
|
@ -673,17 +677,17 @@ contract UserFlowsTest is StakeManagerTest {
|
|||
|
||||
assertEq(ERC20(stakeToken).balanceOf(address(userVault)), 100);
|
||||
assertEq(ERC20(stakeToken).balanceOf(address(user2Vault)), 100);
|
||||
assertEq(stakeManager.totalSupplyBalance(), 200);
|
||||
assertEq(stakeManager.totalStaked(), 200);
|
||||
|
||||
vm.startPrank(testUser);
|
||||
userVault.unstake(100);
|
||||
assertEq(ERC20(stakeToken).balanceOf(address(userVault)), 0);
|
||||
assertEq(stakeManager.totalSupplyBalance(), 100);
|
||||
assertEq(stakeManager.totalStaked(), 100);
|
||||
|
||||
vm.startPrank(testUser2);
|
||||
user2Vault.unstake(100);
|
||||
assertEq(ERC20(stakeToken).balanceOf(address(user2Vault)), 0);
|
||||
assertEq(stakeManager.totalSupplyBalance(), 0);
|
||||
assertEq(stakeManager.totalStaked(), 0);
|
||||
}
|
||||
|
||||
function test_StakeWithLockUpTimeLocksStake() public {
|
||||
|
@ -694,7 +698,7 @@ contract UserFlowsTest is StakeManagerTest {
|
|||
vm.startPrank(testUser);
|
||||
|
||||
// unstaking should fail as lockup time isn't over yet
|
||||
vm.expectRevert(StakeManager.StakeManager__FundsLocked.selector);
|
||||
vm.expectRevert(IStakeManager.StakeManager__FundsLocked.selector);
|
||||
userVault.unstake(100);
|
||||
|
||||
// fast forward 12 weeks
|
||||
|
@ -702,7 +706,7 @@ contract UserFlowsTest is StakeManagerTest {
|
|||
|
||||
userVault.unstake(100);
|
||||
assertEq(ERC20(stakeToken).balanceOf(address(userVault)), 0);
|
||||
assertEq(stakeManager.totalSupplyBalance(), 0);
|
||||
assertEq(stakeManager.totalStaked(), 0);
|
||||
}
|
||||
|
||||
function test_PendingMPToBeMintedCannotBeGreaterThanTotalSupplyMP(
|
||||
|
@ -722,30 +726,30 @@ contract UserFlowsTest is StakeManagerTest {
|
|||
userVaults.push(
|
||||
_createStakingAccount(makeAddr(string(abi.encode(keccak256(abi.encode(accountNum))))), thisAccStake, 0)
|
||||
);
|
||||
uint256 thisAccReachCapIn = stakeManager.calculateMPToMint(
|
||||
thisAccStake, stakeManager.MAX_BOOST() * stakeManager.YEAR()
|
||||
) / stakeManager.calculateMPToMint(thisAccStake, stakeManager.EPOCH_SIZE());
|
||||
uint256 thisAccReachCapIn = stakeManager.calculateMP(
|
||||
thisAccStake, stakeManager.MAX_MULTIPLIER() * stakeManager.YEAR()
|
||||
) / stakeManager.calculateMP(thisAccStake, stakeManager.EPOCH_SIZE());
|
||||
if (thisAccReachCapIn > epochsAmountToReachCap) {
|
||||
epochsAmountToReachCap = thisAccReachCapIn; //uses the amount to reach cap from the account that takes
|
||||
// longer to reach cap
|
||||
}
|
||||
}
|
||||
|
||||
//tests up to epochs to reach MAX_BOOST + 10 epochs
|
||||
//tests up to epochs to reach MAX_MULTIPLIER + 10 epochs
|
||||
for (uint256 i = 0; i < epochsAmountToReachCap + 10; i++) {
|
||||
vm.warp(stakeManager.epochEnd());
|
||||
stakeManager.executeEpoch();
|
||||
uint256 pendingMPToBeMintedBefore = stakeManager.pendingMPToBeMinted();
|
||||
uint256 totalSupplyMP = stakeManager.totalSupplyMP();
|
||||
uint256 pendingMPToBeMintedBefore = stakeManager.potentialMP();
|
||||
uint256 totalMP = stakeManager.totalMP();
|
||||
for (uint256 j = 0; j < userVaults.length; j++) {
|
||||
(,,, uint256 totalMPBefore, uint256 lastMintBefore,, uint256 epochBefore,) =
|
||||
stakeManager.accounts(address(userVaults[j]));
|
||||
|
||||
stakeManager.executeAccount(address(userVaults[j]), epochBefore + 1);
|
||||
}
|
||||
uint256 pendingMPToBeMintedAfter = stakeManager.pendingMPToBeMinted();
|
||||
uint256 pendingMPToBeMintedAfter = stakeManager.potentialMP();
|
||||
|
||||
assertEq(pendingMPToBeMintedBefore + totalSupplyMP, stakeManager.totalSupplyMP());
|
||||
assertEq(pendingMPToBeMintedBefore + totalMP, stakeManager.totalMP());
|
||||
assertEq(pendingMPToBeMintedAfter, 0);
|
||||
}
|
||||
}
|
||||
|
@ -764,9 +768,9 @@ contract MigrationStakeManagerTest is StakeManagerTest {
|
|||
assertEq(newStakeManager.owner(), deployer);
|
||||
assertEq(newStakeManager.currentEpoch(), 0);
|
||||
assertEq(newStakeManager.pendingReward(), 0);
|
||||
assertEq(newStakeManager.totalSupplyMP(), 0);
|
||||
assertEq(newStakeManager.totalSupplyBalance(), 0);
|
||||
assertEq(address(newStakeManager.stakedToken()), stakeToken);
|
||||
assertEq(newStakeManager.totalMP(), 0);
|
||||
assertEq(newStakeManager.totalStaked(), 0);
|
||||
assertEq(address(newStakeManager.rewardToken()), stakeToken);
|
||||
assertEq(address(newStakeManager.previousManager()), address(stakeManager));
|
||||
assertEq(newStakeManager.totalSupply(), 0);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue