Skip to content

Commit

Permalink
feat(Karma): make karma upgradeable
Browse files Browse the repository at this point in the history
  • Loading branch information
gravityblast authored and 0x-r4bbit committed Feb 28, 2025
1 parent ef3abad commit aa3442b
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 98 deletions.
88 changes: 52 additions & 36 deletions .gas-report

Large diffs are not rendered by default.

74 changes: 41 additions & 33 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -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, μ: 521175, ~: 515988)
FuzzTests:testFuzz_EmergencyExit(uint256,uint256) (runs: 1000, μ: 510620, ~: 501981)
FuzzTests:testFuzz_Rewards(uint256,uint256,uint256,uint16,uint16) (runs: 1000, μ: 604739, ~: 605984)
FuzzTests:testFuzz_Stake(uint256,uint256) (runs: 1000, μ: 404072, ~: 395433)
FuzzTests:testFuzz_Unstake(uint256,uint256,uint16,uint256) (runs: 1000, μ: 530269, ~: 529967)
FuzzTests:testFuzz_AccrueMP(uint256,uint256,uint16) (runs: 1000, μ: 521149, ~: 515988)
FuzzTests:testFuzz_EmergencyExit(uint256,uint256) (runs: 1000, μ: 510563, ~: 501981)
FuzzTests:testFuzz_Rewards(uint256,uint256,uint256,uint16,uint16) (runs: 1000, μ: 609644, ~: 610896)
FuzzTests:testFuzz_Stake(uint256,uint256) (runs: 1000, μ: 404015, ~: 395433)
FuzzTests:testFuzz_Unstake(uint256,uint256,uint16,uint256) (runs: 1000, μ: 530259, ~: 529949)
IntegrationTest:testStakeFoo() (gas: 1403381)
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)
KarmaMintAllowanceTest:testAddKarmaDistributorOnlyOwner() (gas: 361283)
KarmaMintAllowanceTest:testBalanceOf() (gas: 443874)
KarmaMintAllowanceTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49544)
KarmaMintAllowanceTest:testMintAllowance_Available() (gas: 355546)
KarmaMintAllowanceTest:testMintAllowance_NotAvailable() (gas: 355549)
KarmaMintAllowanceTest:testMintOnlyOwner() (gas: 397631)
KarmaMintAllowanceTest:testMint_Ok() (gas: 425475)
KarmaMintAllowanceTest:testMint_RevertWithAllowanceExceeded() (gas: 405471)
KarmaMintAllowanceTest:testRemoveKarmaDistributorOnlyOwner() (gas: 88715)
KarmaMintAllowanceTest:testRemoveUnknownKarmaDistributor() (gas: 41419)
KarmaMintAllowanceTest:testTotalSupply() (gas: 351996)
KarmaMintAllowanceTest:testTransfersNotAllowed() (gas: 40205)
KarmaNFTTest:testApproveNotAllowed() (gas: 10500)
KarmaNFTTest:testGetApproved() (gas: 10523)
KarmaNFTTest:testIsApprovedForAll() (gas: 10698)
Expand All @@ -35,20 +35,28 @@ KarmaNFTTest:testSetMetadataGenerator() (gas: 966687)
KarmaNFTTest:testSetMetadataGeneratorRevert() (gas: 963218)
KarmaNFTTest:testTokenURI() (gas: 102963)
KarmaNFTTest:testTransferNotAllowed() (gas: 10715)
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)
KarmaOwnershipTest:testAddKarmaDistributorOnlyOwner() (gas: 361271)
KarmaOwnershipTest:testBalanceOf() (gas: 443844)
KarmaOwnershipTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49522)
KarmaOwnershipTest:testInitialOwner() (gas: 17603)
KarmaOwnershipTest:testMintOnlyOwner() (gas: 397595)
KarmaOwnershipTest:testOwnershipTransfer() (gas: 98118)
KarmaOwnershipTest:testRemoveKarmaDistributorOnlyOwner() (gas: 88680)
KarmaOwnershipTest:testRemoveUnknownKarmaDistributor() (gas: 41401)
KarmaOwnershipTest:testTotalSupply() (gas: 351966)
KarmaOwnershipTest:testTransfersNotAllowed() (gas: 40182)
KarmaTest:testAddKarmaDistributorOnlyOwner() (gas: 361271)
KarmaTest:testBalanceOf() (gas: 443844)
KarmaTest:testBalanceOfWithNoSystemTotalKarma() (gas: 49588)
KarmaTest:testMintOnlyOwner() (gas: 397595)
KarmaTest:testRemoveKarmaDistributorOnlyOwner() (gas: 88658)
KarmaTest:testRemoveUnknownKarmaDistributor() (gas: 41401)
KarmaTest:testTotalSupply() (gas: 351966)
KarmaTest:testTransfersNotAllowed() (gas: 40227)
LeaveTest:test_LeaveShouldProperlyUpdateAccounting() (gas: 8942756)
LeaveTest:test_RevertWhenStakeManagerIsTrusted() (gas: 350352)
LeaveTest:test_TrustNewStakeManager() (gas: 8995615)
LockTest:test_LockFailsWithInvalidPeriod(uint256) (runs: 1000, μ: 406915, ~: 406944)
LockTest:test_LockFailsWithInvalidPeriod(uint256) (runs: 1000, μ: 406918, ~: 406944)
LockTest:test_LockFailsWithNoStake() (gas: 114574)
LockTest:test_LockFailsWithZero() (gas: 367631)
LockTest:test_LockWithoutPriorLock() (gas: 465087)
Expand All @@ -67,12 +75,12 @@ NFTMetadataGeneratorSVGTest:testSetImageStringsRevert() (gas: 35804)
NFTMetadataGeneratorURLTest:testGenerateMetadata() (gas: 101558)
NFTMetadataGeneratorURLTest:testSetBaseURL() (gas: 49555)
NFTMetadataGeneratorURLTest:testSetBaseURLRevert() (gas: 35979)
RewardsStreamerMP_RewardsTest:testRewardsBalanceOf() (gas: 1309888)
RewardsStreamerMP_RewardsTest:testSetRewards() (gas: 219382)
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 56207)
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadDuration() (gas: 95985)
RewardsStreamerMP_RewardsTest:testRewardsBalanceOf() (gas: 1319704)
RewardsStreamerMP_RewardsTest:testSetRewards() (gas: 224290)
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadAmount() (gas: 61119)
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsBadDuration() (gas: 100897)
RewardsStreamerMP_RewardsTest:testSetRewards_RevertsNotAuthorized() (gas: 39295)
RewardsStreamerMP_RewardsTest:testTotalRewardsSupply() (gas: 746993)
RewardsStreamerMP_RewardsTest:testTotalRewardsSupply() (gas: 756809)
StakeTest:test_StakeMultipleAccounts() (gas: 591097)
StakeTest:test_StakeMultipleAccountsAndRewards() (gas: 599025)
StakeTest:test_StakeMultipleAccountsMPIncreasesMaxMPDoesNotChange() (gas: 1024300)
Expand Down
4 changes: 3 additions & 1 deletion certora/confs/Karma.conf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"loop_iter": "3",
"packages": [
"forge-std=lib/forge-std/src",
"@openzeppelin=lib/openzeppelin-contracts"
"@openzeppelin=lib/openzeppelin-contracts",
"@openzeppelin/contracts/utils=lib/openzeppelin-contracts/contracts/utils",
"@openzeppelin/contracts-upgradeable=lib/openzeppelin-contracts-upgradeable/contracts"
]
}
14 changes: 11 additions & 3 deletions script/DeployKarma.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@ pragma solidity ^0.8.26;
import { BaseScript } from "./Base.s.sol";
import { DeploymentConfig } from "./DeploymentConfig.s.sol";

import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

import { Karma } from "../src/Karma.sol";

contract DeployKarmaScript is BaseScript {
function run() public returns (Karma) {
function run() public returns (Karma, DeploymentConfig) {
DeploymentConfig deploymentConfig = new DeploymentConfig(broadcaster);
(address deployer,,) = deploymentConfig.activeNetworkConfig();

vm.startBroadcast(deployer);
address karma = address(new Karma());

// Deploy Karma logic contract
bytes memory initializeData = abi.encodeCall(Karma.initialize, (deployer));
address impl = address(new Karma());
// Create upgradeable proxy
address proxy = address(new ERC1967Proxy(impl, initializeData));

vm.stopBroadcast();

return Karma(karma);
return (Karma(proxy), deploymentConfig);
}
}
30 changes: 26 additions & 4 deletions src/Karma.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { Ownable, Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { Ownable2StepUpgradeable } from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import { IRewardDistributor } from "./interfaces/IRewardDistributor.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

Expand All @@ -11,7 +13,7 @@ import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableS
* @notice This contract allows for setting rewards for reward distributors.
* @dev Implementation of the Karma token
*/
contract Karma is ERC20, Ownable2Step {
contract Karma is Initializable, ERC20Upgradeable, Ownable2StepUpgradeable, UUPSUpgradeable {
using EnumerableSet for EnumerableSet.AddressSet;

error Karma__TransfersNotAllowed();
Expand Down Expand Up @@ -41,7 +43,19 @@ contract Karma is ERC20, Ownable2Step {
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////*/

constructor() ERC20(NAME, SYMBOL) Ownable(msg.sender) { }
constructor() {
_disableInitializers();
}

/**
* @notice Initializes the contract with the provided owner.
* @param _owner Address of the owner of the contract.
*/
function initialize(address _owner) public initializer {
__ERC20_init(NAME, SYMBOL);
__Ownable_init(_owner);
__UUPSUpgradeable_init();
}

/*//////////////////////////////////////////////////////////////////////////
USER-FACING FUNCTIONS
Expand Down Expand Up @@ -158,6 +172,14 @@ contract Karma is ERC20, Ownable2Step {
return externalSupply;
}

/**
* @notice Authorizes contract upgrades via UUPS.
* @dev This function is only callable by the owner.
*/
function _authorizeUpgrade(address) internal view override {
_checkOwner();
}

/*//////////////////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
Expand Down
38 changes: 18 additions & 20 deletions test/Karma.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,36 @@
pragma solidity ^0.8.26;

import { Test } from "forge-std/Test.sol";
import { DeployKarmaScript } from "../script/DeployKarma.s.sol";
import { DeploymentConfig } from "../script/DeploymentConfig.s.sol";
import { Karma } from "../src/Karma.sol";
import { KarmaDistributorMock } from "./mocks/KarmaDistributorMock.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

contract KarmaTest is Test {
Karma public karma;

address public owner = makeAddr("owner");
address public owner;
address public alice = makeAddr("alice");
address public bob = makeAddr("bob");

KarmaDistributorMock public distributor1;
KarmaDistributorMock public distributor2;

function setUp() public virtual {
vm.prank(owner);
karma = new Karma();
DeployKarmaScript karmaDeployment = new DeployKarmaScript();
(Karma _karma, DeploymentConfig deploymentConfig) = karmaDeployment.run();
karma = _karma;
(address deployer,,) = deploymentConfig.activeNetworkConfig();
owner = deployer;

distributor1 = new KarmaDistributorMock();
distributor2 = new KarmaDistributorMock();

vm.prank(owner);
vm.startBroadcast(owner);
karma.addRewardDistributor(address(distributor1));

vm.prank(owner);
karma.addRewardDistributor(address(distributor2));
vm.stopBroadcast();
}

function testAddKarmaDistributorOnlyOwner() public {
Expand Down Expand Up @@ -145,29 +149,23 @@ contract KarmaTest is Test {
}
}

contract KarmaOwnershipTest is Test {
Karma xpToken;

address owner = makeAddr("owner");
address alice = makeAddr("alice");

function setUp() public {
vm.prank(owner);
xpToken = new Karma();
contract KarmaOwnershipTest is KarmaTest {
function setUp() public override {
super.setUp();
}

function testInitialOwner() public view {
assertEq(xpToken.owner(), owner);
assertEq(karma.owner(), owner);
}

function testOwnershipTransfer() public {
vm.prank(owner);
xpToken.transferOwnership(alice);
assertEq(xpToken.owner(), owner);
karma.transferOwnership(alice);
assertEq(karma.owner(), owner);

vm.prank(alice);
xpToken.acceptOwnership();
assertEq(xpToken.owner(), alice);
karma.acceptOwnership();
assertEq(karma.owner(), alice);
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/RewardsStreamerMP.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ contract RewardsStreamerMPTest is StakeMath, Test {
stakingToken = MockToken(_stakingToken);
vaultFactory = _vaultFactory;
admin = _deployer;
karma = karmaDeployment.run();
(karma,) = karmaDeployment.run();

// set up reward distribution
vm.startPrank(admin);
Expand Down

0 comments on commit aa3442b

Please sign in to comment.