refactor: cleanup, move code around, and adjust deployscript

This commit is contained in:
r4bbit
2025-02-28 10:03:59 +01:00
parent 5755138d8a
commit 60081c14fb
15 changed files with 653 additions and 515 deletions

View File

@@ -4,13 +4,13 @@
+===============================================================================================================+
| Deployment Cost | Deployment Size | | | | |
|-----------------------------------------------------+-----------------+---------+---------+---------+---------|
| 3089188 | 15186 | | | | |
| 3123616 | 15345 | | | | |
|-----------------------------------------------------+-----------------+---------+---------+---------+---------|
| | | | | | |
|-----------------------------------------------------+-----------------+---------+---------+---------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|-----------------------------------------------------+-----------------+---------+---------+---------+---------|
| run | 2533527 | 2533527 | 2533527 | 2533527 | 75 |
| run | 2567823 | 2567823 | 2567823 | 2567823 | 75 |
╰-----------------------------------------------------+-----------------+---------+---------+---------+---------╯
╭-----------------------------------------------------------------------------+-----------------+---------+---------+---------+---------╮
@@ -18,13 +18,13 @@
+=======================================================================================================================================+
| Deployment Cost | Deployment Size | | | | |
|-----------------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| 7717029 | 36770 | | | | |
| 7755573 | 36948 | | | | |
|-----------------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| | | | | | |
|-----------------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|-----------------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| run | 6775418 | 6775418 | 6775418 | 6775418 | 77 |
| run | 6813503 | 6813503 | 6813503 | 6813503 | 77 |
╰-----------------------------------------------------------------------------+-----------------+---------+---------+---------+---------╯
╭---------------------------------------------------------+-----------------+-----+--------+-----+---------╮
@@ -32,13 +32,13 @@
+==========================================================================================================+
| Deployment Cost | Deployment Size | | | | |
|---------------------------------------------------------+-----------------+-----+--------+-----+---------|
| 0 | 7333 | | | | |
| 0 | 7494 | | | | |
|---------------------------------------------------------+-----------------+-----+--------+-----+---------|
| | | | | | |
|---------------------------------------------------------+-----------------+-----+--------+-----+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|---------------------------------------------------------+-----------------+-----+--------+-----+---------|
| activeNetworkConfig | 454 | 454 | 454 | 454 | 229 |
| activeNetworkConfig | 596 | 596 | 596 | 596 | 229 |
╰---------------------------------------------------------+-----------------+-----+--------+-----+---------╯
╭-------------------------------------------------------------------------------+-----------------+---------+---------+---------+---------╮
@@ -46,13 +46,13 @@
+=========================================================================================================================================+
| Deployment Cost | Deployment Size | | | | |
|-------------------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| 3641587 | 17781 | | | | |
| 5331719 | 25630 | | | | |
|-------------------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| | | | | | |
|-------------------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|-------------------------------------------------------------------------------+-----------------+---------+---------+---------+---------|
| run | 3125807 | 3125807 | 3125807 | 3125807 | 3 |
| runWithAdminAndProxy | 3125848 | 3125848 | 3125848 | 3125848 | 3 |
╰-------------------------------------------------------------------------------+-----------------+---------+---------+---------+---------╯
╭------------------------------+-----------------+--------+--------+--------+---------╮
@@ -60,41 +60,41 @@
+=====================================================================================+
| Deployment Cost | Deployment Size | | | | |
|------------------------------+-----------------+--------+--------+--------+---------|
| 1171148 | 5453 | | | | |
| 1167042 | 5434 | | | | |
|------------------------------+-----------------+--------+--------+--------+---------|
| | | | | | |
|------------------------------+-----------------+--------+--------+--------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|------------------------------+-----------------+--------+--------+--------+---------|
| acceptOwnership | 28282 | 28282 | 28282 | 28282 | 1 |
| acceptOwnership | 28260 | 28260 | 28260 | 28260 | 1 |
|------------------------------+-----------------+--------+--------+--------+---------|
| addRewardDistributor | 23973 | 87707 | 92012 | 92012 | 119 |
|------------------------------+-----------------+--------+--------+--------+---------|
| allowance | 492 | 492 | 492 | 492 | 2 |
| allowance | 537 | 537 | 537 | 537 | 2 |
|------------------------------+-----------------+--------+--------+--------+---------|
| approve | 416 | 416 | 416 | 416 | 2 |
|------------------------------+-----------------+--------+--------+--------+---------|
| balanceOf | 3994 | 11660 | 9994 | 20994 | 6 |
| balanceOf | 4037 | 11703 | 10037 | 21037 | 6 |
|------------------------------+-----------------+--------+--------+--------+---------|
| getRewardDistributors | 1112 | 3356 | 3356 | 5600 | 4 |
| getRewardDistributors | 1090 | 3334 | 3334 | 5578 | 4 |
|------------------------------+-----------------+--------+--------+--------+---------|
| mint | 24250 | 80685 | 96635 | 96647 | 14 |
| mint | 24228 | 80663 | 96613 | 96625 | 14 |
|------------------------------+-----------------+--------+--------+--------+---------|
| mintAllowance | 7208 | 7245 | 7245 | 7282 | 2 |
|------------------------------+-----------------+--------+--------+--------+---------|
| owner | 362 | 1028 | 362 | 2362 | 3 |
| owner | 340 | 1006 | 340 | 2340 | 3 |
|------------------------------+-----------------+--------+--------+--------+---------|
| removeRewardDistributor | 24016 | 30281 | 26025 | 40803 | 6 |
| removeRewardDistributor | 23994 | 30259 | 26003 | 40781 | 6 |
|------------------------------+-----------------+--------+--------+--------+---------|
| setReward | 43527 | 175898 | 185831 | 185843 | 283 |
| setReward | 43572 | 175943 | 185876 | 185888 | 283 |
|------------------------------+-----------------+--------+--------+--------+---------|
| totalSupply | 3799 | 5799 | 3799 | 11799 | 8 |
|------------------------------+-----------------+--------+--------+--------+---------|
| transfer | 414 | 414 | 414 | 414 | 2 |
| transfer | 392 | 392 | 392 | 392 | 2 |
|------------------------------+-----------------+--------+--------+--------+---------|
| transferFrom | 507 | 507 | 507 | 507 | 2 |
|------------------------------+-----------------+--------+--------+--------+---------|
| transferOwnership | 47758 | 47758 | 47758 | 47758 | 1 |
| transferOwnership | 47736 | 47736 | 47736 | 47736 | 1 |
╰------------------------------+-----------------+--------+--------+--------+---------╯
╭-------------------------------------------------+-----------------+-------+--------+-------+---------╮
@@ -168,7 +168,7 @@
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| leave | 95475 | 95475 | 95475 | 95475 | 1 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| lock | 14216 | 52151 | 52082 | 93689 | 260 |
| lock | 14216 | 52150 | 52082 | 93689 | 260 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| migrateToVault | 13563 | 72244 | 15769 | 187401 | 3 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
@@ -186,7 +186,7 @@
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| rewardStartTime | 429 | 1429 | 1429 | 2429 | 2 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| rewardsBalanceOf | 2324 | 3507 | 3909 | 6324 | 268 |
| rewardsBalanceOf | 2324 | 3519 | 3909 | 6324 | 268 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| setReward | 2484 | 105517 | 107034 | 107034 | 264 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
@@ -206,17 +206,17 @@
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| totalRewardsAccrued | 384 | 384 | 384 | 384 | 3 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| totalRewardsSupply | 993 | 1609 | 1774 | 6733 | 290 |
| totalRewardsSupply | 993 | 1615 | 1774 | 6733 | 290 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| totalShares | 637 | 637 | 637 | 637 | 6 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| totalStaked | 385 | 385 | 385 | 385 | 1115 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| unstake | 56017 | 81007 | 80911 | 88301 | 269 |
| unstake | 56017 | 81048 | 80911 | 88301 | 269 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| updateGlobalState | 15809 | 27690 | 29200 | 29503 | 276 |
| updateGlobalState | 15809 | 27646 | 29200 | 29503 | 276 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| updateVaultMP | 25103 | 34576 | 36516 | 36819 | 276 |
| updateVaultMP | 25103 | 34530 | 36516 | 36819 | 276 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
| upgradeToAndCall | 3225 | 7892 | 8437 | 10925 | 5 |
|------------------------------------------------------+-----------------+--------+--------+--------+---------|
@@ -242,7 +242,7 @@
|----------------------------------------+-----------------+--------+--------+--------+---------|
| leave | 12167 | 126461 | 69872 | 353935 | 4 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| lock | 12097 | 67387 | 67531 | 109137 | 261 |
| lock | 12097 | 67386 | 67531 | 109137 | 261 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| migrateToVault | 29034 | 98943 | 31240 | 236555 | 3 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
@@ -256,7 +256,7 @@
|----------------------------------------+-----------------+--------+--------+--------+---------|
| trustStakeManager | 7577 | 7577 | 7577 | 7577 | 1 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| unstake | 12054 | 111539 | 112021 | 119411 | 270 |
| unstake | 12054 | 111580 | 112021 | 119411 | 270 |
|----------------------------------------+-----------------+--------+--------+--------+---------|
| withdraw | 20705 | 20705 | 20705 | 20705 | 1 |
╰----------------------------------------+-----------------+--------+--------+--------+---------╯
@@ -272,7 +272,7 @@
|----------------------------------------------------+-----------------+------+--------+--------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|----------------------------------------------------+-----------------+------+--------+--------+---------|
| fallback | 735 | 8101 | 833 | 142290 | 7358 |
| fallback | 735 | 8099 | 833 | 142290 | 7358 |
|----------------------------------------------------+-----------------+------+--------+--------+---------|
| implementation | 343 | 2342 | 2343 | 2343 | 2455 |
╰----------------------------------------------------+-----------------+------+--------+--------+---------╯
@@ -376,11 +376,11 @@
|---------------------------------------------+-----------------+-------+--------+-------+---------|
| Function Name | Min | Avg | Median | Max | # Calls |
|---------------------------------------------+-----------------+-------+--------+-------+---------|
| approve | 29158 | 32603 | 29350 | 46342 | 1586 |
| approve | 29158 | 32602 | 29350 | 46342 | 1586 |
|---------------------------------------------+-----------------+-------+--------+-------+---------|
| balanceOf | 558 | 1118 | 558 | 2558 | 1900 |
|---------------------------------------------+-----------------+-------+--------+-------+---------|
| mint | 34095 | 38638 | 34287 | 68379 | 1598 |
| mint | 34095 | 38637 | 34287 | 68379 | 1598 |
╰---------------------------------------------+-----------------+-------+--------+-------+---------╯
╭-----------------------------------------------------------------------------+-----------------+--------+--------+--------+---------╮

View File

@@ -7,24 +7,24 @@ EmergencyExitTest:test_EmergencyExitToAlternateAddress() (gas: 450444)
EmergencyExitTest:test_EmergencyExitWithLock() (gas: 448187)
EmergencyExitTest:test_EmergencyExitWithRewards() (gas: 435625)
EmergencyExitTest:test_OnlyOwnerCanEnableEmergencyMode() (gas: 39462)
FuzzTests:testFuzz_AccrueMP(uint256,uint256,uint16) (runs: 1000, μ: 521104, ~: 515988)
FuzzTests:testFuzz_EmergencyExit(uint256,uint256) (runs: 1000, μ: 510621, ~: 501981)
FuzzTests:testFuzz_Rewards(uint256,uint256,uint256,uint16,uint16) (runs: 1000, μ: 604657, ~: 605935)
FuzzTests:testFuzz_Stake(uint256,uint256) (runs: 1000, μ: 404073, ~: 395433)
FuzzTests:testFuzz_Unstake(uint256,uint256,uint16,uint256) (runs: 1000, μ: 530258, ~: 529949)
FuzzTests:testFuzz_AccrueMP(uint256,uint256,uint16) (runs: 1000, μ: 521120, ~: 515988)
FuzzTests:testFuzz_EmergencyExit(uint256,uint256) (runs: 1000, μ: 510818, ~: 502017)
FuzzTests:testFuzz_Rewards(uint256,uint256,uint256,uint16,uint16) (runs: 1000, μ: 604742, ~: 605988)
FuzzTests:testFuzz_Stake(uint256,uint256) (runs: 1000, μ: 404270, ~: 395469)
FuzzTests:testFuzz_Unstake(uint256,uint256,uint16,uint256) (runs: 1000, μ: 530267, ~: 529967)
IntegrationTest:testStakeFoo() (gas: 1403381)
KarmaMintAllowanceTest:testAddKarmaDistributorOnlyOwner() (gas: 348463)
KarmaMintAllowanceTest:testBalanceOf() (gas: 428643)
KarmaMintAllowanceTest:testBalanceOfWithNoSystemTotalKarma() (gas: 44170)
KarmaMintAllowanceTest:testMintAllowance_Available() (gas: 340314)
KarmaMintAllowanceTest:testMintAllowance_NotAvailable() (gas: 340317)
KarmaMintAllowanceTest:testMintOnlyOwner() (gas: 377209)
KarmaMintAllowanceTest:testMint_Ok() (gas: 405049)
KarmaMintAllowanceTest:testMint_RevertWithAllowanceExceeded() (gas: 385796)
KarmaMintAllowanceTest:testRemoveKarmaDistributorOnlyOwner() (gas: 76009)
KarmaMintAllowanceTest:testRemoveUnknownKarmaDistributor() (gas: 36519)
KarmaMintAllowanceTest:testTotalSupply() (gas: 336830)
KarmaMintAllowanceTest:testTransfersNotAllowed() (gas: 20640)
KarmaMintAllowanceTest:testAddKarmaDistributorOnlyOwner() (gas: 348441)
KarmaMintAllowanceTest:testBalanceOf() (gas: 428754)
KarmaMintAllowanceTest:testBalanceOfWithNoSystemTotalKarma() (gas: 44256)
KarmaMintAllowanceTest:testMintAllowance_Available() (gas: 340382)
KarmaMintAllowanceTest:testMintAllowance_NotAvailable() (gas: 340385)
KarmaMintAllowanceTest:testMintOnlyOwner() (gas: 377255)
KarmaMintAllowanceTest:testMint_Ok() (gas: 405095)
KarmaMintAllowanceTest:testMint_RevertWithAllowanceExceeded() (gas: 385842)
KarmaMintAllowanceTest:testRemoveKarmaDistributorOnlyOwner() (gas: 75943)
KarmaMintAllowanceTest:testRemoveUnknownKarmaDistributor() (gas: 36497)
KarmaMintAllowanceTest:testTotalSupply() (gas: 336898)
KarmaMintAllowanceTest:testTransfersNotAllowed() (gas: 20663)
KarmaNFTTest:testApproveNotAllowed() (gas: 10500)
KarmaNFTTest:testGetApproved() (gas: 10523)
KarmaNFTTest:testIsApprovedForAll() (gas: 10698)
@@ -35,20 +35,20 @@ KarmaNFTTest:testSetMetadataGenerator() (gas: 966687)
KarmaNFTTest:testSetMetadataGeneratorRevert() (gas: 963218)
KarmaNFTTest:testTokenURI() (gas: 102963)
KarmaNFTTest:testTransferNotAllowed() (gas: 10715)
KarmaOwnershipTest:testInitialOwner() (gas: 12634)
KarmaOwnershipTest:testOwnershipTransfer() (gas: 87258)
KarmaTest:testAddKarmaDistributorOnlyOwner() (gas: 348451)
KarmaTest:testBalanceOf() (gas: 428613)
KarmaTest:testBalanceOfWithNoSystemTotalKarma() (gas: 44214)
KarmaTest:testMintOnlyOwner() (gas: 377173)
KarmaTest:testRemoveKarmaDistributorOnlyOwner() (gas: 75952)
KarmaTest:testRemoveUnknownKarmaDistributor() (gas: 36513)
KarmaTest:testTotalSupply() (gas: 336800)
KarmaTest:testTransfersNotAllowed() (gas: 20662)
LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 7249868)
KarmaOwnershipTest:testInitialOwner() (gas: 12612)
KarmaOwnershipTest:testOwnershipTransfer() (gas: 87170)
KarmaTest:testAddKarmaDistributorOnlyOwner() (gas: 348429)
KarmaTest:testBalanceOf() (gas: 428724)
KarmaTest:testBalanceOfWithNoSystemTotalKarma() (gas: 44300)
KarmaTest:testMintOnlyOwner() (gas: 377219)
KarmaTest:testRemoveKarmaDistributorOnlyOwner() (gas: 75886)
KarmaTest:testRemoveUnknownKarmaDistributor() (gas: 36491)
KarmaTest:testTotalSupply() (gas: 336868)
KarmaTest:testTransfersNotAllowed() (gas: 20685)
LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 8942756)
LeaveTest:test_RevertWhenStakeManagerIsTrusted() (gas: 350352)
LeaveTest:test_TrustNewStakeManager() (gas: 7302750)
LockTest:test_LockFailsWithInvalidPeriod(uint256) (runs: 1000, μ: 406918, ~: 406944)
LeaveTest:test_TrustNewStakeManager() (gas: 8995615)
LockTest:test_LockFailsWithInvalidPeriod(uint256) (runs: 1000, μ: 406917, ~: 406944)
LockTest:test_LockFailsWithNoStake() (gas: 114574)
LockTest:test_LockFailsWithZero() (gas: 367631)
LockTest:test_LockWithoutPriorLock() (gas: 465087)
@@ -67,12 +67,12 @@ NFTMetadataGeneratorSVGTest:testSetImageStringsRevert() (gas: 35804)
NFTMetadataGeneratorURLTest:testGenerateMetadata() (gas: 101558)
NFTMetadataGeneratorURLTest:testSetBaseURL() (gas: 49555)
NFTMetadataGeneratorURLTest:testSetBaseURLRevert() (gas: 35979)
RewardsStreamerMP_RewardsTest:testRewardsBalanceOf() (gas: 1309798)
RewardsStreamerMP_RewardsTest:testSetRewards() (gas: 219337)
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 56162)
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadDuration() (gas: 95940)
RewardsStreamerMP_RewardsTest:testRewardsBalanceOf() (gas: 1309888)
RewardsStreamerMP_RewardsTest:testSetRewards() (gas: 219382)
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 56207)
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadDuration() (gas: 95985)
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsNotAuthorized() (gas: 39295)
RewardsStreamerMP_RewardsTest:testTotalRewardsSupply() (gas: 746903)
RewardsStreamerMP_RewardsTest:testTotalRewardsSupply() (gas: 746993)
StakeTest:test_StakeMultipleAccounts() (gas: 591097)
StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 599025)
StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 1024300)
@@ -110,6 +110,6 @@ UnstakeTest:test_UnstakeOneAccountAndAccruedMP() (gas: 592850)
UnstakeTest:test_UnstakeOneAccountAndRewards() (gas: 488388)
UnstakeTest:test_UnstakeOneAccountWithLockUpAndAccruedMP() (gas: 622983)
UpgradeTest:test_RevertWhenNotOwner() (gas: 3399068)
UpgradeTest:test_UpgradeStakeManager() (gas: 7137669)
UpgradeTest:test_UpgradeStakeManager() (gas: 8830553)
VaultRegistrationTest:test_VaultRegistration() (gas: 62040)
WithdrawTest:test_CannotWithdrawStakedFunds() (gas: 365961)

View File

@@ -9,7 +9,7 @@ import { Karma } from "../src/Karma.sol";
contract DeployKarmaScript is BaseScript {
function run() public returns (Karma) {
DeploymentConfig deploymentConfig = new DeploymentConfig(broadcaster);
(address deployer,) = deploymentConfig.activeNetworkConfig();
(address deployer,,) = deploymentConfig.activeNetworkConfig();
vm.startBroadcast(deployer);
address karma = address(new Karma());

View File

@@ -15,7 +15,7 @@ import { VaultFactory } from "../src/VaultFactory.sol";
contract DeployRewardsStreamerMPScript is BaseScript {
function run() public returns (RewardsStreamerMP, VaultFactory, DeploymentConfig) {
DeploymentConfig deploymentConfig = new DeploymentConfig(broadcaster);
(address deployer, address stakingToken) = deploymentConfig.activeNetworkConfig();
(address deployer, address stakingToken,) = deploymentConfig.activeNetworkConfig();
bytes memory initializeData = abi.encodeCall(RewardsStreamerMP.initialize, (deployer, stakingToken));

View File

@@ -13,6 +13,7 @@ contract DeploymentConfig is Script {
struct NetworkConfig {
address deployer;
address stakingToken;
address currentImplProxy;
}
NetworkConfig public activeNetworkConfig;
@@ -22,6 +23,9 @@ contract DeploymentConfig is Script {
// solhint-disable-next-line var-name-mixedcase
address internal SNT_ADDRESS_SEPOLIA = 0xE452027cdEF746c7Cd3DB31CB700428b16cD8E51;
// solhint-disable-next-line var-name-mixedcase
address internal STAKING_MANAGER_PROXY_ADDRESS_SEPOLIA = 0xD302Bd9F60c5192e46258028a2F3b4B2B846F61F;
constructor(address _broadcaster) {
if (_broadcaster == address(0)) revert DeploymentConfig_InvalidDeployerAddress();
deployer = _broadcaster;
@@ -36,11 +40,15 @@ contract DeploymentConfig is Script {
function getOrCreateAnvilEthConfig() public returns (NetworkConfig memory) {
MockToken stakingToken = new MockToken("Staking Token", "ST");
return NetworkConfig({ deployer: deployer, stakingToken: address(stakingToken) });
return NetworkConfig({ deployer: deployer, stakingToken: address(stakingToken), currentImplProxy: address(0) });
}
function getSepoliaConfig() public view returns (NetworkConfig memory) {
return NetworkConfig({ deployer: deployer, stakingToken: SNT_ADDRESS_SEPOLIA });
return NetworkConfig({
deployer: deployer,
stakingToken: SNT_ADDRESS_SEPOLIA,
currentImplProxy: STAKING_MANAGER_PROXY_ADDRESS_SEPOLIA
});
}
// This function is a hack to have it excluded by `forge coverage` until

View File

@@ -5,9 +5,16 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils
import { BaseScript } from "./Base.s.sol";
import { RewardsStreamerMP } from "../src/RewardsStreamerMP.sol";
import { IStakeManagerProxy } from "../src/interfaces/IStakeManagerProxy.sol";
import { DeploymentConfig } from "./DeploymentConfig.s.sol";
contract UpgradeRewardsStreamerMPScript is BaseScript {
function run(address admin, IStakeManagerProxy currentImplProxy) public {
function run() public returns (address) {
DeploymentConfig deploymentConfig = new DeploymentConfig(broadcaster);
(address deployer,, address currentImplProxy) = deploymentConfig.activeNetworkConfig();
return runWithAdminAndProxy(deployer, IStakeManagerProxy(currentImplProxy));
}
function runWithAdminAndProxy(address admin, IStakeManagerProxy currentImplProxy) public returns (address) {
address deployer = broadcaster;
if (admin != address(0)) {
deployer = admin;
@@ -18,5 +25,6 @@ contract UpgradeRewardsStreamerMPScript is BaseScript {
bytes memory initializeData;
UUPSUpgradeable(address(currentImplProxy)).upgradeToAndCall(nextImpl, initializeData);
vm.stopBroadcast();
return nextImpl;
}
}

View File

@@ -14,6 +14,17 @@ import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableS
contract Karma is ERC20, Ownable2Step {
using EnumerableSet for EnumerableSet.AddressSet;
error Karma__TransfersNotAllowed();
error Karma__MintAllowanceExceeded();
error Karma__DistributorAlreadyAdded();
error Karma__UnknownDistributor();
event RewardDistributorAdded(address distributor);
/*//////////////////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////////////////*/
/// @notice The name of the token
string public constant NAME = "Karma";
/// @notice The symbol of the token
@@ -26,15 +37,16 @@ contract Karma is ERC20, Ownable2Step {
/// @notice Mapping of reward distributor to allocation
mapping(address distributor => uint256 allocation) public rewardDistributorAllocations;
error Karma__TransfersNotAllowed();
error Karma__MintAllowanceExceeded();
error Karma__DistributorAlreadyAdded();
error Karma__UnknownDistributor();
event RewardDistributorAdded(address distributor);
/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/
constructor() ERC20(NAME, SYMBOL) Ownable(msg.sender) { }
/*//////////////////////////////////////////////////////////////////////////
USER-FACING FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Adds a reward distributor to the set of reward distributors.
* @dev Only the owner can add a reward distributor.
@@ -81,27 +93,6 @@ contract Karma is ERC20, Ownable2Step {
IRewardDistributor(rewardsDistributor).setReward(amount, duration);
}
/**
* @notice Returns the reward distributors.
* @return The reward distributors.
*/
function getRewardDistributors() external view returns (address[] memory) {
return rewardDistributors.values();
}
function _totalSupply() public view returns (uint256) {
return super.totalSupply() + _externalSupply();
}
/**
* @notice Returns the total supply of the token.
* @dev The total supply is the sum of the token supply and the external supply.
* @return The total supply of the token.
*/
function totalSupply() public view override returns (uint256) {
return _totalSupply();
}
/**
* @notice Mints tokens to an account.
* @dev Only the owner can mint tokens.
@@ -117,6 +108,26 @@ contract Karma is ERC20, Ownable2Step {
_mint(account, amount);
}
function transfer(address, uint256) public pure override returns (bool) {
revert Karma__TransfersNotAllowed();
}
function approve(address, uint256) public pure override returns (bool) {
revert Karma__TransfersNotAllowed();
}
function transferFrom(address, address, uint256) public pure override returns (bool) {
revert Karma__TransfersNotAllowed();
}
/*//////////////////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
function _totalSupply() internal view returns (uint256) {
return super.totalSupply() + _externalSupply();
}
function _mintAllowance() internal view returns (uint256) {
uint256 maxSupply = _externalSupply() * 3;
uint256 fullTotalSupply = _totalSupply();
@@ -127,15 +138,6 @@ contract Karma is ERC20, Ownable2Step {
return maxSupply - fullTotalSupply;
}
/**
* @notice Returns the mint allowance.
* @dev The mint allowance is the difference between the external supply and the total supply.
* @return The mint allowance.
*/
function mintAllowance() public view returns (uint256) {
return _mintAllowance();
}
/**
* @notice Returns the external supply of the token.
* @dev The external supply is the sum of the rewards from all reward distributors.
@@ -153,10 +155,39 @@ contract Karma is ERC20, Ownable2Step {
externalSupply += supply;
}
return externalSupply;
}
/*//////////////////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Returns the total supply of the token.
* @dev The total supply is the sum of the token supply and the external supply.
* @return The total supply of the token.
*/
function totalSupply() public view override returns (uint256) {
return _totalSupply();
}
/**
* @notice Returns the reward distributors.
* @return The reward distributors.
*/
function getRewardDistributors() external view returns (address[] memory) {
return rewardDistributors.values();
}
/**
* @notice Returns the mint allowance.
* @dev The mint allowance is the difference between the external supply and the total supply.
* @return The mint allowance.
*/
function mintAllowance() public view returns (uint256) {
return _mintAllowance();
}
/**
* @notice Returns the balance of an account.
* @dev The balance of an account is the sum of the balance of the account and the external rewards
@@ -174,18 +205,6 @@ contract Karma is ERC20, Ownable2Step {
return super.balanceOf(account) + externalBalance;
}
function transfer(address, uint256) public pure override returns (bool) {
revert Karma__TransfersNotAllowed();
}
function approve(address, uint256) public pure override returns (bool) {
revert Karma__TransfersNotAllowed();
}
function transferFrom(address, address, uint256) public pure override returns (bool) {
revert Karma__TransfersNotAllowed();
}
function allowance(address, address) public pure override returns (uint256) {
return 0;
}

View File

@@ -27,33 +27,6 @@ contract RewardsStreamerMP is
IRewardDistributor,
StakeMath
{
/// @notice Token that is staked in the vaults (SNT).
IERC20 public STAKING_TOKEN;
/// @notice Scale factor used for rewards calculation.
uint256 public constant SCALE_FACTOR = 1e27;
uint256 public totalStaked;
/// @notice Total multiplier points accrued.
uint256 public totalMPAccrued;
/// @notice Total maximum multiplier points that can be accrued.
uint256 public totalMaxMP;
/// @notice Index of the current reward period.
uint256 public rewardIndex;
/// @notice Time of the last multiplier points update.
uint256 public lastMPUpdatedTime;
/// @notice Flag to enable emergency mode.
bool public emergencyModeEnabled;
/// @notice Total rewards accrued in the system.
uint256 public totalRewardsAccrued;
/// @notice Amount of rewards available for distribution.
uint256 public rewardAmount;
/// @notice Time of the last reward update.
uint256 public lastRewardTime;
/// @notice Time when rewards start.
uint256 public rewardStartTime;
/// @notice Time when rewards end.
uint256 public rewardEndTime;
struct VaultData {
uint256 stakedBalance;
uint256 rewardIndex;
@@ -65,17 +38,46 @@ contract RewardsStreamerMP is
uint256 rewardsAccrued;
}
/*//////////////////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////////////////*/
/// @notice Token that is staked in the vaults (SNT).
IERC20 public STAKING_TOKEN;
/// @notice Scale factor used for rewards calculation.
uint256 public constant SCALE_FACTOR = 1e27;
/// @notice Total staked balance in the system.
uint256 public totalStaked;
/// @notice Total amount of staked multiplier points
uint256 public totalMPStaked;
/// @notice Total multiplier points accrued.
uint256 public totalMPAccrued;
/// @notice Total rewards accrued in the system.
uint256 public totalRewardsAccrued;
/// @notice Total maximum multiplier points that can be accrued.
uint256 public totalMaxMP;
/// @notice Time of the last multiplier points update.
uint256 public lastMPUpdatedTime;
/// @notice Index of the current reward period.
uint256 public rewardIndex;
/// @notice The address that can set rewards
address public rewardsSupplier;
/// @notice Amount of rewards available for distribution.
uint256 public rewardAmount;
/// @notice Time of the last reward update.
uint256 public lastRewardTime;
/// @notice Time when rewards start.
uint256 public rewardStartTime;
/// @notice Time when rewards end.
uint256 public rewardEndTime;
/// @notice Maps vault addresses to vault data
mapping(address vault => VaultData data) public vaultData;
/// @notice Maps Account address to a list of vaults
mapping(address owner => address[] vault) public vaults;
/// @notice Maps vault addresses to their owners
mapping(address vault => address owner) public vaultOwners;
/// @notice Total amount of staked multiplier points
uint256 public totalMPStaked;
/// @notice The address that can set rewards
address public rewardsSupplier;
/// @notice Flag to enable emergency mode.
bool public emergencyModeEnabled;
modifier onlyRegisteredVault() {
if (vaultOwners[msg.sender] == address(0)) {
@@ -98,6 +100,10 @@ contract RewardsStreamerMP is
_;
}
/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Initializes the contract.
* @dev Disables initializers to prevent reinitialization.
@@ -106,13 +112,17 @@ contract RewardsStreamerMP is
_disableInitializers();
}
/*//////////////////////////////////////////////////////////////////////////
USER-FACING FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Initializes the contract with the provided owner and staking token.
* @dev Also sets the initial `lastMPUpdatedTime`
* @param _owner Address of the owner of the contract.
* @param _stakingToken Address of the staking token.
*/
function initialize(address _owner, address _stakingToken) public initializer {
function initialize(address _owner, address _stakingToken) external initializer {
__TrustedCodehashAccess_init(_owner);
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
@@ -130,14 +140,6 @@ contract RewardsStreamerMP is
rewardsSupplier = _rewardsSupplier;
}
/**
* @notice Authorizes contract upgrades via UUPS.
* @dev This function is only callable by the owner.
*/
function _authorizeUpgrade(address) internal view override {
_checkOwner();
}
/**
* @notice Registers a vault with its owner. Called by the vault itself during initialization.
* @dev Only callable by contracts with trusted codehash
@@ -160,64 +162,6 @@ contract RewardsStreamerMP is
emit VaultRegistered(vault, owner);
}
/**
* @notice Get the vaults owned by a user
* @param account The address of the user
* @return The vaults owned by the user
*/
function getAccountVaults(address account) external view returns (address[] memory) {
return vaults[account];
}
/**
* @notice Get the total multiplier points for a user
* @dev Iterates over all vaults owned by the user and sums the multiplier points
* @param account The address of the user
* @return The total multiplier points for the user
*/
function mpBalanceOfAccount(address account) external view returns (uint256) {
address[] memory accountVaults = vaults[account];
uint256 accountTotalMP = 0;
for (uint256 i = 0; i < accountVaults.length; i++) {
VaultData storage vault = vaultData[accountVaults[i]];
accountTotalMP += vault.mpAccrued + _getVaultPendingMP(vault);
}
return accountTotalMP;
}
/**
* @notice Get the total maximum multiplier points for a user
* @dev Iterates over all vaults owned by the user and sums the maximum multiplier points
* @param account The address of the user
* @return The total maximum multiplier points for the user
*/
function getAccountTotalMaxMP(address account) external view returns (uint256) {
address[] memory accountVaults = vaults[account];
uint256 accountTotalMaxMP = 0;
for (uint256 i = 0; i < accountVaults.length; i++) {
accountTotalMaxMP += vaultData[accountVaults[i]].maxMP;
}
return accountTotalMaxMP;
}
/**
* @notice Get the total staked balance for a user
* @dev Iterates over all vaults owned by the user and sums the staked balances
* @param account The address of the user
* @return The total staked balance for the user
*/
function getAccountTotalStakedBalance(address account) external view returns (uint256) {
address[] memory accountVaults = vaults[account];
uint256 accountTotalStake = 0;
for (uint256 i = 0; i < accountVaults.length; i++) {
accountTotalStake += vaultData[accountVaults[i]].stakedBalance;
}
return accountTotalStake;
}
/**
* @notice Allows users to stake and start accruing MPs.
* @dev Called by trusted `StakeVault`.
@@ -337,31 +281,6 @@ contract RewardsStreamerMP is
emit Unstaked(msg.sender, amount);
}
function _unstake(uint256 amount, VaultData storage vault, address vaultAddress) internal {
_updateGlobalState();
_updateVault(vaultAddress, true);
(uint256 _deltaMpTotal, uint256 _deltaMpMax) = _calculateUnstake(
vault.stakedBalance, vault.lockUntil, block.timestamp, vault.mpAccrued, vault.maxMP, amount
);
vault.stakedBalance -= amount;
vault.maxMP -= _deltaMpMax;
vault.rewardIndex = rewardIndex;
vault.mpAccrued -= _deltaMpTotal;
// A vault's "staked" MP will be reduced if the accrued MP is less than the staked MP.
// This is because the accrued MP represents the vault's total MP
if (vault.mpAccrued < vault.mpStaked) {
totalMPStaked -= vault.mpStaked - vault.mpAccrued;
vault.mpStaked = vault.mpAccrued;
totalMPStaked -= vault.mpStaked - vault.mpAccrued;
}
totalMPAccrued -= _deltaMpTotal;
totalMaxMP -= _deltaMpMax;
totalStaked -= amount;
}
// @notice Allows an account to leave the system. This can happen when a
// user doesn't agree with an upgrade of the stake manager.
// @dev This function is protected by whitelisting the codehash of the caller.
@@ -384,52 +303,15 @@ contract RewardsStreamerMP is
emit AccountLeft(msg.sender);
}
function _updateGlobalState() internal {
updateGlobalMP();
updateRewardIndex();
}
/**
* @notice Allows the owner to update the global state.
* @dev This function is only callable when emergency mode is disabled.
* @dev Takes care of updating the global MP and reward index.
*/
function updateGlobalState() external onlyNotEmergencyMode {
_updateGlobalState();
}
function updateGlobalMP() internal {
uint256 newTotalMPAccrued = _liveTotalMP();
if (newTotalMPAccrued > totalMPAccrued) {
totalMPAccrued = newTotalMPAccrued;
lastMPUpdatedTime = block.timestamp;
}
}
/**
* @notice Returns the total multiplier points accrued in the system.
* @return The total multiplier points accrued in the system.
*/
function totalMP() external view returns (uint256) {
return _liveTotalMP();
}
function _liveTotalMP() internal view returns (uint256) {
if (totalMaxMP == 0) {
return totalMPAccrued;
}
uint256 currentTime = block.timestamp;
uint256 timeDiff = currentTime - lastMPUpdatedTime;
if (timeDiff == 0) {
return totalMPAccrued;
}
uint256 accruedMP = _accrueMP(totalStaked, timeDiff);
if (totalMPAccrued + accruedMP > totalMaxMP) {
accruedMP = totalMaxMP - totalMPAccrued;
}
uint256 newTotalMPAccrued = totalMPAccrued + accruedMP;
return newTotalMPAccrued;
}
/**
* @notice Allows an admin to set the reward amount and duration.
* @dev This function is only callable by the owner.
@@ -460,65 +342,6 @@ contract RewardsStreamerMP is
rewardEndTime = block.timestamp + duration;
}
function _calculatePendingRewards() internal view returns (uint256) {
if (rewardEndTime <= rewardStartTime) {
// No active reward period
return 0;
}
uint256 currentTime = block.timestamp < rewardEndTime ? block.timestamp : rewardEndTime;
if (currentTime <= lastRewardTime) {
// No new rewards have accrued since lastRewardTime
return 0;
}
uint256 timeElapsed = currentTime - lastRewardTime;
uint256 duration = rewardEndTime - rewardStartTime;
if (duration == 0) {
// Prevent division by zero
return 0;
}
uint256 accruedRewards = Math.mulDiv(timeElapsed, rewardAmount, duration);
return accruedRewards;
}
function updateRewardIndex() internal {
uint256 accruedRewards;
uint256 newRewardIndex;
(accruedRewards, newRewardIndex) = _liveRewardIndex();
totalRewardsAccrued += accruedRewards;
if (newRewardIndex > rewardIndex) {
rewardIndex = newRewardIndex;
lastRewardTime = block.timestamp < rewardEndTime ? block.timestamp : rewardEndTime;
}
}
/**
* @notice Returns the total shares in the system.
* @dev Total shares are total staked tokens and total multiplier points staked.
* @return The total shares in the system.
*/
function totalShares() external view returns (uint256) {
return _totalShares();
}
function _totalShares() internal view returns (uint256) {
return totalStaked + totalMPStaked;
}
/**
* @notice Returns the total shares of a given vault.
* @return The total vault shares
*/
function vaultShares(address vaultAddress) external view returns (uint256) {
return _vaultShares(vaultAddress);
}
/**
* @notice Allows users to compound their accrued MP.
* @dev This function is only callable when emergency mode is disabled.
@@ -541,64 +364,6 @@ contract RewardsStreamerMP is
emit Compound(vaultAddress, mpToStake);
}
function _vaultShares(address vaultAddress) internal view returns (uint256) {
VaultData storage vault = vaultData[vaultAddress];
return vault.stakedBalance + vault.mpStaked;
}
function _liveRewardIndex() internal view returns (uint256, uint256) {
uint256 shares = _totalShares();
if (shares == 0) {
return (0, rewardIndex);
}
uint256 currentTime = block.timestamp;
uint256 applicableTime = rewardEndTime > currentTime ? currentTime : rewardEndTime;
uint256 elapsedTime = applicableTime - lastRewardTime;
if (elapsedTime == 0) {
return (0, rewardIndex);
}
uint256 accruedRewards = _calculatePendingRewards();
if (accruedRewards == 0) {
return (0, rewardIndex);
}
uint256 newRewardIndex = rewardIndex + Math.mulDiv(accruedRewards, SCALE_FACTOR, shares);
return (accruedRewards, newRewardIndex);
}
function _getVaultPendingMP(VaultData storage vault) internal view returns (uint256) {
if (block.timestamp == vault.lastMPUpdateTime) {
return 0;
}
if (vault.maxMP == 0 || vault.stakedBalance == 0) {
return 0;
}
uint256 deltaMP = _calculateAccrual(
vault.stakedBalance, vault.mpAccrued, vault.maxMP, vault.lastMPUpdateTime, block.timestamp
);
return deltaMP;
}
function _updateVault(address vaultAddress, bool forceMPUpdate) internal {
VaultData storage vault = vaultData[vaultAddress];
uint256 accruedMP = _getVaultPendingMP(vault);
if (accruedMP > 0 || forceMPUpdate) {
vault.mpAccrued += accruedMP;
vault.lastMPUpdateTime = block.timestamp;
}
uint256 rewardsAccrued = _vaultPendingRewards(vault);
vault.rewardsAccrued += rewardsAccrued;
vault.rewardIndex = rewardIndex;
}
/**
* @notice Allows users to claim their accrued rewards.
* @dev This function is only callable when emergency mode is disabled.
@@ -618,87 +383,6 @@ contract RewardsStreamerMP is
emit EmergencyModeEnabled();
}
/**
* @notice Returns the staked balance of a vault.
* @param vaultAddress The address of the vault.
*/
function getStakedBalance(address vaultAddress) public view returns (uint256) {
return vaultData[vaultAddress].stakedBalance;
}
/**
* @notice Returns vault data for a given vault address.
* @return Vault data for the given vault address
*/
function getVault(address vaultAddress) external view returns (VaultData memory) {
return vaultData[vaultAddress];
}
/**
* @notice Returns total rewards supply in the system.
* @return Total rewards supply in the system.
*/
function totalRewardsSupply() public view returns (uint256) {
return totalRewardsAccrued + _calculatePendingRewards();
}
/**
* @notice Returns the rewards balance of a vault.
* @return Rewards balance of the vault.
*/
function rewardsBalanceOf(address vaultAddress) public view returns (uint256) {
VaultData storage vault = vaultData[vaultAddress];
return vault.rewardsAccrued + _vaultPendingRewards(vault);
}
function _vaultPendingRewards(VaultData storage vault) internal view returns (uint256) {
uint256 newRewardIndex;
(, newRewardIndex) = _liveRewardIndex();
uint256 accountShares = vault.stakedBalance + vault.mpStaked;
uint256 deltaRewardIndex = newRewardIndex - vault.rewardIndex;
return (accountShares * deltaRewardIndex) / SCALE_FACTOR;
}
/**
* @notice Returns the rewards balance of an account.
* @dev Iterates over all vaults owned by the account and sums the rewards.
* @return Rewards balance of the account.
*/
function rewardsBalanceOfAccount(address account) external view returns (uint256) {
address[] memory accountVaults = vaults[account];
uint256 accountTotalRewards = 0;
for (uint256 i = 0; i < accountVaults.length; i++) {
accountTotalRewards += rewardsBalanceOf(accountVaults[i]);
}
return accountTotalRewards;
}
function _mpBalanceOf(address vaultAddress) internal view returns (uint256) {
VaultData storage vault = vaultData[vaultAddress];
return vault.mpAccrued + _getVaultPendingMP(vault);
}
/**
* @notice Returns the multiplier points balance of a vault.
* @return Multiplier points balance of the vault.
*/
function mpBalanceOf(address vaultAddress) external view returns (uint256) {
return _mpBalanceOf(vaultAddress);
}
/**
* @notice Returns staked multiplier points of a vault.
* @return Staked multiplier points of the vault.
*/
function mpStakedOf(address vaultAddress) external view returns (uint256) {
VaultData storage vault = vaultData[vaultAddress];
return vault.mpStaked;
}
/**
* @notice Migrate the staked balance of a vault to another vault.
* @param migrateTo The address of the vault to migrate to.
@@ -736,4 +420,342 @@ contract RewardsStreamerMP is
emit VaultMigrated(msg.sender, migrateTo);
}
/*//////////////////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
function _updateGlobalState() internal {
_updateGlobalMP();
updateRewardIndex();
}
function _updateGlobalMP() internal {
uint256 newTotalMPAccrued = _liveTotalMP();
if (newTotalMPAccrued > totalMPAccrued) {
totalMPAccrued = newTotalMPAccrued;
lastMPUpdatedTime = block.timestamp;
}
}
function _updateVault(address vaultAddress, bool forceMPUpdate) internal {
VaultData storage vault = vaultData[vaultAddress];
uint256 accruedMP = _getVaultPendingMP(vault);
if (accruedMP > 0 || forceMPUpdate) {
vault.mpAccrued += accruedMP;
vault.lastMPUpdateTime = block.timestamp;
}
uint256 rewardsAccrued = _vaultPendingRewards(vault);
vault.rewardsAccrued += rewardsAccrued;
vault.rewardIndex = rewardIndex;
}
function updateRewardIndex() internal {
uint256 accruedRewards;
uint256 newRewardIndex;
(accruedRewards, newRewardIndex) = _liveRewardIndex();
totalRewardsAccrued += accruedRewards;
if (newRewardIndex > rewardIndex) {
rewardIndex = newRewardIndex;
lastRewardTime = block.timestamp < rewardEndTime ? block.timestamp : rewardEndTime;
}
}
function _unstake(uint256 amount, VaultData storage vault, address vaultAddress) internal {
_updateGlobalState();
_updateVault(vaultAddress, true);
(uint256 _deltaMpTotal, uint256 _deltaMpMax) = _calculateUnstake(
vault.stakedBalance, vault.lockUntil, block.timestamp, vault.mpAccrued, vault.maxMP, amount
);
vault.stakedBalance -= amount;
vault.maxMP -= _deltaMpMax;
vault.rewardIndex = rewardIndex;
vault.mpAccrued -= _deltaMpTotal;
// A vault's "staked" MP will be reduced if the accrued MP is less than the staked MP.
// This is because the accrued MP represents the vault's total MP
if (vault.mpAccrued < vault.mpStaked) {
totalMPStaked -= vault.mpStaked - vault.mpAccrued;
vault.mpStaked = vault.mpAccrued;
totalMPStaked -= vault.mpStaked - vault.mpAccrued;
}
totalMPAccrued -= _deltaMpTotal;
totalMaxMP -= _deltaMpMax;
totalStaked -= amount;
}
function _totalShares() internal view returns (uint256) {
return totalStaked + totalMPStaked;
}
function _vaultShares(address vaultAddress) internal view returns (uint256) {
VaultData storage vault = vaultData[vaultAddress];
return vault.stakedBalance + vault.mpStaked;
}
function _liveTotalMP() internal view returns (uint256) {
if (totalMaxMP == 0) {
return totalMPAccrued;
}
uint256 currentTime = block.timestamp;
uint256 timeDiff = currentTime - lastMPUpdatedTime;
if (timeDiff == 0) {
return totalMPAccrued;
}
uint256 accruedMP = _accrueMP(totalStaked, timeDiff);
if (totalMPAccrued + accruedMP > totalMaxMP) {
accruedMP = totalMaxMP - totalMPAccrued;
}
uint256 newTotalMPAccrued = totalMPAccrued + accruedMP;
return newTotalMPAccrued;
}
function _liveRewardIndex() internal view returns (uint256, uint256) {
uint256 shares = _totalShares();
if (shares == 0) {
return (0, rewardIndex);
}
uint256 currentTime = block.timestamp;
uint256 applicableTime = rewardEndTime > currentTime ? currentTime : rewardEndTime;
uint256 elapsedTime = applicableTime - lastRewardTime;
if (elapsedTime == 0) {
return (0, rewardIndex);
}
uint256 accruedRewards = _calculatePendingRewards();
if (accruedRewards == 0) {
return (0, rewardIndex);
}
uint256 newRewardIndex = rewardIndex + Math.mulDiv(accruedRewards, SCALE_FACTOR, shares);
return (accruedRewards, newRewardIndex);
}
function _calculatePendingRewards() internal view returns (uint256) {
if (rewardEndTime <= rewardStartTime) {
// No active reward period
return 0;
}
uint256 currentTime = block.timestamp < rewardEndTime ? block.timestamp : rewardEndTime;
if (currentTime <= lastRewardTime) {
// No new rewards have accrued since lastRewardTime
return 0;
}
uint256 timeElapsed = currentTime - lastRewardTime;
uint256 duration = rewardEndTime - rewardStartTime;
if (duration == 0) {
// Prevent division by zero
return 0;
}
uint256 accruedRewards = Math.mulDiv(timeElapsed, rewardAmount, duration);
return accruedRewards;
}
function _getVaultPendingMP(VaultData storage vault) internal view returns (uint256) {
if (block.timestamp == vault.lastMPUpdateTime) {
return 0;
}
if (vault.maxMP == 0 || vault.stakedBalance == 0) {
return 0;
}
uint256 deltaMP = _calculateAccrual(
vault.stakedBalance, vault.mpAccrued, vault.maxMP, vault.lastMPUpdateTime, block.timestamp
);
return deltaMP;
}
function _mpBalanceOf(address vaultAddress) internal view returns (uint256) {
VaultData storage vault = vaultData[vaultAddress];
return vault.mpAccrued + _getVaultPendingMP(vault);
}
function _vaultPendingRewards(VaultData storage vault) internal view returns (uint256) {
uint256 newRewardIndex;
(, newRewardIndex) = _liveRewardIndex();
uint256 accountShares = vault.stakedBalance + vault.mpStaked;
uint256 deltaRewardIndex = newRewardIndex - vault.rewardIndex;
return (accountShares * deltaRewardIndex) / SCALE_FACTOR;
}
/**
* @notice Authorizes contract upgrades via UUPS.
* @dev This function is only callable by the owner.
*/
function _authorizeUpgrade(address) internal view override {
_checkOwner();
}
/*//////////////////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Get the vaults owned by a user
* @param account The address of the user
* @return The vaults owned by the user
*/
function getAccountVaults(address account) external view returns (address[] memory) {
return vaults[account];
}
/**
* @notice Returns the total multiplier points accrued in the system.
* @return The total multiplier points accrued in the system.
*/
function totalMP() external view returns (uint256) {
return _liveTotalMP();
}
/**
* @notice Returns the total shares in the system.
* @dev Total shares are total staked tokens and total multiplier points staked.
* @return The total shares in the system.
*/
function totalShares() external view returns (uint256) {
return _totalShares();
}
/**
* @notice Returns total rewards supply in the system.
* @return Total rewards supply in the system.
*/
function totalRewardsSupply() external view returns (uint256) {
return totalRewardsAccrued + _calculatePendingRewards();
}
/**
* @notice Returns the total shares of a given vault.
* @return The total vault shares
*/
function vaultShares(address vaultAddress) external view returns (uint256) {
return _vaultShares(vaultAddress);
}
/**
* @notice Returns vault data for a given vault address.
* @return Vault data for the given vault address
*/
function getVault(address vaultAddress) external view returns (VaultData memory) {
return vaultData[vaultAddress];
}
/**
* @notice Returns the staked balance of a vault.
* @param vaultAddress The address of the vault.
*/
function getStakedBalance(address vaultAddress) external view returns (uint256) {
return vaultData[vaultAddress].stakedBalance;
}
/**
* @notice Returns the rewards balance of a vault.
* @return Rewards balance of the vault.
*/
function rewardsBalanceOf(address vaultAddress) public view returns (uint256) {
VaultData storage vault = vaultData[vaultAddress];
return vault.rewardsAccrued + _vaultPendingRewards(vault);
}
/**
* @notice Returns the multiplier points balance of a vault.
* @return Multiplier points balance of the vault.
*/
function mpBalanceOf(address vaultAddress) external view returns (uint256) {
return _mpBalanceOf(vaultAddress);
}
/**
* @notice Returns staked multiplier points of a vault.
* @return Staked multiplier points of the vault.
*/
function mpStakedOf(address vaultAddress) external view returns (uint256) {
VaultData storage vault = vaultData[vaultAddress];
return vault.mpStaked;
}
/**
* @notice Get the total multiplier points for a user
* @dev Iterates over all vaults owned by the user and sums the multiplier points
* @param account The address of the user
* @return The total multiplier points for the user
*/
function mpBalanceOfAccount(address account) external view returns (uint256) {
address[] memory accountVaults = vaults[account];
uint256 accountTotalMP = 0;
for (uint256 i = 0; i < accountVaults.length; i++) {
VaultData storage vault = vaultData[accountVaults[i]];
accountTotalMP += vault.mpAccrued + _getVaultPendingMP(vault);
}
return accountTotalMP;
}
/**
* @notice Get the total maximum multiplier points for a user
* @dev Iterates over all vaults owned by the user and sums the maximum multiplier points
* @param account The address of the user
* @return The total maximum multiplier points for the user
*/
function getAccountTotalMaxMP(address account) external view returns (uint256) {
address[] memory accountVaults = vaults[account];
uint256 accountTotalMaxMP = 0;
for (uint256 i = 0; i < accountVaults.length; i++) {
accountTotalMaxMP += vaultData[accountVaults[i]].maxMP;
}
return accountTotalMaxMP;
}
/**
* @notice Get the total staked balance for a user
* @dev Iterates over all vaults owned by the user and sums the staked balances
* @param account The address of the user
* @return The total staked balance for the user
*/
function getAccountTotalStakedBalance(address account) external view returns (uint256) {
address[] memory accountVaults = vaults[account];
uint256 accountTotalStake = 0;
for (uint256 i = 0; i < accountVaults.length; i++) {
accountTotalStake += vaultData[accountVaults[i]].stakedBalance;
}
return accountTotalStake;
}
/**
* @notice Returns the rewards balance of an account.
* @dev Iterates over all vaults owned by the account and sums the rewards.
* @return Rewards balance of the account.
*/
function rewardsBalanceOfAccount(address account) external view returns (uint256) {
address[] memory accountVaults = vaults[account];
uint256 accountTotalRewards = 0;
for (uint256 i = 0; i < accountVaults.length; i++) {
accountTotalRewards += rewardsBalanceOf(accountVaults[i]);
}
return accountTotalRewards;
}
}

View File

@@ -34,6 +34,10 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
/// @notice Emitted when migration failed
error StakeVault__MigrationFailed();
/*//////////////////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////////////////*/
/// @notice Staking token - must be set immutable due to codehash check in StakeManager
IERC20 public immutable STAKING_TOKEN;
/// @notice Stake manager proxy contract
@@ -55,6 +59,10 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
_;
}
/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Initializes the contract with the staking token address.
* @dev The staking token address is immutable and cannot be changed after deployment.
@@ -66,6 +74,10 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
_disableInitializers();
}
/*//////////////////////////////////////////////////////////////////////////
USER-FACING FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Initializes the contract with the owner and the stake manager.
* @dev Ensures that the stake manager implementation is trusted.
@@ -96,14 +108,6 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
stakeManager.registerVault();
}
/**
* @notice Returns the address of the current owner.
* @return The address of the owner.
*/
function owner() public view override(OwnableUpgradeable, IStakeVault) returns (address) {
return super.owner();
}
/**
* @notice Stake tokens for a specified time.
* @dev This function is only callable by the owner.
@@ -201,6 +205,21 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
}
}
/**
* @notice Migrate all funds to a new vault.
* @dev This function is only callable by the owner.
* @dev This function is only callable if the current stake manager is trusted.
* @dev Reverts when the stake manager reverts or the funds can't be transferred.
* @param migrateTo The address of the new vault.
*/
function migrateToVault(address migrateTo) external onlyOwner onlyTrustedStakeManager {
stakeManager.migrateToVault(migrateTo);
bool success = STAKING_TOKEN.transfer(migrateTo, STAKING_TOKEN.balanceOf(address(this)));
if (!success) {
revert StakeVault__MigrationFailed();
}
}
/**
* @notice Withdraw tokens from the contract.
* @dev This function is only callable by the owner.
@@ -246,6 +265,10 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
return _token.balanceOf(address(this));
}
/*//////////////////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Stakes tokens for a specified time.
* @dev Reverts if the staking token transfer fails.
@@ -290,14 +313,6 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
_token.transfer(_destination, _amount);
}
/**
* @notice Returns the amount of tokens staked by the vault.
* @return The amount of tokens staked.
*/
function amountStaked() public view returns (uint256) {
return stakeManager.getStakedBalance(address(this));
}
/**
* @notice Allows vaults to exit the system in case of emergency or the system is rigged.
* @param _destination The address to receive the funds.
@@ -328,18 +343,23 @@ contract StakeVault is IStakeVault, Initializable, OwnableUpgradeable {
return stakeManagerImplementationAddress == stakeManager.implementation();
}
/*//////////////////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Migrate all funds to a new vault.
* @dev This function is only callable by the owner.
* @dev This function is only callable if the current stake manager is trusted.
* @dev Reverts when the stake manager reverts or the funds can't be transferred.
* @param migrateTo The address of the new vault.
* @notice Returns the address of the current owner.
* @return The address of the owner.
*/
function migrateToVault(address migrateTo) external onlyOwner onlyTrustedStakeManager {
stakeManager.migrateToVault(migrateTo);
bool success = STAKING_TOKEN.transfer(migrateTo, STAKING_TOKEN.balanceOf(address(this)));
if (!success) {
revert StakeVault__MigrationFailed();
}
function owner() public view override(OwnableUpgradeable, IStakeVault) returns (address) {
return super.owner();
}
/**
* @notice Returns the amount of tokens staked by the vault.
* @return The amount of tokens staked.
*/
function amountStaked() public view returns (uint256) {
return stakeManager.getStakedBalance(address(this));
}
}

View File

@@ -26,19 +26,27 @@ import { StakeVault } from "./StakeVault.sol";
contract VaultFactory is Ownable {
/// @notice Emitted when the provided `StakeManager` address is zero.
error VaultFactory__InvalidStakeManagerAddress();
/// @notice Emitted when a new vault is created.
event VaultCreated(address indexed vault, address indexed owner);
/// @notice Emitted when the `StakeManager` contract address is changed.
event StakeManagerAddressChanged(address indexed newStakeManagerAddress);
/// @notice Emitted when the `StakeVault` implementation contract address is changed.
event VaultImplementationChanged(address indexed newVaultImplementation);
/*//////////////////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////////////////*/
/// @dev Address of the `StakeManager` contract instance.
address public stakeManager;
/// @dev Address of the `StakeVault` implementation contract.
address public vaultImplementation;
/*//////////////////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Creates a new `VaultFactory` contract.
* @dev Reverts if the provided `_stakeManager` address is zero.
@@ -54,6 +62,10 @@ contract VaultFactory is Ownable {
vaultImplementation = _vaultImplementation;
}
/*//////////////////////////////////////////////////////////////////////////
USER-FACING FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/**
* @notice Sets the `StakeManager` contract address.
* @dev Only the owner can call this function.

View File

@@ -6,13 +6,24 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { INFTMetadataGenerator } from "../interfaces/INFTMetadataGenerator.sol";
/**
* @title BaseNFTMetadataGenerator
*
* @dev Base contract for generating NFT metadata
*/
abstract contract BaseNFTMetadataGenerator is INFTMetadataGenerator, Ownable {
/// @notice NFT contract address
address public nft;
constructor(address _nft) Ownable(msg.sender) {
nft = _nft;
}
/**
* @notice Generates metadata for the NFT based on the owner's address and balance
* @param account The address of the NFT owner
* @param balance The balance of the NFT owner
*/
function generate(address account, uint256 balance) external view returns (string memory) {
string memory baseName = "KarmaNFT ";
string memory baseDescription = "This is a KarmaNFT for address ";
@@ -32,5 +43,10 @@ abstract contract BaseNFTMetadataGenerator is INFTMetadataGenerator, Ownable {
return string(abi.encodePacked("data:application/json;base64,", jsonBase64));
}
/**
* @notice Generates the image URI for the NFT based on the owner's address and balance
* @param account The address of the NFT owner
* @param balance The balance of the NFT owner
*/
function generateImageURI(address account, uint256 balance) internal view virtual returns (string memory);
}

View File

@@ -5,8 +5,15 @@ import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { BaseNFTMetadataGenerator } from "./BaseNFTMetadataGenerator.sol";
/**
* @title NFTMetadataGeneratorSVG
* @notice NFT metadata generator contract that generates SVG image URIs.
* @dev Generates SVG image URIs with a balance number.
*/
contract NFTMetadataGeneratorSVG is BaseNFTMetadataGenerator {
/// @notice SVG image prefix
string public imagePrefix = "";
/// @notice SVG image suffix
string public imageSuffix = "";
constructor(address nft, string memory _imagePrefix, string memory _imageSuffix) BaseNFTMetadataGenerator(nft) {
@@ -14,11 +21,21 @@ contract NFTMetadataGeneratorSVG is BaseNFTMetadataGenerator {
imageSuffix = _imageSuffix;
}
/**
* @notice Sets the SVG image prefix and suffix
* @dev Only the owner can call this function
* @param _imagePrefix The SVG image prefix
* @param _imageSuffix The SVG image suffix
*/
function setImageStrings(string memory _imagePrefix, string memory _imageSuffix) external onlyOwner {
imagePrefix = _imagePrefix;
imageSuffix = _imageSuffix;
}
/**
* @notice Generates the image URI for the NFT based on the owner's address and balance
* @param balance The balance of the NFT owner
*/
function generateImageURI(address, uint256 balance) internal view override returns (string memory) {
string memory text = Strings.toString(balance / 1e18);
bytes memory svg = abi.encodePacked(imagePrefix, text, imageSuffix);

View File

@@ -4,8 +4,14 @@ pragma solidity ^0.8.26;
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { BaseNFTMetadataGenerator } from "./BaseNFTMetadataGenerator.sol";
/**
* @title NFTMetadataGeneratorURL
* @notice NFT metadata generator contract that generates image URI based on account address.
*/
contract NFTMetadataGeneratorURL is BaseNFTMetadataGenerator {
/// @notice URL prefix
string public urlPrefix;
/// @notice URL suffix
string public urlSuffix;
constructor(address nft, string memory _urlPrefix, string memory _urlSuffix) BaseNFTMetadataGenerator(nft) {
@@ -13,11 +19,21 @@ contract NFTMetadataGeneratorURL is BaseNFTMetadataGenerator {
urlSuffix = _urlSuffix;
}
/**
* @notice Sets the URL prefix and suffix
* @dev Only the owner can call this function
* @param _urlPrefix The URL prefix
* @param _urlSuffix The URL suffix
*/
function setURLStrings(string memory _urlPrefix, string memory _urlSuffix) external onlyOwner {
urlPrefix = _urlPrefix;
urlSuffix = _urlSuffix;
}
/**
* @notice Generates the image URI for the NFT based on the owner's address and balance
* @param account The address of the NFT owner
*/
function generateImageURI(address account, uint256) internal view override returns (string memory) {
return string(abi.encodePacked(urlPrefix, Strings.toHexString(account), urlSuffix));
}

View File

@@ -41,7 +41,7 @@ contract RewardsStreamerMPTest is StakeMath, Test {
(RewardsStreamerMP stakeManager, VaultFactory _vaultFactory, DeploymentConfig deploymentConfig) =
deployment.run();
(address _deployer, address _stakingToken) = deploymentConfig.activeNetworkConfig();
(address _deployer, address _stakingToken,) = deploymentConfig.activeNetworkConfig();
streamer = stakeManager;
stakingToken = MockToken(_stakingToken);
@@ -169,7 +169,7 @@ contract RewardsStreamerMPTest is StakeMath, Test {
function _upgradeStakeManager() internal {
UpgradeRewardsStreamerMPScript upgrade = new UpgradeRewardsStreamerMPScript();
upgrade.run(admin, IStakeManagerProxy(address(streamer)));
upgrade.runWithAdminAndProxy(admin, IStakeManagerProxy(address(streamer)));
}
function _setRewards(uint256 amount, uint256 period) internal {

View File

@@ -36,7 +36,7 @@ contract StakeVaultTest is Test {
DeployRewardsStreamerMPScript deployment = new DeployRewardsStreamerMPScript();
(RewardsStreamerMP stakeManager, VaultFactory _vaultFactory, DeploymentConfig deploymentConfig) =
deployment.run();
(, address _stakingToken) = deploymentConfig.activeNetworkConfig();
(, address _stakingToken,) = deploymentConfig.activeNetworkConfig();
streamer = stakeManager;
stakingToken = MockToken(_stakingToken);