diff --git a/contracts/PushCore/PushCoreStorageV2.sol b/contracts/PushCore/PushCoreStorageV2.sol index 27e84828..c3d63d63 100644 --- a/contracts/PushCore/PushCoreStorageV2.sol +++ b/contracts/PushCore/PushCoreStorageV2.sol @@ -15,6 +15,9 @@ contract PushCoreStorageV2 { /** * Staking V2 state variables * */ + + //UNUSED STATE VARIABLES START HERE + mapping(address => uint256) public usersRewardsClaimed; uint256 public genesisEpoch; // Block number at which Stakig starts @@ -25,11 +28,13 @@ contract PushCoreStorageV2 { uint256 public constant epochDuration = 21 * 7156; // 21 * number of blocks per day(7156) ~ 20 day approx /// @notice Stores all the individual epoch rewards - mapping(uint256 => uint256) public epochRewards; + mapping(uint256 => uint256) public epochRewards; /// @notice Stores User's Fees Details - mapping(address => StakingTypes.UserFeesInfo) public userFeesInfo; + mapping(address => StakingTypes.UserFeesInfo) public userFeesInfo; /// @notice Stores the total staked weight at a specific epoch. - mapping(uint256 => uint256) public epochToTotalStakedWeight; + mapping(uint256 => uint256) public epochToTotalStakedWeight; + + //UNUSED STATE VARIABLES END HERE /** * Handling bridged information * diff --git a/contracts/PushStaking/PushStaking.sol b/contracts/PushStaking/PushStaking.sol index 9a50af85..83d3eaa5 100644 --- a/contracts/PushStaking/PushStaking.sol +++ b/contracts/PushStaking/PushStaking.sol @@ -9,6 +9,8 @@ import { Errors } from "../libraries/Errors.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; +import { GenericTypes } from "../libraries/DataTypes.sol"; +import { BaseHelper } from "../libraries/BaseHelper.sol"; contract PushStaking is Initializable, PushStakingStorage { using SafeERC20 for IERC20; @@ -16,8 +18,8 @@ contract PushStaking is Initializable, PushStakingStorage { event Staked(address indexed user, uint256 indexed amountStaked); event Unstaked(address indexed user, uint256 indexed amountUnstaked); event RewardsHarvested(address indexed user, uint256 indexed rewardAmount, uint256 fromEpoch, uint256 tillEpoch); - event NewSharesIssued(address indexed Wallet, uint256 indexed Shares); - event SharesRemoved(address indexed Wallet, uint256 indexed Shares); + event NewSharesIssued(address indexed wallet, uint256 indexed shares); + event SharesRemoved(address indexed wallet, uint256 indexed shares); event SharesDecreased(address indexed Wallet, uint256 indexed oldShares, uint256 newShares); function initialize(address _pushChannelAdmin, address _core, address _pushToken) public initializer { @@ -61,6 +63,10 @@ contract PushStaking is Initializable, PushStakingStorage { removeWalletShare(oldFoundation); } + function getEpochToWalletShare(address wallet, uint epoch) public view returns(uint){ + return walletShareInfo[wallet].epochToWalletShares[epoch]; + } + function initializeStake(uint256 _walletTotalShares) external { require(genesisEpoch == 0, "PushCoreV2::initializeStake: Already Initialized"); genesisEpoch = block.number; @@ -78,11 +84,9 @@ contract PushStaking is Initializable, PushStakingStorage { emit NewSharesIssued(FOUNDATION, sharesToBeAllocated); } - /* - * Fetching shares for a wallet - * 1. Internal helper function - * 2. helps in getting the shares to be assigned for a wallet based on params passed in this function - */ + /** + * @notice Calcultes the share amount based on requested shares and total shares + */ function getSharesAmount( uint256 _totalShares, GenericTypes.Percentage memory _percentage @@ -97,18 +101,13 @@ contract PushStaking is Initializable, PushStakingStorage { sharesToBeAllocated = (_percentage.percentageNumber * _totalShares) / ((100 * (10 ** _percentage.decimalPlaces)) - _percentage.percentageNumber); } + /** + * @notice allows Governance to add/increase wallet shares. + * @notice If a wallet has already has a share, then it acts as a "increase share" function, given that the percenatge passed + * @notice should be greater than the already assigned percentge. + * Emits NewSharesIssued + */ - /* - * Adding Wallet Share to a Wallet - * 1. addWalletShare(address wallet, uint256 percentageOfShares) - * 2. Can be called by governance. - * 3. Uses the formulae to derive the percent of shares to be assigned to a specific wallet - * 4. Updates WALLET_TOTAL_SHARES - * 5. Updates walletShareInfo mapping - * 6. Emits out an event. - * 7. If a wallet has already has a share, then it acts as a "increase share" function. And the percenatge passed - * should be greater than the already assigned percentge. - */ function addWalletShare(address _walletAddress, GenericTypes.Percentage memory _percentage) public onlyGovernance { if(_walletAddress == address(0)){ revert Errors.InvalidArgument_WrongAddress(_walletAddress); @@ -129,18 +128,20 @@ contract PushStaking is Initializable, PushStakingStorage { WALLET_TOTAL_SHARES = TotalShare + sharesToBeAllocated; emit NewSharesIssued(_walletAddress, sharesToBeAllocated); } - /* - * Removing Wallet Share from a Wallet - * 1. removes the shares from a wallet completely - * 2. Can be called by governance. - * 3. Updates WALLET_TOTAL_SHARES - * 4. Emits out an event. - */ + + /** + * @notice allows Governance to remove wallet shares. + * @notice shares to be removed are given back to FOUNDATION + * Emits SharesRemoved + */ function removeWalletShare(address _walletAddress) public onlyGovernance { if(_walletAddress == address(0) || _walletAddress == FOUNDATION) { revert Errors.InvalidArgument_WrongAddress(_walletAddress); } + if (block.number <= walletShareInfo[_walletAddress].lastStakedBlock + epochDuration) { + revert Errors.PushStaking_InvalidEpoch_LessThanExpected(); + } uint256 sharesToBeRemoved = walletShareInfo[_walletAddress].walletShare; _adjustWalletAndTotalStake(_walletAddress, 0, sharesToBeRemoved); _adjustWalletAndTotalStake(FOUNDATION, sharesToBeRemoved, 0); @@ -149,31 +150,40 @@ contract PushStaking is Initializable, PushStakingStorage { } function decreaseWalletShare( - address _walletAddress, - GenericTypes.Percentage memory _percentage - ) - external - onlyGovernance - { - uint256 sharesToBeRemoved = walletShareInfo[_walletAddress].walletShare; - removeWalletShare(_walletAddress); - addWalletShare(_walletAddress, _percentage); - emit SharesDecreased(_walletAddress, sharesToBeRemoved, walletShareInfo[_walletAddress].walletShare); - } + address _walletAddress, + GenericTypes.Percentage memory _percentage + ) + external + onlyGovernance + { + if(_walletAddress == address(0) || _walletAddress == FOUNDATION) { + revert Errors.InvalidArgument_WrongAddress(_walletAddress); + } + + uint currentEpoch = lastEpochRelative(genesisEpoch,block.number); + + uint256 currentShares = walletShareInfo[_walletAddress].walletShare; + uint256 sharesToBeAllocated = BaseHelper.calcPercentage(WALLET_TOTAL_SHARES, _percentage); - /* - *Reward Calculation for a Given Wallet - * 1. calculateWalletRewards(address wallet) - * 2. public helper function - * 3. Helps in calculating rewards for a specific wallet based on - * a. Their Wallet Share - * b. Total Wallet Shares - * c. Total Rewards available in WALLET_TOTAL_SHARES - * 4. Once calculated rewards for a sepcific wallet can be updated in WalletToRewards mapping - * 5. Reward can be calculated for wallets similar they are calculated for Token holders in a specific epoch. - * Reward for a Given Wallet X = ( Wallet Share of X / WALLET_TOTAL_SHARES) * WALLET_FEE_POOL - */ - //TODO logic yet to be finalized + if(sharesToBeAllocated >= currentShares){ + revert Errors.InvalidArg_MoreThanExpected(currentShares, sharesToBeAllocated); + } + uint256 sharesToBeRemoved = currentShares - sharesToBeAllocated; + walletShareInfo[_walletAddress].walletShare = sharesToBeAllocated; + walletShareInfo[_walletAddress].epochToWalletShares[currentEpoch] = sharesToBeAllocated; + + walletShareInfo[FOUNDATION].walletShare += sharesToBeRemoved; + walletShareInfo[FOUNDATION].epochToWalletShares[currentEpoch] += sharesToBeRemoved; + + emit SharesDecreased(_walletAddress, currentShares, sharesToBeAllocated); + } + + /** + * @notice calculates rewards for share holders, for any given epoch. + * @notice The rewards are calcluated based on -Their Wallet Share in that epoch, Total Wallet Shares in that epoch, + * @notice Total Rewards available in that epoch + * @dev Reward for a Given Wallet X in epoch i = ( Wallet Share of X in epoch i * Rewards in epoch i) / WALLET_TOTAL_SHARES in epoch i + */ function calculateWalletRewards(address _wallet, uint256 _epochId) public view returns (uint256) { return (walletShareInfo[_wallet].epochToWalletShares[_epochId] * epochRewardsForWallets[_epochId]) / epochToTotalShares[_epochId]; @@ -192,7 +202,7 @@ contract PushStaking is Initializable, PushStakingStorage { rewards = rewards + claimableReward; } - usersRewardsClaimed[msg.sender] = usersRewardsClaimed[msg.sender] + rewards; + walletRewardsClaimed[msg.sender] = walletRewardsClaimed[msg.sender] + rewards; // set the lastClaimedBlock to blocknumer at the end of `_tillEpoch` uint256 _epoch_to_block_number = genesisEpoch + _tillEpoch * epochDuration; walletShareInfo[msg.sender].lastClaimedBlock = _epoch_to_block_number; diff --git a/contracts/PushStaking/PushStakingStorage.sol b/contracts/PushStaking/PushStakingStorage.sol index b5ae1fe2..b3188e9f 100644 --- a/contracts/PushStaking/PushStakingStorage.sol +++ b/contracts/PushStaking/PushStakingStorage.sol @@ -1,40 +1,45 @@ pragma solidity ^0.8.20; -import { StakingTypes, GenericTypes } from "../libraries/DataTypes.sol"; +import { StakingTypes } from "../libraries/DataTypes.sol"; contract PushStakingStorage { - /** - * Staking V2 state variables * - */ - mapping(address => uint256) public usersRewardsClaimed; - + //State variables for Stakers uint256 public genesisEpoch; // Block number at which Stakig starts uint256 lastEpochInitialized; // The last EPOCH ID initialized with the respective epoch rewards uint256 lastTotalStakeEpochInitialized; // The last EPOCH ID initialized with the respective total staked weight + uint256 public previouslySetEpochRewards; // Amount of rewards set in last initialized epoch + uint256 public totalStakedAmount; // Total token weight staked in Protocol at any given time + + // State variables for Share holders uint256 walletLastEpochInitialized; // todo new variable uint256 walletLastTotalStakeEpochInitialized;//todo new variable - uint256 public totalStakedAmount; // Total token weight staked in Protocol at any given time - uint256 public previouslySetEpochRewards; // Amount of rewards set in last initialized epoch uint256 public walletPreviouslySetEpochRewards; //todo new variable - uint256 public constant epochDuration = 21 * 7156; // 21 * number of blocks per day(7156) ~ 20 day approx uint256 public WALLET_TOTAL_SHARES; //Total Shares + uint256 public constant epochDuration = 21 * 7156; // 21 * number of blocks per day(7156) ~ 20 day approx address public pushChannelAdmin; address public PUSH_TOKEN_ADDRESS; address public governance; address public core; address public FOUNDATION; + //Mappings for Stakers + ///@notice stores total rewards claimed by a user + mapping(address => uint256) public usersRewardsClaimed; /// @notice Stores all the individual epoch rewards for stakers mapping(uint256 => uint256) public epochRewardsForStakers; - /// @notice Stores all the individual epoch rewards for Wallet share holders - mapping(uint256 => uint256) public epochRewardsForWallets; /// @notice Stores User's Fees Details mapping(address => StakingTypes.UserFeesInfo) public userFeesInfo; - ///@notice stores Wallet share details for a given address - mapping(address => StakingTypes.WalletShareInfo) public walletShareInfo; /// @notice Stores the total staked weight at a specific epoch. mapping(uint256 => uint256) public epochToTotalStakedWeight; + + //Mappings for Share Holders + ///@notice stores total reward claimed by a wallet + mapping(address => uint256) public walletRewardsClaimed; + /// @notice Stores all the individual epoch rewards for Wallet share holders + mapping(uint256 => uint256) public epochRewardsForWallets; + ///@notice stores Wallet share details for a given address + mapping(address => StakingTypes.WalletShareInfo) public walletShareInfo; ///@notice stores the total shares in a specific epoch mapping(uint256 => uint256) public epochToTotalShares; } diff --git a/test/PushStaking/WalletSharesStaking/BaseWalletSharesStaking.t.sol b/test/PushStaking/WalletSharesStaking/BaseWalletSharesStaking.t.sol index ba290c83..8b805274 100644 --- a/test/PushStaking/WalletSharesStaking/BaseWalletSharesStaking.t.sol +++ b/test/PushStaking/WalletSharesStaking/BaseWalletSharesStaking.t.sol @@ -48,7 +48,7 @@ contract BaseWalletSharesStaking is BasePushStaking { aliceWalletShares + charlieWalletShares + tonyWalletShares; - assertEq(walletTotalShares, totalSharesSum); + assertEq(walletTotalShares, totalSharesSum,"wallet Share Sum"); } /** @@ -57,7 +57,7 @@ contract BaseWalletSharesStaking is BasePushStaking { function _validateEpochShares() internal { uint256 walletTotalShares = pushStaking.WALLET_TOTAL_SHARES(); for (uint256 i=genesisEpoch; i<=getCurrentEpoch(); ) { - assertLe(pushStaking.epochToTotalShares(i), walletTotalShares); + assertLe(pushStaking.epochToTotalShares(i), walletTotalShares, "Epoch Shares"); unchecked { i++; } diff --git a/test/PushStaking/WalletSharesStaking/unit_tests/ClaimRewards/ClaimRewards.t.sol b/test/PushStaking/WalletSharesStaking/unit_tests/ClaimRewards/ClaimRewards.t.sol index b3c58c33..3e7f9428 100644 --- a/test/PushStaking/WalletSharesStaking/unit_tests/ClaimRewards/ClaimRewards.t.sol +++ b/test/PushStaking/WalletSharesStaking/unit_tests/ClaimRewards/ClaimRewards.t.sol @@ -47,7 +47,7 @@ contract ClaimRewardsTest is BaseWalletSharesStaking { assertEq(bobStakedBlockBefore, bobStakedBlockAfter, "StakedBlock"); assertEq(bobClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewards = pushStaking.usersRewardsClaimed(actor.bob_channel_owner); + uint256 claimedRewards = pushStaking.walletRewardsClaimed(actor.bob_channel_owner); assertEq(balanceBobBefore + expectedRewards, pushToken.balanceOf(actor.bob_channel_owner), "Balance"); assertEq(expectedRewards, claimedRewards); @@ -94,7 +94,7 @@ contract ClaimRewardsTest is BaseWalletSharesStaking { assertEq(bobStakedBlockBefore, bobStakedBlockAfter, "StakedBlock"); assertEq(bobClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewards = pushStaking.usersRewardsClaimed(actor.bob_channel_owner); + uint256 claimedRewards = pushStaking.walletRewardsClaimed(actor.bob_channel_owner); assertEq(balanceBobBefore + expectedRewards, pushToken.balanceOf(actor.bob_channel_owner), "Balance"); assertEq(expectedRewards, claimedRewards); @@ -106,7 +106,7 @@ contract ClaimRewardsTest is BaseWalletSharesStaking { test_WhenUserClaims_in_DifferentEpoch(); uint256 balanceBobBefore = pushToken.balanceOf(actor.bob_channel_owner); - uint256 claimedRewardsBefore = pushStaking.usersRewardsClaimed(actor.bob_channel_owner); + uint256 claimedRewardsBefore = pushStaking.walletRewardsClaimed(actor.bob_channel_owner); (uint256 bobWalletSharesBefore, uint256 bobStakedBlockBefore, uint256 bobLastClaimedBlock) = pushStaking.walletShareInfo(actor.bob_channel_owner); @@ -129,7 +129,7 @@ contract ClaimRewardsTest is BaseWalletSharesStaking { assertEq(bobStakedBlockBefore, bobStakedBlockAfter2, "StakedBlock"); assertEq(bobClaimedBlockAfter2, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewards = pushStaking.usersRewardsClaimed(actor.bob_channel_owner); + uint256 claimedRewards = pushStaking.walletRewardsClaimed(actor.bob_channel_owner); assertEq(balanceBobBefore, pushToken.balanceOf(actor.bob_channel_owner), "Balance"); assertEq(claimedRewardsBefore, claimedRewards); } @@ -184,7 +184,7 @@ contract ClaimRewardsTest is BaseWalletSharesStaking { assertEq(bobStakedBlockBefore, bobStakedBlockAfter, "StakedBlock"); assertEq(bobClaimedBlockAfter2, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewards = pushStaking.usersRewardsClaimed(actor.bob_channel_owner); + uint256 claimedRewards = pushStaking.walletRewardsClaimed(actor.bob_channel_owner); assertEq(balanceBobBefore + expectedRewards, pushToken.balanceOf(actor.bob_channel_owner), "Balance"); assertEq(expectedRewards, claimedRewards); @@ -228,7 +228,7 @@ contract ClaimRewardsTest is BaseWalletSharesStaking { assertEq(adminStakedBlockBefore, adminStakedBlockAfter, "StakedBlock"); assertEq(adminClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewards = pushStaking.usersRewardsClaimed(actor.admin); + uint256 claimedRewards = pushStaking.walletRewardsClaimed(actor.admin); assertEq(balanceAdminBefore + expectedRewards, pushToken.balanceOf(actor.admin), "Balance"); assertEq(expectedRewards, claimedRewards); @@ -282,7 +282,7 @@ contract ClaimRewardsTest is BaseWalletSharesStaking { assertEq(bobStakedBlockBefore, bobStakedBlockAfter, "StakedBlock"); assertEq(bobClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewardsBob = pushStaking.usersRewardsClaimed(actor.bob_channel_owner); + uint256 claimedRewardsBob = pushStaking.walletRewardsClaimed(actor.bob_channel_owner); uint256 expectedRewardsBob = (coreProxy.WALLET_FEE_POOL() * 10) / 100; assertEq(balanceBobBefore + expectedRewardsBob, pushToken.balanceOf(actor.bob_channel_owner), "balanceBob"); @@ -292,7 +292,7 @@ contract ClaimRewardsTest is BaseWalletSharesStaking { assertEq(aliceStakedBlockBefore, aliceStakedBlockAfter, "StakedBlock"); assertEq(aliceClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewardsAlice = pushStaking.usersRewardsClaimed(actor.alice_channel_owner); + uint256 claimedRewardsAlice = pushStaking.walletRewardsClaimed(actor.alice_channel_owner); uint256 expectedRewardsAlice = coreProxy.WALLET_FEE_POOL() * 50 / 100; assertEq( diff --git a/test/PushStaking/WalletSharesStaking/unit_tests/DecreaseWalletShare/DecreaseWalletShare.t.sol b/test/PushStaking/WalletSharesStaking/unit_tests/DecreaseWalletShare/DecreaseWalletShare.t.sol index 199785f2..fcebd0fc 100644 --- a/test/PushStaking/WalletSharesStaking/unit_tests/DecreaseWalletShare/DecreaseWalletShare.t.sol +++ b/test/PushStaking/WalletSharesStaking/unit_tests/DecreaseWalletShare/DecreaseWalletShare.t.sol @@ -5,6 +5,7 @@ import { BaseWalletSharesStaking } from "../../BaseWalletSharesStaking.t.sol"; import { GenericTypes } from "../../../../../contracts/libraries/DataTypes.sol"; import {console2} from "forge-std/console2.sol"; import { Errors } from "contracts/libraries/Errors.sol"; +import { BaseHelper } from "contracts/libraries/BaseHelper.sol"; contract DecreaseWalletShareTest is BaseWalletSharesStaking { @@ -32,27 +33,31 @@ contract DecreaseWalletShareTest is BaseWalletSharesStaking { function test_Revertwhen_InvalidPercentage() public validateShareInvariants { changePrank(actor.admin); GenericTypes.Percentage memory percentAllocationZero = GenericTypes.Percentage({ percentageNumber: 0, decimalPlaces: 0 }); - vm.expectRevert(abi.encodeWithSelector(Errors.InvalidArg_MoreThanExpected.selector, 99, 0)); + vm.expectRevert(abi.encodeWithSelector(Errors.InvalidArg_MoreThanExpected.selector, 0, 0)); pushStaking.decreaseWalletShare(actor.bob_channel_owner, percentAllocationZero); GenericTypes.Percentage memory percentAllocationHundred = GenericTypes.Percentage({ percentageNumber: 100, decimalPlaces: 0 }); - vm.expectRevert(abi.encodeWithSelector(Errors.InvalidArg_MoreThanExpected.selector, 99, 100)); + uint256 calculatedShares = BaseHelper.calcPercentage(pushStaking.WALLET_TOTAL_SHARES(), percentAllocationHundred); + vm.expectRevert(abi.encodeWithSelector(Errors.InvalidArg_MoreThanExpected.selector, 0, calculatedShares)); pushStaking.decreaseWalletShare(actor.bob_channel_owner, percentAllocationHundred); } function test_Revertwhen_Percentage_GE_Allocated() public validateShareInvariants { changePrank(actor.admin); GenericTypes.Percentage memory percentAllocation1 = GenericTypes.Percentage({ percentageNumber: 20, decimalPlaces: 0 }); - pushStaking.decreaseWalletShare(actor.bob_channel_owner, percentAllocation1); + pushStaking.addWalletShare(actor.bob_channel_owner, percentAllocation1); + (uint256 bobWalletShares,,) = pushStaking.walletShareInfo(actor.bob_channel_owner); // revert when new allocation is equal to already allocated shares GenericTypes.Percentage memory percentAllocation2 = GenericTypes.Percentage({ percentageNumber: 20, decimalPlaces: 0 }); - vm.expectRevert(abi.encodeWithSelector(Errors.InvalidArg_MoreThanExpected.selector, 99, 20)); + uint256 calculatedShares = BaseHelper.calcPercentage(pushStaking.WALLET_TOTAL_SHARES(), percentAllocation2); + vm.expectRevert(abi.encodeWithSelector(Errors.InvalidArg_MoreThanExpected.selector, bobWalletShares, calculatedShares)); pushStaking.decreaseWalletShare(actor.bob_channel_owner, percentAllocation2); // revert when new allocation is greater than already allocated shares GenericTypes.Percentage memory percentAllocation3 = GenericTypes.Percentage({ percentageNumber: 30, decimalPlaces: 0 }); - vm.expectRevert(abi.encodeWithSelector(Errors.InvalidArg_MoreThanExpected.selector, 99, 30)); + uint256 calculatedShares2 = BaseHelper.calcPercentage(pushStaking.WALLET_TOTAL_SHARES(), percentAllocation3); + vm.expectRevert(abi.encodeWithSelector(Errors.InvalidArg_MoreThanExpected.selector, bobWalletShares, calculatedShares2)); pushStaking.decreaseWalletShare(actor.bob_channel_owner, percentAllocation3); } @@ -67,25 +72,29 @@ contract DecreaseWalletShareTest is BaseWalletSharesStaking { uint256 walletTotalSharesBefore = pushStaking.WALLET_TOTAL_SHARES(); uint256 epochToTotalSharesBefore = pushStaking.epochToTotalShares(getCurrentEpoch()); (uint256 charlieWalletSharesBefore, , uint256 charlieClaimedBlockBefore) = pushStaking.walletShareInfo(actor.charlie_channel_owner); + (uint256 bobWalletSharesBefore, ,) = pushStaking.walletShareInfo(actor.bob_channel_owner); (uint256 foundationWalletSharesBefore, , ) = pushStaking.walletShareInfo(actor.admin); // Decrease wallet shares of charlie from 50 to 40% GenericTypes.Percentage memory newPercentAllocationCharlie = GenericTypes.Percentage({ percentageNumber: 40, decimalPlaces: 0 }); - uint256 expectedCharlieShares = (newPercentAllocationCharlie.percentageNumber * charlieWalletSharesBefore) / percentAllocationFifty.percentageNumber; + uint256 expectedCharlieShares = (newPercentAllocationCharlie.percentageNumber * walletTotalSharesBefore ) / 100; + vm.expectEmit(true,true, false, true); emit SharesDecreased(actor.charlie_channel_owner, charlieWalletSharesBefore, expectedCharlieShares); pushStaking.decreaseWalletShare(actor.charlie_channel_owner, newPercentAllocationCharlie); uint256 walletTotalSharesAfter = pushStaking.WALLET_TOTAL_SHARES(); uint256 epochToTotalSharesAfter = pushStaking.epochToTotalShares(getCurrentEpoch()); (uint256 charlieWalletSharesAfter, , uint256 charlieClaimedBlockAfter) = pushStaking.walletShareInfo(actor.charlie_channel_owner); + (uint256 bobWalletSharesAfter, ,) = pushStaking.walletShareInfo(actor.bob_channel_owner); (uint256 foundationWalletSharesAfter, uint256 foundationStakedBlockAfter, ) = pushStaking.walletShareInfo(actor.admin); assertEq(charlieWalletSharesAfter, expectedCharlieShares); + assertEq(bobWalletSharesBefore, bobWalletSharesAfter,"bob shares"); assertEq(walletTotalSharesBefore, walletTotalSharesAfter); assertEq(epochToTotalSharesBefore, epochToTotalSharesAfter); assertEq(foundationWalletSharesAfter, foundationWalletSharesBefore + (charlieWalletSharesBefore - charlieWalletSharesAfter)); - assertEq(foundationStakedBlockAfter, block.number); - assertEq(charlieClaimedBlockAfter, charlieClaimedBlockBefore); + assertEq(foundationStakedBlockAfter, block.number,"foundation staked block"); + assertEq(charlieClaimedBlockAfter, charlieClaimedBlockBefore,"charlie claimed block"); } function test_DecreaseWalletShare_DifferentEpoch() public validateShareInvariants { @@ -102,24 +111,72 @@ contract DecreaseWalletShareTest is BaseWalletSharesStaking { uint256 walletTotalSharesBefore = pushStaking.WALLET_TOTAL_SHARES(); uint256 epochToTotalSharesBefore = pushStaking.epochToTotalShares(getCurrentEpoch()); (uint256 charlieWalletSharesBefore, , uint256 charlieClaimedBlockBefore) = pushStaking.walletShareInfo(actor.charlie_channel_owner); + (uint256 bobWalletSharesBefore, ,) = pushStaking.walletShareInfo(actor.bob_channel_owner); (uint256 foundationWalletSharesBefore, , ) = pushStaking.walletShareInfo(actor.admin); // Decrease wallet shares of charlie from 50 to 40% GenericTypes.Percentage memory newPercentAllocationCharlie = GenericTypes.Percentage({ percentageNumber: 40, decimalPlaces: 0 }); - uint256 expectedCharlieShares = (newPercentAllocationCharlie.percentageNumber * charlieWalletSharesBefore) / percentAllocationFifty.percentageNumber; + uint256 expectedCharlieShares = (newPercentAllocationCharlie.percentageNumber * walletTotalSharesBefore) / 100; + vm.expectEmit(true,true, false, true); emit SharesDecreased(actor.charlie_channel_owner, charlieWalletSharesBefore, expectedCharlieShares); pushStaking.decreaseWalletShare(actor.charlie_channel_owner, newPercentAllocationCharlie); uint256 walletTotalSharesAfter = pushStaking.WALLET_TOTAL_SHARES(); uint256 epochToTotalSharesAfter = pushStaking.epochToTotalShares(getCurrentEpoch()); (uint256 charlieWalletSharesAfter, , uint256 charlieClaimedBlockAfter) = pushStaking.walletShareInfo(actor.charlie_channel_owner); - (uint256 foundationWalletSharesAfter, uint256 foundationStakedBlockAfter, ) = pushStaking.walletShareInfo(actor.admin); - + (uint256 bobWalletSharesAfter, ,) = pushStaking.walletShareInfo(actor.bob_channel_owner); + (uint256 foundationWalletSharesAfter, , ) = pushStaking.walletShareInfo(actor.admin); + assertEq(charlieWalletSharesAfter, expectedCharlieShares); + assertEq(bobWalletSharesBefore, bobWalletSharesAfter,"bob shares"); + assertEq(expectedCharlieShares, pushStaking.getEpochToWalletShare(actor.charlie_channel_owner,2),"E2TW"); assertEq(walletTotalSharesBefore, walletTotalSharesAfter); assertEq(epochToTotalSharesBefore, epochToTotalSharesAfter); assertEq(foundationWalletSharesAfter, foundationWalletSharesBefore + (charlieWalletSharesBefore - charlieWalletSharesAfter)); - assertEq(foundationStakedBlockAfter, block.number); - assertEq(charlieClaimedBlockAfter, charlieClaimedBlockBefore); + assertEq(charlieClaimedBlockAfter, charlieClaimedBlockBefore,"charlie claimed block"); + } + + + function test_DecreaseWalletShare_3_Wallets() public validateShareInvariants { + addPool(1000); + changePrank(actor.admin); + // Add wallet shares of bob + GenericTypes.Percentage memory percentAllocation20 = GenericTypes.Percentage({ percentageNumber: 20, decimalPlaces: 0 }); + GenericTypes.Percentage memory percentAllocation25 = GenericTypes.Percentage({ percentageNumber: 25, decimalPlaces: 0 }); + GenericTypes.Percentage memory percentAllocation30 = GenericTypes.Percentage({ percentageNumber: 30, decimalPlaces: 0 }); + + pushStaking.addWalletShare(actor.alice_channel_owner, percentAllocation25); + pushStaking.addWalletShare(actor.charlie_channel_owner, percentAllocation30); + pushStaking.addWalletShare(actor.bob_channel_owner, percentAllocation20); + + roll(epochDuration * 2); + addPool(1000); + + uint256 walletTotalSharesBefore = pushStaking.WALLET_TOTAL_SHARES(); + uint256 epochToTotalSharesBefore = pushStaking.epochToTotalShares(getCurrentEpoch()); + (uint256 charlieWalletSharesBefore, ,) = pushStaking.walletShareInfo(actor.charlie_channel_owner); + (uint256 bobWalletSharesBefore, ,) = pushStaking.walletShareInfo(actor.bob_channel_owner); + (uint256 aliceWalletSharesBefore, ,) = pushStaking.walletShareInfo(actor.alice_channel_owner); + (uint256 foundationWalletSharesBefore, , ) = pushStaking.walletShareInfo(actor.admin); + + // Decrease wallet shares of charlie from 50 to 40% + GenericTypes.Percentage memory newPercentAllocation = GenericTypes.Percentage({ percentageNumber: 10, decimalPlaces: 0 }); + uint256 expectedBobShares = (newPercentAllocation.percentageNumber * walletTotalSharesBefore) / 100; + vm.expectEmit(true,true, false, true); + emit SharesDecreased(actor.bob_channel_owner, bobWalletSharesBefore, expectedBobShares); + pushStaking.decreaseWalletShare(actor.bob_channel_owner, newPercentAllocation); + + uint256 walletTotalSharesAfter = pushStaking.WALLET_TOTAL_SHARES(); + uint256 epochToTotalSharesAfter = pushStaking.epochToTotalShares(getCurrentEpoch()); + (uint256 charlieWalletSharesAfter, ,) = pushStaking.walletShareInfo(actor.charlie_channel_owner); + (uint256 bobWalletSharesAfter, ,) = pushStaking.walletShareInfo(actor.bob_channel_owner); + (uint256 aliceWalletSharesAfter, ,) = pushStaking.walletShareInfo(actor.alice_channel_owner); + (uint256 foundationWalletSharesAfter, , ) = pushStaking.walletShareInfo(actor.admin); + + assertEq(bobWalletSharesAfter, expectedBobShares); + assertEq(walletTotalSharesBefore, walletTotalSharesAfter); + assertEq(epochToTotalSharesBefore, epochToTotalSharesAfter); + assertEq(charlieWalletSharesBefore, charlieWalletSharesAfter); + assertEq(aliceWalletSharesBefore, aliceWalletSharesAfter); } } diff --git a/test/PushStaking/WalletSharesStaking/unit_tests/RemoveWalletShare/RemoveWalletShare.t.sol b/test/PushStaking/WalletSharesStaking/unit_tests/RemoveWalletShare/RemoveWalletShare.t.sol index b9161845..2727067d 100644 --- a/test/PushStaking/WalletSharesStaking/unit_tests/RemoveWalletShare/RemoveWalletShare.t.sol +++ b/test/PushStaking/WalletSharesStaking/unit_tests/RemoveWalletShare/RemoveWalletShare.t.sol @@ -27,7 +27,17 @@ contract RemoveWalletShareTest is BaseWalletSharesStaking { pushStaking.removeWalletShare(actor.admin); } - function test_RemoveWalletShare_SameEpoch() public validateShareInvariants { + function test_Revertwhen_RemovesBefore_1Epoch() public validateShareInvariants { + changePrank(actor.admin); + // Add wallet shares of bob + GenericTypes.Percentage memory percentAllocation = GenericTypes.Percentage({ percentageNumber: 20, decimalPlaces: 0 }); + pushStaking.addWalletShare(actor.bob_channel_owner, percentAllocation); + + vm.expectRevert(abi.encodeWithSelector(Errors.PushStaking_InvalidEpoch_LessThanExpected.selector)); + pushStaking.removeWalletShare(actor.bob_channel_owner); + } + + function test_RemoveWalletShare() public validateShareInvariants { addPool(1000); changePrank(actor.admin); // Add wallet shares of bob @@ -39,6 +49,8 @@ contract RemoveWalletShareTest is BaseWalletSharesStaking { (uint256 bobWalletSharesBefore, , uint256 bobClaimedBlockBefore) = pushStaking.walletShareInfo(actor.bob_channel_owner); (uint256 foundationWalletSharesBefore, , uint256 foundationClaimedBlockBefore) = pushStaking.walletShareInfo(actor.admin); + roll(epochDuration * 2); + emit SharesRemoved(actor.bob_channel_owner, bobWalletSharesBefore); // Remove wallet shares of bob pushStaking.removeWalletShare(actor.bob_channel_owner); @@ -58,7 +70,7 @@ contract RemoveWalletShareTest is BaseWalletSharesStaking { } function test_RemoveWalletShare_SameEpoch_Rewards() public validateShareInvariants { - test_RemoveWalletShare_SameEpoch(); + test_RemoveWalletShare(); uint256 bobRewards = pushStaking.calculateWalletRewards(actor.bob_channel_owner, getCurrentEpoch()); assertEq(bobRewards, 0); diff --git a/test/PushStaking/WalletSharesStaking/unit_tests/WalletShare/WalletShare.t.sol b/test/PushStaking/WalletSharesStaking/unit_tests/WalletShare/WalletShare.t.sol index 7eccf114..a18188f3 100644 --- a/test/PushStaking/WalletSharesStaking/unit_tests/WalletShare/WalletShare.t.sol +++ b/test/PushStaking/WalletSharesStaking/unit_tests/WalletShare/WalletShare.t.sol @@ -35,7 +35,7 @@ contract WalletShareTest is BaseWalletSharesStaking { assertEq(adminStakedBlockBefore, adminStakedBlockAfter, "StakedBlock"); assertEq(adminClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewards = pushStaking.usersRewardsClaimed(actor.admin); + uint256 claimedRewards = pushStaking.walletRewardsClaimed(actor.admin); uint256 expectedRewards = (coreProxy.WALLET_FEE_POOL() * 80) / 100; assertEq(balanceAdminBefore + expectedRewards, pushToken.balanceOf(actor.admin), "Balance"); assertEq(expectedRewards, claimedRewards); @@ -78,7 +78,7 @@ contract WalletShareTest is BaseWalletSharesStaking { assertEq(bobStakedBlockBefore, bobStakedBlockAfter, "StakedBlock"); assertEq(bobClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewards = pushStaking.usersRewardsClaimed(actor.bob_channel_owner); + uint256 claimedRewards = pushStaking.walletRewardsClaimed(actor.bob_channel_owner); uint256 expectedRewards = (coreProxy.WALLET_FEE_POOL() * 20) / 100; assertEq(balanceBobBefore + expectedRewards, pushToken.balanceOf(actor.bob_channel_owner), "Balance"); assertEq(expectedRewards, claimedRewards); @@ -109,7 +109,7 @@ contract WalletShareTest is BaseWalletSharesStaking { assertEq(bobStakedBlockBefore, bobStakedBlockAfter, "StakedBlock"); assertEq(bobClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewardsBob = pushStaking.usersRewardsClaimed(actor.bob_channel_owner); + uint256 claimedRewardsBob = pushStaking.walletRewardsClaimed(actor.bob_channel_owner); uint256 expectedRewardsBob = (coreProxy.WALLET_FEE_POOL() * 10) / 100; assertEq(balanceBobBefore + expectedRewardsBob, pushToken.balanceOf(actor.bob_channel_owner), "balanceBob"); @@ -119,7 +119,7 @@ contract WalletShareTest is BaseWalletSharesStaking { assertEq(aliceStakedBlockBefore, aliceStakedBlockAfter, "StakedBlock"); assertEq(aliceClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - uint256 claimedRewardsAlice = pushStaking.usersRewardsClaimed(actor.alice_channel_owner); + uint256 claimedRewardsAlice = pushStaking.walletRewardsClaimed(actor.alice_channel_owner); uint256 expectedRewardsAlice = coreProxy.WALLET_FEE_POOL() * 50 / 100; assertEq( balanceAliceBefore + expectedRewardsAlice, pushToken.balanceOf(actor.alice_channel_owner), "balanceAlice" @@ -157,7 +157,7 @@ contract WalletShareTest is BaseWalletSharesStaking { // assertEq(bobStakedBlockBefore, bobStakedBlockAfter, "StakedBlock"); // assertEq(bobClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - // uint256 claimedRewardsBob = pushStaking.usersRewardsClaimed(actor.bob_channel_owner); + // uint256 claimedRewardsBob = pushStaking.walletRewardsClaimed(actor.bob_channel_owner); // uint256 expectedRewardsBob = (coreProxy.WALLET_FEE_POOL() * 10) / 100; // assertEq(balanceBobBefore + expectedRewardsBob, pushToken.balanceOf(actor.bob_channel_owner), "balanceBob"); @@ -167,7 +167,7 @@ contract WalletShareTest is BaseWalletSharesStaking { // assertEq(aliceStakedBlockBefore, aliceStakedBlockAfter, "StakedBlock"); // assertEq(aliceClaimedBlockAfter, genesisEpoch + (getCurrentEpoch() - 1) * epochDuration, "ClaimedBlock"); - // uint256 claimedRewardsAlice = pushStaking.usersRewardsClaimed(actor.alice_channel_owner); + // uint256 claimedRewardsAlice = pushStaking.walletRewardsClaimed(actor.alice_channel_owner); // uint256 expectedRewardsAlice = coreProxy.WALLET_FEE_POOL() * 50 / 100; // assertEq( @@ -217,6 +217,7 @@ contract WalletShareTest is BaseWalletSharesStaking { pushStaking.walletShareInfo(actor.bob_channel_owner); (uint256 aliceWalletSharesBefore,,) = pushStaking.walletShareInfo(actor.alice_channel_owner); (uint256 foundationWalletSharesBefore,,) = pushStaking.walletShareInfo(actor.admin); + roll(epochDuration * 2); changePrank(actor.admin); pushStaking.removeWalletShare(actor.bob_channel_owner); @@ -228,7 +229,7 @@ contract WalletShareTest is BaseWalletSharesStaking { (uint256 foundationWalletSharesAfter,,) = pushStaking.walletShareInfo(actor.admin); assertEq(bobWalletSharesAfter, 0, "bob wallet share"); - assertEq(aliceWalletSharesAfter, aliceWalletSharesBefore, "akice wallet share"); + assertEq(aliceWalletSharesAfter, aliceWalletSharesBefore, "alice wallet share"); assertEq( foundationWalletSharesAfter, foundationWalletSharesBefore + bobWalletSharesBefore, "foundation wallet share" ); @@ -347,62 +348,6 @@ contract WalletShareTest is BaseWalletSharesStaking { uint256 percentage = (bobWalletSharesAfter * 100) / actualTotalShares; assertEq(percentage, 20); } - - function test_DecreaseWalletShare() public { - // assigns actor.bob_channel_owner 20% allocation - test_WalletGets_20PercentAllocation(); - - // let's decrease actor.bob_channel_owner allocation to 10% - uint256 totalSharesBefore = pushStaking.WALLET_TOTAL_SHARES(); - (uint256 bobWalletSharesBefore, uint256 bobStakedBlockBefore, uint256 bobClaimedBlockBefore) = - pushStaking.walletShareInfo(actor.bob_channel_owner); - (uint256 foundationWalletSharesBefore,,) = pushStaking.walletShareInfo(actor.admin); - - GenericTypes.Percentage memory percentAllocation = - GenericTypes.Percentage({ percentageNumber: 10, decimalPlaces: 0 }); - - uint256 expectedAllocationShares = - pushStaking.getSharesAmount(pushStaking.WALLET_TOTAL_SHARES(), percentAllocation); - changePrank(actor.admin); - pushStaking.decreaseWalletShare(actor.bob_channel_owner, percentAllocation); - uint256 totalSharesAfter = pushStaking.WALLET_TOTAL_SHARES(); - (uint256 bobWalletSharesAfter, uint256 bobStakedBlockAfter, uint256 bobClaimedBlockAfter) = - pushStaking.walletShareInfo(actor.bob_channel_owner); - (uint256 foundationWalletSharesAfter,,) = pushStaking.walletShareInfo(actor.admin); - - assertEq(bobWalletSharesBefore, 25_000 * 1e18); - assertEq(totalSharesBefore, 125_000 * 1e18); - assertEq(foundationWalletSharesBefore, 100_000 * 1e18); - assertEq(bobWalletSharesAfter, expectedAllocationShares); - assertEq(totalSharesAfter, 125_000 * 1e18 + expectedAllocationShares); - assertEq(foundationWalletSharesAfter, 125_000 * 1e18); - } - - // FUZZ TESTS - function testFuzz_AddShares(address _walletAddress, GenericTypes.Percentage memory _percentage) public { - _percentage.percentageNumber = bound(_percentage.percentageNumber, 1, 100); - _percentage.decimalPlaces = bound(_percentage.decimalPlaces, 1, 10); - vm.assume(_walletAddress != actor.admin && _walletAddress != address(0)); - // percentage must be less than 100 - vm.assume(_percentage.percentageNumber / 10 ** _percentage.decimalPlaces < 100); - changePrank(actor.admin); - pushStaking.addWalletShare(_walletAddress, _percentage); - } - - function testFuzz_RemoveShares(address _walletAddress, GenericTypes.Percentage memory _percentage) public { - _percentage.percentageNumber = bound(_percentage.percentageNumber, 0, 100); - vm.assume(_percentage.decimalPlaces < 10); - // percentage must be less than 100 - vm.assume(_percentage.percentageNumber / 10 ** _percentage.decimalPlaces < 100); - testFuzz_AddShares(_walletAddress, _percentage); - - changePrank(actor.admin); - pushStaking.removeWalletShare(_walletAddress); - (uint256 foundationWalletShares,,) = pushStaking.walletShareInfo(actor.admin); - - assertEq(pushStaking.WALLET_TOTAL_SHARES(), foundationWalletShares); - } - function test_whenWallet_SharesIncrease_InSameEpoch() public { (uint256 bobWalletSharesBefore, uint256 bobStakedBlockBefore, uint256 bobClaimedBlockBefore) = pushStaking.walletShareInfo(actor.bob_channel_owner); @@ -506,7 +451,7 @@ contract WalletShareTest is BaseWalletSharesStaking { addPool(1000); GenericTypes.Percentage memory percentAllocation2 = GenericTypes.Percentage({ percentageNumber: 50, decimalPlaces: 0 }); pushStaking.addWalletShare(actor.charlie_channel_owner, percentAllocation2); - roll(epochDuration + 1); + roll(epochDuration + 2); addPool(1000); pushStaking.setFoundationAddress(actor.bob_channel_owner); // GenericTypes.Percentage memory percentAllocation2 = GenericTypes.Percentage({ percentageNumber: 50, decimalPlaces: 0 }); @@ -527,10 +472,8 @@ contract WalletShareTest is BaseWalletSharesStaking { assertEq(foundationWalletShares, 100_000 ether); assertEq(foundationStakedBlock, genesisEpoch); assertEq(foundationClaimedBlock, genesisEpoch); - roll(epochDuration + 1); changePrank(actor.admin); - GenericTypes.Percentage memory percentAllocation2 = GenericTypes.Percentage({ percentageNumber: 50, decimalPlaces: 0 }); - pushStaking.addWalletShare(actor.bob_channel_owner, percentAllocation2); + roll(epochDuration + 2); pushStaking.setFoundationAddress(actor.bob_channel_owner); addPool(1000);