diff --git a/src/DelegateStaking.sol b/src/DelegateStaking.sol index 7223987..b9e9767 100644 --- a/src/DelegateStaking.sol +++ b/src/DelegateStaking.sol @@ -30,8 +30,6 @@ interface IStaking { * * When staking, you must specify a keyper address. This symbolically demonstrates your support * for that keyper. The keyper address must be a valid keyper in the staking contract. - * Staking, unstaking, and claiming rewards are based on shares rather than the balance directly. - * This method ensures the balance can change over time without needing too many storage updates. * * @dev SHU tokens transferred into the contract without using the `stake` function will be included * in the rewards distribution and shared among all stakers. This contract only supports SHU diff --git a/src/Staking.sol b/src/Staking.sol index 7ae6fae..9124c4f 100644 --- a/src/Staking.sol +++ b/src/Staking.sol @@ -281,7 +281,6 @@ contract Staking is BaseStaking { /// @param _minStake The minimum stake amount function setMinStake(uint256 _minStake) external onlyOwner { minStake = _minStake; - emit NewMinStake(_minStake); } @@ -321,7 +320,7 @@ contract Staking is BaseStaking { /// - if the keyper sSHU balance is less or equal than the minimum /// stake or the total locked amount, the function will return 0 /// @param keyper The keyper address - /// @param unlockedAmount The amount of assets to unlock + /// @param unlockedAmount The amount of unlocked assets /// @return amount The maximum amount of assets that a keyper can withdraw after unlocking a certain amount function _maxWithdraw( address keyper, diff --git a/test/DelegateStaking.t.sol b/test/DelegateStaking.t.sol index 98d5ad6..d055646 100644 --- a/test/DelegateStaking.t.sol +++ b/test/DelegateStaking.t.sol @@ -1561,4 +1561,120 @@ contract ViewFunctions is DelegateStakingTest { assertApproxEqAbs(maxWithdraw, rewards, 0.1e18, "Wrong max withdraw"); } + + function testFuzz_MaxWithdrawDepositorHasMultipleLockedStakes( + address _keyper, + address _depositor, + uint256 _amount1, + uint256 _amount2, + uint256 _jump + ) public { + _amount1 = _boundToRealisticStake(_amount1); + _amount2 = _boundToRealisticStake(_amount2); + _jump = _boundUnlockedTime(_jump); + + _mintGovToken(_depositor, _amount1 + _amount2); + _setKeyper(_keyper, true); + + _stake(_depositor, _keyper, _amount1); + _stake(_depositor, _keyper, _amount2); + + uint256 maxWithdraw = delegate.maxWithdraw(_depositor); + assertEq(maxWithdraw, 0, "Wrong max withdraw"); + } + + function testFuzz_convertToSharesNoSupply(uint256 assets) public view { + assertEq(delegate.convertToShares(assets), assets); + } + + function testFuzz_ConvertToSharesHasSupplySameBlock( + address _keyper, + address _depositor, + uint256 _assets + ) public { + _assets = _boundToRealisticStake(_assets); + + _mintGovToken(_depositor, _assets); + _setKeyper(_keyper, true); + + _stake(_depositor, _keyper, _assets); + + uint256 shares = delegate.convertToShares(_assets); + + assertEq(shares, _assets, "Wrong shares"); + } + + function testFuzz_ConvertToAssetsHasSupplySameBlock( + address _keyper, + address _depositor, + uint256 _assets + ) public { + _assets = _boundToRealisticStake(_assets); + + _mintGovToken(_depositor, _assets); + _setKeyper(_keyper, true); + + _stake(_depositor, _keyper, _assets); + + uint256 shares = delegate.convertToShares(_assets); + uint256 assets = delegate.convertToAssets(shares); + + assertEq(assets, _assets, "Wrong assets"); + } + + function testFuzz_GetUserStakeIds( + address _keyper, + address _depositor, + uint256 _amount1, + uint256 _amount2 + ) public { + _amount1 = _boundToRealisticStake(_amount1); + _amount2 = _boundToRealisticStake(_amount2); + + _mintGovToken(_depositor, _amount1 + _amount2); + _setKeyper(_keyper, true); + + uint256 stakeId1 = _stake(_depositor, _keyper, _amount1); + uint256 stakeId2 = _stake(_depositor, _keyper, _amount2); + + uint256[] memory stakeIds = delegate.getUserStakeIds(_depositor); + + assertEq(stakeIds.length, 2, "Wrong stake ids"); + assertEq(stakeIds[0], stakeId1, "Wrong stake id"); + assertEq(stakeIds[1], stakeId2, "Wrong stake id"); + } +} + +contract Transfer is DelegateStakingTest { + function testFuzz_RevertWith_transferDisabled( + address _from, + address _to, + uint256 _amount + ) public { + _amount = _boundToRealisticStake(_amount); + + _mintGovToken(_from, _amount); + _setKeyper(_from, true); + + _stake(_from, _from, _amount); + + vm.expectRevert(BaseStaking.TransferDisabled.selector); + delegate.transfer(_to, _amount); + } + + function testFuzz_RevertWith_transferFromDisabled( + address _from, + address _to, + uint256 _amount + ) public { + _amount = _boundToRealisticStake(_amount); + + _mintGovToken(_from, _amount); + _setKeyper(_from, true); + + _stake(_from, _from, _amount); + + vm.expectRevert(BaseStaking.TransferDisabled.selector); + delegate.transferFrom(_from, _to, _amount); + } } diff --git a/test/RewardsDistributor.t.sol b/test/RewardsDistributor.t.sol index 778962c..1170d10 100644 --- a/test/RewardsDistributor.t.sol +++ b/test/RewardsDistributor.t.sol @@ -136,6 +136,7 @@ contract OwnableFunctions is RewardsDistributorTest { function testFuzz_RevertIf_SetRewardConfigurationEmissionRateZero( address _receiver ) public { + vm.assume(_receiver != address(0)); vm.expectRevert(RewardsDistributor.EmissionRateZero.selector); rewardsDistributor.setRewardConfiguration(_receiver, 0); } diff --git a/test/Staking.t.sol b/test/Staking.t.sol index f8ae633..29c0765 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -1771,7 +1771,7 @@ contract ViewFunctions is StakingTest { assertEq(assets, _assets, "Wrong assets"); } - function testFuzz_GetKeyperStakeIds( + function testFuzz_GetUserStakeIds( address _depositor, uint256 _amount1, uint256 _amount2