mirror of
https://github.com/vacp2p/staking-reward-streamer.git
synced 2026-01-08 20:48:00 -05:00
refactor(RewardStreamerMP): extract MP and Stake mathematical formulas to abstract contracts
This commit is contained in:
committed by
r4bbit
parent
9807f498e0
commit
a22da253c3
143
.gas-report
143
.gas-report
@@ -1,119 +1,92 @@
|
||||
| script/DeployRewardsStreamerMP.s.sol:DeployRewardsStreamerMPScript contract | | | | | |
|
||||
|-----------------------------------------------------------------------------|-----------------|---------|---------|---------|---------|
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 6351315 | 30400 | | | | |
|
||||
| 6539451 | 31274 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| run | 5443041 | 5443041 | 5443041 | 5443041 | 63 |
|
||||
| run | 5618268 | 5618268 | 5618268 | 5618268 | 64 |
|
||||
|
||||
|
||||
| script/DeploymentConfig.s.sol:DeploymentConfig contract | | | | | |
|
||||
|---------------------------------------------------------|-----------------|-----|--------|-----|---------|
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 0 | 0 | | | | |
|
||||
| 0 | 7333 | | | | |
|
||||
| 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 | 371 | 371 | 371 | 371 | 6 |
|
||||
| MAX_MULTIPLIER | 295 | 295 | 295 | 295 | 33 |
|
||||
| MIN_LOCKUP_PERIOD | 252 | 252 | 252 | 252 | 15 |
|
||||
| MP_RATE_PER_YEAR | 253 | 253 | 253 | 253 | 9 |
|
||||
| STAKING_TOKEN | 428 | 2036 | 2428 | 2428 | 322 |
|
||||
| MAX_LOCKUP_PERIOD | 382 | 382 | 382 | 382 | 4 |
|
||||
| MAX_MULTIPLIER | 262 | 262 | 262 | 262 | 9 |
|
||||
| MIN_LOCKUP_PERIOD | 308 | 308 | 308 | 308 | 15 |
|
||||
| STAKING_TOKEN | 395 | 2003 | 2395 | 2395 | 327 |
|
||||
| emergencyModeEnabled | 2398 | 2398 | 2398 | 2398 | 7 |
|
||||
| enableEmergencyMode | 2507 | 19414 | 24699 | 24699 | 8 |
|
||||
| getAccountTotalMaxMP | 3122 | 3122 | 3122 | 3122 | 1 |
|
||||
| getAccountTotalStakedBalance | 15119 | 15119 | 15119 | 15119 | 1 |
|
||||
| getAccountVaults | 5202 | 5202 | 5202 | 5202 | 4 |
|
||||
| getStakedBalance | 2629 | 2629 | 2629 | 2629 | 1 |
|
||||
| getVault | 1643 | 1643 | 1643 | 1643 | 72 |
|
||||
| initialize | 115611 | 115611 | 115611 | 115611 | 65 |
|
||||
| lastRewardTime | 373 | 1373 | 1373 | 2373 | 2 |
|
||||
| leave | 59961 | 59961 | 59961 | 59961 | 1 |
|
||||
| lock | 12041 | 35382 | 16458 | 77648 | 3 |
|
||||
| mpBalanceOfAccount | 9228 | 9228 | 9228 | 9228 | 1 |
|
||||
| proxiableUUID | 353 | 353 | 353 | 353 | 3 |
|
||||
| registerVault | 55888 | 72788 | 72988 | 72988 | 257 |
|
||||
| rewardEndTime | 373 | 1373 | 1373 | 2373 | 2 |
|
||||
| rewardStartTime | 352 | 1352 | 1352 | 2352 | 2 |
|
||||
| rewardsBalanceOf | 3231 | 6627 | 7074 | 7341 | 8 |
|
||||
| setReward | 2561 | 58349 | 86319 | 105709 | 7 |
|
||||
| setTrustedCodehash | 24243 | 24304 | 24243 | 26243 | 65 |
|
||||
| stake | 134819 | 172398 | 179212 | 199545 | 66 |
|
||||
| totalMPAccrued | 351 | 351 | 351 | 351 | 81 |
|
||||
| totalMaxMP | 373 | 373 | 373 | 373 | 81 |
|
||||
| totalRewardsAccrued | 373 | 373 | 373 | 373 | 3 |
|
||||
| totalRewardsSupply | 1025 | 1984 | 1806 | 6765 | 30 |
|
||||
| totalStaked | 374 | 374 | 374 | 374 | 82 |
|
||||
| unstake | 64077 | 64613 | 64077 | 67567 | 13 |
|
||||
| updateGlobalState | 14339 | 26645 | 28616 | 28616 | 19 |
|
||||
| updateVaultMP | 12238 | 17353 | 17955 | 17955 | 19 |
|
||||
| upgradeToAndCall | 3225 | 7901 | 8448 | 10936 | 5 |
|
||||
| getAccountTotalMaxMP | 3133 | 3133 | 3133 | 3133 | 1 |
|
||||
| getAccountTotalStakedBalance | 15173 | 15173 | 15173 | 15173 | 1 |
|
||||
| getAccountVaults | 5225 | 5225 | 5225 | 5225 | 4 |
|
||||
| getStakedBalance | 2618 | 2618 | 2618 | 2618 | 1 |
|
||||
| getVault | 1621 | 1621 | 1621 | 1621 | 72 |
|
||||
| initialize | 115654 | 115654 | 115654 | 115654 | 66 |
|
||||
| lastRewardTime | 428 | 1428 | 1428 | 2428 | 2 |
|
||||
| leave | 79955 | 79955 | 79955 | 79955 | 1 |
|
||||
| lock | 14282 | 42729 | 42692 | 78446 | 259 |
|
||||
| mpBalanceOfAccount | 10308 | 10308 | 10308 | 10308 | 1 |
|
||||
| proxiableUUID | 387 | 387 | 387 | 387 | 3 |
|
||||
| registerVault | 55866 | 72769 | 72966 | 72966 | 261 |
|
||||
| rewardEndTime | 362 | 1362 | 1362 | 2362 | 2 |
|
||||
| rewardStartTime | 407 | 1407 | 1407 | 2407 | 2 |
|
||||
| rewardsBalanceOf | 2942 | 6975 | 7525 | 7746 | 8 |
|
||||
| setReward | 2606 | 58415 | 86507 | 105754 | 7 |
|
||||
| setTrustedCodehash | 24199 | 24259 | 24199 | 26199 | 66 |
|
||||
| stake | 136333 | 179336 | 180726 | 201200 | 322 |
|
||||
| totalMPAccrued | 384 | 384 | 384 | 384 | 81 |
|
||||
| totalMaxMP | 406 | 406 | 406 | 406 | 81 |
|
||||
| totalRewardsAccrued | 407 | 407 | 407 | 407 | 3 |
|
||||
| totalRewardsSupply | 1036 | 1995 | 1817 | 6776 | 30 |
|
||||
| totalStaked | 427 | 427 | 427 | 427 | 82 |
|
||||
| unstake | 63857 | 64502 | 63857 | 68054 | 13 |
|
||||
| updateGlobalState | 14339 | 26780 | 28759 | 28759 | 19 |
|
||||
| updateVaultMP | 11707 | 17581 | 18273 | 18273 | 19 |
|
||||
| upgradeToAndCall | 3181 | 7875 | 8438 | 10881 | 5 |
|
||||
|
||||
|
||||
| src/StakeManagerProxy.sol:StakeManagerProxy contract | | | | | |
|
||||
|------------------------------------------------------|-----------------|-------|--------|--------|---------|
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 256467 | 1263 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| MAX_LOCKUP_PERIOD | 798 | 3798 | 5298 | 5298 | 6 |
|
||||
| MAX_MULTIPLIER | 722 | 1949 | 722 | 5222 | 33 |
|
||||
| MIN_LOCKUP_PERIOD | 679 | 3379 | 5179 | 5179 | 15 |
|
||||
| MP_RATE_PER_YEAR | 680 | 1180 | 680 | 5180 | 9 |
|
||||
| STAKING_TOKEN | 855 | 6083 | 7355 | 7355 | 322 |
|
||||
| emergencyModeEnabled | 7325 | 7325 | 7325 | 7325 | 7 |
|
||||
| enableEmergencyMode | 28502 | 45403 | 50687 | 50687 | 8 |
|
||||
| getAccountTotalMaxMP | 3552 | 3552 | 3552 | 3552 | 1 |
|
||||
| getAccountTotalStakedBalance | 15549 | 15549 | 15549 | 15549 | 1 |
|
||||
| getAccountVaults | 5638 | 6763 | 5638 | 10138 | 4 |
|
||||
| getStakedBalance | 7559 | 7559 | 7559 | 7559 | 1 |
|
||||
| getVault | 2097 | 2097 | 2097 | 2097 | 72 |
|
||||
| implementation | 343 | 775 | 343 | 2343 | 412 |
|
||||
| lastRewardTime | 800 | 1800 | 1800 | 2800 | 2 |
|
||||
| mpBalanceOfAccount | 9658 | 9658 | 9658 | 9658 | 1 |
|
||||
| rewardEndTime | 800 | 1800 | 1800 | 2800 | 2 |
|
||||
| rewardStartTime | 779 | 4029 | 4029 | 7279 | 2 |
|
||||
| rewardsBalanceOf | 3661 | 7057 | 7504 | 7771 | 8 |
|
||||
| setReward | 28841 | 84663 | 112677 | 132067 | 7 |
|
||||
| setTrustedCodehash | 52889 | 52889 | 52889 | 52889 | 2 |
|
||||
| totalMPAccrued | 778 | 778 | 778 | 778 | 81 |
|
||||
| totalMaxMP | 800 | 800 | 800 | 800 | 81 |
|
||||
| totalRewardsAccrued | 800 | 800 | 800 | 800 | 3 |
|
||||
| totalRewardsSupply | 1452 | 2561 | 2233 | 11692 | 30 |
|
||||
| totalStaked | 801 | 801 | 801 | 801 | 82 |
|
||||
| updateGlobalState | 40327 | 52633 | 54604 | 54604 | 19 |
|
||||
| updateVaultMP | 38597 | 43712 | 44314 | 44314 | 19 |
|
||||
| upgradeToAndCall | 29868 | 33720 | 33720 | 37572 | 2 |
|
||||
| src/StakeManagerProxy.sol:StakeManagerProxy contract | | | | | |
|
||||
|------------------------------------------------------|-----------------|------|--------|--------|---------|
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 256510 | 1231 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| fallback | 689 | 7187 | 2075 | 132112 | 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 | 36353 | 48857 | 48091 | 65191 | 7 |
|
||||
| leave | 33507 | 132182 | 62122 | 370978 | 4 |
|
||||
| lock | 33245 | 61584 | 50823 | 111445 | 4 |
|
||||
| owner | 2339 | 2339 | 2339 | 2339 | 257 |
|
||||
| register | 87037 | 103937 | 104137 | 104137 | 257 |
|
||||
| stake | 33411 | 243726 | 253716 | 274097 | 67 |
|
||||
| stakeManager | 368 | 368 | 368 | 368 | 257 |
|
||||
| leave | 33507 | 136181 | 70120 | 370978 | 4 |
|
||||
| lock | 33245 | 79219 | 79302 | 112243 | 260 |
|
||||
| owner | 2339 | 2339 | 2339 | 2339 | 261 |
|
||||
| register | 87015 | 103918 | 104115 | 104115 | 261 |
|
||||
| stake | 33411 | 253159 | 255230 | 275752 | 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 | | | | | |
|
||||
|
||||
129
.gas-snapshot
129
.gas-snapshot
@@ -1,74 +1,75 @@
|
||||
EmergencyExitTest:test_CannotEnableEmergencyModeTwice() (gas: 92734)
|
||||
EmergencyExitTest:test_CannotLeaveBeforeEmergencyMode() (gas: 299008)
|
||||
EmergencyExitTest:test_EmergencyExitBasic() (gas: 385662)
|
||||
EmergencyExitTest:test_EmergencyExitMultipleUsers() (gas: 664160)
|
||||
EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 393691)
|
||||
EmergencyExitTest:test_EmergencyExitWithLock() (gas: 393030)
|
||||
EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 378630)
|
||||
EmergencyExitTest:test_OnlyOwnerCanEnableEmergencyMode() (gas: 39470)
|
||||
IntegrationTest:testStakeFoo() (gas: 1212157)
|
||||
LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 5836880)
|
||||
LeaveTest:test_RevertWhenStakeManagerIsTrusted() (gas: 296161)
|
||||
LeaveTest:test_TrustNewStakeManager() (gas: 5907277)
|
||||
LockTest:test_LockFailsWithInvalidPeriod() (gas: 311224)
|
||||
LockTest:test_LockFailsWithNoStake() (gas: 63663)
|
||||
LockTest:test_LockWithoutPriorLock() (gas: 390931)
|
||||
MaliciousUpgradeTest:test_UpgradeStackOverflowStakeManager() (gas: 1746581)
|
||||
MathTest:test_CalcAbsoluteMaxTotalMP() (gas: 18995)
|
||||
MathTest:test_CalcAccrueMP() (gas: 22229)
|
||||
MathTest:test_CalcBonusMP() (gas: 17645)
|
||||
MathTest:test_CalcInitialMP() (gas: 5330)
|
||||
MathTest:test_CalcMaxAccruedMP() (gas: 15696)
|
||||
MathTest:test_CalcMaxTotalMP() (gas: 23339)
|
||||
MultipleVaultsStakeTest:test_StakeMultipleVaults() (gas: 725540)
|
||||
EmergencyExitTest:test_CannotEnableEmergencyModeTwice() (gas: 92757)
|
||||
EmergencyExitTest:test_CannotLeaveBeforeEmergencyMode() (gas: 300544)
|
||||
EmergencyExitTest:test_EmergencyExitBasic() (gas: 387340)
|
||||
EmergencyExitTest:test_EmergencyExitMultipleUsers() (gas: 667427)
|
||||
EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 395139)
|
||||
EmergencyExitTest:test_EmergencyExitWithLock() (gas: 394708)
|
||||
EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 380241)
|
||||
EmergencyExitTest:test_OnlyOwnerCanEnableEmergencyMode() (gas: 39471)
|
||||
IntegrationTest:testStakeFoo() (gas: 1218594)
|
||||
LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 6214173)
|
||||
LeaveTest:test_RevertWhenStakeManagerIsTrusted() (gas: 297675)
|
||||
LeaveTest:test_TrustNewStakeManager() (gas: 6269901)
|
||||
LockTest:test_LockFailsWithInvalidPeriod(uint256) (runs: 1002, μ: 344783, ~: 344801)
|
||||
LockTest:test_LockFailsWithNoStake() (gas: 102637)
|
||||
LockTest:test_LockFailsWithZero() (gas: 315022)
|
||||
LockTest:test_LockWithoutPriorLock() (gas: 393335)
|
||||
MaliciousUpgradeTest:test_UpgradeStackOverflowStakeManager() (gas: 1752531)
|
||||
MathTest:test_CalcAbsoluteMaxTotalMP() (gas: 4996)
|
||||
MathTest:test_CalcAccrueMP() (gas: 7990)
|
||||
MathTest:test_CalcBonusMP() (gas: 18676)
|
||||
MathTest:test_CalcInitialMP() (gas: 5352)
|
||||
MathTest:test_CalcMaxAccruedMP() (gas: 4642)
|
||||
MathTest:test_CalcMaxTotalMP() (gas: 19449)
|
||||
MultipleVaultsStakeTest:test_StakeMultipleVaults() (gas: 731369)
|
||||
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: 486274)
|
||||
RewardsStreamerMP_RewardsTest:testSetRewards() (gas: 160637)
|
||||
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 39317)
|
||||
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadDuration() (gas: 39340)
|
||||
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsNotAuthorized() (gas: 39375)
|
||||
RewardsStreamerMP_RewardsTest:testTotalRewardsSupply() (gas: 618553)
|
||||
StakeTest:test_StakeMultipleAccounts() (gas: 499457)
|
||||
StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 505374)
|
||||
StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 842563)
|
||||
StakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 515891)
|
||||
StakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 538001)
|
||||
StakeTest:test_StakeOneAccount() (gas: 278207)
|
||||
StakeTest:test_StakeOneAccountAndRewards() (gas: 284155)
|
||||
StakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 507692)
|
||||
StakeTest:test_StakeOneAccountReachingMPLimit() (gas: 499083)
|
||||
StakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 298124)
|
||||
StakeTest:test_StakeOneAccountWithMinLockUp() (gas: 299768)
|
||||
StakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 299857)
|
||||
RewardsStreamerMP_RewardsTest:testRewardsBalanceOf() (gas: 490632)
|
||||
RewardsStreamerMP_RewardsTest:testSetRewards() (gas: 160880)
|
||||
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 39384)
|
||||
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadDuration() (gas: 39407)
|
||||
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsNotAuthorized() (gas: 39442)
|
||||
RewardsStreamerMP_RewardsTest:testTotalRewardsSupply() (gas: 620722)
|
||||
StakeTest:test_StakeMultipleAccounts() (gas: 502561)
|
||||
StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 508596)
|
||||
StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 847390)
|
||||
StakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 517705)
|
||||
StakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 539649)
|
||||
StakeTest:test_StakeOneAccount() (gas: 279841)
|
||||
StakeTest:test_StakeOneAccountAndRewards() (gas: 285896)
|
||||
StakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 510467)
|
||||
StakeTest:test_StakeOneAccountReachingMPLimit() (gas: 500009)
|
||||
StakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 300111)
|
||||
StakeTest:test_StakeOneAccountWithMinLockUp() (gas: 300696)
|
||||
StakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 300763)
|
||||
StakingTokenTest:testStakeToken() (gas: 10422)
|
||||
UnstakeTest:test_StakeMultipleAccounts() (gas: 499479)
|
||||
UnstakeTest:test_StakeMultipleAccountsAndRewards() (gas: 505396)
|
||||
UnstakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 842585)
|
||||
UnstakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 515935)
|
||||
UnstakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 537957)
|
||||
UnstakeTest:test_StakeOneAccount() (gas: 278230)
|
||||
UnstakeTest:test_StakeOneAccountAndRewards() (gas: 284133)
|
||||
UnstakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 507736)
|
||||
UnstakeTest:test_StakeOneAccountReachingMPLimit() (gas: 499040)
|
||||
UnstakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 298124)
|
||||
UnstakeTest:test_StakeOneAccountWithMinLockUp() (gas: 299768)
|
||||
UnstakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 299856)
|
||||
UnstakeTest:test_UnstakeBonusMPAndAccuredMP() (gas: 546251)
|
||||
UnstakeTest:test_UnstakeMultipleAccounts() (gas: 704925)
|
||||
UnstakeTest:test_UnstakeMultipleAccountsAndRewards() (gas: 800718)
|
||||
UnstakeTest:test_UnstakeOneAccount() (gas: 479941)
|
||||
UnstakeTest:test_UnstakeOneAccountAndAccruedMP() (gas: 502893)
|
||||
UnstakeTest:test_UnstakeOneAccountAndRewards() (gas: 409031)
|
||||
UnstakeTest:test_UnstakeOneAccountWithLockUpAndAccruedMP() (gas: 531430)
|
||||
UpgradeTest:test_RevertWhenNotOwner() (gas: 2709437)
|
||||
UpgradeTest:test_UpgradeStakeManager() (gas: 5749376)
|
||||
VaultRegistrationTest:test_VaultRegistration() (gas: 62017)
|
||||
WithdrawTest:test_CannotWithdrawStakedFunds() (gas: 311841)
|
||||
UnstakeTest:test_StakeMultipleAccounts() (gas: 502560)
|
||||
UnstakeTest:test_StakeMultipleAccountsAndRewards() (gas: 508640)
|
||||
UnstakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 847367)
|
||||
UnstakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 517704)
|
||||
UnstakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 539693)
|
||||
UnstakeTest:test_StakeOneAccount() (gas: 279841)
|
||||
UnstakeTest:test_StakeOneAccountAndRewards() (gas: 285874)
|
||||
UnstakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 510511)
|
||||
UnstakeTest:test_StakeOneAccountReachingMPLimit() (gas: 500008)
|
||||
UnstakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 300111)
|
||||
UnstakeTest:test_StakeOneAccountWithMinLockUp() (gas: 300718)
|
||||
UnstakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 300762)
|
||||
UnstakeTest:test_UnstakeBonusMPAndAccuredMP() (gas: 546541)
|
||||
UnstakeTest:test_UnstakeMultipleAccounts() (gas: 707663)
|
||||
UnstakeTest:test_UnstakeMultipleAccountsAndRewards() (gas: 803659)
|
||||
UnstakeTest:test_UnstakeOneAccount() (gas: 481480)
|
||||
UnstakeTest:test_UnstakeOneAccountAndAccruedMP() (gas: 505028)
|
||||
UnstakeTest:test_UnstakeOneAccountAndRewards() (gas: 410671)
|
||||
UnstakeTest:test_UnstakeOneAccountWithLockUpAndAccruedMP() (gas: 530083)
|
||||
UpgradeTest:test_RevertWhenNotOwner() (gas: 2897740)
|
||||
UpgradeTest:test_UpgradeStakeManager() (gas: 6114750)
|
||||
VaultRegistrationTest:test_VaultRegistration() (gas: 62154)
|
||||
WithdrawTest:test_CannotWithdrawStakedFunds() (gas: 313397)
|
||||
XPNFTTokenTest:testApproveNotAllowed() (gas: 10500)
|
||||
XPNFTTokenTest:testGetApproved() (gas: 10523)
|
||||
XPNFTTokenTest:testIsApprovedForAll() (gas: 10698)
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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();
|
||||
_updateVaultMP(msg.sender, true);
|
||||
|
||||
@@ -204,29 +196,23 @@ contract RewardsStreamerMP is
|
||||
if (vault.lockUntil != 0 && vault.lockUntil > block.timestamp) {
|
||||
revert StakingManager__CannotRestakeWithLockedFunds();
|
||||
}
|
||||
(uint256 _deltaMpTotal, uint256 _deltaMPMax, uint256 _newLockEnd) =
|
||||
_calculateStake(vault.stakedBalance, vault.maxMP, vault.lockUntil, block.timestamp, amount, lockPeriod);
|
||||
|
||||
vault.stakedBalance += amount;
|
||||
totalStaked += amount;
|
||||
|
||||
uint256 initialMP = amount;
|
||||
uint256 potentialMP = amount * MAX_MULTIPLIER;
|
||||
uint256 bonusMP = 0;
|
||||
|
||||
if (lockPeriod != 0) {
|
||||
bonusMP = _calculateBonusMP(amount, lockPeriod);
|
||||
vault.lockUntil = block.timestamp + lockPeriod;
|
||||
vault.lockUntil = _newLockEnd;
|
||||
} else {
|
||||
vault.lockUntil = 0;
|
||||
}
|
||||
|
||||
uint256 vaultMaxMP = initialMP + bonusMP + potentialMP;
|
||||
uint256 vaultMP = initialMP + bonusMP;
|
||||
vault.mpAccrued += _deltaMpTotal;
|
||||
totalMPAccrued += _deltaMpTotal;
|
||||
|
||||
vault.mpAccrued += vaultMP;
|
||||
totalMPAccrued += vaultMP;
|
||||
|
||||
vault.maxMP += vaultMaxMP;
|
||||
totalMaxMP += vaultMaxMP;
|
||||
vault.maxMP += _deltaMPMax;
|
||||
totalMaxMP += _deltaMPMax;
|
||||
|
||||
vault.rewardIndex = rewardIndex;
|
||||
}
|
||||
@@ -238,33 +224,29 @@ contract RewardsStreamerMP is
|
||||
onlyRegisteredVault
|
||||
nonReentrant
|
||||
{
|
||||
if (lockPeriod < MIN_LOCKUP_PERIOD || lockPeriod > MAX_LOCKUP_PERIOD) {
|
||||
revert StakingManager__InvalidLockingPeriod();
|
||||
}
|
||||
|
||||
VaultData storage vault = vaultData[msg.sender];
|
||||
|
||||
if (vault.lockUntil > 0) {
|
||||
revert StakingManager__AlreadyLocked();
|
||||
}
|
||||
|
||||
if (vault.stakedBalance == 0) {
|
||||
revert StakingManager__InsufficientBalance();
|
||||
if (lockPeriod == 0) {
|
||||
revert StakingManager__LockingPeriodCannotBeZero();
|
||||
}
|
||||
|
||||
_updateGlobalState();
|
||||
_updateVaultMP(msg.sender, true);
|
||||
(uint256 deltaMp, uint256 newLockEnd) =
|
||||
_calculateLock(vault.stakedBalance, vault.maxMP, vault.lockUntil, block.timestamp, lockPeriod);
|
||||
|
||||
uint256 additionalBonusMP = _calculateBonusMP(vault.stakedBalance, lockPeriod);
|
||||
|
||||
// Update vault state
|
||||
vault.lockUntil = block.timestamp + lockPeriod;
|
||||
vault.mpAccrued += additionalBonusMP;
|
||||
vault.maxMP += additionalBonusMP;
|
||||
// Update account state
|
||||
vault.lockUntil = newLockEnd;
|
||||
vault.mpAccrued += deltaMp;
|
||||
vault.maxMP += deltaMp;
|
||||
|
||||
// Update global state
|
||||
totalMPAccrued += additionalBonusMP;
|
||||
totalMaxMP += additionalBonusMP;
|
||||
totalMPAccrued += deltaMp;
|
||||
totalMaxMP += deltaMp;
|
||||
|
||||
vault.rewardIndex = rewardIndex;
|
||||
}
|
||||
@@ -277,13 +259,6 @@ contract RewardsStreamerMP is
|
||||
nonReentrant
|
||||
{
|
||||
VaultData storage vault = vaultData[msg.sender];
|
||||
if (amount > vault.stakedBalance) {
|
||||
revert StakingManager__InsufficientBalance();
|
||||
}
|
||||
|
||||
if (block.timestamp < vault.lockUntil) {
|
||||
revert StakingManager__TokensAreLocked();
|
||||
}
|
||||
_unstake(amount, vault, msg.sender);
|
||||
}
|
||||
|
||||
@@ -291,18 +266,15 @@ contract RewardsStreamerMP is
|
||||
_updateGlobalState();
|
||||
_updateVaultMP(vaultAddress, true);
|
||||
|
||||
uint256 previousStakedBalance = vault.stakedBalance;
|
||||
|
||||
// solhint-disable-next-line
|
||||
uint256 mpToReduce = Math.mulDiv(vault.mpAccrued, amount, previousStakedBalance);
|
||||
uint256 maxMPToReduce = Math.mulDiv(vault.maxMP, amount, previousStakedBalance);
|
||||
|
||||
(uint256 _deltaMpTotal, uint256 _deltaMpMax) = _calculateUnstake(
|
||||
vault.stakedBalance, vault.lockUntil, block.timestamp, vault.mpAccrued, vault.maxMP, amount
|
||||
);
|
||||
vault.stakedBalance -= amount;
|
||||
vault.mpAccrued -= mpToReduce;
|
||||
vault.maxMP -= maxMPToReduce;
|
||||
vault.mpAccrued -= _deltaMpTotal;
|
||||
vault.maxMP -= _deltaMpMax;
|
||||
vault.rewardIndex = rewardIndex;
|
||||
totalMPAccrued -= mpToReduce;
|
||||
totalMaxMP -= maxMPToReduce;
|
||||
totalMPAccrued -= _deltaMpTotal;
|
||||
totalMaxMP -= _deltaMpMax;
|
||||
totalStaked -= amount;
|
||||
}
|
||||
|
||||
@@ -315,6 +287,8 @@ contract RewardsStreamerMP is
|
||||
VaultData storage vault = vaultData[msg.sender];
|
||||
|
||||
if (vault.stakedBalance > 0) {
|
||||
//updates lockuntil to allow unstake early
|
||||
vault.lockUntil = block.timestamp;
|
||||
// calling `_unstake` to update accounting accordingly
|
||||
_unstake(vault.stakedBalance, vault, msg.sender);
|
||||
|
||||
@@ -358,7 +332,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 +439,19 @@ contract RewardsStreamerMP is
|
||||
return (accruedRewards, newRewardIndex);
|
||||
}
|
||||
|
||||
function _calculateBonusMP(uint256 amount, uint256 lockPeriod) internal pure returns (uint256) {
|
||||
return Math.mulDiv(amount, lockPeriod, YEAR);
|
||||
}
|
||||
|
||||
function _getVaultPendingMP(VaultData storage vault) internal view returns (uint256) {
|
||||
if (block.timestamp == vault.lastMPUpdateTime) {
|
||||
return 0;
|
||||
}
|
||||
if (vault.maxMP == 0 || vault.stakedBalance == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint256 timeDiff = block.timestamp - vault.lastMPUpdateTime;
|
||||
if (timeDiff == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint256 deltaMpTotal = _calculateAccrual(
|
||||
vault.stakedBalance, vault.mpAccrued, vault.maxMP, vault.lastMPUpdateTime, block.timestamp
|
||||
);
|
||||
|
||||
uint256 accruedMP = Math.mulDiv(timeDiff * vault.stakedBalance, MP_RATE_PER_YEAR, YEAR);
|
||||
|
||||
if (vault.mpAccrued + accruedMP > vault.maxMP) {
|
||||
accruedMP = vault.maxMP - vault.mpAccrued;
|
||||
}
|
||||
return accruedMP;
|
||||
return deltaMpTotal;
|
||||
}
|
||||
|
||||
function _updateVaultMP(address vaultAddress, bool forceMPUpdate) internal {
|
||||
|
||||
17
src/interfaces/IStakeConstants.sol
Normal file
17
src/interfaces/IStakeConstants.sol
Normal file
@@ -0,0 +1,17 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.26;
|
||||
|
||||
import { ITrustedCodehashAccess } from "./ITrustedCodehashAccess.sol";
|
||||
|
||||
/**
|
||||
* @title IStakeConstants
|
||||
* @author Ricardo Guilherme Schmidt <ricardo3@status.im>
|
||||
* @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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
166
src/math/MultiplierPointMath.sol
Normal file
166
src/math/MultiplierPointMath.sol
Normal file
@@ -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 <ricardo3@status.im>
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
182
src/math/StakeMath.sol
Normal file
182
src/math/StakeMath.sol
Normal file
@@ -0,0 +1,182 @@
|
||||
// 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 <ricardo3@status.im>
|
||||
* @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;
|
||||
_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();
|
||||
}
|
||||
_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 (_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;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
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 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(
|
||||
_calculateBonusMP(10e18, streamer.MIN_LOCKUP_PERIOD() + 13 days),
|
||||
2_821_917_808_219_178_082,
|
||||
"wrong bonus MP"
|
||||
_maxTotalMP(10e18, streamer.MIN_LOCKUP_PERIOD() + 13 days), 52_821_917_808_219_178_082, "wrong max total MP"
|
||||
);
|
||||
assertEq(_calculateBonusMP(100e18, 0), 0, "wrong bonus MP");
|
||||
assertEq(_maxTotalMP(100e18, 0), 500e18, "wrong max total 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"
|
||||
);
|
||||
assertEq(
|
||||
_calculateMaxTotalMP(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");
|
||||
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_CalcAbsoluteMaxTotalMP() public {
|
||||
assertEq(_calculateAbsoluteMaxTotalMP(10e18), 90e18, "wrong absolute max total MP");
|
||||
assertEq(_calculateAbsoluteMaxTotalMP(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.updateVaultMP(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.updateVaultMP(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.updateVaultMP(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.updateVaultMP(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.updateVaultMP(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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user