diff --git a/.gas-report b/.gas-report index 6acad6b..651b63b 100644 --- a/.gas-report +++ b/.gas-report @@ -1,119 +1,92 @@ | script/DeployRewardsStreamerMP.s.sol:DeployRewardsStreamerMPScript contract | | | | | | |-----------------------------------------------------------------------------|-----------------|---------|---------|---------|---------| | Deployment Cost | Deployment Size | | | | | -| 6307818 | 30199 | | | | | +| 6495944 | 31073 | | | | | | Function Name | min | avg | median | max | # calls | -| run | 5401845 | 5401845 | 5401845 | 5401845 | 63 | +| run | 5577072 | 5577072 | 5577072 | 5577072 | 64 | | script/DeploymentConfig.s.sol:DeploymentConfig contract | | | | | | |---------------------------------------------------------|-----------------|-----|--------|-----|---------| | Deployment Cost | Deployment Size | | | | | -| 0 | 0 | | | | | +| 0 | 7132 | | | | | | Function Name | min | avg | median | max | # calls | -| activeNetworkConfig | 454 | 454 | 454 | 454 | 126 | +| activeNetworkConfig | 454 | 454 | 454 | 454 | 128 | | script/UpgradeRewardsStreamerMP.s.sol:UpgradeRewardsStreamerMPScript contract | | | | | | |-------------------------------------------------------------------------------|-----------------|---------|---------|---------|---------| | Deployment Cost | Deployment Size | | | | | -| 2952925 | 14574 | | | | | +| 3140997 | 15448 | | | | | | Function Name | min | avg | median | max | # calls | -| run | 2482684 | 2482684 | 2482684 | 2482684 | 3 | +| run | 2657933 | 2657933 | 2657933 | 2657933 | 3 | | src/RewardsStreamerMP.sol:RewardsStreamerMP contract | | | | | | |------------------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 2632986 | 12135 | | | | | +| 2821054 | 13009 | | | | | | Function Name | min | avg | median | max | # calls | -| MAX_LOCKUP_PERIOD | 349 | 349 | 349 | 349 | 6 | -| MAX_MULTIPLIER | 251 | 251 | 251 | 251 | 33 | -| MIN_LOCKUP_PERIOD | 297 | 297 | 297 | 297 | 15 | -| MP_RATE_PER_YEAR | 253 | 253 | 253 | 253 | 9 | -| STAKING_TOKEN | 428 | 2036 | 2428 | 2428 | 322 | -| emergencyModeEnabled | 2420 | 2420 | 2420 | 2420 | 7 | +| MAX_LOCKUP_PERIOD | 360 | 360 | 360 | 360 | 4 | +| MAX_MULTIPLIER | 284 | 284 | 284 | 284 | 9 | +| MIN_LOCKUP_PERIOD | 286 | 286 | 286 | 286 | 15 | +| STAKING_TOKEN | 395 | 2003 | 2395 | 2395 | 327 | +| emergencyModeEnabled | 2442 | 2442 | 2442 | 2442 | 7 | | enableEmergencyMode | 2485 | 19392 | 24677 | 24677 | 8 | | getAccount | 1661 | 1661 | 1661 | 1661 | 72 | -| getStakedBalance | 2629 | 2629 | 2629 | 2629 | 1 | -| getUserTotalMaxMP | 3123 | 3123 | 3123 | 3123 | 1 | -| getUserTotalStakedBalance | 15162 | 15162 | 15162 | 15162 | 1 | +| getStakedBalance | 2618 | 2618 | 2618 | 2618 | 1 | +| getUserTotalMaxMP | 3177 | 3177 | 3177 | 3177 | 1 | +| getUserTotalStakedBalance | 15151 | 15151 | 15151 | 15151 | 1 | | getUserVaults | 5201 | 5201 | 5201 | 5201 | 4 | -| initialize | 115611 | 115611 | 115611 | 115611 | 65 | -| lastRewardTime | 395 | 1395 | 1395 | 2395 | 2 | -| leave | 59939 | 59939 | 59939 | 59939 | 1 | -| lock | 12063 | 35404 | 16480 | 77670 | 3 | -| mpBalanceOfUser | 9185 | 9185 | 9185 | 9185 | 1 | -| proxiableUUID | 353 | 353 | 353 | 353 | 3 | -| registerVault | 55866 | 72766 | 72966 | 72966 | 257 | -| rewardEndTime | 373 | 1373 | 1373 | 2373 | 2 | -| rewardStartTime | 352 | 1352 | 1352 | 2352 | 2 | -| rewardsBalanceOf | 3231 | 6627 | 7074 | 7341 | 8 | -| setReward | 2583 | 58371 | 86341 | 105731 | 7 | -| setTrustedCodehash | 24243 | 24304 | 24243 | 26243 | 65 | -| stake | 134841 | 172420 | 179234 | 199567 | 66 | -| totalMPAccrued | 395 | 395 | 395 | 395 | 81 | -| totalMaxMP | 350 | 350 | 350 | 350 | 81 | -| totalRewardsAccrued | 351 | 351 | 351 | 351 | 3 | -| totalRewardsSupply | 1025 | 1984 | 1806 | 6765 | 30 | -| totalStaked | 374 | 374 | 374 | 374 | 82 | -| unstake | 64077 | 64613 | 64077 | 67567 | 13 | -| updateAccountMP | 12217 | 17332 | 17934 | 17934 | 19 | -| updateGlobalState | 14317 | 26623 | 28594 | 28594 | 19 | -| upgradeToAndCall | 3225 | 7901 | 8448 | 10936 | 5 | - - -| src/StakeManagerProxy.sol:StakeManagerProxy contract | | | | | | -|------------------------------------------------------|-----------------|-------|--------|--------|---------| -| Deployment Cost | Deployment Size | | | | | -| 256467 | 1263 | | | | | -| Function Name | min | avg | median | max | # calls | -| MAX_LOCKUP_PERIOD | 776 | 3776 | 5276 | 5276 | 6 | -| MAX_MULTIPLIER | 678 | 1905 | 678 | 5178 | 33 | -| MIN_LOCKUP_PERIOD | 724 | 3424 | 5224 | 5224 | 15 | -| MP_RATE_PER_YEAR | 680 | 1180 | 680 | 5180 | 9 | -| STAKING_TOKEN | 855 | 6083 | 7355 | 7355 | 322 | -| emergencyModeEnabled | 7347 | 7347 | 7347 | 7347 | 7 | -| enableEmergencyMode | 28480 | 45381 | 50665 | 50665 | 8 | -| getAccount | 2115 | 2115 | 2115 | 2115 | 72 | -| getStakedBalance | 7559 | 7559 | 7559 | 7559 | 1 | -| getUserTotalMaxMP | 3553 | 3553 | 3553 | 3553 | 1 | -| getUserTotalStakedBalance | 15592 | 15592 | 15592 | 15592 | 1 | -| getUserVaults | 5637 | 6762 | 5637 | 10137 | 4 | -| implementation | 343 | 775 | 343 | 2343 | 412 | -| lastRewardTime | 822 | 1822 | 1822 | 2822 | 2 | -| mpBalanceOfUser | 9615 | 9615 | 9615 | 9615 | 1 | -| rewardEndTime | 800 | 1800 | 1800 | 2800 | 2 | -| rewardStartTime | 779 | 4029 | 4029 | 7279 | 2 | -| rewardsBalanceOf | 3661 | 7057 | 7504 | 7771 | 8 | -| setReward | 28863 | 84685 | 112699 | 132089 | 7 | -| setTrustedCodehash | 52889 | 52889 | 52889 | 52889 | 2 | -| totalMPAccrued | 822 | 822 | 822 | 822 | 81 | -| totalMaxMP | 777 | 777 | 777 | 777 | 81 | -| totalRewardsAccrued | 778 | 778 | 778 | 778 | 3 | -| totalRewardsSupply | 1452 | 2561 | 2233 | 11692 | 30 | -| totalStaked | 801 | 801 | 801 | 801 | 82 | -| updateAccountMP | 38576 | 43691 | 44293 | 44293 | 19 | -| updateGlobalState | 40305 | 52611 | 54582 | 54582 | 19 | -| upgradeToAndCall | 29868 | 33720 | 33720 | 37572 | 2 | +| initialize | 115654 | 115654 | 115654 | 115654 | 66 | +| lastRewardTime | 362 | 1362 | 1362 | 2362 | 2 | +| leave | 79933 | 79933 | 79933 | 79933 | 1 | +| lock | 14238 | 42684 | 42648 | 78402 | 259 | +| mpBalanceOfUser | 10331 | 10331 | 10331 | 10331 | 1 | +| proxiableUUID | 387 | 387 | 387 | 387 | 3 | +| registerVault | 55844 | 72747 | 72944 | 72944 | 261 | +| rewardEndTime | 362 | 1362 | 1362 | 2362 | 2 | +| rewardStartTime | 407 | 1407 | 1407 | 2407 | 2 | +| rewardsBalanceOf | 2942 | 6975 | 7525 | 7746 | 8 | +| setReward | 2561 | 58370 | 86462 | 105709 | 7 | +| setTrustedCodehash | 24199 | 24259 | 24199 | 26199 | 66 | +| stake | 136355 | 179358 | 180748 | 201222 | 322 | +| totalMPAccrued | 428 | 428 | 428 | 428 | 81 | +| totalMaxMP | 362 | 362 | 362 | 362 | 81 | +| totalRewardsAccrued | 385 | 385 | 385 | 385 | 3 | +| totalRewardsSupply | 1036 | 1995 | 1817 | 6776 | 30 | +| totalStaked | 427 | 427 | 427 | 427 | 82 | +| unstake | 63857 | 64502 | 63857 | 68054 | 13 | +| updateAccountMP | 11686 | 17560 | 18252 | 18252 | 19 | +| updateGlobalState | 14317 | 26758 | 28737 | 28737 | 19 | +| upgradeToAndCall | 3181 | 7875 | 8438 | 10881 | 5 | + + +| src/StakeManagerProxy.sol:StakeManagerProxy contract | | | | | | +|------------------------------------------------------|-----------------|------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 256510 | 1231 | | | | | +| Function Name | min | avg | median | max | # calls | +| fallback | 711 | 7189 | 2115 | 132067 | 790 | +| implementation | 343 | 1636 | 2343 | 2343 | 929 | | src/StakeVault.sol:StakeVault contract | | | | | | |----------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 1420425 | 6695 | | | | | +| 1420392 | 6695 | | | | | | Function Name | min | avg | median | max | # calls | | STAKING_TOKEN | 216 | 216 | 216 | 216 | 1 | -| emergencyExit | 36375 | 48879 | 48113 | 65213 | 7 | -| leave | 33507 | 132178 | 62113 | 370978 | 4 | -| lock | 33245 | 61600 | 50845 | 111467 | 4 | -| owner | 2339 | 2339 | 2339 | 2339 | 257 | -| register | 87015 | 103915 | 104115 | 104115 | 257 | -| stake | 33411 | 243748 | 253738 | 274119 | 67 | -| stakeManager | 368 | 368 | 368 | 368 | 257 | +| emergencyExit | 36397 | 48901 | 48135 | 65235 | 7 | +| leave | 33507 | 136177 | 70111 | 370978 | 4 | +| lock | 33245 | 79174 | 79258 | 112199 | 260 | +| owner | 2339 | 2339 | 2339 | 2339 | 261 | +| register | 86993 | 103896 | 104093 | 104093 | 261 | +| stake | 33411 | 253181 | 255252 | 275774 | 323 | +| stakeManager | 368 | 368 | 368 | 368 | 261 | | trustStakeManager | 28953 | 28953 | 28953 | 28953 | 1 | -| unstake | 33282 | 99955 | 105755 | 113604 | 14 | -| withdraw | 42289 | 42289 | 42289 | 42289 | 1 | +| unstake | 33282 | 99858 | 105888 | 113384 | 14 | +| withdraw | 42278 | 42278 | 42278 | 42278 | 1 | | src/XPNFTToken.sol:XPNFTToken contract | | | | | | @@ -189,18 +162,18 @@ | Deployment Cost | Deployment Size | | | | | | 625454 | 3260 | | | | | | Function Name | min | avg | median | max | # calls | -| approve | 46330 | 46339 | 46342 | 46342 | 257 | +| approve | 46330 | 46339 | 46342 | 46342 | 261 | | balanceOf | 558 | 926 | 558 | 2558 | 103 | -| mint | 51279 | 56407 | 51279 | 68379 | 270 | +| mint | 51279 | 56395 | 51279 | 68379 | 274 | | test/mocks/StackOverflowStakeManager.sol:StackOverflowStakeManager contract | | | | | | |-----------------------------------------------------------------------------|-----------------|--------|--------|--------|---------| | Deployment Cost | Deployment Size | | | | | -| 1026739 | 4584 | | | | | +| 1031089 | 4604 | | | | | | Function Name | min | avg | median | max | # calls | | leave | 391 | 161316 | 161316 | 322322 | 334 | -| proxiableUUID | 341 | 341 | 341 | 341 | 1 | +| proxiableUUID | 330 | 330 | 330 | 330 | 1 | | test/mocks/XPProviderMock.sol:XPProviderMock contract | | | | | | diff --git a/.gas-snapshot b/.gas-snapshot index d29f2e1..7af2255 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,74 +1,75 @@ -EmergencyExitTest:test_CannotEnableEmergencyModeTwice() (gas: 92690) -EmergencyExitTest:test_CannotLeaveBeforeEmergencyMode() (gas: 299030) -EmergencyExitTest:test_EmergencyExitBasic() (gas: 385790) -EmergencyExitTest:test_EmergencyExitMultipleUsers() (gas: 664369) -EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 393709) -EmergencyExitTest:test_EmergencyExitWithLock() (gas: 393097) -EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 378651) -EmergencyExitTest:test_OnlyOwnerCanEnableEmergencyMode() (gas: 39426) -IntegrationTest:testStakeFoo() (gas: 1212612) -LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 5836926) -LeaveTest:test_RevertWhenStakeManagerIsTrusted() (gas: 296161) -LeaveTest:test_TrustNewStakeManager() (gas: 5907344) -LockTest:test_LockFailsWithInvalidPeriod() (gas: 311246) -LockTest:test_LockFailsWithNoStake() (gas: 63730) -LockTest:test_LockWithoutPriorLock() (gas: 390945) -MaliciousUpgradeTest:test_UpgradeStackOverflowStakeManager() (gas: 1746668) -MathTest:test_CalcAbsoluteMaxTotalMP() (gas: 18908) -MathTest:test_CalcAccrueMP() (gas: 22207) -MathTest:test_CalcBonusMP() (gas: 17713) -MathTest:test_CalcInitialMP() (gas: 5395) -MathTest:test_CalcMaxAccruedMP() (gas: 15586) -MathTest:test_CalcMaxTotalMP() (gas: 23298) -MultipleVaultsStakeTest:test_StakeMultipleVaults() (gas: 725628) +EmergencyExitTest:test_CannotEnableEmergencyModeTwice() (gas: 92713) +EmergencyExitTest:test_CannotLeaveBeforeEmergencyMode() (gas: 300588) +EmergencyExitTest:test_EmergencyExitBasic() (gas: 387402) +EmergencyExitTest:test_EmergencyExitMultipleUsers() (gas: 667595) +EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 395246) +EmergencyExitTest:test_EmergencyExitWithLock() (gas: 394796) +EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 380308) +EmergencyExitTest:test_OnlyOwnerCanEnableEmergencyMode() (gas: 39427) +IntegrationTest:testStakeFoo() (gas: 1219212) +LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 6214246) +LeaveTest:test_RevertWhenStakeManagerIsTrusted() (gas: 297675) +LeaveTest:test_TrustNewStakeManager() (gas: 6269967) +LockTest:test_LockFailsWithInvalidPeriod(uint256) (runs: 1002, μ: 344806, ~: 344835) +LockTest:test_LockFailsWithNoStake() (gas: 102637) +LockTest:test_LockFailsWithZero() (gas: 314978) +LockTest:test_LockWithoutPriorLock() (gas: 393480) +MaliciousUpgradeTest:test_UpgradeStackOverflowStakeManager() (gas: 1752531) +MathTest:test_CalcAbsoluteMaxTotalMP() (gas: 4974) +MathTest:test_CalcAccrueMP() (gas: 8033) +MathTest:test_CalcBonusMP() (gas: 18610) +MathTest:test_CalcInitialMP() (gas: 5330) +MathTest:test_CalcMaxAccruedMP() (gas: 4665) +MathTest:test_CalcMaxTotalMP() (gas: 19383) +MultipleVaultsStakeTest:test_StakeMultipleVaults() (gas: 731480) NFTMetadataGeneratorSVGTest:testGenerateMetadata() (gas: 85934) NFTMetadataGeneratorSVGTest:testSetImageStrings() (gas: 58332) NFTMetadataGeneratorSVGTest:testSetImageStringsRevert() (gas: 35804) NFTMetadataGeneratorURLTest:testGenerateMetadata() (gas: 102512) NFTMetadataGeneratorURLTest:testSetBaseURL() (gas: 49555) NFTMetadataGeneratorURLTest:testSetBaseURLRevert() (gas: 35979) -RewardsStreamerMP_RewardsTest:testRewardsBalanceOf() (gas: 486296) +RewardsStreamerMP_RewardsTest:testRewardsBalanceOf() (gas: 490632) RewardsStreamerMP_RewardsTest:testSetRewards() (gas: 160703) -RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 39404) +RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 39317) RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadDuration() (gas: 39340) RewardsStreamerMP_RewardsTest:testSetRewards_RevertsNotAuthorized() (gas: 39375) -RewardsStreamerMP_RewardsTest:testTotalRewardsSupply() (gas: 618531) -StakeTest:test_StakeMultipleAccounts() (gas: 499536) -StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 505474) -StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 842540) -StakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 515936) -StakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 537979) -StakeTest:test_StakeOneAccount() (gas: 278268) -StakeTest:test_StakeOneAccountAndRewards() (gas: 284215) -StakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 507661) -StakeTest:test_StakeOneAccountReachingMPLimit() (gas: 499052) -StakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 298123) -StakeTest:test_StakeOneAccountWithMinLockUp() (gas: 299812) -StakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 299879) +RewardsStreamerMP_RewardsTest:testTotalRewardsSupply() (gas: 620566) +StakeTest:test_StakeMultipleAccounts() (gas: 502663) +StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 508698) +StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 847568) +StakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 517705) +StakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 539672) +StakeTest:test_StakeOneAccount() (gas: 279903) +StakeTest:test_StakeOneAccountAndRewards() (gas: 285936) +StakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 510528) +StakeTest:test_StakeOneAccountReachingMPLimit() (gas: 500089) +StakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 300134) +StakeTest:test_StakeOneAccountWithMinLockUp() (gas: 300696) +StakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 300807) StakingTokenTest:testStakeToken() (gas: 10422) -UnstakeTest:test_StakeMultipleAccounts() (gas: 499558) -UnstakeTest:test_StakeMultipleAccountsAndRewards() (gas: 505496) -UnstakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 842562) -UnstakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 515935) -UnstakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 538001) -UnstakeTest:test_StakeOneAccount() (gas: 278291) -UnstakeTest:test_StakeOneAccountAndRewards() (gas: 284259) -UnstakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 507705) -UnstakeTest:test_StakeOneAccountReachingMPLimit() (gas: 499054) -UnstakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 298123) -UnstakeTest:test_StakeOneAccountWithMinLockUp() (gas: 299812) -UnstakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 299878) -UnstakeTest:test_UnstakeBonusMPAndAccuredMP() (gas: 546326) -UnstakeTest:test_UnstakeMultipleAccounts() (gas: 705083) -UnstakeTest:test_UnstakeMultipleAccountsAndRewards() (gas: 800935) -UnstakeTest:test_UnstakeOneAccount() (gas: 480104) -UnstakeTest:test_UnstakeOneAccountAndAccruedMP() (gas: 502976) -UnstakeTest:test_UnstakeOneAccountAndRewards() (gas: 409152) -UnstakeTest:test_UnstakeOneAccountWithLockUpAndAccruedMP() (gas: 531453) -UpgradeTest:test_RevertWhenNotOwner() (gas: 2709437) -UpgradeTest:test_UpgradeStakeManager() (gas: 5749483) -VaultRegistrationTest:test_VaultRegistration() (gas: 62013) -WithdrawTest:test_CannotWithdrawStakedFunds() (gas: 311863) +UnstakeTest:test_StakeMultipleAccounts() (gas: 502662) +UnstakeTest:test_StakeMultipleAccountsAndRewards() (gas: 508742) +UnstakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 847545) +UnstakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 517704) +UnstakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 539671) +UnstakeTest:test_StakeOneAccount() (gas: 279903) +UnstakeTest:test_StakeOneAccountAndRewards() (gas: 285980) +UnstakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 510527) +UnstakeTest:test_StakeOneAccountReachingMPLimit() (gas: 500091) +UnstakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 300156) +UnstakeTest:test_StakeOneAccountWithMinLockUp() (gas: 300718) +UnstakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 300807) +UnstakeTest:test_UnstakeBonusMPAndAccuredMP() (gas: 546596) +UnstakeTest:test_UnstakeMultipleAccounts() (gas: 707867) +UnstakeTest:test_UnstakeMultipleAccountsAndRewards() (gas: 803881) +UnstakeTest:test_UnstakeOneAccount() (gas: 481560) +UnstakeTest:test_UnstakeOneAccountAndAccruedMP() (gas: 505025) +UnstakeTest:test_UnstakeOneAccountAndRewards() (gas: 410773) +UnstakeTest:test_UnstakeOneAccountWithLockUpAndAccruedMP() (gas: 529996) +UpgradeTest:test_RevertWhenNotOwner() (gas: 2897740) +UpgradeTest:test_UpgradeStakeManager() (gas: 6114750) +VaultRegistrationTest:test_VaultRegistration() (gas: 62058) +WithdrawTest:test_CannotWithdrawStakedFunds() (gas: 313419) XPNFTTokenTest:testApproveNotAllowed() (gas: 10500) XPNFTTokenTest:testGetApproved() (gas: 10523) XPNFTTokenTest:testIsApprovedForAll() (gas: 10698) diff --git a/certora/specs/EmergencyMode.spec b/certora/specs/EmergencyMode.spec index d50adf1..292158e 100644 --- a/certora/specs/EmergencyMode.spec +++ b/certora/specs/EmergencyMode.spec @@ -9,7 +9,12 @@ definition isViewFunction(method f) returns bool = ( f.selector == sig:streamer.YEAR().selector || f.selector == sig:streamer.STAKING_TOKEN().selector || f.selector == sig:streamer.SCALE_FACTOR().selector || - f.selector == sig:streamer.MP_RATE_PER_YEAR().selector || + f.selector == sig:streamer.MP_APY().selector || + f.selector == sig:streamer.MP_MPY().selector || + f.selector == sig:streamer.MP_MPY_ABSOLUTE().selector || + f.selector == sig:streamer.ACCRUE_RATE().selector || + f.selector == sig:streamer.MIN_BALANCE().selector || + f.selector == sig:streamer.MAX_BALANCE().selector || f.selector == sig:streamer.MIN_LOCKUP_PERIOD().selector || f.selector == sig:streamer.MAX_LOCKUP_PERIOD().selector || f.selector == sig:streamer.MAX_MULTIPLIER().selector || diff --git a/src/RewardsStreamerMP.sol b/src/RewardsStreamerMP.sol index 8fbe7a1..851b893 100644 --- a/src/RewardsStreamerMP.sol +++ b/src/RewardsStreamerMP.sol @@ -10,6 +10,7 @@ import { IStakeManager } from "./interfaces/IStakeManager.sol"; import { IStakeVault } from "./interfaces/IStakeVault.sol"; import { IRewardProvider } from "./interfaces/IRewardProvider.sol"; import { TrustedCodehashAccess } from "./TrustedCodehashAccess.sol"; +import { StakeMath } from "./math/StakeMath.sol"; // Rewards Streamer with Multiplier Points contract RewardsStreamerMP is @@ -18,7 +19,8 @@ contract RewardsStreamerMP is IStakeManager, TrustedCodehashAccess, ReentrancyGuardUpgradeable, - IRewardProvider + IRewardProvider, + StakeMath { error StakingManager__InvalidVault(); error StakingManager__VaultNotRegistered(); @@ -26,7 +28,7 @@ contract RewardsStreamerMP is error StakingManager__AmountCannotBeZero(); error StakingManager__TransferFailed(); error StakingManager__InsufficientBalance(); - error StakingManager__InvalidLockingPeriod(); + error StakingManager__LockingPeriodCannotBeZero(); error StakingManager__CannotRestakeWithLockedFunds(); error StakingManager__TokensAreLocked(); error StakingManager__AlreadyLocked(); @@ -36,12 +38,6 @@ contract RewardsStreamerMP is IERC20 public STAKING_TOKEN; uint256 public constant SCALE_FACTOR = 1e18; - uint256 public constant MP_RATE_PER_YEAR = 1; - - uint256 public constant YEAR = 365 days; - uint256 public constant MIN_LOCKUP_PERIOD = 90 days; - uint256 public constant MAX_LOCKUP_PERIOD = 4 * YEAR; - uint256 public constant MAX_MULTIPLIER = 4; uint256 public totalStaked; uint256 public totalMPAccrued; @@ -193,10 +189,6 @@ contract RewardsStreamerMP is revert StakingManager__AmountCannotBeZero(); } - if (lockPeriod != 0 && (lockPeriod < MIN_LOCKUP_PERIOD || lockPeriod > MAX_LOCKUP_PERIOD)) { - revert StakingManager__InvalidLockingPeriod(); - } - _updateGlobalState(); _updateAccountMP(msg.sender, true); @@ -204,29 +196,24 @@ contract RewardsStreamerMP is if (account.lockUntil != 0 && account.lockUntil > block.timestamp) { revert StakingManager__CannotRestakeWithLockedFunds(); } + (uint256 _deltaMpTotal, uint256 _deltaMPMax, uint256 _newLockEnd) = _calculateStake( + account.stakedBalance, account.maxMP, account.lockUntil, block.timestamp, amount, lockPeriod + ); account.stakedBalance += amount; totalStaked += amount; - uint256 initialMP = amount; - uint256 potentialMP = amount * MAX_MULTIPLIER; - uint256 bonusMP = 0; - if (lockPeriod != 0) { - bonusMP = _calculateBonusMP(amount, lockPeriod); - account.lockUntil = block.timestamp + lockPeriod; + account.lockUntil = _newLockEnd; } else { account.lockUntil = 0; } - uint256 accountMaxMP = initialMP + bonusMP + potentialMP; - uint256 accountMP = initialMP + bonusMP; - - account.mpAccrued += accountMP; - totalMPAccrued += accountMP; + account.mpAccrued += _deltaMpTotal; + totalMPAccrued += _deltaMpTotal; - account.maxMP += accountMaxMP; - totalMaxMP += accountMaxMP; + account.maxMP += _deltaMPMax; + totalMaxMP += _deltaMPMax; account.accountRewardIndex = rewardIndex; } @@ -238,33 +225,29 @@ contract RewardsStreamerMP is onlyRegisteredVault nonReentrant { - if (lockPeriod < MIN_LOCKUP_PERIOD || lockPeriod > MAX_LOCKUP_PERIOD) { - revert StakingManager__InvalidLockingPeriod(); - } - Account storage account = accounts[msg.sender]; if (account.lockUntil > 0) { revert StakingManager__AlreadyLocked(); } - if (account.stakedBalance == 0) { - revert StakingManager__InsufficientBalance(); + if (lockPeriod == 0) { + revert StakingManager__LockingPeriodCannotBeZero(); } _updateGlobalState(); _updateAccountMP(msg.sender, true); - - uint256 additionalBonusMP = _calculateBonusMP(account.stakedBalance, lockPeriod); + (uint256 deltaMp, uint256 newLockEnd) = + _calculateLock(account.stakedBalance, account.maxMP, account.lockUntil, block.timestamp, lockPeriod); // Update account state - account.lockUntil = block.timestamp + lockPeriod; - account.mpAccrued += additionalBonusMP; - account.maxMP += additionalBonusMP; + account.lockUntil = newLockEnd; + account.mpAccrued += deltaMp; + account.maxMP += deltaMp; // Update global state - totalMPAccrued += additionalBonusMP; - totalMaxMP += additionalBonusMP; + totalMPAccrued += deltaMp; + totalMaxMP += deltaMp; account.accountRewardIndex = rewardIndex; } @@ -277,13 +260,6 @@ contract RewardsStreamerMP is nonReentrant { Account storage account = accounts[msg.sender]; - if (amount > account.stakedBalance) { - revert StakingManager__InsufficientBalance(); - } - - if (block.timestamp < account.lockUntil) { - revert StakingManager__TokensAreLocked(); - } _unstake(amount, account, msg.sender); } @@ -291,18 +267,15 @@ contract RewardsStreamerMP is _updateGlobalState(); _updateAccountMP(accountAddress, true); - uint256 previousStakedBalance = account.stakedBalance; - - // solhint-disable-next-line - uint256 mpToReduce = Math.mulDiv(account.mpAccrued, amount, previousStakedBalance); - uint256 maxMPToReduce = Math.mulDiv(account.maxMP, amount, previousStakedBalance); - + (uint256 _deltaMpTotal, uint256 _deltaMpMax) = _calculateUnstake( + account.stakedBalance, account.lockUntil, block.timestamp, account.mpAccrued, account.maxMP, amount + ); account.stakedBalance -= amount; - account.mpAccrued -= mpToReduce; - account.maxMP -= maxMPToReduce; + account.mpAccrued -= _deltaMpTotal; + account.maxMP -= _deltaMpMax; account.accountRewardIndex = rewardIndex; - totalMPAccrued -= mpToReduce; - totalMaxMP -= maxMPToReduce; + totalMPAccrued -= _deltaMpTotal; + totalMaxMP -= _deltaMpMax; totalStaked -= amount; } @@ -315,6 +288,8 @@ contract RewardsStreamerMP is Account storage account = accounts[msg.sender]; if (account.stakedBalance > 0) { + //updates lockuntil to allow unstake early + account.lockUntil = block.timestamp; // calling `_unstake` to update accounting accordingly _unstake(account.stakedBalance, account, msg.sender); @@ -358,7 +333,7 @@ contract RewardsStreamerMP is return (adjustedRewardIndex, totalMPAccrued); } - uint256 accruedMP = (timeDiff * totalStaked * MP_RATE_PER_YEAR) / YEAR; + uint256 accruedMP = _accrueMP(totalStaked, timeDiff); if (totalMPAccrued + accruedMP > totalMaxMP) { accruedMP = totalMaxMP - totalMPAccrued; } @@ -465,26 +440,19 @@ contract RewardsStreamerMP is return (accruedRewards, newRewardIndex); } - function _calculateBonusMP(uint256 amount, uint256 lockPeriod) internal pure returns (uint256) { - return Math.mulDiv(amount, lockPeriod, YEAR); - } - function _getAccountPendingdMP(Account storage account) internal view returns (uint256) { - if (account.maxMP == 0 || account.stakedBalance == 0) { + if (block.timestamp == account.lastMPUpdateTime) { return 0; } - - uint256 timeDiff = block.timestamp - account.lastMPUpdateTime; - if (timeDiff == 0) { + if (account.maxMP == 0 || account.stakedBalance == 0) { return 0; } - uint256 accruedMP = Math.mulDiv(timeDiff * account.stakedBalance, MP_RATE_PER_YEAR, YEAR); + uint256 deltaMpTotal = _calculateAccrual( + account.stakedBalance, account.mpAccrued, account.maxMP, account.lastMPUpdateTime, block.timestamp + ); - if (account.mpAccrued + accruedMP > account.maxMP) { - accruedMP = account.maxMP - account.mpAccrued; - } - return accruedMP; + return deltaMpTotal; } function _updateAccountMP(address accountAddress, bool forceMPUpdate) internal { diff --git a/src/interfaces/IStakeConstants.sol b/src/interfaces/IStakeConstants.sol new file mode 100644 index 0000000..7c46c1f --- /dev/null +++ b/src/interfaces/IStakeConstants.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +import { ITrustedCodehashAccess } from "./ITrustedCodehashAccess.sol"; + +/** + * @title IStakeConstants + * @author Ricardo Guilherme Schmidt + * @notice Interface for Stake Constants + * @dev This interface is necessary to linearize the inheritance of StakeMath and MultiplierPointMath + */ +interface IStakeConstants { + function MIN_LOCKUP_PERIOD() external view returns (uint256); + function MAX_LOCKUP_PERIOD() external view returns (uint256); + function MP_APY() external view returns (uint256); + function MAX_MULTIPLIER() external view returns (uint256); +} diff --git a/src/interfaces/IStakeManager.sol b/src/interfaces/IStakeManager.sol index 98c5629..c3e21fb 100644 --- a/src/interfaces/IStakeManager.sol +++ b/src/interfaces/IStakeManager.sol @@ -3,8 +3,9 @@ pragma solidity ^0.8.26; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ITrustedCodehashAccess } from "./ITrustedCodehashAccess.sol"; +import { IStakeConstants } from "./IStakeConstants.sol"; -interface IStakeManager is ITrustedCodehashAccess { +interface IStakeManager is ITrustedCodehashAccess, IStakeConstants { error StakingManager__FundsLocked(); error StakingManager__InvalidLockTime(); error StakingManager__InsufficientFunds(); @@ -24,8 +25,4 @@ interface IStakeManager is ITrustedCodehashAccess { function getStakedBalance(address _vault) external view returns (uint256 _balance); function STAKING_TOKEN() external view returns (IERC20); - function MIN_LOCKUP_PERIOD() external view returns (uint256); - function MAX_LOCKUP_PERIOD() external view returns (uint256); - function MP_RATE_PER_YEAR() external view returns (uint256); - function MAX_MULTIPLIER() external view returns (uint256); } diff --git a/src/math/MultiplierPointMath.sol b/src/math/MultiplierPointMath.sol new file mode 100644 index 0000000..d83846f --- /dev/null +++ b/src/math/MultiplierPointMath.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: MIT-1.0 +pragma solidity ^0.8.26; + +import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; +import { IStakeConstants } from "../interfaces/IStakeConstants.sol"; + +/** + * @title MultiplierPointMath + * @author Ricardo Guilherme Schmidt + * @notice Provides mathematical operations and utilities for managing multiplier points in the staking system. + */ +abstract contract MultiplierPointMath is IStakeConstants { + /// @notice One (mean) tropical year, in seconds. + uint256 public constant YEAR = 365 days; + /// @notice Accrued multiplier points maximum multiplier. + uint256 public constant MAX_MULTIPLIER = 4; + /// @notice Multiplier points annual percentage yield. + uint256 public constant MP_APY = 100; + /// @notice Multiplier points accrued maximum percentage yield. + uint256 public constant MP_MPY = MAX_MULTIPLIER * MP_APY; + /// @notice Multiplier points absolute maximum percentage yield. + uint256 public constant MP_MPY_ABSOLUTE = 100 + (2 * (MAX_MULTIPLIER * MP_APY)); + /// @notice The accrue rate period of time over which multiplier points are calculated. + uint256 public constant ACCRUE_RATE = 1 seconds; + /// @notice Minimal value to generate 1 multiplier point in the accrue rate period (rounded up). + uint256 public constant MIN_BALANCE = (((YEAR * 100) - 1) / (MP_APY * ACCRUE_RATE)) + 1; + /// @notice Maximum value to not overflow unsigned integer of 256 bits. + uint256 public constant MAX_BALANCE = type(uint256).max / (MP_APY * ACCRUE_RATE); + + /** + * @notice Calculates the accrued multiplier points (MPs) over a time period Δt, based on the account balance + * @param _balance Represents the current account balance + * @param _deltaTime The time difference or the duration over which the multiplier points are accrued, expressed in + * seconds + * @return accruedMP points accrued for given `_balance` and `_seconds` + */ + function _accrueMP(uint256 _balance, uint256 _deltaTime) internal pure returns (uint256 accruedMP) { + return Math.mulDiv(_balance, _deltaTime * MP_APY, YEAR * 100); + } + + /** + * @notice Calculates the bonus multiplier points (MPs) earned when a balance Δa is locked for a specified duration + * t_lock. + * It is equivalent to the accrued multiplier points function but specifically applied in the context of a locked + * balance. + * @param _balance quantity of tokens + * @param _lockedSeconds time in seconds locked + * @return bonusMP bonus multiplier points for given `_balance` and `_lockedSeconds` + */ + function _bonusMP(uint256 _balance, uint256 _lockedSeconds) internal pure returns (uint256 bonusMP) { + return _accrueMP(_balance, _lockedSeconds); + } + + /** + * @notice Calculates the initial multiplier points (MPs) based on the balance change Δa. The result is equal to + * the amount of balance added. + * @param _balance Represents the change in balance. + * @return initialMP Initial Multiplier Points + */ + function _initialMP(uint256 _balance) internal pure returns (uint256 initialMP) { + return _balance; + } + + /** + * @notice Calculates the reduction in multiplier points (MPs) when a portion of the balance Δa `_reducedAmount` is + * removed from the total balance a_bal `_balance`. + * The reduction is proportional to the ratio of the removed balance to the total balance, applied to the current + * multiplier points $mp$. + * @param _balance The total account balance before the removal of Δa `_reducedBalance` + * @param _mp Represents the current multiplier points + * @param _reducedAmount reduced balance + * @return reducedMP Multiplier points to reduce from `_mp` + */ + function _reduceMP( + uint256 _balance, + uint256 _mp, + uint256 _reducedAmount + ) + internal + pure + returns (uint256 reducedMP) + { + return Math.mulDiv(_mp, _reducedAmount, _balance); + } + + /** + * @notice Calculates maximum stake a given `_balance` can be generated with `MAX_MULTIPLIER` + * @param _balance quantity of tokens + * @return maxMPAccrued maximum quantity of muliplier points that can be generated for given `_balance` + */ + function _maxAccrueMP(uint256 _balance) internal pure returns (uint256 maxMPAccrued) { + return Math.mulDiv(_balance, MP_MPY, 100); + } + + /** + * @notice The maximum total multiplier points that can be generated for a determined amount of balance and lock + * duration. + * @param _balance Represents the current account balance + * @param _lockTime The time duration for which the balance is locked + * @return maxMP Maximum Multiplier Points that can be generated for given `_balance` and `_lockTime` + */ + function _maxTotalMP(uint256 _balance, uint256 _lockTime) internal pure returns (uint256 maxMP) { + return _balance + Math.mulDiv(_balance * MP_APY, (MAX_MULTIPLIER * YEAR) + _lockTime, YEAR * 100); + } + + /** + * @notice The absolute maximum total multiplier points that some balance could have, which is the sum of the + * maximum + * lockup time bonus possible and the maximum accrued multiplier points. + * @param _balance quantity of tokens + * @return maxMPAbsolute Absolute Maximum Multiplier Points + */ + function _maxAbsoluteTotalMP(uint256 _balance) internal pure returns (uint256 maxMPAbsolute) { + return Math.mulDiv(_balance, MP_MPY_ABSOLUTE, 100); + } + + /** + * @dev Caution: This value is estimated and can be incorrect due precision loss. + * @notice Calculates the remaining lock time available for a given `_mpMax` and `_balance` + * @param _balance Current balance used to calculate the maximum multiplier points. + * @param _mpMax Maximum multiplier points calculated from the current balance. + * @return lockTime Amount of lock time allowed to be increased + */ + function _lockTimeAvailable(uint256 _balance, uint256 _mpMax) internal pure returns (uint256 lockTime) { + return Math.mulDiv((_balance * MP_MPY_ABSOLUTE) - _mpMax, YEAR, _balance * 100); + } + + /** + * @notice Calculates the time required to accrue a specific multiplier point value. + * @param _balance The current balance. + * @param _mp The target multiplier points to accrue. + * @return timeToReachMaxMP The time required to reach the specified multiplier points, in seconds. + */ + function _timeToAccrueMP(uint256 _balance, uint256 _mp) internal pure returns (uint256 timeToReachMaxMP) { + return Math.mulDiv(_mp * 100, YEAR, _balance * MP_APY); + } + + /** + * @notice Calculates the bonus multiplier points based on the balance and maximum multiplier points. + * @param _balance The current balance. + * @param _maxMP The maximum multiplier points. + * @return bonusMP The calculated bonus multiplier points. + */ + function _retrieveBonusMP(uint256 _balance, uint256 _maxMP) internal pure returns (uint256 bonusMP) { + return _maxMP - (_balance + _maxAccrueMP(_balance)); + } + + /** + * @notice Retrieves the accrued multiplier points based on the total and maximum multiplier points. + * @param _balance The current balance. + * @param _totalMP The total multiplier points. + * @param _maxMP The maximum multiplier points. + * @return accruedMP The calculated accrued multiplier points. + */ + function _retrieveAccruedMP( + uint256 _balance, + uint256 _totalMP, + uint256 _maxMP + ) + internal + pure + returns (uint256 accruedMP) + { + return _totalMP + _maxAccrueMP(_balance) - _maxMP; + } +} diff --git a/src/math/StakeMath.sol b/src/math/StakeMath.sol new file mode 100644 index 0000000..cf95e19 --- /dev/null +++ b/src/math/StakeMath.sol @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT-1.0 +pragma solidity ^0.8.26; + +import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; +import { MultiplierPointMath } from "./MultiplierPointMath.sol"; + +/** + * @title StakeMath + * @author Ricardo Guilherme Schmidt + * @notice Provides mathematical operations and utilities for managing staking operations. + */ +abstract contract StakeMath is MultiplierPointMath { + error StakeMath__FundsLocked(); + error StakeMath__InvalidLockingPeriod(); + error StakeMath__StakeIsTooLow(); + error StakeMath__InsufficientBalance(); + error StakeMath__AccrueTimeNotReached(); + error StakeMath__AbsoluteMaxMPOverflow(); + + event StakeMathTest(uint256 lockTime); + /// @notice Minimal lockup time + + uint256 public constant MIN_LOCKUP_PERIOD = 90 days; + /// @notice Maximum lockup period + uint256 public constant MAX_LOCKUP_PERIOD = MAX_MULTIPLIER * YEAR; + + /** + * @notice Calculates the bonus multiplier points earned when a balance Δa is increased an optionally locked for a + * specified duration + * @param _balance Account current balance + * @param _currentMaxMP Account current max multiplier points + * @param _currentLockEndTime Account current lock end timestamp + * @param _processTime Process current timestamp + * @param _increasedAmount Increased amount of balance + * @param _increasedLockSeconds Increased amount of seconds to lock + * @return _deltaMpTotal Increased amount of total multiplier points + * @return _deltaMpMax Increased amount of max multiplier points + * @return _newLockEnd Account new lock end timestamp + */ + function _calculateStake( + uint256 _balance, + uint256 _currentMaxMP, + uint256 _currentLockEndTime, + uint256 _processTime, + uint256 _increasedAmount, + uint256 _increasedLockSeconds + ) + internal + pure + returns (uint256 _deltaMpTotal, uint256 _deltaMpMax, uint256 _newLockEnd) + { + uint256 newBalance = _balance + _increasedAmount; + /*if (newBalance < MIN_BALANCE) { + revert StakeMath__StakeIsTooLow(); + }*/ + _newLockEnd = Math.max(_currentLockEndTime, _processTime) + _increasedLockSeconds; + uint256 dt_lock = _newLockEnd - _processTime; + if (dt_lock != 0 && (dt_lock < MIN_LOCKUP_PERIOD || dt_lock > MAX_LOCKUP_PERIOD)) { + revert StakeMath__InvalidLockingPeriod(); + } + + uint256 deltaMpBonus; + if (dt_lock > 0) { + deltaMpBonus = _bonusMP(_increasedAmount, dt_lock); + } + + if (_balance > 0 && _increasedLockSeconds > 0) { + deltaMpBonus += _bonusMP(_balance, _increasedLockSeconds); + } + + _deltaMpTotal = _initialMP(_increasedAmount) + deltaMpBonus; + _deltaMpMax = _deltaMpTotal + _accrueMP(_increasedAmount, MAX_MULTIPLIER * YEAR); + + if (_deltaMpMax + _currentMaxMP > MP_MPY_ABSOLUTE * newBalance) { + revert StakeMath__AbsoluteMaxMPOverflow(); + } + } + + /** + * @notice Calculates the bonus multiplier points earned when a balance Δa is locked for a specified duration + * @param _balance Account current balance + * @param _currentMaxMP Account current max multiplier points + * @param _currentLockEndTime Account current lock end timestamp + * @param _processTime Process current timestamp + * @param _increasedLockSeconds Increased amount of seconds to lock + * @return _deltaMp Increased amount of total and max multiplier points + * @return _newLockEnd Account new lock end timestamp + */ + function _calculateLock( + uint256 _balance, + uint256 _currentMaxMP, + uint256 _currentLockEndTime, + uint256 _processTime, + uint256 _increasedLockSeconds + ) + internal + pure + returns (uint256 _deltaMp, uint256 _newLockEnd) + { + if (_balance == 0) { + revert StakeMath__InsufficientBalance(); + } + + _newLockEnd = Math.max(_currentLockEndTime, _processTime) + _increasedLockSeconds; + uint256 dt_lock = _newLockEnd - _processTime; + if (dt_lock != 0 && (dt_lock < MIN_LOCKUP_PERIOD || dt_lock > MAX_LOCKUP_PERIOD)) { + revert StakeMath__InvalidLockingPeriod(); + } + + _deltaMp = _bonusMP(_balance, _increasedLockSeconds); + + if (_deltaMp + _currentMaxMP > MP_MPY_ABSOLUTE * _balance) { + revert StakeMath__AbsoluteMaxMPOverflow(); + } + } + + /** + * + * @param _balance Account current balance + * @param _currentLockEndTime Account current lock end timestamp + * @param _processTime Process current timestamp + * @param _currentTotalMP Account current total multiplier points + * @param _currentMaxMP Account current max multiplier points + * @param _reducedAmount Reduced amount of balance + * @return _deltaMpTotal Increased amount of total multiplier points + * @return _deltaMpMax Increased amount of max multiplier points + */ + function _calculateUnstake( + uint256 _balance, + uint256 _currentLockEndTime, + uint256 _processTime, + uint256 _currentTotalMP, + uint256 _currentMaxMP, + uint256 _reducedAmount + ) + internal + pure + returns (uint256 _deltaMpTotal, uint256 _deltaMpMax) + { + if (_reducedAmount > _balance) { + revert StakeMath__InsufficientBalance(); + } + if (_currentLockEndTime > _processTime) { + revert StakeMath__FundsLocked(); + } + /*uint256 newBalance = _balance - _reducedAmount; + if (newBalance > 0 && newBalance < MIN_BALANCE) { + revert StakeMath__StakeIsTooLow(); + }*/ + _deltaMpTotal = _reduceMP(_balance, _currentTotalMP, _reducedAmount); + _deltaMpMax = _reduceMP(_balance, _currentMaxMP, _reducedAmount); + } + + /** + * @notice Calculates the accrued multiplier points for a given balance and seconds passed since last accrual + * @param _balance Account current balance + * @param _currentTotalMP Account current total multiplier points + * @param _currentMaxMP Account current max multiplier points + * @param _lastAccrualTime Account current last accrual timestamp + * @param _processTime Process current timestamp + * @return _deltaMpTotal Increased amount of total multiplier points + */ + function _calculateAccrual( + uint256 _balance, + uint256 _currentTotalMP, + uint256 _currentMaxMP, + uint256 _lastAccrualTime, + uint256 _processTime + ) + internal + pure + returns (uint256 _deltaMpTotal) + { + uint256 dt = _processTime - _lastAccrualTime; + /*if (dt < ACCRUE_RATE) { + revert StakeMath__AccrueTimeNotReached(); + }*/ + if (_currentTotalMP < _currentMaxMP) { + _deltaMpTotal = Math.min(_accrueMP(_balance, dt), _currentMaxMP - _currentTotalMP); + } + } + + /** + * @dev Caution: This value is estimated and can be incorrect due precision loss. + * @notice Estimates the time an account set as locked time. + * @param _mpMax Maximum multiplier points calculated from the current balance. + * @param _balance Current balance used to calculate the maximum multiplier points. + */ + function _estimateLockTime(uint256 _mpMax, uint256 _balance) internal pure returns (uint256 _lockTime) { + return Math.mulDiv((_mpMax - _balance) * 100, YEAR, _balance * MP_APY, Math.Rounding.Ceil) - MAX_LOCKUP_PERIOD; + } +} diff --git a/test/RewardsStreamerMP.t.sol b/test/RewardsStreamerMP.t.sol index a56d849..bc4f84f 100644 --- a/test/RewardsStreamerMP.t.sol +++ b/test/RewardsStreamerMP.t.sol @@ -9,13 +9,14 @@ import { DeploymentConfig } from "../script/DeploymentConfig.s.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { RewardsStreamerMP } from "../src/RewardsStreamerMP.sol"; +import { StakeMath } from "../src/math/StakeMath.sol"; import { StakeVault } from "../src/StakeVault.sol"; import { IStakeManagerProxy } from "../src/interfaces/IStakeManagerProxy.sol"; import { StakeManagerProxy } from "../src/StakeManagerProxy.sol"; import { MockToken } from "./mocks/MockToken.sol"; import { StackOverflowStakeManager } from "./mocks/StackOverflowStakeManager.sol"; -contract RewardsStreamerMPTest is Test { +contract RewardsStreamerMPTest is StakeMath, Test { MockToken stakingToken; RewardsStreamerMP public streamer; @@ -133,47 +134,12 @@ contract RewardsStreamerMPTest is Test { vault.leave(account); } - function _calculeInitialMP(uint256 amount) internal pure returns (uint256) { - return amount; - } - - function _calculateMaxAccruedMP(uint256 amount) internal view returns (uint256) { - return amount * streamer.MAX_MULTIPLIER(); - } - - function _calculateAbsoluteMaxTotalMP(uint256 amount) internal view returns (uint256) { - return _calculeInitialMP(amount) + _calculateBonusMP(amount, streamer.MAX_LOCKUP_PERIOD()) - + _calculateMaxAccruedMP(amount); - } - - function _calculateMaxTotalMP(uint256 amount, uint256 lockPeriod) internal view returns (uint256 maxTotalMaxMP) { - uint256 bonusMP = 0; - if (lockPeriod != 0) { - bonusMP = _calculateBonusMP(amount, lockPeriod); - } - return _calculeInitialMP(amount) + bonusMP + _calculateMaxAccruedMP(amount); - } - - function _calculateBonusMP(uint256 amount, uint256 lockupTime) internal view returns (uint256) { - // solhint-disable-next-line - return Math.mulDiv(amount, lockupTime, 365 days); - } - - function _calculateAccuredMP(uint256 totalStaked, uint256 timeDiff) internal view returns (uint256) { - return Math.mulDiv(timeDiff * totalStaked, streamer.MP_RATE_PER_YEAR(), 365 days); - } - - function _calculateTimeToAccureMPLimit(uint256 amount) internal view returns (uint256) { + function _timeToAccrueMPLimit(uint256 amount) internal view returns (uint256) { uint256 maxMP = amount * streamer.MAX_MULTIPLIER(); - uint256 timeInSeconds = _calculateTimeToAccureMP(amount, maxMP); + uint256 timeInSeconds = _timeToAccrueMP(amount, maxMP); return timeInSeconds; } - function _calculateTimeToAccureMP(uint256 amount, uint256 target) internal view returns (uint256) { - uint256 mpPerYear = amount * streamer.MP_RATE_PER_YEAR(); - return target * 365 days / mpPerYear; - } - function _upgradeStakeManager() internal { UpgradeRewardsStreamerMPScript upgrade = new UpgradeRewardsStreamerMPScript(); upgrade.run(admin, IStakeManagerProxy(address(streamer))); @@ -181,53 +147,45 @@ contract RewardsStreamerMPTest is Test { } contract MathTest is RewardsStreamerMPTest { - function test_CalcInitialMP() public { - assertEq(_calculeInitialMP(1), 1, "wrong initial MP"); - assertEq(_calculeInitialMP(10e18), 10e18, "wrong initial MP"); - assertEq(_calculeInitialMP(20e18), 20e18, "wrong initial MP"); - assertEq(_calculeInitialMP(30e18), 30e18, "wrong initial MP"); + function test_CalcInitialMP() public pure { + assertEq(_initialMP(1), 1, "wrong initial MP"); + assertEq(_initialMP(10e18), 10e18, "wrong initial MP"); + assertEq(_initialMP(20e18), 20e18, "wrong initial MP"); + assertEq(_initialMP(30e18), 30e18, "wrong initial MP"); } - function test_CalcAccrueMP() public { - assertEq(_calculateAccuredMP(10e18, 0), 0, "wrong accrued MP"); - assertEq(_calculateAccuredMP(10e18, 365 days / 2), 5e18, "wrong accrued MP"); - assertEq(_calculateAccuredMP(10e18, 365 days), 10e18, "wrong accrued MP"); - assertEq(_calculateAccuredMP(10e18, 365 days * 2), 20e18, "wrong accrued MP"); - assertEq(_calculateAccuredMP(10e18, 365 days * 3), 30e18, "wrong accrued MP"); + function test_CalcAccrueMP() public pure { + assertEq(_accrueMP(10e18, 0), 0, "wrong accrued MP"); + assertEq(_accrueMP(10e18, 365 days / 2), 5e18, "wrong accrued MP"); + assertEq(_accrueMP(10e18, 365 days), 10e18, "wrong accrued MP"); + assertEq(_accrueMP(10e18, 365 days * 2), 20e18, "wrong accrued MP"); + assertEq(_accrueMP(10e18, 365 days * 3), 30e18, "wrong accrued MP"); } - function test_CalcBonusMP() public { - assertEq(_calculateBonusMP(10e18, 0), 0, "wrong bonus MP"); - assertEq(_calculateBonusMP(10e18, streamer.MIN_LOCKUP_PERIOD()), 2_465_753_424_657_534_246, "wrong bonus MP"); - assertEq( - _calculateBonusMP(10e18, streamer.MIN_LOCKUP_PERIOD() + 13 days), - 2_821_917_808_219_178_082, - "wrong bonus MP" - ); - assertEq(_calculateBonusMP(100e18, 0), 0, "wrong bonus MP"); + function test_CalcBonusMP() public view { + assertEq(_bonusMP(10e18, 0), 0, "wrong bonus MP"); + assertEq(_bonusMP(10e18, streamer.MIN_LOCKUP_PERIOD()), 2_465_753_424_657_534_246, "wrong bonus MP"); + assertEq(_bonusMP(10e18, streamer.MIN_LOCKUP_PERIOD() + 13 days), 2_821_917_808_219_178_082, "wrong bonus MP"); + assertEq(_bonusMP(100e18, 0), 0, "wrong bonus MP"); } - function test_CalcMaxTotalMP() public { - assertEq(_calculateMaxTotalMP(10e18, 0), 50e18, "wrong max total MP"); - assertEq( - _calculateMaxTotalMP(10e18, streamer.MIN_LOCKUP_PERIOD()), 52_465_753_424_657_534_246, "wrong max total MP" - ); + function test_CalcMaxTotalMP() public view { + assertEq(_maxTotalMP(10e18, 0), 50e18, "wrong max total MP"); + assertEq(_maxTotalMP(10e18, streamer.MIN_LOCKUP_PERIOD()), 52_465_753_424_657_534_246, "wrong max total MP"); assertEq( - _calculateMaxTotalMP(10e18, streamer.MIN_LOCKUP_PERIOD() + 13 days), - 52_821_917_808_219_178_082, - "wrong max total MP" + _maxTotalMP(10e18, streamer.MIN_LOCKUP_PERIOD() + 13 days), 52_821_917_808_219_178_082, "wrong max total MP" ); - assertEq(_calculateMaxTotalMP(100e18, 0), 500e18, "wrong max total MP"); + assertEq(_maxTotalMP(100e18, 0), 500e18, "wrong max total MP"); } - function test_CalcAbsoluteMaxTotalMP() public { - assertEq(_calculateAbsoluteMaxTotalMP(10e18), 90e18, "wrong absolute max total MP"); - assertEq(_calculateAbsoluteMaxTotalMP(100e18), 900e18, "wrong absolute max total MP"); + function test_CalcAbsoluteMaxTotalMP() public pure { + assertEq(_maxAbsoluteTotalMP(10e18), 90e18, "wrong absolute max total MP"); + assertEq(_maxAbsoluteTotalMP(100e18), 900e18, "wrong absolute max total MP"); } - function test_CalcMaxAccruedMP() public { - assertEq(_calculateMaxAccruedMP(10e18), 40e18, "wrong max accrued MP"); - assertEq(_calculateMaxAccruedMP(100e18), 400e18, "wrong max accrued MP"); + function test_CalcMaxAccruedMP() public pure { + assertEq(_maxAccrueMP(10e18), 40e18, "wrong max accrued MP"); + assertEq(_maxAccrueMP(100e18), 400e18, "wrong max accrued MP"); } } @@ -372,7 +330,7 @@ contract IntegrationTest is RewardsStreamerMPTest { // T4 uint256 currentTime = vm.getBlockTimestamp(); - vm.warp(currentTime + (365 days / 2)); + vm.warp(currentTime + (YEAR / 2)); streamer.updateGlobalState(); checkStreamer( @@ -660,10 +618,10 @@ contract StakeTest is RewardsStreamerMPTest { function test_StakeOneAccountWithMinLockUp() public { uint256 stakeAmount = 10e18; uint256 lockUpPeriod = streamer.MIN_LOCKUP_PERIOD(); - uint256 expectedBonusMP = _calculateBonusMP(stakeAmount, lockUpPeriod); + uint256 expectedBonusMP = _bonusMP(stakeAmount, lockUpPeriod); _stake(alice, stakeAmount, lockUpPeriod); - uint256 expectedMaxTotalMP = _calculateMaxTotalMP(stakeAmount, lockUpPeriod); + uint256 expectedMaxTotalMP = _maxTotalMP(stakeAmount, lockUpPeriod); checkStreamer( CheckStreamerParams({ @@ -681,7 +639,7 @@ contract StakeTest is RewardsStreamerMPTest { function test_StakeOneAccountWithMaxLockUp() public { uint256 stakeAmount = 10e18; uint256 lockUpPeriod = streamer.MAX_LOCKUP_PERIOD(); - uint256 expectedBonusMP = _calculateBonusMP(stakeAmount, lockUpPeriod); + uint256 expectedBonusMP = _bonusMP(stakeAmount, lockUpPeriod); _stake(alice, stakeAmount, lockUpPeriod); @@ -701,10 +659,10 @@ contract StakeTest is RewardsStreamerMPTest { function test_StakeOneAccountWithRandomLockUp() public { uint256 stakeAmount = 10e18; uint256 lockUpPeriod = streamer.MIN_LOCKUP_PERIOD() + 13 days; - uint256 expectedBonusMP = _calculateBonusMP(stakeAmount, lockUpPeriod); + uint256 expectedBonusMP = _bonusMP(stakeAmount, lockUpPeriod); _stake(alice, stakeAmount, lockUpPeriod); - uint256 expectedMaxTotalMP = _calculateMaxTotalMP(stakeAmount, lockUpPeriod); + uint256 expectedMaxTotalMP = _maxTotalMP(stakeAmount, lockUpPeriod); checkStreamer( CheckStreamerParams({ @@ -738,7 +696,7 @@ contract StakeTest is RewardsStreamerMPTest { ); uint256 currentTime = vm.getBlockTimestamp(); - vm.warp(currentTime + (365 days)); + vm.warp(currentTime + (YEAR)); streamer.updateGlobalState(); streamer.updateAccountMP(vaults[alice]); @@ -770,7 +728,7 @@ contract StakeTest is RewardsStreamerMPTest { ); currentTime = vm.getBlockTimestamp(); - vm.warp(currentTime + (365 days / 2)); + vm.warp(currentTime + (YEAR / 2)); streamer.updateGlobalState(); streamer.updateAccountMP(vaults[alice]); @@ -833,7 +791,7 @@ contract StakeTest is RewardsStreamerMPTest { ); uint256 currentTime = vm.getBlockTimestamp(); - uint256 timeToMaxMP = _calculateTimeToAccureMP(stakeAmount, totalMaxMP - totalMPAccrued); + uint256 timeToMaxMP = _timeToAccrueMP(stakeAmount, totalMaxMP - totalMPAccrued); vm.warp(currentTime + timeToMaxMP); streamer.updateGlobalState(); @@ -982,11 +940,11 @@ contract StakeTest is RewardsStreamerMPTest { function test_StakeMultipleAccountsWithMinLockUp() public { uint256 aliceStakeAmount = 10e18; uint256 aliceLockUpPeriod = streamer.MIN_LOCKUP_PERIOD(); - uint256 aliceExpectedBonusMP = _calculateBonusMP(aliceStakeAmount, aliceLockUpPeriod); + uint256 aliceExpectedBonusMP = _bonusMP(aliceStakeAmount, aliceLockUpPeriod); uint256 bobStakeAmount = 30e18; uint256 bobLockUpPeriod = 0; - uint256 bobExpectedBonusMP = _calculateBonusMP(bobStakeAmount, bobLockUpPeriod); + uint256 bobExpectedBonusMP = _bonusMP(bobStakeAmount, bobLockUpPeriod); // alice stakes with lockup period _stake(alice, aliceStakeAmount, aliceLockUpPeriod); @@ -996,8 +954,8 @@ contract StakeTest is RewardsStreamerMPTest { uint256 sumOfStakeAmount = aliceStakeAmount + bobStakeAmount; uint256 sumOfExpectedBonusMP = aliceExpectedBonusMP + bobExpectedBonusMP; - uint256 expectedMaxTotalMP = _calculateMaxTotalMP(aliceStakeAmount, aliceLockUpPeriod) - + _calculateMaxTotalMP(bobStakeAmount, bobLockUpPeriod); + uint256 expectedMaxTotalMP = + _maxTotalMP(aliceStakeAmount, aliceLockUpPeriod) + _maxTotalMP(bobStakeAmount, bobLockUpPeriod); checkStreamer( CheckStreamerParams({ totalStaked: sumOfStakeAmount, @@ -1013,11 +971,11 @@ contract StakeTest is RewardsStreamerMPTest { function test_StakeMultipleAccountsWithRandomLockUp() public { uint256 aliceStakeAmount = 10e18; uint256 aliceLockUpPeriod = streamer.MAX_LOCKUP_PERIOD() - 21 days; - uint256 aliceExpectedBonusMP = _calculateBonusMP(aliceStakeAmount, aliceLockUpPeriod); + uint256 aliceExpectedBonusMP = _bonusMP(aliceStakeAmount, aliceLockUpPeriod); uint256 bobStakeAmount = 30e18; uint256 bobLockUpPeriod = streamer.MIN_LOCKUP_PERIOD() + 43 days; - uint256 bobExpectedBonusMP = _calculateBonusMP(bobStakeAmount, bobLockUpPeriod); + uint256 bobExpectedBonusMP = _bonusMP(bobStakeAmount, bobLockUpPeriod); // alice stakes with lockup period _stake(alice, aliceStakeAmount, aliceLockUpPeriod); @@ -1027,8 +985,8 @@ contract StakeTest is RewardsStreamerMPTest { uint256 sumOfStakeAmount = aliceStakeAmount + bobStakeAmount; uint256 sumOfExpectedBonusMP = aliceExpectedBonusMP + bobExpectedBonusMP; - uint256 expectedMaxTotalMP = _calculateMaxTotalMP(aliceStakeAmount, aliceLockUpPeriod) - + _calculateMaxTotalMP(bobStakeAmount, bobLockUpPeriod); + uint256 expectedMaxTotalMP = + _maxTotalMP(aliceStakeAmount, aliceLockUpPeriod) + _maxTotalMP(bobStakeAmount, bobLockUpPeriod); checkStreamer( CheckStreamerParams({ @@ -1093,7 +1051,7 @@ contract StakeTest is RewardsStreamerMPTest { ); uint256 currentTime = vm.getBlockTimestamp(); - vm.warp(currentTime + (365 days)); + vm.warp(currentTime + (YEAR)); streamer.updateGlobalState(); streamer.updateAccountMP(vaults[alice]); @@ -1142,7 +1100,7 @@ contract StakeTest is RewardsStreamerMPTest { ); currentTime = vm.getBlockTimestamp(); - vm.warp(currentTime + (365 days / 2)); + vm.warp(currentTime + (YEAR / 2)); streamer.updateGlobalState(); streamer.updateAccountMP(vaults[alice]); @@ -1244,7 +1202,7 @@ contract UnstakeTest is StakeTest { // wait for 1 year uint256 currentTime = vm.getBlockTimestamp(); - vm.warp(currentTime + (365 days)); + vm.warp(currentTime + (YEAR)); streamer.updateGlobalState(); streamer.updateAccountMP(vaults[alice]); @@ -1281,7 +1239,7 @@ contract UnstakeTest is StakeTest { uint256 stakeAmount = 10e18; uint256 lockUpPeriod = streamer.MIN_LOCKUP_PERIOD(); // 10e18 is what's used in `test_StakeOneAccountWithMinLockUp` - uint256 expectedBonusMP = _calculateBonusMP(stakeAmount, lockUpPeriod); + uint256 expectedBonusMP = _bonusMP(stakeAmount, lockUpPeriod); uint256 unstakeAmount = 5e18; uint256 warpLength = (365 days); // wait for 1 year @@ -1297,7 +1255,7 @@ contract UnstakeTest is StakeTest { totalStaked: stakeAmount, totalMPAccrued: (stakeAmount + expectedBonusMP) + stakeAmount, // we do `+ stakeAmount` we've accrued // `stakeAmount` after 1 year - totalMaxMP: _calculateMaxTotalMP(stakeAmount, lockUpPeriod), + totalMaxMP: _maxTotalMP(stakeAmount, lockUpPeriod), stakingBalance: 10e18, rewardBalance: 0, rewardIndex: 0 @@ -1307,13 +1265,13 @@ contract UnstakeTest is StakeTest { // unstake half of the tokens _unstake(alice, unstakeAmount); - uint256 expectedTotalMP = _calculeInitialMP(newBalance) + _calculateBonusMP(newBalance, lockUpPeriod) - + _calculateAccuredMP(newBalance, warpLength); + uint256 expectedTotalMP = + _initialMP(newBalance) + _bonusMP(newBalance, lockUpPeriod) + _accrueMP(newBalance, warpLength); checkStreamer( CheckStreamerParams({ totalStaked: newBalance, totalMPAccrued: expectedTotalMP, - totalMaxMP: _calculateMaxTotalMP(newBalance, lockUpPeriod), + totalMaxMP: _maxTotalMP(newBalance, lockUpPeriod), stakingBalance: newBalance, rewardBalance: 0, rewardIndex: 0 @@ -1355,7 +1313,7 @@ contract UnstakeTest is StakeTest { uint256 amountStaked = 10e18; uint256 secondsLocked = streamer.MIN_LOCKUP_PERIOD(); uint256 reducedStake = 5e18; - uint256 increasedTime = 365 days; + uint256 increasedTime = YEAR; //initialize memory placehodlders uint256[4] memory timestamp; @@ -1371,8 +1329,8 @@ contract UnstakeTest is StakeTest { { timestamp[stage] = block.timestamp; totalStaked[stage] = amountStaked; - predictedBonusMP[stage] = totalStaked[stage] + _calculateBonusMP(totalStaked[stage], secondsLocked); - predictedTotalMaxMP[stage] = _calculateMaxTotalMP(totalStaked[stage], secondsLocked); + predictedBonusMP[stage] = totalStaked[stage] + _bonusMP(totalStaked[stage], secondsLocked); + predictedTotalMaxMP[stage] = _maxTotalMP(totalStaked[stage], secondsLocked); increasedAccuredMP[stage] = 0; //no increased accured MP in first stage predictedAccuredMP[stage] = 0; //no accured MP in first stage predictedTotalMP[stage] = predictedBonusMP[stage] + predictedAccuredMP[stage]; @@ -1384,7 +1342,7 @@ contract UnstakeTest is StakeTest { predictedBonusMP[stage] = predictedBonusMP[stage - 1]; //no change in bonusMP in second stage predictedTotalMaxMP[stage] = predictedTotalMaxMP[stage - 1]; // solhint-disable-next-line max-line-length - increasedAccuredMP[stage] = _calculateAccuredMP(totalStaked[stage], timestamp[stage] - timestamp[stage - 1]); + increasedAccuredMP[stage] = _accrueMP(totalStaked[stage], timestamp[stage] - timestamp[stage - 1]); predictedAccuredMP[stage] = predictedAccuredMP[stage - 1] + increasedAccuredMP[stage]; predictedTotalMP[stage] = predictedBonusMP[stage] + predictedAccuredMP[stage]; } @@ -1604,8 +1562,8 @@ contract LockTest is RewardsStreamerMPTest { ); // Lock for 1 year - uint256 lockPeriod = 365 days; - uint256 expectedBonusMP = _calculateBonusMP(stakeAmount, lockPeriod); + uint256 lockPeriod = YEAR; + uint256 expectedBonusMP = _bonusMP(stakeAmount, lockPeriod); _lock(alice, lockPeriod); @@ -1624,17 +1582,28 @@ contract LockTest is RewardsStreamerMPTest { } function test_LockFailsWithNoStake() public { - vm.expectRevert(RewardsStreamerMP.StakingManager__InsufficientBalance.selector); - _lock(alice, 365 days); + vm.expectRevert(StakeMath.StakeMath__InsufficientBalance.selector); + _lock(alice, YEAR); } - function test_LockFailsWithInvalidPeriod() public { + function test_LockFailsWithZero() public { _stake(alice, 10e18, 0); // Test with period = 0 - vm.expectRevert(RewardsStreamerMP.StakingManager__InvalidLockingPeriod.selector); + vm.expectRevert(RewardsStreamerMP.StakingManager__LockingPeriodCannotBeZero.selector); _lock(alice, 0); } + + function test_LockFailsWithInvalidPeriod(uint256 _lockPeriod) public { + vm.assume(_lockPeriod > 0); + vm.assume(_lockPeriod < MIN_LOCKUP_PERIOD || _lockPeriod > MAX_LOCKUP_PERIOD); + vm.assume(_lockPeriod < (type(uint256).max - block.timestamp)); //prevents arithmetic overflow + + _stake(alice, 10e18, 0); + + vm.expectRevert(StakeMath.StakeMath__InvalidLockingPeriod.selector); + _lock(alice, _lockPeriod); + } } contract EmergencyExitTest is RewardsStreamerMPTest { diff --git a/test/mocks/StackOverflowStakeManager.sol b/test/mocks/StackOverflowStakeManager.sol index 410476d..70b1464 100644 --- a/test/mocks/StackOverflowStakeManager.sol +++ b/test/mocks/StackOverflowStakeManager.sol @@ -6,6 +6,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { TrustedCodehashAccess } from "./../../src/TrustedCodehashAccess.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; +import { IStakeConstants } from "./../../src/interfaces/IStakeConstants.sol"; contract StackOverflowStakeManager is UUPSUpgradeable, @@ -16,7 +17,7 @@ contract StackOverflowStakeManager is IERC20 public STAKING_TOKEN; uint256 public constant SCALE_FACTOR = 1e18; - uint256 public constant MP_RATE_PER_YEAR = 1e18; + uint256 public constant MP_APY = 100; uint256 public constant MIN_LOCKUP_PERIOD = 90 days; uint256 public constant MAX_LOCKUP_PERIOD = 4 * 365 days;