feat(RewardsStreamerMP): make RewardsStreamerMP upgradeable

This commit introduces upgradeability of the `RewardsStreamerMP`
contract by leveraging the UUPS pattern.

This means, for deployment, we have to first deploy an instance of
`RewardsStreamerMP` contract as a "template" logic contract and then
create a ERC1967Proxy that points to it.

The proxy ensures the implementation address is stored in a
deterministic storage slot.

This will later be leveraged by the `StakeVault` contract to implement
the functionality to leave the system in case there was a malicious
upgrade.

Closes #22
This commit is contained in:
Ricardo Guilherme Schmidt
2024-11-02 12:23:13 -03:00
committed by r4bbit
parent e823711c9a
commit 8561a68ffd
16 changed files with 339 additions and 191 deletions

View File

@@ -1,144 +1,177 @@
| lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol:ERC1967Proxy contract | | | | | |
|-------------------------------------------------------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 259350 | 1159 | | | | |
| Function Name | min | avg | median | max | # calls |
| MAX_LOCKUP_PERIOD | 644 | 1426 | 644 | 5144 | 23 |
| MAX_MULTIPLIER | 667 | 1567 | 667 | 5167 | 30 |
| MIN_LOCKUP_PERIOD | 646 | 3918 | 5146 | 5146 | 11 |
| MP_RATE_PER_YEAR | 602 | 602 | 602 | 602 | 3 |
| SCALE_FACTOR | 600 | 600 | 600 | 600 | 41 |
| STAKING_TOKEN | 7253 | 7253 | 7253 | 7253 | 182 |
| accountedRewards | 722 | 1289 | 722 | 2722 | 74 |
| emergencyModeEnabled | 7270 | 7270 | 7270 | 7270 | 7 |
| enableEmergencyMode | 28422 | 45323 | 50607 | 50607 | 8 |
| getAccount | 2020 | 2020 | 2020 | 2020 | 71 |
| getStakedBalance | 7464 | 7464 | 7464 | 7464 | 1 |
| isTrustedCodehash | 938 | 1454 | 938 | 2938 | 182 |
| rewardIndex | 766 | 793 | 766 | 2766 | 74 |
| setTrustedCodehash | 52794 | 52794 | 52794 | 52794 | 47 |
| totalMP | 723 | 723 | 723 | 723 | 77 |
| totalMaxMP | 722 | 722 | 722 | 722 | 77 |
| totalStaked | 723 | 723 | 723 | 723 | 78 |
| updateAccountMP | 41679 | 43917 | 44181 | 44181 | 19 |
| updateGlobalState | 36999 | 67243 | 56463 | 89414 | 28 |
| upgradeToAndCall | 29734 | 33540 | 33540 | 37346 | 2 |
| src/RewardsStreamer.sol:RewardsStreamer contract | | | | | |
|--------------------------------------------------|-----------------|--------|--------|--------|---------|
| Deployment Cost | Deployment Size | | | | |
| 690902 | 3154 | | | | |
| 677674 | 3080 | | | | |
| Function Name | min | avg | median | max | # calls |
| accountedRewards | 351 | 601 | 351 | 2351 | 8 |
| getUserInfo | 793 | 793 | 793 | 793 | 12 |
| rewardIndex | 350 | 600 | 350 | 2350 | 8 |
| stake | 85235 | 100736 | 105135 | 111838 | 3 |
| totalStaked | 351 | 351 | 351 | 351 | 8 |
| unstake | 110100 | 110106 | 110106 | 110112 | 2 |
| updateRewardIndex | 23374 | 45581 | 39585 | 73785 | 3 |
| accountedRewards | 350 | 600 | 350 | 2350 | 8 |
| getUserInfo | 789 | 789 | 789 | 789 | 12 |
| rewardIndex | 349 | 599 | 349 | 2349 | 8 |
| stake | 85202 | 100705 | 105102 | 111812 | 3 |
| totalStaked | 350 | 350 | 350 | 350 | 8 |
| unstake | 110056 | 110062 | 110062 | 110068 | 2 |
| updateRewardIndex | 23372 | 45573 | 39574 | 73774 | 3 |
| src/RewardsStreamerMP.sol:RewardsStreamerMP contract | | | | | |
|------------------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 1425736 | 6528 | | | | |
| Function Name | min | avg | median | max | # calls |
| MAX_LOCKUP_PERIOD | 228 | 228 | 228 | 228 | 23 |
| MAX_MULTIPLIER | 274 | 274 | 274 | 274 | 30 |
| MIN_LOCKUP_PERIOD | 275 | 275 | 275 | 275 | 11 |
| MP_RATE_PER_YEAR | 231 | 231 | 231 | 231 | 3 |
| SCALE_FACTOR | 295 | 295 | 295 | 295 | 41 |
| STAKING_TOKEN | 273 | 273 | 273 | 273 | 174 |
| accountedRewards | 351 | 906 | 351 | 2351 | 72 |
| emergencyModeEnabled | 2377 | 2377 | 2377 | 2377 | 7 |
| enableEmergencyMode | 23504 | 40411 | 45696 | 45696 | 8 |
| getAccount | 1596 | 1596 | 1596 | 1596 | 71 |
| getStakedBalance | 2579 | 2579 | 2579 | 2579 | 1 |
| isTrustedCodehash | 496 | 1013 | 496 | 2496 | 174 |
| rewardIndex | 373 | 400 | 373 | 2373 | 72 |
| setTrustedCodehash | 47926 | 47926 | 47926 | 47926 | 45 |
| totalMP | 330 | 330 | 330 | 330 | 75 |
| totalMaxMP | 351 | 351 | 351 | 351 | 75 |
| totalStaked | 330 | 330 | 330 | 330 | 76 |
| updateAccountMP | 36758 | 38996 | 39260 | 39260 | 19 |
| updateGlobalState | 32134 | 60366 | 49513 | 82460 | 28 |
| src/RewardsStreamerMP.sol:RewardsStreamerMP contract | | | | | |
|------------------------------------------------------|-----------------|--------|--------|--------|---------|
| Deployment Cost | Deployment Size | | | | |
| 2084418 | 9588 | | | | |
| Function Name | min | avg | median | max | # calls |
| MAX_LOCKUP_PERIOD | 272 | 272 | 272 | 272 | 23 |
| MAX_MULTIPLIER | 295 | 295 | 295 | 295 | 30 |
| MIN_LOCKUP_PERIOD | 274 | 274 | 274 | 274 | 11 |
| MP_RATE_PER_YEAR | 230 | 230 | 230 | 230 | 3 |
| SCALE_FACTOR | 228 | 228 | 228 | 228 | 41 |
| STAKING_TOKEN | 2381 | 2381 | 2381 | 2381 | 182 |
| accountedRewards | 350 | 917 | 350 | 2350 | 74 |
| emergencyModeEnabled | 2398 | 2398 | 2398 | 2398 | 7 |
| enableEmergencyMode | 2482 | 19389 | 24674 | 24674 | 8 |
| getAccount | 1621 | 1621 | 1621 | 1621 | 71 |
| getStakedBalance | 2589 | 2589 | 2589 | 2589 | 1 |
| initialize | 137795 | 137795 | 137795 | 137795 | 47 |
| isTrustedCodehash | 563 | 1079 | 563 | 2563 | 182 |
| lock | 9839 | 33584 | 14168 | 76747 | 3 |
| proxiableUUID | 297 | 297 | 297 | 297 | 1 |
| rewardIndex | 394 | 421 | 394 | 2394 | 74 |
| setTrustedCodehash | 26203 | 26203 | 26203 | 26203 | 47 |
| stake | 134612 | 171004 | 176214 | 196693 | 57 |
| totalMP | 351 | 351 | 351 | 351 | 77 |
| totalMaxMP | 350 | 350 | 350 | 350 | 77 |
| totalStaked | 351 | 351 | 351 | 351 | 78 |
| unstake | 63925 | 84721 | 63925 | 120391 | 13 |
| updateAccountMP | 15375 | 17613 | 17877 | 17877 | 19 |
| updateGlobalState | 11066 | 41310 | 30530 | 63481 | 28 |
| upgradeToAndCall | 3146 | 6955 | 6955 | 10765 | 2 |
| src/StakeVault.sol:StakeVault contract | | | | | |
|----------------------------------------|-----------------|--------|--------|--------|---------|
| Deployment Cost | Deployment Size | | | | |
| 1095864 | 5202 | | | | |
| 1086958 | 5110 | | | | |
| Function Name | min | avg | median | max | # calls |
| STAKING_TOKEN | 217 | 217 | 217 | 217 | 1 |
| emergencyExit | 31410 | 43924 | 43160 | 60260 | 7 |
| lock | 38362 | 60516 | 42741 | 100445 | 3 |
| stake | 199213 | 237055 | 242906 | 263435 | 56 |
| unstake | 84988 | 113999 | 103434 | 146128 | 13 |
| withdraw | 37318 | 37318 | 37318 | 37318 | 1 |
| STAKING_TOKEN | 216 | 216 | 216 | 216 | 1 |
| emergencyExit | 36298 | 48802 | 48036 | 65136 | 7 |
| lock | 43256 | 66100 | 47633 | 107411 | 3 |
| stake | 206077 | 242480 | 247679 | 268206 | 57 |
| unstake | 90514 | 120704 | 110342 | 153215 | 13 |
| withdraw | 42194 | 42194 | 42194 | 42194 | 1 |
| src/XPNFTToken.sol:XPNFTToken contract | | | | | |
|-------------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 737164 | 3407 | | | | |
| 721357 | 3311 | | | | |
| Function Name | min | avg | median | max | # calls |
| approve | 393 | 393 | 393 | 393 | 1 |
| getApproved | 296 | 296 | 296 | 296 | 1 |
| isApprovedForAll | 510 | 510 | 510 | 510 | 1 |
| metadataGenerator | 325 | 325 | 325 | 325 | 1 |
| safeTransferFrom(address,address,uint256) | 485 | 485 | 485 | 485 | 1 |
| safeTransferFrom(address,address,uint256,bytes) | 677 | 677 | 677 | 677 | 1 |
| setApprovalForAll | 475 | 475 | 475 | 475 | 1 |
| setMetadataGenerator | 23996 | 26489 | 26489 | 28983 | 2 |
| tokenURI | 76129 | 76129 | 76129 | 76129 | 1 |
| transferFrom | 530 | 530 | 530 | 530 | 1 |
| approve | 391 | 391 | 391 | 391 | 1 |
| getApproved | 293 | 293 | 293 | 293 | 1 |
| isApprovedForAll | 507 | 507 | 507 | 507 | 1 |
| metadataGenerator | 324 | 324 | 324 | 324 | 1 |
| safeTransferFrom(address,address,uint256) | 482 | 482 | 482 | 482 | 1 |
| safeTransferFrom(address,address,uint256,bytes) | 673 | 673 | 673 | 673 | 1 |
| setApprovalForAll | 473 | 473 | 473 | 473 | 1 |
| setMetadataGenerator | 23993 | 26486 | 26486 | 28980 | 2 |
| tokenURI | 71322 | 71322 | 71322 | 71322 | 1 |
| transferFrom | 527 | 527 | 527 | 527 | 1 |
| src/XPToken.sol:XPToken contract | | | | | |
|----------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 1061807 | 4968 | | | | |
| 1041788 | 4854 | | | | |
| Function Name | min | avg | median | max | # calls |
| acceptOwnership | 28284 | 28284 | 28284 | 28284 | 1 |
| addXPProvider | 23968 | 57630 | 51091 | 68191 | 44 |
| allowance | 533 | 533 | 533 | 533 | 2 |
| approve | 413 | 413 | 413 | 413 | 2 |
| balanceOf | 3618 | 11284 | 9618 | 20618 | 6 |
| getXPProviders | 1032 | 3285 | 3285 | 5538 | 4 |
| mint | 24246 | 75896 | 91049 | 91061 | 14 |
| mintAllowance | 5626 | 5662 | 5662 | 5699 | 2 |
| owner | 374 | 1040 | 374 | 2374 | 3 |
| removeXPProvider | 23688 | 28097 | 25803 | 34800 | 6 |
| totalSupply | 2963 | 4963 | 2963 | 10963 | 8 |
| transfer | 411 | 411 | 411 | 411 | 2 |
| transferFrom | 499 | 499 | 499 | 499 | 2 |
| transferOwnership | 47732 | 47732 | 47732 | 47732 | 1 |
| acceptOwnership | 28282 | 28282 | 28282 | 28282 | 1 |
| addXPProvider | 23966 | 57627 | 51088 | 68188 | 44 |
| allowance | 530 | 530 | 530 | 530 | 2 |
| approve | 410 | 410 | 410 | 410 | 2 |
| balanceOf | 3601 | 11267 | 9601 | 20601 | 6 |
| getXPProviders | 1028 | 3281 | 3281 | 5534 | 4 |
| mint | 24244 | 75876 | 91025 | 91037 | 14 |
| mintAllowance | 5604 | 5641 | 5641 | 5678 | 2 |
| owner | 373 | 1039 | 373 | 2373 | 3 |
| removeXPProvider | 23686 | 28093 | 25801 | 34793 | 6 |
| totalSupply | 2952 | 4952 | 2952 | 10952 | 8 |
| transfer | 408 | 408 | 408 | 408 | 2 |
| transferFrom | 495 | 495 | 495 | 495 | 2 |
| transferOwnership | 47730 | 47730 | 47730 | 47730 | 1 |
| src/nft-metadata-generators/NFTMetadataGeneratorSVG.sol:NFTMetadataGeneratorSVG contract | | | | | |
|------------------------------------------------------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 1196494 | 5995 | | | | |
| 1146955 | 5713 | | | | |
| Function Name | min | avg | median | max | # calls |
| generate | 54832 | 54832 | 54832 | 54832 | 1 |
| imagePrefix | 1183 | 2183 | 2183 | 3183 | 2 |
| imageSuffix | 1227 | 2227 | 2227 | 3227 | 2 |
| setImageStrings | 25255 | 30614 | 30614 | 35974 | 2 |
| generate | 51746 | 51746 | 51746 | 51746 | 1 |
| imagePrefix | 1074 | 2074 | 2074 | 3074 | 2 |
| imageSuffix | 1118 | 2118 | 2118 | 3118 | 2 |
| setImageStrings | 25248 | 30606 | 30606 | 35965 | 2 |
| src/nft-metadata-generators/NFTMetadataGeneratorURL.sol:NFTMetadataGeneratorURL contract | | | | | |
|------------------------------------------------------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 1165211 | 5808 | | | | |
| 1120404 | 5550 | | | | |
| Function Name | min | avg | median | max | # calls |
| generate | 64528 | 64528 | 64528 | 64528 | 1 |
| setURLStrings | 25423 | 30782 | 30782 | 36142 | 2 |
| urlPrefix | 1184 | 1184 | 1184 | 1184 | 1 |
| urlSuffix | 1250 | 1250 | 1250 | 1250 | 1 |
| generate | 61198 | 61198 | 61198 | 61198 | 1 |
| setURLStrings | 25416 | 30774 | 30774 | 36133 | 2 |
| urlPrefix | 1075 | 1075 | 1075 | 1075 | 1 |
| urlSuffix | 1141 | 1141 | 1141 | 1141 | 1 |
| test/mocks/MockMetadataGenerator.sol:MockMetadataGenerator contract | | | | | |
|---------------------------------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 940871 | 4711 | | | | |
| 896351 | 4458 | | | | |
| Function Name | min | avg | median | max | # calls |
| generate | 60984 | 60984 | 60984 | 60984 | 1 |
| generate | 57807 | 57807 | 57807 | 57807 | 1 |
| test/mocks/MockToken.sol:MockToken contract | | | | | |
|---------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 639406 | 3369 | | | | |
| 625454 | 3260 | | | | |
| Function Name | min | avg | median | max | # calls |
| approve | 46334 | 46343 | 46346 | 46346 | 222 |
| balanceOf | 561 | 1382 | 561 | 2561 | 336 |
| mint | 51284 | 58898 | 51284 | 68384 | 238 |
| transfer | 34390 | 48859 | 51490 | 51490 | 13 |
| approve | 46330 | 46339 | 46342 | 46342 | 232 |
| balanceOf | 558 | 1380 | 558 | 2558 | 338 |
| mint | 51279 | 58862 | 51279 | 68379 | 248 |
| transfer | 34384 | 48853 | 51484 | 51484 | 13 |
| test/mocks/XPProviderMock.sol:XPProviderMock contract | | | | | |
|-------------------------------------------------------|-----------------|-------|--------|-------|---------|
| Deployment Cost | Deployment Size | | | | |
| 152145 | 489 | | | | |
| 149299 | 472 | | | | |
| Function Name | min | avg | median | max | # calls |
| getTotalXPShares | 302 | 968 | 302 | 2302 | 72 |
| getUserXPShare | 504 | 1837 | 2504 | 2504 | 12 |
| setTotalXPShares | 43632 | 43632 | 43632 | 43632 | 20 |
| setUserXPShare | 44128 | 44128 | 44128 | 44128 | 4 |
| getTotalXPShares | 301 | 967 | 301 | 2301 | 72 |
| getUserXPShare | 501 | 1834 | 2501 | 2501 | 12 |
| setTotalXPShares | 43630 | 43630 | 43630 | 43630 | 20 |
| setUserXPShare | 44125 | 44125 | 44125 | 44125 | 4 |

View File

@@ -1,84 +1,86 @@
EmergencyExitTest:test_CannotEnableEmergencyModeTwice() (gas: 79829)
EmergencyExitTest:test_CannotLeaveBeforeEmergencyMode() (gas: 283234)
EmergencyExitTest:test_EmergencyExitBasic() (gas: 379874)
EmergencyExitTest:test_EmergencyExitMultipleUsers() (gas: 788714)
EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 517455)
EmergencyExitTest:test_EmergencyExitWithLock() (gas: 370304)
EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 507361)
EmergencyExitTest:test_OnlyOwnerCanEnableEmergencyMode() (gas: 34607)
IntegrationTest:testStakeFoo() (gas: 1490300)
LockTest:test_LockFailsWithInvalidPeriod() (gas: 290178)
LockTest:test_LockFailsWithNoStake() (gas: 53357)
LockTest:test_LockWithoutPriorLock() (gas: 383272)
NFTMetadataGeneratorSVGTest:testGenerateMetadata() (gas: 92874)
NFTMetadataGeneratorSVGTest:testSetImageStrings() (gas: 60081)
NFTMetadataGeneratorSVGTest:testSetImageStringsRevert() (gas: 35818)
NFTMetadataGeneratorURLTest:testGenerateMetadata() (gas: 109345)
NFTMetadataGeneratorURLTest:testSetBaseURL() (gas: 50653)
NFTMetadataGeneratorURLTest:testSetBaseURLRevert() (gas: 35993)
RewardsStreamerTest:testStake() (gas: 869874)
StakeTest:test_StakeMultipleAccounts() (gas: 493279)
StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 640763)
StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 818252)
StakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 499381)
StakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 520783)
StakeTest:test_StakeOneAccount() (gas: 284277)
StakeTest:test_StakeOneAccountAndRewards() (gas: 431756)
StakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 498901)
StakeTest:test_StakeOneAccountReachingMPLimit() (gas: 494078)
StakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 298175)
StakeTest:test_StakeOneAccountWithMinLockUp() (gas: 298187)
StakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 298298)
StakingTokenTest:testStakeToken() (gas: 10426)
UnstakeTest:test_StakeMultipleAccounts() (gas: 493323)
UnstakeTest:test_StakeMultipleAccountsAndRewards() (gas: 640807)
UnstakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 818251)
UnstakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 499380)
UnstakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 520827)
UnstakeTest:test_StakeOneAccount() (gas: 284300)
UnstakeTest:test_StakeOneAccountAndRewards() (gas: 431800)
UnstakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 498945)
UnstakeTest:test_StakeOneAccountReachingMPLimit() (gas: 494080)
UnstakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 298132)
UnstakeTest:test_StakeOneAccountWithMinLockUp() (gas: 298187)
UnstakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 298298)
UnstakeTest:test_UnstakeBonusMPAndAccuredMP() (gas: 508511)
UnstakeTest:test_UnstakeMultipleAccounts() (gas: 688755)
UnstakeTest:test_UnstakeMultipleAccountsAndRewards() (gas: 1014239)
UnstakeTest:test_UnstakeOneAccount() (gas: 480152)
UnstakeTest:test_UnstakeOneAccountAndAccruedMP() (gas: 496638)
UnstakeTest:test_UnstakeOneAccountAndRewards() (gas: 585964)
UnstakeTest:test_UnstakeOneAccountWithLockUpAndAccruedMP() (gas: 518574)
WithdrawTest:test_CannotWithdrawStakedFunds() (gas: 295608)
XPNFTTokenTest:testApproveNotAllowed() (gas: 10507)
XPNFTTokenTest:testGetApproved() (gas: 10531)
XPNFTTokenTest:testIsApprovedForAll() (gas: 10705)
XPNFTTokenTest:testSafeTransferNotAllowed() (gas: 10688)
XPNFTTokenTest:testSafeTransferWithDataNotAllowed() (gas: 10906)
XPNFTTokenTest:testSetApprovalForAllNotAllowed() (gas: 8474)
XPNFTTokenTest:testSetMetadataGenerator() (gas: 1014075)
XPNFTTokenTest:testSetMetadataGeneratorRevert() (gas: 1010606)
XPNFTTokenTest:testTokenURI() (gas: 111913)
XPNFTTokenTest:testTransferNotAllowed() (gas: 10723)
XPTokenMintAllowanceTest:testAddXPProviderOnlyOwner() (gas: 285721)
XPTokenMintAllowanceTest:testBalanceOf() (gas: 294561)
XPTokenMintAllowanceTest:testBalanceOfWithNoSystemTotalXP() (gas: 43428)
XPTokenMintAllowanceTest:testMintAllowance_Available() (gas: 205169)
XPTokenMintAllowanceTest:testMintAllowance_NotAvailable() (gas: 205107)
XPTokenMintAllowanceTest:testMintOnlyOwner() (gas: 241956)
XPTokenMintAllowanceTest:testMint_Ok() (gas: 264233)
XPTokenMintAllowanceTest:testMint_RevertWithAllowanceExceeded() (gas: 246656)
XPTokenMintAllowanceTest:testRemoveXPProviderIndexOutOfBounds() (gas: 36295)
XPTokenMintAllowanceTest:testRemoveXPProviderOnlyOwner() (gas: 72176)
XPTokenMintAllowanceTest:testTotalSupply() (gas: 202454)
XPTokenMintAllowanceTest:testTransfersNotAllowed() (gas: 20658)
XPTokenOwnershipTest:testInitialOwner() (gas: 12649)
XPTokenOwnershipTest:testOwnershipTransfer() (gas: 87271)
XPTokenTest:testAddXPProviderOnlyOwner() (gas: 285753)
XPTokenTest:testBalanceOf() (gas: 294565)
XPTokenTest:testBalanceOfWithNoSystemTotalXP() (gas: 43405)
XPTokenTest:testMintOnlyOwner() (gas: 241932)
XPTokenTest:testRemoveXPProviderIndexOutOfBounds() (gas: 36277)
XPTokenTest:testRemoveXPProviderOnlyOwner() (gas: 72141)
XPTokenTest:testTotalSupply() (gas: 202403)
XPTokenTest:testTransfersNotAllowed() (gas: 20702)
EmergencyExitTest:test_CannotEnableEmergencyModeTwice() (gas: 89646)
EmergencyExitTest:test_CannotLeaveBeforeEmergencyMode() (gas: 292881)
EmergencyExitTest:test_EmergencyExitBasic() (gas: 395173)
EmergencyExitTest:test_EmergencyExitMultipleUsers() (gas: 823179)
EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 538257)
EmergencyExitTest:test_EmergencyExitWithLock() (gas: 384549)
EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 529680)
EmergencyExitTest:test_OnlyOwnerCanEnableEmergencyMode() (gas: 39344)
IntegrationTest:testStakeFoo() (gas: 1559037)
LockTest:test_LockFailsWithInvalidPeriod() (gas: 299830)
LockTest:test_LockFailsWithNoStake() (gas: 58241)
LockTest:test_LockWithoutPriorLock() (gas: 396542)
NFTMetadataGeneratorSVGTest:testGenerateMetadata() (gas: 85934)
NFTMetadataGeneratorSVGTest:testSetImageStrings() (gas: 58332)
NFTMetadataGeneratorSVGTest:testSetImageStringsRevert() (gas: 35804)
NFTMetadataGeneratorURLTest:testGenerateMetadata() (gas: 102512)
NFTMetadataGeneratorURLTest:testSetBaseURL() (gas: 49555)
NFTMetadataGeneratorURLTest:testSetBaseURLRevert() (gas: 35979)
RewardsStreamerTest:testStake() (gas: 869181)
StakeTest:test_StakeMultipleAccounts() (gas: 506030)
StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 661839)
StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 872090)
StakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 520069)
StakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 541881)
StakeTest:test_StakeOneAccount() (gas: 290297)
StakeTest:test_StakeOneAccountAndRewards() (gas: 446101)
StakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 536165)
StakeTest:test_StakeOneAccountReachingMPLimit() (gas: 532401)
StakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 310643)
StakeTest:test_StakeOneAccountWithMinLockUp() (gas: 310610)
StakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 310721)
StakingTokenTest:testStakeToken() (gas: 10422)
UnstakeTest:test_StakeMultipleAccounts() (gas: 506074)
UnstakeTest:test_StakeMultipleAccountsAndRewards() (gas: 661883)
UnstakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 872089)
UnstakeTest:test_StakeMultipleAccountsWithMinLockUp() (gas: 520068)
UnstakeTest:test_StakeMultipleAccountsWithRandomLockUp() (gas: 541925)
UnstakeTest:test_StakeOneAccount() (gas: 290320)
UnstakeTest:test_StakeOneAccountAndRewards() (gas: 446145)
UnstakeTest:test_StakeOneAccountMPIncreasesMaxMPDoesNotChange() (gas: 536209)
UnstakeTest:test_StakeOneAccountReachingMPLimit() (gas: 532403)
UnstakeTest:test_StakeOneAccountWithMaxLockUp() (gas: 310600)
UnstakeTest:test_StakeOneAccountWithMinLockUp() (gas: 310610)
UnstakeTest:test_StakeOneAccountWithRandomLockUp() (gas: 310721)
UnstakeTest:test_UnstakeBonusMPAndAccuredMP() (gas: 541859)
UnstakeTest:test_UnstakeMultipleAccounts() (gas: 714931)
UnstakeTest:test_UnstakeMultipleAccountsAndRewards() (gas: 1057383)
UnstakeTest:test_UnstakeOneAccount() (gas: 499845)
UnstakeTest:test_UnstakeOneAccountAndAccruedMP() (gas: 524183)
UnstakeTest:test_UnstakeOneAccountAndRewards() (gas: 608646)
UnstakeTest:test_UnstakeOneAccountWithLockUpAndAccruedMP() (gas: 555708)
UpgradeTest:test_RevertWhenNotOwner() (gas: 2159991)
UpgradeTest:test_UpgradeStakeManager() (gas: 2445373)
WithdrawTest:test_CannotWithdrawStakedFunds() (gas: 305631)
XPNFTTokenTest:testApproveNotAllowed() (gas: 10500)
XPNFTTokenTest:testGetApproved() (gas: 10523)
XPNFTTokenTest:testIsApprovedForAll() (gas: 10698)
XPNFTTokenTest:testSafeTransferNotAllowed() (gas: 10680)
XPNFTTokenTest:testSafeTransferWithDataNotAllowed() (gas: 10897)
XPNFTTokenTest:testSetApprovalForAllNotAllowed() (gas: 8467)
XPNFTTokenTest:testSetMetadataGenerator() (gas: 969770)
XPNFTTokenTest:testSetMetadataGeneratorRevert() (gas: 966301)
XPNFTTokenTest:testTokenURI() (gas: 103894)
XPNFTTokenTest:testTransferNotAllowed() (gas: 10715)
XPTokenMintAllowanceTest:testAddXPProviderOnlyOwner() (gas: 282868)
XPTokenMintAllowanceTest:testBalanceOf() (gas: 294494)
XPTokenMintAllowanceTest:testBalanceOfWithNoSystemTotalXP() (gas: 43385)
XPTokenMintAllowanceTest:testMintAllowance_Available() (gas: 205108)
XPTokenMintAllowanceTest:testMintAllowance_NotAvailable() (gas: 205044)
XPTokenMintAllowanceTest:testMintOnlyOwner() (gas: 241885)
XPTokenMintAllowanceTest:testMint_Ok() (gas: 264142)
XPTokenMintAllowanceTest:testMint_RevertWithAllowanceExceeded() (gas: 246592)
XPTokenMintAllowanceTest:testRemoveXPProviderIndexOutOfBounds() (gas: 36286)
XPTokenMintAllowanceTest:testRemoveXPProviderOnlyOwner() (gas: 72143)
XPTokenMintAllowanceTest:testTotalSupply() (gas: 202403)
XPTokenMintAllowanceTest:testTransfersNotAllowed() (gas: 20631)
XPTokenOwnershipTest:testInitialOwner() (gas: 12645)
XPTokenOwnershipTest:testOwnershipTransfer() (gas: 87252)
XPTokenTest:testAddXPProviderOnlyOwner() (gas: 282900)
XPTokenTest:testBalanceOf() (gas: 294498)
XPTokenTest:testBalanceOfWithNoSystemTotalXP() (gas: 43362)
XPTokenTest:testMintOnlyOwner() (gas: 241861)
XPTokenTest:testRemoveXPProviderIndexOutOfBounds() (gas: 36268)
XPTokenTest:testRemoveXPProviderOnlyOwner() (gas: 72108)
XPTokenTest:testTotalSupply() (gas: 202352)
XPTokenTest:testTransfersNotAllowed() (gas: 20675)

View File

@@ -162,7 +162,8 @@ jobs:
run: "pnpm install"
- name: Verify rules
run: "pnpm verify"
run: |
pnpm ${{matrix.rule}}
env:
CERTORAKEY: ${{ secrets.CERTORAKEY }}

3
.gitmodules vendored
View File

@@ -4,3 +4,6 @@
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable

View File

@@ -15,7 +15,8 @@
"loop_iter": "3",
"packages": [
"forge-std=lib/forge-std/src",
"@openzeppelin=lib/openzeppelin-contracts"
"@openzeppelin=lib/openzeppelin-contracts",
"@openzeppelin/contracts-upgradeable=lib/openzeppelin-contracts-upgradeable/contracts"
]
}

View File

@@ -14,6 +14,7 @@
"loop_iter": "3",
"packages": [
"forge-std=lib/forge-std/src",
"@openzeppelin=lib/openzeppelin-contracts"
"@openzeppelin/contracts=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts",
"@openzeppelin/contracts-upgradeable=lib/openzeppelin-contracts-upgradeable/contracts"
]
}

View File

@@ -38,6 +38,17 @@ definition isTrustedCodehashAccessFunction(method f) returns bool = (
f.selector == sig:streamer.isTrustedCodehash(bytes32).selector
);
definition isInitializerFunction(method f) returns bool = (
f.selector == sig:streamer.initialize(address,address,address).selector
);
definition isUUPSUpgradeableFunction(method f) returns bool = (
f.selector == sig:streamer.proxiableUUID().selector ||
f.selector == sig:streamer.UPGRADE_INTERFACE_VERSION().selector ||
f.selector == sig:streamer.upgradeToAndCall(address, bytes).selector ||
f.selector == sig:streamer.__TrustedCodehashAccess_init(address).selector
);
rule accountCanOnlyLeaveInEmergencyMode(method f) {
env e;
calldataarg args;
@@ -49,6 +60,8 @@ rule accountCanOnlyLeaveInEmergencyMode(method f) {
assert !isReverted => isViewFunction(f) ||
isOwnableFunction(f) ||
isTrustedCodehashAccessFunction(f);
isTrustedCodehashAccessFunction(f) ||
isInitializerFunction(f) ||
isUUPSUpgradeableFunction(f);
}

View File

@@ -41,13 +41,22 @@ function getAccountLockUntil(address account) returns uint256 {
}
invariant sumOfBalancesIsTotalStaked()
sumOfBalances == to_mathint(totalStaked());
sumOfBalances == to_mathint(totalStaked())
filtered {
f -> f.selector != sig:upgradeToAndCall(address,bytes).selector
}
invariant accountMPLessEqualAccountMaxMP(address account)
to_mathint(getAccountMP(account)) <= to_mathint(getAccountMaxMP(account));
to_mathint(getAccountMP(account)) <= to_mathint(getAccountMaxMP(account))
filtered {
f -> f.selector != sig:upgradeToAndCall(address,bytes).selector
}
invariant accountMPGreaterEqualAccountStakedBalance(address account)
to_mathint(getAccountMP(account)) >= to_mathint(getAccountStakedBalance(account));
to_mathint(getAccountMP(account)) >= to_mathint(getAccountStakedBalance(account))
filtered {
f -> f.selector != sig:upgradeToAndCall(address,bytes).selector
}
rule stakingMintsMultiplierPoints1To1Ratio {

View File

@@ -5,7 +5,7 @@
block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT
bytecode_hash = "none"
cbor_metadata = false
evm_version = "paris"
evm_version = "cancun"
fuzz = { runs = 1_000 }
gas_reports = ["*"]
libs = ["lib"]
@@ -16,7 +16,6 @@
solc = "0.8.26"
src = "src"
test = "test"
[profile.ci]
fuzz = { runs = 10_000 }
verbosity = 4

View File

@@ -1,2 +1,3 @@
forge-std/=lib/forge-std/src/
@openzeppelin/contracts=./lib/openzeppelin-contracts/contracts
@openzeppelin/contracts=lib/openzeppelin-contracts/contracts
@openzeppelin/contracts-upgradeable=lib/openzeppelin-contracts-upgradeable/contracts

View File

@@ -2,6 +2,7 @@
"detectors_to_exclude": "naming-convention,reentrancy-events,solc-version,timestamp",
"filter_paths": "(lib|test)",
"solc_remaps": [
"@openzeppelin/contracts-upgradeable=lib/openzeppelin-contracts-upgradeable/contracts",
"@openzeppelin/contracts=lib/openzeppelin-contracts/contracts/",
"forge-std/=lib/forge-std/src/"
]

View File

@@ -2,12 +2,14 @@
pragma solidity ^0.8.26;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { IStakeManager } from "./interfaces/IStakeManager.sol";
import { TrustedCodehashAccess } from "./TrustedCodehashAccess.sol";
// Rewards Streamer with Multiplier Points
contract RewardsStreamerMP is IStakeManager, TrustedCodehashAccess, ReentrancyGuard {
contract RewardsStreamerMP is UUPSUpgradeable, IStakeManager, TrustedCodehashAccess, ReentrancyGuardUpgradeable {
error StakingManager__AmountCannotBeZero();
error StakingManager__TransferFailed();
error StakingManager__InsufficientBalance();
@@ -17,8 +19,8 @@ contract RewardsStreamerMP is IStakeManager, TrustedCodehashAccess, ReentrancyGu
error StakingManager__AlreadyLocked();
error StakingManager__EmergencyModeEnabled();
IERC20 public immutable STAKING_TOKEN;
IERC20 public immutable REWARD_TOKEN;
IERC20 public STAKING_TOKEN;
IERC20 public REWARD_TOKEN;
uint256 public constant SCALE_FACTOR = 1e18;
uint256 public constant MP_RATE_PER_YEAR = 1e18;
@@ -53,12 +55,24 @@ contract RewardsStreamerMP is IStakeManager, TrustedCodehashAccess, ReentrancyGu
_;
}
constructor(address _owner, address _stakingToken, address _rewardToken) TrustedCodehashAccess(_owner) {
constructor() {
_disableInitializers();
}
function initialize(address _owner, address _stakingToken, address _rewardToken) public initializer {
__TrustedCodehashAccess_init(_owner);
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
STAKING_TOKEN = IERC20(_stakingToken);
REWARD_TOKEN = IERC20(_rewardToken);
lastMPUpdatedTime = block.timestamp;
}
function _authorizeUpgrade(address) internal view override {
_checkOwner();
}
function stake(uint256 amount, uint256 lockPeriod) external onlyTrustedCodehash onlyNotEmergencyMode nonReentrant {
if (amount == 0) {
revert StakingManager__AmountCannotBeZero();

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { ITrustedCodehashAccess } from "./interfaces/ITrustedCodehashAccess.sol";
/**
* @title TrustedCodehashAccess
@@ -10,7 +10,7 @@ import { ITrustedCodehashAccess } from "./interfaces/ITrustedCodehashAccess.sol"
* interact with the functions using the `onlyTrustedCodehash` modifier.
*/
contract TrustedCodehashAccess is ITrustedCodehashAccess, Ownable {
abstract contract TrustedCodehashAccess is ITrustedCodehashAccess, OwnableUpgradeable {
mapping(bytes32 codehash => bool permission) private trustedCodehashes;
/**
@@ -25,7 +25,9 @@ contract TrustedCodehashAccess is ITrustedCodehashAccess, Ownable {
_;
}
constructor(address _owner) Ownable(_owner) { }
function __TrustedCodehashAccess_init(address _initialOwner) public onlyInitializing {
__Ownable_init(_initialOwner);
}
/**
* @notice Allows the owner to set or update the trust status for a contract's codehash.

View File

@@ -3,6 +3,8 @@ pragma solidity ^0.8.26;
import { Test } from "forge-std/Test.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { RewardsStreamerMP } from "../src/RewardsStreamerMP.sol";
import { StakeVault } from "../src/StakeVault.sol";
import { MockToken } from "./mocks/MockToken.sol";
@@ -23,7 +25,12 @@ contract RewardsStreamerMPTest is Test {
function setUp() public virtual {
rewardToken = new MockToken("Reward Token", "RT");
stakingToken = new MockToken("Staking Token", "ST");
streamer = new RewardsStreamerMP(address(this), address(stakingToken), address(rewardToken));
bytes memory initializeData =
abi.encodeCall(RewardsStreamerMP.initialize, (address(this), address(stakingToken), address(rewardToken)));
address impl = address(new RewardsStreamerMP());
address proxy = address(new ERC1967Proxy(impl, initializeData));
streamer = RewardsStreamerMP(proxy);
address[4] memory accounts = [alice, bob, charlie, dave];
for (uint256 i = 0; i < accounts.length; i++) {
@@ -1779,3 +1786,57 @@ contract EmergencyExitTest is RewardsStreamerMPTest {
);
}
}
contract UpgradeTest is RewardsStreamerMPTest {
function _upgradeStakeManager() internal {
address newImpl = address(new RewardsStreamerMP());
bytes memory initializeData;
UUPSUpgradeable(streamer).upgradeToAndCall(newImpl, initializeData);
}
function setUp() public override {
super.setUp();
}
function test_RevertWhenNotOwner() public {
address newImpl = address(new RewardsStreamerMP());
bytes memory initializeData;
vm.prank(alice);
vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, alice));
UUPSUpgradeable(streamer).upgradeToAndCall(newImpl, initializeData);
}
function test_UpgradeStakeManager() public {
// first, change state of existing stake manager
_stake(alice, 10e18, 0);
// check initial state
checkStreamer(
CheckStreamerParams({
totalStaked: 10e18,
totalMP: 10e18,
totalMaxMP: 50e18,
stakingBalance: 10e18,
rewardBalance: 0,
rewardIndex: 0,
accountedRewards: 0
})
);
// next, upgrade the stake manager
_upgradeStakeManager();
// ensure state is available in upgraded contract
checkStreamer(
CheckStreamerParams({
totalStaked: 10e18,
totalMP: 10e18,
totalMaxMP: 50e18,
stakingBalance: 10e18,
rewardBalance: 0,
rewardIndex: 0,
accountedRewards: 0
})
);
}
}

View File

@@ -2,6 +2,7 @@
pragma solidity ^0.8.26;
import { Test } from "forge-std/Test.sol";
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { RewardsStreamerMP } from "../src/RewardsStreamerMP.sol";
import { StakeVault } from "../src/StakeVault.sol";
@@ -30,7 +31,12 @@ contract StakeVaultTest is Test {
function setUp() public virtual {
rewardToken = new MockToken("Reward Token", "RT");
stakingToken = new MockToken("Staking Token", "ST");
streamer = new RewardsStreamerMP(address(this), address(stakingToken), address(rewardToken));
address impl = address(new RewardsStreamerMP());
bytes memory initializeData = abi.encodeWithSelector(
RewardsStreamerMP.initialize.selector, address(this), address(stakingToken), address(rewardToken)
);
address proxy = address(new ERC1967Proxy(impl, initializeData));
streamer = RewardsStreamerMP(proxy);
stakingToken.mint(alice, 10_000e18);
stakeVault = _createTestVault(alice);