fix(StakeVault): add lockUntil validity check

In case the `StakeManager` is malicious and returns `lockUntil` values
that don't make sense, we calculate the new `lockUntil` values in the
`StakeVault` to compare against those.

If the values coming from `StakeManager` are not matching, something's
off and we revert.

We still do `lockUntil` calculations in `StakeManager` via `StakeMath`
to ensure absolute max MP isn't exceeded.

We don't have the notion of multiplier points in the `StakeVault`, nor
should it know about it. Hence, we're just doing the `lockUntil`
calculation.

**Note**

This introduces another "vulnerability" that I consider "acknowledged",
but won't fix:

In theory, a malicious stake manager upgrade could remove the upper
bound checks on absolute max MP. In such a case, users could pass a very
large `lockUntil` to `StakeVault`. `StakeManager` would simply calculate
the same `lockUntil` but wouldn't revert on the max cases.

So in other words, users can hurt themselves if stake manager removed
the check and users willingly lock for very long time.

Closes #249
This commit is contained in:
r4bbit
2025-08-26 15:34:15 +02:00
parent 1f9e9f678c
commit 0fa101df14
4 changed files with 138 additions and 111 deletions

View File

@@ -10,7 +10,7 @@
|-------------------------------------------------------------------------------------------+-----------------+-------+--------+--------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|-------------------------------------------------------------------------------------------+-----------------+-------+--------+--------+---------|
| fallback | 5145 | 65021 | 33119 | 193478 | 3520 |
| fallback | 5145 | 65032 | 33119 | 193478 | 3520 |
╰-------------------------------------------------------------------------------------------+-----------------+-------+--------+--------+---------╯
╭-----------------------------------------------------+-----------------+---------+---------+---------+---------╮
@@ -60,13 +60,13 @@
+=============================================================================================================================+
| Deployment Cost | Deployment Size | | | | |
|-------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| 8323525 | 39701 | | | | |
| 8362470 | 39883 | | | | |
|-------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| | | | | | |
|-------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|-------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| run | 7263905 | 7263905 | 7263905 | 7263905 | 88 |
| run | 7300365 | 7300365 | 7300365 | 7300365 | 88 |
╰-------------------------------------------------------------------+-----------------+---------+---------+---------+---------╯
╭---------------------------------------------------------+-----------------+------+--------+------+---------╮
@@ -124,7 +124,7 @@
|------------------------------+-----------------+--------+--------+--------+---------|
| approve | 453 | 453 | 453 | 453 | 8 |
|------------------------------+-----------------+--------+--------+--------+---------|
| balanceOf | 17795 | 28159 | 28160 | 28233 | 287 |
| balanceOf | 17795 | 28157 | 28160 | 28233 | 287 |
|------------------------------+-----------------+--------+--------+--------+---------|
| calculateSlashAmount | 2763 | 2801 | 2804 | 2804 | 774 |
|------------------------------+-----------------+--------+--------+--------+---------|
@@ -144,7 +144,7 @@
|------------------------------+-----------------+--------+--------+--------+---------|
| setReward | 4845 | 144102 | 166754 | 166754 | 319 |
|------------------------------+-----------------+--------+--------+--------+---------|
| slash | 4803 | 103578 | 85757 | 123125 | 520 |
| slash | 4803 | 103656 | 85757 | 123125 | 520 |
|------------------------------+-----------------+--------+--------+--------+---------|
| slashedAmountOf | 17682 | 28099 | 28120 | 28120 | 516 |
|------------------------------+-----------------+--------+--------+--------+---------|
@@ -232,7 +232,7 @@
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| getAccountVaults | 5230 | 5230 | 5230 | 5230 | 4 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| getVault | 13653 | 13653 | 13653 | 13653 | 4182 |
| getVault | 13653 | 13653 | 13653 | 13653 | 4179 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| initialize | 92774 | 92774 | 92774 | 92774 | 88 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
@@ -240,7 +240,7 @@
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| leave | 66358 | 66358 | 66358 | 66358 | 2 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| lock | 7096 | 40159 | 43888 | 62877 | 1034 |
| lock | 7096 | 40082 | 43888 | 62877 | 1034 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| migrateToVault | 9229 | 57567 | 16864 | 187311 | 4 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
@@ -258,7 +258,7 @@
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| rewardStartTime | 2386 | 2386 | 2386 | 2386 | 2 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| rewardsBalanceOf | 20317 | 24410 | 25930 | 26151 | 268 |
| rewardsBalanceOf | 20317 | 24431 | 25930 | 26151 | 268 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| rewardsBalanceOfAccount | 62242 | 62242 | 62242 | 62242 | 1 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
@@ -268,31 +268,31 @@
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| setTrustedCodehash | 24171 | 24171 | 24171 | 24171 | 88 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| stake | 2713 | 125321 | 57926 | 213584 | 2667 |
| stake | 2713 | 122925 | 57926 | 213584 | 2667 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| totalMP | 6827 | 8279 | 8279 | 9732 | 6 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| totalMPAccrued | 2407 | 2407 | 2407 | 2407 | 4162 |
| totalMPAccrued | 2407 | 2407 | 2407 | 2407 | 4159 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| totalMPStaked | 2363 | 2363 | 2363 | 2363 | 4165 |
| totalMPStaked | 2363 | 2363 | 2363 | 2363 | 4162 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| totalMaxMP | 2429 | 2429 | 2429 | 2429 | 4162 |
| totalMaxMP | 2429 | 2429 | 2429 | 2429 | 4159 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| totalRewardsAccrued | 2407 | 2407 | 2407 | 2407 | 3 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| totalRewardsSupply | 6759 | 11071 | 11814 | 11925 | 290 |
| totalRewardsSupply | 6759 | 11080 | 11814 | 11925 | 290 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| totalShares | 4619 | 4619 | 4619 | 4619 | 6 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| totalStaked | 2408 | 2408 | 2408 | 2408 | 4168 |
| totalStaked | 2408 | 2408 | 2408 | 2408 | 4165 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| unstake | 36868 | 39344 | 36894 | 76663 | 258 |
| unstake | 36868 | 39261 | 36894 | 76663 | 263 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| updateAccount | 347651 | 347651 | 347651 | 347651 | 1 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| updateGlobalState | 15809 | 25865 | 29219 | 29219 | 8 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| updateVault | 31936 | 34446 | 31936 | 110567 | 1024 |
| updateVault | 31936 | 33921 | 31936 | 110567 | 1021 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
| upgradeTo | 10323 | 10939 | 10323 | 12789 | 4 |
|--------------------------------------------+-----------------+--------+--------+--------+---------|
@@ -306,7 +306,7 @@
+===============================================================================================+
| Deployment Cost | Deployment Size | | | | |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| 1623745 | 7673 | | | | |
| 1662679 | 7855 | | | | |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| | | | | | |
|----------------------------------------+-----------------+--------+--------+--------+---------|
@@ -320,9 +320,9 @@
|----------------------------------------+-----------------+--------+--------+--------+---------|
| leave | 43439 | 145443 | 89216 | 359902 | 4 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| lock | 21526 | 54657 | 58318 | 80340 | 1034 |
| lock | 21538 | 54583 | 58330 | 80720 | 1034 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| lockUntil | 2363 | 2363 | 2363 | 2363 | 3819 |
| lockUntil | 2363 | 2363 | 2363 | 2363 | 3816 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| migrateFromVault | 24497 | 24497 | 24497 | 24497 | 1 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
@@ -332,11 +332,11 @@
|----------------------------------------+-----------------+--------+--------+--------+---------|
| register | 3354 | 79838 | 83167 | 83167 | 374 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| stake | 2636 | 167931 | 72429 | 288304 | 2673 |
| stake | 2636 | 164756 | 72429 | 288666 | 2673 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| stakeManager | 347 | 347 | 347 | 347 | 360 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| unstake(uint256) | 4655 | 52505 | 51424 | 106772 | 272 |
| unstake(uint256) | 4655 | 53330 | 51424 | 106772 | 272 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| unstake(uint256,address) | 2630 | 2630 | 2630 | 2630 | 1 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
@@ -358,7 +358,7 @@
|----------------------------------------------------+-----------------+-------+--------+--------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|----------------------------------------------------+-----------------+-------+--------+--------+---------|
| fallback | 5230 | 12829 | 7353 | 374028 | 23163 |
| fallback | 5230 | 12802 | 7353 | 374028 | 23145 |
╰----------------------------------------------------+-----------------+-------+--------+--------+---------╯
╭--------------------------------------------+-----------------+--------+--------+--------+---------╮
@@ -464,7 +464,7 @@
|-------------------------------------------------------------------+-----------------+-------+--------+-------+---------|
| setTotalKarmaShares | 43589 | 43589 | 43589 | 43589 | 48 |
|-------------------------------------------------------------------+-----------------+-------+--------+-------+---------|
| setUserKarmaShare | 24210 | 44032 | 44134 | 44266 | 531 |
| setUserKarmaShare | 24210 | 44107 | 44134 | 44266 | 531 |
|-------------------------------------------------------------------+-----------------+-------+--------+-------+---------|
| totalRewardsSupply | 2324 | 2324 | 2324 | 2324 | 48 |
╰-------------------------------------------------------------------+-----------------+-------+--------+-------+---------╯
@@ -488,7 +488,7 @@
+============================================================================================================+
| Deployment Cost | Deployment Size | | | | |
|-----------------------------------------------------------+-----------------+-----+--------+-----+---------|
| 228844 | 844 | | | | |
| 251818 | 952 | | | | |
|-----------------------------------------------------------+-----------------+-----+--------+-----+---------|
| | | | | | |
|-----------------------------------------------------------+-----------------+-----+--------+-----+---------|
@@ -502,17 +502,17 @@
+==================================================================================================+
| Deployment Cost | Deployment Size | | | | |
|---------------------------------------------+-----------------+-------+--------+-------+---------|
| 770717 | 3987 | | | | |
| 770657 | 3987 | | | | |
|---------------------------------------------+-----------------+-------+--------+-------+---------|
| | | | | | |
|---------------------------------------------+-----------------+-------+--------+-------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|---------------------------------------------+-----------------+-------+--------+-------+---------|
| approve | 29075 | 31546 | 29183 | 46259 | 2676 |
| approve | 29075 | 31545 | 29183 | 46259 | 2676 |
|---------------------------------------------+-----------------+-------+--------+-------+---------|
| balanceOf | 2561 | 2561 | 2561 | 2561 | 4966 |
| balanceOf | 2561 | 2561 | 2561 | 2561 | 4963 |
|---------------------------------------------+-----------------+-------+--------+-------+---------|
| mint | 33964 | 37264 | 34072 | 68248 | 2688 |
| mint | 33964 | 37263 | 34072 | 68248 | 2688 |
╰---------------------------------------------+-----------------+-------+--------+-------+---------╯
╭-----------------------------------------------------------------------------+-----------------+--------+--------+--------+---------╮

View File

@@ -9,23 +9,23 @@ AddRewardDistributorTest:testTotalSupply() (gas: 359391)
AddRewardDistributorTest:testTransfersNotAllowed() (gas: 61947)
AddRewardDistributorTest:test_RevertWhen_SenderIsNotDefaultAdmin() (gas: 68406)
EmergencyExitTest:test_CannotEnableEmergencyModeTwice() (gas: 93420)
EmergencyExitTest:test_CannotLeaveBeforeEmergencyMode() (gas: 352335)
EmergencyExitTest:test_EmergencyExitBasic() (gas: 540735)
EmergencyExitTest:test_EmergencyExitMultipleUsers() (gas: 969992)
EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 495287)
EmergencyExitTest:test_EmergencyExitWithLock() (gas: 446582)
EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 500965)
EmergencyExitTest:test_CannotLeaveBeforeEmergencyMode() (gas: 352697)
EmergencyExitTest:test_EmergencyExitBasic() (gas: 541097)
EmergencyExitTest:test_EmergencyExitMultipleUsers() (gas: 970716)
EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 495649)
EmergencyExitTest:test_EmergencyExitWithLock() (gas: 446958)
EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 501327)
EmergencyExitTest:test_OnlyOwnerCanEnableEmergencyMode() (gas: 39109)
FuzzTests:testFuzz_AccrueMP(uint128,uint64,uint64) (runs: 1024, μ: 581604, ~: 545120)
FuzzTests:testFuzz_AccrueMP_Relock(uint128,uint64,uint64,uint64) (runs: 1024, μ: 802844, ~: 769362)
FuzzTests:testFuzz_EmergencyExit(uint256,uint256) (runs: 1007, μ: 593693, ~: 593595)
FuzzTests:testFuzz_Lock(uint256,uint64) (runs: 1025, μ: 992626, ~: 993766)
FuzzTests:testFuzz_Relock(uint256,uint64,uint64) (runs: 1025, μ: 592561, ~: 566387)
FuzzTests:testFuzz_Rewards(uint256,uint256,uint256,uint16,uint16) (runs: 1001, μ: 645371, ~: 647577)
FuzzTests:testFuzz_Stake(uint256,uint64) (runs: 1025, μ: 377229, ~: 342206)
FuzzTests:testFuzz_Unstake(uint128,uint64,uint16,uint128) (runs: 1024, μ: 794426, ~: 772803)
FuzzTests:testFuzz_UpdateVault(uint128,uint64,uint64) (runs: 1024, μ: 581627, ~: 545143)
IntegrationTest:testStakeFoo() (gas: 2389875)
FuzzTests:testFuzz_AccrueMP(uint128,uint64,uint64) (runs: 1006, μ: 578092, ~: 545120)
FuzzTests:testFuzz_AccrueMP_Relock(uint128,uint64,uint64,uint64) (runs: 1006, μ: 799679, ~: 769377)
FuzzTests:testFuzz_EmergencyExit(uint256,uint256) (runs: 1000, μ: 594052, ~: 593971)
FuzzTests:testFuzz_Lock(uint256,uint64) (runs: 1005, μ: 993190, ~: 994140)
FuzzTests:testFuzz_Relock(uint256,uint64,uint64) (runs: 1005, μ: 589603, ~: 566399)
FuzzTests:testFuzz_Rewards(uint256,uint256,uint256,uint16,uint16) (runs: 1000, μ: 645709, ~: 647953)
FuzzTests:testFuzz_Stake(uint256,uint64) (runs: 1005, μ: 371209, ~: 342206)
FuzzTests:testFuzz_Unstake(uint128,uint64,uint16,uint128) (runs: 1006, μ: 794179, ~: 772781)
FuzzTests:testFuzz_UpdateVault(uint128,uint64,uint64) (runs: 1006, μ: 578115, ~: 545143)
IntegrationTest:testStakeFoo() (gas: 2390961)
KarmaNFTTest:testApproveNotAllowed() (gas: 10507)
KarmaNFTTest:testGetApproved() (gas: 10531)
KarmaNFTTest:testIsApprovedForAll() (gas: 10705)
@@ -63,23 +63,23 @@ KarmaTiersTest:test_Revert_When_TiersNotStartingAtZero() (gas: 37667)
KarmaTiersTest:test_Revert_When_UpdateTiersCalledByNonOwner() (gas: 36642)
KarmaTiersTest:test_Success_When_LastTierIsUnlimited() (gas: 242295)
KarmaTiersTest:test_Success_When_TiersAreContiguous() (gas: 336294)
LeaveTest:test_LeaveShouldKeepFundsLockedInStakeVault() (gas: 9717501)
LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 9812124)
LockTest:test_LockFailsWithInvalidPeriod(uint256) (runs: 1025, μ: 396949, ~: 396973)
LockTest:test_LockFailsWithNoStake() (gas: 85767)
LockTest:test_LockFailsWithZero() (gas: 358576)
LockTest:test_LockMultipleTimesExceedMaxLock() (gas: 736448)
LockTest:test_LockWithPriorLock() (gas: 668725)
LockTest:test_LockWithoutPriorLock() (gas: 515360)
LockTest:test_RevertWhenVaultToLockIsEmpty() (gas: 85767)
MaliciousUpgradeTest:test_UpgradeStackOverflowStakeManager() (gas: 2095407)
LeaveTest:test_LeaveShouldKeepFundsLockedInStakeVault() (gas: 9717877)
LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 9812486)
LockTest:test_LockFailsWithInvalidPeriod(uint256) (runs: 1006, μ: 397323, ~: 397347)
LockTest:test_LockFailsWithNoStake() (gas: 85779)
LockTest:test_LockFailsWithZero() (gas: 358950)
LockTest:test_LockMultipleTimesExceedMaxLock() (gas: 737202)
LockTest:test_LockWithPriorLock() (gas: 669837)
LockTest:test_LockWithoutPriorLock() (gas: 516102)
LockTest:test_RevertWhenVaultToLockIsEmpty() (gas: 85779)
MaliciousUpgradeTest:test_UpgradeStackOverflowStakeManager() (gas: 2095769)
MathTest:test_CalcAbsoluteMaxTotalMP() (gas: 5240)
MathTest:test_CalcAccrueMP() (gas: 8599)
MathTest:test_CalcBonusMP() (gas: 30744)
MathTest:test_CalcInitialMP() (gas: 5836)
MathTest:test_CalcMaxAccruedMP() (gas: 4886)
MathTest:test_CalcMaxTotalMP() (gas: 31506)
MultipleVaultsStakeTest:test_StakeMultipleVaults() (gas: 967977)
MultipleVaultsStakeTest:test_StakeMultipleVaults() (gas: 969063)
NFTMetadataGeneratorSVGTest:testGenerateMetadata() (gas: 92580)
NFTMetadataGeneratorSVGTest:testSetImageStrings() (gas: 77581)
NFTMetadataGeneratorSVGTest:testSetImageStringsRevert() (gas: 35891)
@@ -129,7 +129,7 @@ SetRewardTest:test_RevertWhen_SenderIsNotOperator() (gas: 61893)
SlashAmountOfTest:testAddKarmaDistributorOnlyAdmin() (gas: 438224)
SlashAmountOfTest:testBalanceOf() (gas: 456642)
SlashAmountOfTest:testBalanceOfWithNoSystemTotalKarma() (gas: 83783)
SlashAmountOfTest:testFuzz_SlashAmountOf(uint256,uint256,uint256) (runs: 1004, μ: 408220, ~: 409081)
SlashAmountOfTest:testFuzz_SlashAmountOf(uint256,uint256,uint256) (runs: 1002, μ: 408298, ~: 409081)
SlashAmountOfTest:testMintOnlyAdmin() (gas: 429075)
SlashAmountOfTest:testRemoveKarmaDistributorOnlyOwner() (gas: 163437)
SlashAmountOfTest:testRemoveUnknownKarmaDistributor() (gas: 41654)
@@ -139,7 +139,7 @@ SlashAmountOfTest:test_SlashAmountOf() (gas: 327608)
SlashTest:testAddKarmaDistributorOnlyAdmin() (gas: 438270)
SlashTest:testBalanceOf() (gas: 456648)
SlashTest:testBalanceOfWithNoSystemTotalKarma() (gas: 83827)
SlashTest:testFuzz_Slash(uint256) (runs: 1025, μ: 280205, ~: 280146)
SlashTest:testFuzz_Slash(uint256) (runs: 1006, μ: 280204, ~: 280146)
SlashTest:testMintOnlyAdmin() (gas: 429131)
SlashTest:testRemoveKarmaDistributorOnlyOwner() (gas: 163461)
SlashTest:testRemoveRewardDistributorShouldReduceSlashAmount() (gas: 610762)
@@ -150,69 +150,69 @@ SlashTest:test_RevertWhen_KarmaBalanceIsInvalid() (gas: 71550)
SlashTest:test_RevertWhen_SenderIsNotDefaultAdminOrSlasher() (gas: 43232)
SlashTest:test_Slash() (gas: 428385)
SlashTest:test_SlashRemainingBalanceIfBalanceIsLow() (gas: 251800)
StakeManager_RewardsTest:testRewardsBalanceOf() (gas: 2745571)
StakeManager_RewardsTest:testRewardsBalanceOf() (gas: 2746295)
StakeManager_RewardsTest:testSetRewards() (gas: 278063)
StakeManager_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 63800)
StakeManager_RewardsTest:testSetRewards_RevertsBadDuration() (gas: 103558)
StakeManager_RewardsTest:testSetRewards_RevertsNotAuthorized() (gas: 39367)
StakeManager_RewardsTest:testTotalRewardsSupply() (gas: 1297734)
StakeTest:test_StakeMultipleAccounts() (gas: 699422)
StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 754392)
StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 1357071)
StakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 625649)
StakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 634268)
StakeTest:test_StakeMultipleTimesDoesNotExceedsMaxMP() (gas: 1734589)
StakeTest:test_StakeMultipleTimesWithLockIncreaseAtSameBlock() (gas: 676201)
StakeTest:test_StakeMultipleTimesWithLockZeroAfterMaxLock() (gas: 1163144)
StakeTest:test_StakeOneAccount() (gas: 406658)
StakeTest:test_StakeOneAccountAndRewards() (gas: 461691)
StakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 841530)
StakeTest:test_StakeOneAccountReachingMPLimit() (gas: 731042)
StakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 375840)
StakeTest:test_StakeOneAccountWithMinLockUp() (gas: 376480)
StakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 376525)
StakeManager_RewardsTest:testTotalRewardsSupply() (gas: 1298096)
StakeTest:test_StakeMultipleAccounts() (gas: 700146)
StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 755116)
StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 1357795)
StakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 626387)
StakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 635020)
StakeTest:test_StakeMultipleTimesDoesNotExceedsMaxMP() (gas: 1737597)
StakeTest:test_StakeMultipleTimesWithLockIncreaseAtSameBlock() (gas: 676943)
StakeTest:test_StakeMultipleTimesWithLockZeroAfterMaxLock() (gas: 1165352)
StakeTest:test_StakeOneAccount() (gas: 407020)
StakeTest:test_StakeOneAccountAndRewards() (gas: 462053)
StakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 841892)
StakeTest:test_StakeOneAccountReachingMPLimit() (gas: 731404)
StakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 376216)
StakeTest:test_StakeOneAccountWithMinLockUp() (gas: 376856)
StakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 376901)
StakeVaultCoverageTest:testOwner() (gas: 15390)
StakeVaultCoverageTest:test_LeaveTransfersAllFunds() (gas: 153521)
StakeVaultCoverageTest:test_LeaveTransfersAllFunds() (gas: 173956)
StakeVaultCoverageTest:test_StakeRevertsIfNotOwner() (gas: 37258)
StakeVaultCoverageTest:test_StakeTransfersTokensToVault() (gas: 92532)
StakeVaultCoverageTest:test_UnstakeRevertsWithInvalidDestination() (gas: 112521)
StakeVaultCoverageTest:test_UnstakeTransfersTokensBackToOwner() (gas: 142860)
StakeVaultCoverageTest:test_StakeTransfersTokensToVault() (gas: 112981)
StakeVaultCoverageTest:test_UnstakeRevertsWithInvalidDestination() (gas: 132956)
StakeVaultCoverageTest:test_UnstakeTransfersTokensBackToOwner() (gas: 163295)
StakeVaultCoverageTest:test_WithdrawOtherTokenTransfersToDestination() (gas: 142271)
StakeVaultCoverageTest:test_WithdrawRevertsIfInsufficientAvailableBalance() (gas: 125765)
StakeVaultCoverageTest:test_WithdrawRevertsIfInsufficientAvailableBalance() (gas: 146200)
StakeVaultCoverageTest:test_WithdrawRevertsIfInvalidDestination() (gas: 111115)
StakeVaultCoverageTest:test_WithdrawTransfersGenericTokenToOwner() (gas: 139665)
StakeVaultMigrationTest:testMigrateToVault() (gas: 1163794)
StakeVaultMigrationTest:testMigrateToVault() (gas: 1164156)
StakeVaultMigrationTest:test_RevertWhenDestinationVaultIsNotRegistered() (gas: 163494)
StakeVaultMigrationTest:test_RevertWhenMigrationVaultNotEmpty() (gas: 621880)
StakeVaultMigrationTest:test_RevertWhenMigrationVaultNotEmpty() (gas: 622242)
StakeVaultMigrationTest:test_RevertWhenNotOwnerOfMigrationVault() (gas: 67284)
StakeVaultTest:testOwner() (gas: 15285)
StakingTokenTest:testOwner() (gas: 15285)
StakingTokenTest:testStakeToken() (gas: 13144)
TrustedCodehashAccessTest:test_RevertWhenProxyCloneCodehashNotTrusted() (gas: 1933652)
UnstakeTest:test_RevertWhen_FundsLocked() (gas: 432938)
UnstakeTest:test_StakeMultipleAccounts() (gas: 699401)
UnstakeTest:test_StakeMultipleAccountsAndRewards() (gas: 754436)
UnstakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 1357137)
UnstakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 625715)
UnstakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 634267)
UnstakeTest:test_StakeMultipleTimesDoesNotExceedsMaxMP() (gas: 1734645)
UnstakeTest:test_StakeMultipleTimesWithLockIncreaseAtSameBlock() (gas: 676156)
UnstakeTest:test_StakeMultipleTimesWithLockZeroAfterMaxLock() (gas: 1163155)
UnstakeTest:test_StakeOneAccount() (gas: 406680)
UnstakeTest:test_StakeOneAccountAndRewards() (gas: 461690)
UnstakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 841529)
UnstakeTest:test_StakeOneAccountReachingMPLimit() (gas: 731022)
UnstakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 375862)
UnstakeTest:test_StakeOneAccountWithMinLockUp() (gas: 376480)
UnstakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 376525)
UnstakeTest:test_UnstakeBonusMPAndAccuredMP() (gas: 744854)
UnstakeTest:test_UnstakeMultipleAccounts() (gas: 1062174)
UnstakeTest:test_UnstakeMultipleAccountsAndRewards() (gas: 1345299)
UnstakeTest:test_UnstakeOneAccount() (gas: 769214)
UnstakeTest:test_UnstakeOneAccountAndAccruedMP() (gas: 731845)
UnstakeTest:test_UnstakeOneAccountAndRewards() (gas: 686049)
UnstakeTest:test_UnstakeOneAccountWithLockUpAndAccruedMP() (gas: 712558)
UpdateVaultTest:test_UpdateAccount() (gas: 2583507)
TrustedCodehashAccessTest:test_RevertWhenProxyCloneCodehashNotTrusted() (gas: 1972628)
UnstakeTest:test_RevertWhen_FundsLocked() (gas: 433314)
UnstakeTest:test_StakeMultipleAccounts() (gas: 700125)
UnstakeTest:test_StakeMultipleAccountsAndRewards() (gas: 755160)
UnstakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 1357861)
UnstakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 626453)
UnstakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 635019)
UnstakeTest:test_StakeMultipleTimesDoesNotExceedsMaxMP() (gas: 1737653)
UnstakeTest:test_StakeMultipleTimesWithLockIncreaseAtSameBlock() (gas: 676898)
UnstakeTest:test_StakeMultipleTimesWithLockZeroAfterMaxLock() (gas: 1165363)
UnstakeTest:test_StakeOneAccount() (gas: 407042)
UnstakeTest:test_StakeOneAccountAndRewards() (gas: 462052)
UnstakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 841891)
UnstakeTest:test_StakeOneAccountReachingMPLimit() (gas: 731384)
UnstakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 376238)
UnstakeTest:test_StakeOneAccountWithMinLockUp() (gas: 376856)
UnstakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 376901)
UnstakeTest:test_UnstakeBonusMPAndAccuredMP() (gas: 745230)
UnstakeTest:test_UnstakeMultipleAccounts() (gas: 1062898)
UnstakeTest:test_UnstakeMultipleAccountsAndRewards() (gas: 1346023)
UnstakeTest:test_UnstakeOneAccount() (gas: 769576)
UnstakeTest:test_UnstakeOneAccountAndAccruedMP() (gas: 732207)
UnstakeTest:test_UnstakeOneAccountAndRewards() (gas: 686411)
UnstakeTest:test_UnstakeOneAccountWithLockUpAndAccruedMP() (gas: 712934)
UpdateVaultTest:test_UpdateAccount() (gas: 2584955)
UpgradeTest:test_RevertWhenNotOwner() (gas: 3586110)
UpgradeTest:test_UpgradeStakeManager() (gas: 9658332)
UpgradeTest:test_UpgradeStakeManager() (gas: 9658694)
VaultRegistrationTest:test_VaultRegistration() (gas: 90138)

View File

@@ -7,6 +7,7 @@ import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/I
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IStakeManagerProxy } from "./interfaces/IStakeManagerProxy.sol";
import { IStakeVault } from "./interfaces/IStakeVault.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @title StakeVault
@@ -37,6 +38,8 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
error StakeVault__NotAuthorized();
/// @notice Emitted when withdrawing funds from vault fails
error StakeVault__WithdrawFromVaultFailed();
/// @notice Emitted when receiving invalid lock end timestamp from stake manager
error StakeVault__InvalidLockEnd();
/*//////////////////////////////////////////////////////////////////////////
STATE VARIABLES
@@ -128,7 +131,9 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
* @param _seconds The time period to lock the staked amount for.
*/
function lock(uint256 _seconds) external onlyOwner {
lockUntil = stakeManager.lock(_seconds, lockUntil);
uint256 newLockUntil = stakeManager.lock(_seconds, lockUntil);
_ensureLockUntilValid(newLockUntil, _seconds);
lockUntil = newLockUntil;
}
/**
@@ -278,7 +283,9 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
* @param _source The address from which tokens will be transferred.
*/
function _stake(uint256 _amount, uint256 _seconds, address _source) internal {
lockUntil = stakeManager.stake(_amount, _seconds, lockUntil);
uint256 newLockUntil = stakeManager.stake(_amount, _seconds, lockUntil);
_ensureLockUntilValid(newLockUntil, _seconds);
lockUntil = newLockUntil;
bool success = STAKING_TOKEN.transferFrom(_source, address(this), _amount);
if (!success) {
revert StakeVault__StakingFailed();
@@ -317,6 +324,25 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
_token.transfer(_destination, _amount);
}
/**
* @notice Ensures that the new lock until timestamp is valid.
* @dev Reverts if the new lock until timestamp is not as expected.
* @param _newLockUntil The new lock until timestamp.
* @param _seconds The time period to stake for.
*/
function _ensureLockUntilValid(uint256 _newLockUntil, uint256 _seconds) internal {
uint256 expectedLockUntil = Math.max(lockUntil, block.timestamp) + _seconds;
if (_seconds > 0 && _newLockUntil != expectedLockUntil) {
// This should never happen, unless there was a malicious
// upgrade of stake manager which tries to lock funds for longer than expected.
// Also, there's no case where `newLockUntil < expectedLockUntil`.
// It should revert in `StakeManager` already.
// We still do a `!=` check to be extra safe, in case a stake manager upgrade
// removes all other checks.
revert StakeVault__InvalidLockEnd();
}
}
/**
* @notice Allows vaults to exit the system in case of emergency or the system is rigged.
* @param _destination The address to receive the funds.

View File

@@ -3,6 +3,7 @@ pragma solidity 0.8.26;
import { IStakeManager } from "../../src/interfaces/IStakeManager.sol";
import { ITrustedCodehashAccess } from "../../src/interfaces/ITrustedCodehashAccess.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
contract MockStakeManager is ITrustedCodehashAccess, IStakeManager {
function implementation() external view returns (address) {
@@ -21,8 +22,8 @@ contract MockStakeManager is ITrustedCodehashAccess, IStakeManager {
return;
}
function stake(uint256, uint256, uint256) external returns (uint256) {
return 0;
function stake(uint256 _amount, uint256 _seconds, uint256 _lockUntil) external returns (uint256) {
return Math.max(_lockUntil, block.timestamp) + _seconds;
}
function lock(uint256, uint256) external returns (uint256) {