diff --git a/.gas-report b/.gas-report index dd205d3..c261d01 100644 --- a/.gas-report +++ b/.gas-report @@ -10,7 +10,7 @@ |-------------------------------------------------------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-------------------------------------------------------------------------------------------+-----------------+--------+--------+--------+---------| -| fallback | 746 | 135370 | 193312 | 193348 | 500 | +| fallback | 666 | 112892 | 97112 | 193429 | 693 | ╰-------------------------------------------------------------------------------------------+-----------------+--------+--------+--------+---------╯ ╭-----------------------------------------------------+-----------------+---------+---------+---------+---------╮ @@ -18,13 +18,13 @@ +===============================================================================================================+ | Deployment Cost | Deployment Size | | | | | |-----------------------------------------------------+-----------------+---------+---------+---------+---------| -| 4746226 | 22993 | | | | | +| 5135751 | 24812 | | | | | |-----------------------------------------------------+-----------------+---------+---------+---------+---------| | | | | | | | |-----------------------------------------------------+-----------------+---------+---------+---------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-----------------------------------------------------+-----------------+---------+---------+---------+---------| -| run | 3966846 | 3966846 | 3966846 | 3966846 | 118 | +| run | 4330203 | 4330203 | 4330203 | 4330203 | 152 | ╰-----------------------------------------------------+-----------------+---------+---------+---------+---------╯ ╭-----------------------------------------------------------+-----------------+---------+---------+---------+---------╮ @@ -66,7 +66,7 @@ |---------------------------------------------------------+-----------------+-----+--------+-----+---------| | Function Name | Min | Avg | Median | Max | # Calls | |---------------------------------------------------------+-----------------+-----+--------+-----+---------| -| activeNetworkConfig | 455 | 455 | 455 | 455 | 346 | +| activeNetworkConfig | 455 | 455 | 455 | 455 | 414 | ╰---------------------------------------------------------+-----------------+-----+--------+-----+---------╯ ╭---------------------------------------------------------------------+-----------------+---------+---------+---------+---------╮ @@ -88,41 +88,43 @@ +=====================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------+-----------------+--------+--------+--------+---------| -| 0 | 9875 | | | | | +| 0 | 11694 | | | | | |------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------+-----------------+--------+--------+--------+---------| -| acceptOwnership | 12020 | 12020 | 12020 | 12020 | 1 | +| DEFAULT_ADMIN_ROLE | 306 | 306 | 306 | 306 | 19 | |------------------------------+-----------------+--------+--------+--------+---------| -| addRewardDistributor | 2589 | 65756 | 70586 | 70586 | 152 | +| OPERATOR_ROLE | 283 | 283 | 283 | 283 | 2 | |------------------------------+-----------------+--------+--------+--------+---------| -| allowance | 482 | 482 | 482 | 482 | 3 | +| addRewardDistributor | 29894 | 64182 | 70797 | 70797 | 232 | |------------------------------+-----------------+--------+--------+--------+---------| -| approve | 419 | 419 | 419 | 419 | 3 | +| allowance | 504 | 504 | 504 | 504 | 6 | |------------------------------+-----------------+--------+--------+--------+---------| -| balanceOf | 10019 | 13685 | 10019 | 21019 | 9 | +| approve | 441 | 441 | 441 | 441 | 6 | |------------------------------+-----------------+--------+--------+--------+---------| -| getRewardDistributors | 1140 | 3384 | 3384 | 5628 | 6 | +| balanceOf | 10085 | 13751 | 10085 | 21085 | 18 | |------------------------------+-----------------+--------+--------+--------+---------| -| initialize | 95872 | 95872 | 95872 | 95872 | 118 | +| getRewardDistributors | 1119 | 3523 | 5607 | 5607 | 17 | |------------------------------+-----------------+--------+--------+--------+---------| -| mint | 2632 | 37628 | 51197 | 51197 | 14 | +| grantRole | 29440 | 29440 | 29440 | 29440 | 5 | |------------------------------+-----------------+--------+--------+--------+---------| -| owner | 363 | 1029 | 363 | 2363 | 3 | +| hasRole | 685 | 2185 | 2685 | 2685 | 4 | |------------------------------+-----------------+--------+--------+--------+---------| -| removeRewardDistributor | 2632 | 12148 | 4824 | 28990 | 9 | +| initialize | 94595 | 94595 | 94595 | 94595 | 152 | |------------------------------+-----------------+--------+--------+--------+---------| -| setReward | 9233 | 156626 | 166624 | 166624 | 284 | +| mint | 4790 | 38817 | 51239 | 51239 | 26 | |------------------------------+-----------------+--------+--------+--------+---------| -| totalSupply | 3545 | 7545 | 9545 | 9545 | 9 | +| removeRewardDistributor | 5044 | 22060 | 29223 | 29959 | 21 | |------------------------------+-----------------+--------+--------+--------+---------| -| transfer | 417 | 417 | 417 | 417 | 3 | +| setReward | 4832 | 147872 | 166705 | 166705 | 307 | |------------------------------+-----------------+--------+--------+--------+---------| -| transferFrom | 511 | 511 | 511 | 511 | 3 | +| totalSupply | 3567 | 7567 | 9567 | 9567 | 18 | |------------------------------+-----------------+--------+--------+--------+---------| -| transferOwnership | 26328 | 26328 | 26328 | 26328 | 1 | +| transfer | 439 | 439 | 439 | 439 | 6 | +|------------------------------+-----------------+--------+--------+--------+---------| +| transferFrom | 511 | 511 | 511 | 511 | 6 | ╰------------------------------+-----------------+--------+--------+--------+---------╯ ╭-------------------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -184,7 +186,7 @@ |--------------------------------------------+-----------------+--------+--------+--------+---------| | getAccountVaults | 5230 | 5230 | 5230 | 5230 | 4 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| getVault | 1653 | 5710 | 1653 | 13653 | 4182 | +| getVault | 1653 | 5709 | 1653 | 13653 | 4180 | |--------------------------------------------+-----------------+--------+--------+--------+---------| | initialize | 92752 | 92752 | 92752 | 92752 | 95 | |--------------------------------------------+-----------------+--------+--------+--------+---------| @@ -192,7 +194,7 @@ |--------------------------------------------+-----------------+--------+--------+--------+---------| | leave | 66348 | 66348 | 66348 | 66348 | 2 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| lock | 7040 | 43178 | 46713 | 87964 | 1034 | +| lock | 7040 | 43426 | 46713 | 87964 | 1034 | |--------------------------------------------+-----------------+--------+--------+--------+---------| | migrateToVault | 9294 | 53513 | 17021 | 170715 | 4 | |--------------------------------------------+-----------------+--------+--------+--------+---------| @@ -210,7 +212,7 @@ |--------------------------------------------+-----------------+--------+--------+--------+---------| | rewardStartTime | 364 | 1364 | 1364 | 2364 | 2 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| rewardsBalanceOf | 2295 | 3505 | 3908 | 6295 | 268 | +| rewardsBalanceOf | 2295 | 3511 | 3908 | 6295 | 268 | |--------------------------------------------+-----------------+--------+--------+--------+---------| | rewardsBalanceOfAccount | 10220 | 10220 | 10220 | 10220 | 1 | |--------------------------------------------+-----------------+--------+--------+--------+---------| @@ -220,33 +222,33 @@ |--------------------------------------------+-----------------+--------+--------+--------+---------| | setTrustedCodehash | 24238 | 24238 | 24238 | 24238 | 95 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| stake | 2639 | 131046 | 60725 | 228623 | 2670 | +| stake | 2639 | 131085 | 60725 | 228623 | 2670 | |--------------------------------------------+-----------------+--------+--------+--------+---------| | stakedBalanceOf | 2622 | 2622 | 2622 | 2622 | 1 | |--------------------------------------------+-----------------+--------+--------+--------+---------| | totalMP | 805 | 1257 | 1257 | 1710 | 6 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| totalMPAccrued | 385 | 1064 | 385 | 2385 | 4162 | +| totalMPAccrued | 385 | 1064 | 385 | 2385 | 4160 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| totalMPStaked | 429 | 1107 | 429 | 2429 | 4165 | +| totalMPStaked | 429 | 1107 | 429 | 2429 | 4163 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| totalMaxMP | 407 | 1086 | 407 | 2407 | 4162 | +| totalMaxMP | 407 | 1086 | 407 | 2407 | 4160 | |--------------------------------------------+-----------------+--------+--------+--------+---------| | totalRewardsAccrued | 407 | 407 | 407 | 407 | 3 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| totalRewardsSupply | 998 | 1627 | 1792 | 6737 | 290 | +| totalRewardsSupply | 998 | 1629 | 1792 | 6737 | 290 | |--------------------------------------------+-----------------+--------+--------+--------+---------| | totalShares | 597 | 597 | 597 | 597 | 6 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| totalStaked | 408 | 1086 | 408 | 2408 | 4169 | +| totalStaked | 408 | 1086 | 408 | 2408 | 4167 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| unstake | 9886 | 41014 | 39781 | 79550 | 271 | +| unstake | 9886 | 41548 | 39781 | 79550 | 271 | |--------------------------------------------+-----------------+--------+--------+--------+---------| | updateAccount | 347677 | 347677 | 347677 | 347677 | 1 | |--------------------------------------------+-----------------+--------+--------+--------+---------| | updateGlobalState | 15820 | 25876 | 29230 | 29230 | 8 | |--------------------------------------------+-----------------+--------+--------+--------+---------| -| updateVault | 31948 | 34282 | 31948 | 110579 | 1024 | +| updateVault | 31948 | 34368 | 31948 | 110579 | 1022 | |--------------------------------------------+-----------------+--------+--------+--------+---------| | upgradeTo | 10279 | 10772 | 10279 | 12745 | 5 | |--------------------------------------------+-----------------+--------+--------+--------+---------| @@ -274,9 +276,9 @@ |----------------------------------------+-----------------+--------+--------+--------+---------| | leave | 12223 | 113138 | 84120 | 356510 | 5 | |----------------------------------------+-----------------+--------+--------+--------+---------| -| lock | 12151 | 58671 | 62251 | 103499 | 1035 | +| lock | 12151 | 58918 | 62251 | 103499 | 1035 | |----------------------------------------+-----------------+--------+--------+--------+---------| -| lockUntil | 363 | 1743 | 2363 | 2363 | 7758 | +| lockUntil | 363 | 1744 | 2363 | 2363 | 7766 | |----------------------------------------+-----------------+--------+--------+--------+---------| | migrateToVault | 24910 | 77530 | 32637 | 219937 | 4 | |----------------------------------------+-----------------+--------+--------+--------+---------| @@ -284,15 +286,15 @@ |----------------------------------------+-----------------+--------+--------+--------+---------| | register | 12742 | 78218 | 78761 | 78761 | 374 | |----------------------------------------+-----------------+--------+--------+--------+---------| -| stake | 12131 | 165238 | 76290 | 284275 | 2671 | +| stake | 12131 | 165292 | 76290 | 284275 | 2671 | |----------------------------------------+-----------------+--------+--------+--------+---------| | stakeManager | 393 | 393 | 393 | 393 | 373 | |----------------------------------------+-----------------+--------+--------+--------+---------| | trustStakeManager | 7650 | 7650 | 7650 | 7650 | 1 | |----------------------------------------+-----------------+--------+--------+--------+---------| -| unstake | 12108 | 57512 | 55296 | 110656 | 272 | +| unstake | 12108 | 58273 | 55296 | 110656 | 272 | |----------------------------------------+-----------------+--------+--------+--------+---------| -| updateLockUntil | 4432 | 20765 | 21532 | 21532 | 509 | +| updateLockUntil | 4432 | 20761 | 21532 | 21532 | 506 | |----------------------------------------+-----------------+--------+--------+--------+---------| | withdraw | 20817 | 20817 | 20817 | 20817 | 1 | |----------------------------------------+-----------------+--------+--------+--------+---------| @@ -310,9 +312,9 @@ |----------------------------------------------------+-----------------+------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------------------+-----------------+------+--------+--------+---------| -| fallback | 708 | 6172 | 2125 | 374054 | 23167 | +| fallback | 708 | 6172 | 2125 | 374054 | 23155 | |----------------------------------------------------+-----------------+------+--------+--------+---------| -| implementation | 346 | 2136 | 2346 | 2346 | 4871 | +| implementation | 346 | 2137 | 2346 | 2346 | 4868 | ╰----------------------------------------------------+-----------------+------+--------+--------+---------╯ ╭--------------------------------------------+-----------------+--------+--------+--------+---------╮ @@ -336,7 +338,7 @@ +===============================================================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| 1204853 | 6015 | | | | | +| 1204853 | 6207 | | | | | |------------------------------------------------------------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |------------------------------------------------------------------------------------------+-----------------+-------+--------+-------+---------| @@ -382,13 +384,13 @@ |-------------------------------------------------------------------+-----------------+-------+--------+-------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-------------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| rewardsBalanceOfAccount | 549 | 1882 | 2549 | 2549 | 18 | +| rewardsBalanceOfAccount | 549 | 1882 | 2549 | 2549 | 36 | |-------------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| setTotalKarmaShares | 43589 | 43589 | 43589 | 43589 | 18 | +| setTotalKarmaShares | 43589 | 43589 | 43589 | 43589 | 36 | |-------------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| setUserKarmaShare | 44194 | 44194 | 44194 | 44194 | 6 | +| setUserKarmaShare | 44194 | 44194 | 44194 | 44194 | 12 | |-------------------------------------------------------------------+-----------------+-------+--------+-------+---------| -| totalRewardsSupply | 324 | 324 | 324 | 324 | 18 | +| totalRewardsSupply | 324 | 324 | 324 | 324 | 36 | ╰-------------------------------------------------------------------+-----------------+-------+--------+-------+---------╯ ╭---------------------------------------------------------------------+-----------------+-------+--------+-------+---------╮ @@ -410,7 +412,7 @@ +==================================================================================================+ | Deployment Cost | Deployment Size | | | | | |---------------------------------------------+-----------------+-------+--------+-------+---------| -| 770741 | 3987 | | | | | +| 770657 | 3987 | | | | | |---------------------------------------------+-----------------+-------+--------+-------+---------| | | | | | | | |---------------------------------------------+-----------------+-------+--------+-------+---------| @@ -418,7 +420,7 @@ |---------------------------------------------+-----------------+-------+--------+-------+---------| | approve | 29075 | 31545 | 29183 | 46259 | 2676 | |---------------------------------------------+-----------------+-------+--------+-------+---------| -| balanceOf | 561 | 1346 | 561 | 2561 | 4960 | +| balanceOf | 561 | 1345 | 561 | 2561 | 4958 | |---------------------------------------------+-----------------+-------+--------+-------+---------| | mint | 33964 | 37190 | 34072 | 68248 | 2685 | ╰---------------------------------------------+-----------------+-------+--------+-------+---------╯ diff --git a/.gas-snapshot b/.gas-snapshot index 4e9e7bb..fe3f8c6 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,3 +1,13 @@ +AddRewardDistributorTest:testAddKarmaDistributorOnlyAdmin() (gas: 423055) +AddRewardDistributorTest:testAddRewardDistributorAsOtherAdmin() (gas: 165263) +AddRewardDistributorTest:testBalanceOf() (gas: 431293) +AddRewardDistributorTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49655) +AddRewardDistributorTest:testMintOnlyAdmin() (gas: 378769) +AddRewardDistributorTest:testRemoveKarmaDistributorOnlyOwner() (gas: 147308) +AddRewardDistributorTest:testRemoveUnknownKarmaDistributor() (gas: 41630) +AddRewardDistributorTest:testTotalSupply() (gas: 339166) +AddRewardDistributorTest:testTransfersNotAllowed() (gas: 40285) +AddRewardDistributorTest:test_RevertWhen_SenderIsNotDefaultAdmin() (gas: 68325) EmergencyExitTest:test_CannotEnableEmergencyModeTwice() (gas: 93554) EmergencyExitTest:test_CannotLeaveBeforeEmergencyMode() (gas: 336067) EmergencyExitTest:test_EmergencyExitBasic() (gas: 427580) @@ -6,15 +16,15 @@ EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 433110) EmergencyExitTest:test_EmergencyExitWithLock() (gas: 433444) EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 419310) EmergencyExitTest:test_OnlyOwnerCanEnableEmergencyMode() (gas: 39176) -FuzzTests:testFuzz_AccrueMP(uint128,uint64,uint64) (runs: 1006, μ: 397033, ~: 368037) -FuzzTests:testFuzz_AccrueMP_Relock(uint128,uint64,uint64,uint64) (runs: 1006, μ: 502073, ~: 471734) -FuzzTests:testFuzz_EmergencyExit(uint256,uint256) (runs: 1001, μ: 497104, ~: 487267) -FuzzTests:testFuzz_Lock(uint256,uint64) (runs: 1006, μ: 727730, ~: 726735) -FuzzTests:testFuzz_Relock(uint256,uint64,uint64) (runs: 1006, μ: 409301, ~: 388725) -FuzzTests:testFuzz_Rewards(uint256,uint256,uint256,uint16,uint16) (runs: 1001, μ: 599762, ~: 601124) -FuzzTests:testFuzz_Stake(uint256,uint64) (runs: 1006, μ: 311991, ~: 285086) -FuzzTests:testFuzz_Unstake(uint128,uint64,uint16,uint128) (runs: 1006, μ: 498758, ~: 473111) -FuzzTests:testFuzz_UpdateVault(uint128,uint64,uint64) (runs: 1006, μ: 397056, ~: 368060) +FuzzTests:testFuzz_AccrueMP(uint128,uint64,uint64) (runs: 1006, μ: 400351, ~: 368061) +FuzzTests:testFuzz_AccrueMP_Relock(uint128,uint64,uint64,uint64) (runs: 1006, μ: 501707, ~: 471734) +FuzzTests:testFuzz_EmergencyExit(uint256,uint256) (runs: 1001, μ: 497146, ~: 487267) +FuzzTests:testFuzz_Lock(uint256,uint64) (runs: 1006, μ: 727489, ~: 726735) +FuzzTests:testFuzz_Relock(uint256,uint64,uint64) (runs: 1006, μ: 409218, ~: 388725) +FuzzTests:testFuzz_Rewards(uint256,uint256,uint256,uint16,uint16) (runs: 1001, μ: 599846, ~: 601205) +FuzzTests:testFuzz_Stake(uint256,uint64) (runs: 1006, μ: 311868, ~: 285086) +FuzzTests:testFuzz_Unstake(uint128,uint64,uint16,uint128) (runs: 1006, μ: 501292, ~: 473125) +FuzzTests:testFuzz_UpdateVault(uint128,uint64,uint64) (runs: 1006, μ: 400374, ~: 368084) IntegrationTest:testStakeFoo() (gas: 1362931) KarmaNFTTest:testApproveNotAllowed() (gas: 10507) KarmaNFTTest:testGetApproved() (gas: 10531) @@ -26,24 +36,24 @@ KarmaNFTTest:testSetMetadataGenerator() (gas: 1010377) KarmaNFTTest:testSetMetadataGeneratorRevert() (gas: 1006937) KarmaNFTTest:testTokenURI() (gas: 1105935) KarmaNFTTest:testTransferNotAllowed() (gas: 10701) -KarmaOwnershipTest:testAddKarmaDistributorOnlyOwner() (gas: 364768) -KarmaOwnershipTest:testBalanceOf() (gas: 431045) -KarmaOwnershipTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49479) -KarmaOwnershipTest:testInitialOwner() (gas: 17601) -KarmaOwnershipTest:testMintOnlyOwner() (gas: 376410) -KarmaOwnershipTest:testOwnershipTransfer() (gas: 98047) -KarmaOwnershipTest:testRemoveKarmaDistributorOnlyOwner() (gas: 88820) -KarmaOwnershipTest:testRemoveUnknownKarmaDistributor() (gas: 41398) -KarmaOwnershipTest:testTotalSupply() (gas: 338940) -KarmaOwnershipTest:testTransfersNotAllowed() (gas: 40196) -KarmaTest:testAddKarmaDistributorOnlyOwner() (gas: 364768) -KarmaTest:testBalanceOf() (gas: 431045) -KarmaTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49545) -KarmaTest:testMintOnlyOwner() (gas: 376410) -KarmaTest:testRemoveKarmaDistributorOnlyOwner() (gas: 88798) -KarmaTest:testRemoveUnknownKarmaDistributor() (gas: 41398) -KarmaTest:testTotalSupply() (gas: 338940) -KarmaTest:testTransfersNotAllowed() (gas: 40241) +KarmaOwnershipTest:testAddKarmaDistributorOnlyAdmin() (gas: 423043) +KarmaOwnershipTest:testBalanceOf() (gas: 431293) +KarmaOwnershipTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49677) +KarmaOwnershipTest:testInitialOwner() (gas: 16039) +KarmaOwnershipTest:testMintOnlyAdmin() (gas: 378791) +KarmaOwnershipTest:testOwnershipTransfer() (gas: 76843) +KarmaOwnershipTest:testRemoveKarmaDistributorOnlyOwner() (gas: 147229) +KarmaOwnershipTest:testRemoveUnknownKarmaDistributor() (gas: 41618) +KarmaOwnershipTest:testTotalSupply() (gas: 339166) +KarmaOwnershipTest:testTransfersNotAllowed() (gas: 40285) +KarmaTest:testAddKarmaDistributorOnlyAdmin() (gas: 423021) +KarmaTest:testBalanceOf() (gas: 431293) +KarmaTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49655) +KarmaTest:testMintOnlyAdmin() (gas: 378769) +KarmaTest:testRemoveKarmaDistributorOnlyOwner() (gas: 147274) +KarmaTest:testRemoveUnknownKarmaDistributor() (gas: 41618) +KarmaTest:testTotalSupply() (gas: 339166) +KarmaTest:testTransfersNotAllowed() (gas: 40263) LeaveTest:test_LeaveShouldKeepFundsLockedInStakeVault() (gas: 9899411) LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 9865059) LeaveTest:test_RevertWhenStakeManagerIsTrusted() (gas: 333238) @@ -69,22 +79,46 @@ NFTMetadataGeneratorSVGTest:testSetImageStringsRevert() (gas: 35891) NFTMetadataGeneratorURLTest:testGenerateMetadata() (gas: 108341) NFTMetadataGeneratorURLTest:testSetBaseURL() (gas: 50631) NFTMetadataGeneratorURLTest:testSetBaseURLRevert() (gas: 36066) -OverflowTest:testAddKarmaDistributorOnlyOwner() (gas: 364746) -OverflowTest:testBalanceOf() (gas: 431045) -OverflowTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49545) -OverflowTest:testMintOnlyOwner() (gas: 376410) -OverflowTest:testRemoveKarmaDistributorOnlyOwner() (gas: 88798) -OverflowTest:testRemoveUnknownKarmaDistributor() (gas: 41410) -OverflowTest:testTotalSupply() (gas: 338940) -OverflowTest:testTransfersNotAllowed() (gas: 40241) -OverflowTest:test_RevertWhen_MintingCausesOverflow() (gas: 129363) -OverflowTest:test_RevertWhen_SettingRewardCausesOverflow() (gas: 127641) -StakeManager_RewardsTest:testRewardsBalanceOf() (gas: 1281373) -StakeManager_RewardsTest:testSetRewards() (gas: 227019) -StakeManager_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 63685) -StakeManager_RewardsTest:testSetRewards_RevertsBadDuration() (gas: 103443) +OverflowTest:testAddKarmaDistributorOnlyAdmin() (gas: 423043) +OverflowTest:testBalanceOf() (gas: 431293) +OverflowTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49655) +OverflowTest:testMintOnlyAdmin() (gas: 378769) +OverflowTest:testRemoveKarmaDistributorOnlyOwner() (gas: 147274) +OverflowTest:testRemoveUnknownKarmaDistributor() (gas: 41630) +OverflowTest:testTotalSupply() (gas: 339166) +OverflowTest:testTransfersNotAllowed() (gas: 40263) +OverflowTest:test_RevertWhen_MintingCausesOverflow() (gas: 129464) +OverflowTest:test_RevertWhen_SettingRewardCausesOverflow() (gas: 127792) +RemoveRewardDistributorTest:testAddKarmaDistributorOnlyAdmin() (gas: 423045) +RemoveRewardDistributorTest:testBalanceOf() (gas: 431366) +RemoveRewardDistributorTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49633) +RemoveRewardDistributorTest:testMintOnlyAdmin() (gas: 378759) +RemoveRewardDistributorTest:testRemoveKarmaDistributorOnlyOwner() (gas: 147298) +RemoveRewardDistributorTest:testRemoveRewardDistributor() (gas: 132118) +RemoveRewardDistributorTest:testRemoveRewardDistributorAsOtherAdmin() (gas: 203200) +RemoveRewardDistributorTest:testRemoveUnknownKarmaDistributor() (gas: 41636) +RemoveRewardDistributorTest:testTotalSupply() (gas: 339239) +RemoveRewardDistributorTest:testTransfersNotAllowed() (gas: 40263) +RemoveRewardDistributorTest:test_RevertWhen_SenderIsNotDefaultAdmin() (gas: 66507) +SetRewardTest:testAddKarmaDistributorOnlyAdmin() (gas: 423077) +SetRewardTest:testBalanceOf() (gas: 431293) +SetRewardTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49677) +SetRewardTest:testMintOnlyAdmin() (gas: 378791) +SetRewardTest:testRemoveKarmaDistributorOnlyOwner() (gas: 147241) +SetRewardTest:testRemoveUnknownKarmaDistributor() (gas: 41630) +SetRewardTest:testSetRewardAsAdmin() (gas: 134934) +SetRewardTest:testSetRewardAsOperator() (gas: 126214) +SetRewardTest:testSetRewardAsOtherAdmin() (gas: 199420) +SetRewardTest:testTotalSupply() (gas: 339211) +SetRewardTest:testTransfersNotAllowed() (gas: 40307) +SetRewardTest:test_RevertWhen_SenderIsNotDefaultAdmin() (gas: 43559) +SetRewardTest:test_RevertWhen_SenderIsNotOperator() (gas: 53332) +StakeManager_RewardsTest:testRewardsBalanceOf() (gas: 1281535) +StakeManager_RewardsTest:testSetRewards() (gas: 227100) +StakeManager_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 63751) +StakeManager_RewardsTest:testSetRewards_RevertsBadDuration() (gas: 103509) StakeManager_RewardsTest:testSetRewards_RevertsNotAuthorized() (gas: 39367) -StakeManager_RewardsTest:testTotalRewardsSupply() (gas: 746062) +StakeManager_RewardsTest:testTotalRewardsSupply() (gas: 746224) StakeTest:test_StakeMultipleAccounts() (gas: 556308) StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 564800) StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 942461) @@ -131,7 +165,7 @@ UnstakeTest:test_UnstakeOneAccount() (gas: 545178) UnstakeTest:test_UnstakeOneAccountAndAccruedMP() (gas: 544489) UnstakeTest:test_UnstakeOneAccountAndRewards() (gas: 468681) UnstakeTest:test_UnstakeOneAccountWithLockUpAndAccruedMP() (gas: 570741) -UpdateVaultTest:test_UpdateAccount() (gas: 2397297) +UpdateVaultTest:test_UpdateAccount() (gas: 2397378) UpgradeTest:test_RevertWhenNotOwner() (gas: 3696209) UpgradeTest:test_UpgradeStakeManager() (gas: 9769347) VaultRegistrationTest:test_VaultRegistration() (gas: 63138) diff --git a/certora/confs/Karma.conf b/certora/confs/Karma.conf index 49c63d6..ffd62c6 100644 --- a/certora/confs/Karma.conf +++ b/certora/confs/Karma.conf @@ -1,8 +1,12 @@ { - "files": ["src/Karma.sol"], + "files": [ + "src/StakeManager.sol", + "src/Karma.sol", + ], "msg": "Verifying Karma.sol", "rule_sanity": "basic", "verify": "Karma:certora/specs/Karma.spec", + "parametric_contracts": ["Karma"], "optimistic_loop": true, "loop_iter": "3", "packages": [ diff --git a/certora/specs/Karma.spec b/certora/specs/Karma.spec index 55d074a..16e47bf 100644 --- a/certora/specs/Karma.spec +++ b/certora/specs/Karma.spec @@ -1,11 +1,13 @@ using Karma as karma; methods { - function owner() external returns (address) envfree; function totalDistributorAllocation() external returns (uint256) envfree; function totalSupply() external returns (uint256) envfree; function externalSupply() external returns (uint256) envfree; - function _.setReward(uint256, uint256) external => HAVOC_ECF; + function _.setReward(uint256, uint256) external => DISPATCHER(true); + function hasRole(bytes32, address) external returns (bool) envfree; + function DEFAULT_ADMIN_ROLE() external returns (bytes32) envfree; + function OPERATOR_ROLE() external returns (bytes32) envfree; } persistent ghost mathint sumOfDistributorAllocations { @@ -38,14 +40,17 @@ definition isERC20TransferFunction(method f) returns bool = ( || f.selector == sig:karma.approve(address, uint256).selector ); -definition isOwnableFunction(method f) returns bool = ( +definition isAdminFunction(method f) returns bool = ( f.selector == sig:karma.addRewardDistributor(address).selector || f.selector == sig:karma.removeRewardDistributor(address).selector - || f.selector == sig:karma.setReward(address, uint256, uint256).selector - || f.selector == sig:karma.mint(address, uint256).selector ); +definition isOperatorFunction(method f) returns bool = ( + f.selector == sig:karma.setReward(address, uint256, uint256).selector + || f.selector == sig:karma.mint(address, uint256).selector +); + rule erc20TransferIsDisabled(method f) { env e; calldataarg args; @@ -56,16 +61,29 @@ rule erc20TransferIsDisabled(method f) { assert isERC20TransferFunction(f) => isReverted; } -rule ownableFuncsOnlyCallableByOwner(method f) { +rule adminFuncsOnlyCallableByAdmin(method f) { env e; calldataarg args; - bool isOwner = owner() == e.msg.sender; + bool isOwner = hasRole(DEFAULT_ADMIN_ROLE(), e.msg.sender); f@withrevert(e, args); bool isReverted = lastReverted; - assert isOwnableFunction(f) && !isOwner => isReverted; + assert isAdminFunction(f) && !isOwner => isReverted; +} + +rule operatorFuncsCallableByAdminAndOperators(method f) { + env e; + calldataarg args; + + bool isOwner = hasRole(DEFAULT_ADMIN_ROLE(), e.msg.sender); + bool isOperator = hasRole(OPERATOR_ROLE(), e.msg.sender); + + f@withrevert(e, args); + bool isReverted = lastReverted; + + assert isOperatorFunction(f) && !isOwner && !isOperator => isReverted; } rule totalDistributorAllocationCanOnlyIncrease(method f) filtered { f -> diff --git a/src/Karma.sol b/src/Karma.sol index 8e1a568..3a2f2c0 100644 --- a/src/Karma.sol +++ b/src/Karma.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import { Ownable2StepUpgradeable } from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; @@ -13,12 +13,19 @@ import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableS * @notice This contract allows for setting rewards for reward distributors. * @dev Implementation of the Karma token */ -contract Karma is Initializable, ERC20Upgradeable, Ownable2StepUpgradeable, UUPSUpgradeable { +contract Karma is Initializable, ERC20Upgradeable, UUPSUpgradeable, AccessControlUpgradeable { using EnumerableSet for EnumerableSet.AddressSet; + /// @notice Emitted when the address is invalid + error Karma__InvalidAddress(); + /// @notice Emitted because transfers are not allowed error Karma__TransfersNotAllowed(); + /// @notice Emitted when distributor is already added error Karma__DistributorAlreadyAdded(); + /// @notice Emitted when distributor is not found error Karma__UnknownDistributor(); + /// @notice Emitted sender does not have the required role + error Karma__Unauthorized(); event RewardDistributorAdded(address distributor); @@ -32,12 +39,25 @@ contract Karma is Initializable, ERC20Upgradeable, Ownable2StepUpgradeable, UUPS string public constant SYMBOL = "KARMA"; /// @notice The total allocation for all reward distributors uint256 public totalDistributorAllocation; - /// @notice Set of reward distributors EnumerableSet.AddressSet private rewardDistributors; /// @notice Mapping of reward distributor to allocation mapping(address distributor => uint256 allocation) public rewardDistributorAllocations; + /// @notice Operator role keccak256("OPERATOR_ROLE") + bytes32 public constant OPERATOR_ROLE = 0x97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b929; + /// @notice Gap for upgrade safety. + // solhint-disable-next-line + uint256[30] private __gap_Karma; + + /// @notice Modifier to check if sender is admin or operator + modifier onlyAdminOrOperator() { + if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender) && !hasRole(OPERATOR_ROLE, msg.sender)) { + revert Karma__Unauthorized(); + } + _; + } + /*////////////////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////////*/ @@ -51,9 +71,13 @@ contract Karma is Initializable, ERC20Upgradeable, Ownable2StepUpgradeable, UUPS * @param _owner Address of the owner of the contract. */ function initialize(address _owner) public initializer { + if (_owner == address(0)) { + revert Karma__InvalidAddress(); + } __ERC20_init(NAME, SYMBOL); - _transferOwnership(_owner); __UUPSUpgradeable_init(); + __AccessControl_init(); + _setupRole(DEFAULT_ADMIN_ROLE, _owner); } /*////////////////////////////////////////////////////////////////////////// @@ -66,13 +90,8 @@ contract Karma is Initializable, ERC20Upgradeable, Ownable2StepUpgradeable, UUPS * @dev Emits a `RewardDistributorAdded` event when a distributor is added. * @param distributor The address of the reward distributor. */ - function addRewardDistributor(address distributor) external onlyOwner { - if (rewardDistributors.contains(distributor)) { - revert Karma__DistributorAlreadyAdded(); - } - - rewardDistributors.add(address(distributor)); - emit RewardDistributorAdded(distributor); + function addRewardDistributor(address distributor) public virtual onlyRole(DEFAULT_ADMIN_ROLE) { + _addRewardDistributor(distributor); } /** @@ -80,12 +99,8 @@ contract Karma is Initializable, ERC20Upgradeable, Ownable2StepUpgradeable, UUPS * @dev Only the owner can remove a reward distributor. * @param distributor The address of the reward distributor. */ - function removeRewardDistributor(address distributor) external onlyOwner { - if (!rewardDistributors.contains(distributor)) { - revert Karma__UnknownDistributor(); - } - - rewardDistributors.remove(distributor); + function removeRewardDistributor(address distributor) public virtual onlyRole(DEFAULT_ADMIN_ROLE) { + _removeRewardDistributor(distributor); } /** @@ -96,17 +111,16 @@ contract Karma is Initializable, ERC20Upgradeable, Ownable2StepUpgradeable, UUPS * @param amount The amount of rewards to set. * @param duration The duration of the rewards. */ - function setReward(address rewardsDistributor, uint256 amount, uint256 duration) external onlyOwner { - if (!rewardDistributors.contains(rewardsDistributor)) { - revert Karma__UnknownDistributor(); - } - - _overflowCheck(amount); - - rewardDistributorAllocations[rewardsDistributor] += amount; - totalDistributorAllocation += amount; - - IRewardDistributor(rewardsDistributor).setReward(amount, duration); + function setReward( + address rewardsDistributor, + uint256 amount, + uint256 duration + ) + public + virtual + onlyAdminOrOperator + { + _setReward(rewardsDistributor, amount, duration); } /** @@ -116,7 +130,7 @@ contract Karma is Initializable, ERC20Upgradeable, Ownable2StepUpgradeable, UUPS * @param account The account to mint tokens to. * @param amount The amount of tokens to mint. */ - function mint(address account, uint256 amount) external onlyOwner { + function mint(address account, uint256 amount) public virtual onlyAdminOrOperator { _overflowCheck(amount); _mint(account, amount); } @@ -167,7 +181,47 @@ contract Karma is Initializable, ERC20Upgradeable, Ownable2StepUpgradeable, UUPS * @dev This function is only callable by the owner. */ function _authorizeUpgrade(address) internal view override { - _checkOwner(); + if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) { + revert Karma__Unauthorized(); + } + } + + /** + * @notice Adds a reward distributor to the set of reward distributors. + * @param distributor The address of the reward distributor. + */ + function _addRewardDistributor(address distributor) internal virtual { + if (rewardDistributors.contains(distributor)) { + revert Karma__DistributorAlreadyAdded(); + } + + rewardDistributors.add(distributor); + emit RewardDistributorAdded(distributor); + } + + /** + * @notice Removes a reward distributor from the set of reward distributors. + * @param distributor The address of the reward distributor. + */ + function _removeRewardDistributor(address distributor) internal virtual { + if (!rewardDistributors.contains(distributor)) { + revert Karma__UnknownDistributor(); + } + rewardDistributors.remove(distributor); + } + + /** + * @notice Sets the reward for a reward distributor. + */ + function _setReward(address rewardsDistributor, uint256 amount, uint256 duration) internal virtual { + if (!rewardDistributors.contains(rewardsDistributor)) { + revert Karma__UnknownDistributor(); + } + _overflowCheck(amount); + + rewardDistributorAllocations[rewardsDistributor] += amount; + totalDistributorAllocation += amount; + IRewardDistributor(rewardsDistributor).setReward(amount, duration); } function _overflowCheck(uint256 amount) internal view { diff --git a/test/Karma.t.sol b/test/Karma.t.sol index 29883b0..25aea5f 100644 --- a/test/Karma.t.sol +++ b/test/Karma.t.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.26; +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; + import { Test } from "forge-std/Test.sol"; import { DeployKarmaScript } from "../script/DeployKarma.s.sol"; import { DeploymentConfig } from "../script/DeploymentConfig.s.sol"; @@ -17,6 +19,8 @@ contract KarmaTest is Test { KarmaDistributorMock public distributor1; KarmaDistributorMock public distributor2; + address public operator = makeAddr("operator"); + function setUp() public virtual { DeployKarmaScript karmaDeployment = new DeployKarmaScript(); (Karma _karma, DeploymentConfig deploymentConfig) = karmaDeployment.run(); @@ -33,11 +37,24 @@ contract KarmaTest is Test { vm.stopBroadcast(); } - function testAddKarmaDistributorOnlyOwner() public { + function _accessControlError(address account, bytes32 role) internal pure returns (bytes memory) { + string memory expectedError = string( + abi.encodePacked( + "AccessControl: account ", + Strings.toHexString(uint160(account)), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + return bytes(expectedError); + } + + function testAddKarmaDistributorOnlyAdmin() public { KarmaDistributorMock distributor3 = new KarmaDistributorMock(); + bytes memory expectedError = _accessControlError(alice, karma.DEFAULT_ADMIN_ROLE()); vm.prank(alice); - vm.expectRevert("Ownable: caller is not the owner"); + vm.expectRevert(expectedError); karma.addRewardDistributor(address(distributor3)); vm.prank(owner); @@ -51,8 +68,9 @@ contract KarmaTest is Test { } function testRemoveKarmaDistributorOnlyOwner() public { + bytes memory expectedError = _accessControlError(alice, karma.DEFAULT_ADMIN_ROLE()); vm.prank(alice); - vm.expectRevert("Ownable: caller is not the owner"); + vm.expectRevert(expectedError); karma.removeRewardDistributor(address(distributor1)); vm.prank(owner); @@ -114,7 +132,7 @@ contract KarmaTest is Test { assertEq(balance, expectedBalance); } - function testMintOnlyOwner() public { + function testMintOnlyAdmin() public { vm.startBroadcast(owner); karma.setReward(address(distributor1), 1000 ether, 1000); karma.setReward(address(distributor2), 2000 ether, 2000); @@ -125,7 +143,7 @@ contract KarmaTest is Test { assertEq(karma.totalSupply(), 3000 ether); vm.prank(alice); - vm.expectRevert("Ownable: caller is not the owner"); + vm.expectRevert(Karma.Karma__Unauthorized.selector); karma.mint(alice, 1000e18); vm.prank(owner); @@ -154,17 +172,148 @@ contract KarmaOwnershipTest is KarmaTest { } function testInitialOwner() public view { - assertEq(karma.owner(), owner); + assert(karma.hasRole(karma.DEFAULT_ADMIN_ROLE(), owner)); } function testOwnershipTransfer() public { - vm.prank(owner); - karma.transferOwnership(alice); - assertEq(karma.owner(), owner); + vm.startPrank(owner); + karma.grantRole(karma.DEFAULT_ADMIN_ROLE(), alice); + vm.stopPrank(); + assert(karma.hasRole(karma.DEFAULT_ADMIN_ROLE(), alice)); + } +} - vm.prank(alice); - karma.acceptOwnership(); - assertEq(karma.owner(), alice); +contract AddRewardDistributorTest is KarmaTest { + address public distributor; + + function setUp() public virtual override { + super.setUp(); + distributor = address(new KarmaDistributorMock()); + } + + function test_RevertWhen_SenderIsNotDefaultAdmin() public { + vm.prank(makeAddr("someone")); + vm.expectRevert(); + karma.addRewardDistributor(distributor); + } + + function testAddRewardDistributorAsOtherAdmin() public { + address otherAdmin = makeAddr("otherAdmin"); + vm.startPrank(owner); + karma.grantRole(karma.DEFAULT_ADMIN_ROLE(), otherAdmin); + vm.stopPrank(); + + vm.startPrank(otherAdmin); + karma.addRewardDistributor(distributor); + vm.stopPrank(); + address[] memory distributors = karma.getRewardDistributors(); + assertEq(distributors.length, 3); + assertEq(distributors[2], distributor); + } +} + +contract RemoveRewardDistributorTest is KarmaTest { + address public distributor; + + function setUp() public virtual override { + super.setUp(); + distributor = address(new KarmaDistributorMock()); + } + + function test_RevertWhen_SenderIsNotDefaultAdmin() public { + vm.expectRevert(); + karma.removeRewardDistributor(distributor); + } + + function testRemoveRewardDistributor() public { + // add a distributor + vm.prank(owner); + karma.addRewardDistributor(distributor); + address[] memory distributors = karma.getRewardDistributors(); + assertEq(distributors.length, 3); + assertEq(distributors[2], distributor); + + // remove the distributor + vm.prank(owner); + karma.removeRewardDistributor(distributor); + distributors = karma.getRewardDistributors(); + assertEq(distributors.length, 2); + } + + function testRemoveRewardDistributorAsOtherAdmin() public { + // add a distributor + vm.prank(owner); + karma.addRewardDistributor(distributor); + address[] memory distributors = karma.getRewardDistributors(); + assertEq(distributors.length, 3); + assertEq(distributors[2], distributor); + + // grant admin role + address otherAdmin = makeAddr("otherAdmin"); + vm.startPrank(owner); + karma.grantRole(karma.DEFAULT_ADMIN_ROLE(), otherAdmin); + vm.stopPrank(); + + // remove the distributor + vm.prank(otherAdmin); + karma.removeRewardDistributor(address(distributor1)); + distributors = karma.getRewardDistributors(); + assertEq(distributors.length, 2); + } +} + +contract SetRewardTest is KarmaTest { + address public distributor; + + function setUp() public virtual override { + super.setUp(); + distributor = address(new KarmaDistributorMock()); + } + + function test_RevertWhen_SenderIsNotDefaultAdmin() public { + vm.prank(makeAddr("someone")); + vm.expectRevert(); + karma.setReward(distributor, 0, 0); + } + + function test_RevertWhen_SenderIsNotOperator() public { + assert(karma.hasRole(karma.OPERATOR_ROLE(), operator) == false); + + vm.prank(operator); + vm.expectRevert(); + karma.setReward(distributor, 0, 0); + } + + function testSetRewardAsAdmin() public { + vm.startPrank(owner); + karma.addRewardDistributor(distributor); + karma.setReward(distributor, 0, 0); + vm.stopPrank(); + } + + function testSetRewardAsOtherAdmin() public { + vm.startPrank(owner); + karma.grantRole(karma.DEFAULT_ADMIN_ROLE(), operator); + karma.addRewardDistributor(distributor); + vm.stopPrank(); + + vm.prank(operator); + karma.setReward(distributor, 0, 0); + } + + function testSetRewardAsOperator() public { + // grant operator role + assert(karma.hasRole(karma.DEFAULT_ADMIN_ROLE(), owner)); + + // actually `vm.prank()` should be used here, but for some reason + // foundry seems to mess up the context for what `owner` is + vm.startPrank(owner); + karma.grantRole(karma.OPERATOR_ROLE(), operator); + vm.stopPrank(); + + // set reward as operator + vm.prank(operator); + karma.setReward(address(distributor1), 0, 0); } }