feat(Karma): add AccessControl to Karma

This commit introduces `AccessControl` capabilities to Karma. The reason this is done so that there can be multiple actors in the system with different privileges.

The main changes done here are:

- Introduce internal functions for most of the `Karma` specific logic (this is necessary to allow properly extending the contract's functionality via modifiers) later on
- Inherit AccessControlUpgradeable contract
- Introduce OPERATOR_ROLE next to the already provided DEFAULT_ADMIN_ROLE

This is an alternative solution to the PR in #209.
Instead of providing an upgrade version, this commit is a breaking
change as it introduces a storage layout conflict.

BREAKING CHANGE

This commit introduces a storage layout conflict.
Redeployment required.

Closes #207
This commit is contained in:
r4bbit
2025-05-12 15:35:41 +02:00
parent 05cccfc662
commit efa2c618ba
6 changed files with 400 additions and 139 deletions

View File

@@ -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 |
╰---------------------------------------------+-----------------+-------+--------+-------+---------╯

View File

@@ -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)

View File

@@ -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": [

View File

@@ -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 ->

View File

@@ -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 {

View File

@@ -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);
}
}