From 1e92aa35c1bedee56de4be08b01418993402fbc4 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Thu, 4 Jul 2024 10:02:54 -0300 Subject: [PATCH 01/20] Deploy script --- script/Deploy.s.sol | 54 ++++++++++++++++++++++++++++++++++ test/Staking.integration.t.sol | 16 ++++++++++ 2 files changed, 70 insertions(+) create mode 100644 script/Deploy.s.sol create mode 100644 test/Staking.integration.t.sol diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol new file mode 100644 index 0000000..4b913a6 --- /dev/null +++ b/script/Deploy.s.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import "@forge-std/Script.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import {RewardsDistributor} from "src/RewardsDistributor.sol"; +import {Staking} from "src/Staking.sol"; + +contract Deploy is Script { + address constant STAKING_TOKEN = 0xe485E2f1bab389C08721B291f6b59780feC83Fd7; // shutter token + address constant CONTRACT_OWNER = + 0x36bD3044ab68f600f6d3e081056F34f2a58432c4; // shuter multisig + uint256 constant LOCK_PERIOD = 182 days; + uint256 constant MIN_STAKE = 50_000 * 1e18; + uint256 constant REWARD_RATE = 0.1e18; + + function run() + public + returns (Staking stakingProxy, RewardsDistributor rewardsDistributor) + { + vm.startBroadcast(); + + rewardsDistributor = new RewardsDistributor( + CONTRACT_OWNER, + STAKING_TOKEN + ); + + stakingProxy = Staking( + address( + new TransparentUpgradeableProxy( + address(new Staking()), + address(this), + "" + ) + ) + ); + + stakingProxy.initialize( + CONTRACT_OWNER, + STAKING_TOKEN, + address(rewardsDistributor), + LOCK_PERIOD, + MIN_STAKE + ); + + rewardsDistributor.setRewardConfiguration( + address(stakingProxy), + REWARD_RATE + ); + + vm.stopBroadcast(); + } +} diff --git a/test/Staking.integration.t.sol b/test/Staking.integration.t.sol new file mode 100644 index 0000000..fbee917 --- /dev/null +++ b/test/Staking.integration.t.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import "@forge-std/Test.sol"; + +import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol"; +import {TransparentUpgradeableProxy, ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Staking} from "src/Staking.sol"; +import {RewardsDistributor} from "src/RewardsDistributor.sol"; +import {IRewardsDistributor} from "src/interfaces/IRewardsDistributor.sol"; +import {MockGovToken} from "test/mocks/MockGovToken.sol"; +import {ProxyUtils} from "test/helpers/ProxyUtils.sol"; +import {StakingHarness} from "test/helpers/StakingHarness.sol"; + +contract StakingIntegrationTest is Test {} From 9930e9a73db13821c5496c5a387c0ec1de149876 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Thu, 4 Jul 2024 10:14:41 -0300 Subject: [PATCH 02/20] testFork_DeployStakingContracts --- foundry.toml | 3 +++ script/Constants.sol | 8 ++++++++ script/Deploy.s.sol | 16 ++-------------- test/Staking.integration.t.sol | 28 ++++++++++++++++++++++++++-- 4 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 script/Constants.sol diff --git a/foundry.toml b/foundry.toml index 9445c55..1a879d0 100644 --- a/foundry.toml +++ b/foundry.toml @@ -16,3 +16,6 @@ # Speed up compilation and tests during development. optimizer = false +[rpc_endpoints] + mainnet = "${MAINNET_RPC_URL}" + diff --git a/script/Constants.sol b/script/Constants.sol new file mode 100644 index 0000000..f315d11 --- /dev/null +++ b/script/Constants.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +address constant STAKING_TOKEN = 0xe485E2f1bab389C08721B291f6b59780feC83Fd7; // shutter token +address constant CONTRACT_OWNER = 0x36bD3044ab68f600f6d3e081056F34f2a58432c4; // shuter multisig +uint256 constant LOCK_PERIOD = 182 days; +uint256 constant MIN_STAKE = 50_000 * 1e18; +uint256 constant REWARD_RATE = 0.1e18; diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 4b913a6..6117d2c 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -3,18 +3,11 @@ pragma solidity 0.8.26; import "@forge-std/Script.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - import {RewardsDistributor} from "src/RewardsDistributor.sol"; import {Staking} from "src/Staking.sol"; +import "./Constants.sol"; contract Deploy is Script { - address constant STAKING_TOKEN = 0xe485E2f1bab389C08721B291f6b59780feC83Fd7; // shutter token - address constant CONTRACT_OWNER = - 0x36bD3044ab68f600f6d3e081056F34f2a58432c4; // shuter multisig - uint256 constant LOCK_PERIOD = 182 days; - uint256 constant MIN_STAKE = 50_000 * 1e18; - uint256 constant REWARD_RATE = 0.1e18; - function run() public returns (Staking stakingProxy, RewardsDistributor rewardsDistributor) @@ -30,7 +23,7 @@ contract Deploy is Script { address( new TransparentUpgradeableProxy( address(new Staking()), - address(this), + address(CONTRACT_OWNER), "" ) ) @@ -44,11 +37,6 @@ contract Deploy is Script { MIN_STAKE ); - rewardsDistributor.setRewardConfiguration( - address(stakingProxy), - REWARD_RATE - ); - vm.stopBroadcast(); } } diff --git a/test/Staking.integration.t.sol b/test/Staking.integration.t.sol index fbee917..2cc11f3 100644 --- a/test/Staking.integration.t.sol +++ b/test/Staking.integration.t.sol @@ -11,6 +11,30 @@ import {RewardsDistributor} from "src/RewardsDistributor.sol"; import {IRewardsDistributor} from "src/interfaces/IRewardsDistributor.sol"; import {MockGovToken} from "test/mocks/MockGovToken.sol"; import {ProxyUtils} from "test/helpers/ProxyUtils.sol"; -import {StakingHarness} from "test/helpers/StakingHarness.sol"; +import {Staking} from "src/Staking.sol"; +import {Deploy} from "script/Deploy.s.sol"; +import "script/Constants.sol"; + +contract StakingIntegrationTest is Test { + function setUp() public { + vm.createSelectFork(vm.rpcUrl("mainnet")); + } + + function testFork_DeployStakingContracts() public { + Deploy deployScript = new Deploy(); + (Staking staking, RewardsDistributor rewardsDistributor) = deployScript + .run(); + + assertEq(staking.owner(), CONTRACT_OWNER); + assertEq(address(staking.stakingToken()), STAKING_TOKEN); + assertEq( + address(staking.rewardsDistributor()), + address(rewardsDistributor) + ); + assertEq(staking.lockPeriod(), LOCK_PERIOD); + assertEq(staking.minStake(), MIN_STAKE); -contract StakingIntegrationTest is Test {} + assertEq(rewardsDistributor.owner(), CONTRACT_OWNER); + assertEq(address(rewardsDistributor.rewardToken()), STAKING_TOKEN); + } +} From 2ac456840b31caf3ddb9c49fa16a2a6531dfa665 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Fri, 5 Jul 2024 14:55:04 -0300 Subject: [PATCH 03/20] test staking deploy --- test/Staking.integration.t.sol | 47 +++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/test/Staking.integration.t.sol b/test/Staking.integration.t.sol index 2cc11f3..01cc8f2 100644 --- a/test/Staking.integration.t.sol +++ b/test/Staking.integration.t.sol @@ -6,6 +6,7 @@ import "@forge-std/Test.sol"; import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol"; import {TransparentUpgradeableProxy, ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Staking} from "src/Staking.sol"; import {RewardsDistributor} from "src/RewardsDistributor.sol"; import {IRewardsDistributor} from "src/interfaces/IRewardsDistributor.sol"; @@ -16,15 +17,18 @@ import {Deploy} from "script/Deploy.s.sol"; import "script/Constants.sol"; contract StakingIntegrationTest is Test { + Staking staking; + RewardsDistributor rewardsDistributor; + function setUp() public { + vm.label(STAKING_TOKEN, "SHU"); vm.createSelectFork(vm.rpcUrl("mainnet")); - } - function testFork_DeployStakingContracts() public { Deploy deployScript = new Deploy(); - (Staking staking, RewardsDistributor rewardsDistributor) = deployScript - .run(); + (staking, rewardsDistributor) = deployScript.run(); + } + function testFork_DeployStakingContracts() public view { assertEq(staking.owner(), CONTRACT_OWNER); assertEq(address(staking.stakingToken()), STAKING_TOKEN); assertEq( @@ -37,4 +41,39 @@ contract StakingIntegrationTest is Test { assertEq(rewardsDistributor.owner(), CONTRACT_OWNER); assertEq(address(rewardsDistributor.rewardToken()), STAKING_TOKEN); } + + function testFork_SetRewardConfiguration() public { + vm.prank(CONTRACT_OWNER); + rewardsDistributor.setRewardConfiguration( + address(staking), + REWARD_RATE + ); + + (uint256 emissionRate, uint256 lastUpdate) = rewardsDistributor + .rewardConfigurations(address(staking)); + + assertEq(emissionRate, REWARD_RATE); + assertEq(lastUpdate, block.timestamp); + } + + function testForkFuzz_MultipleDepositorsStake( + address[] calldata depositors, + uint256[] calldata amounts + ) public { + vm.assume(depositors.length == amounts.length); + + vm.prank(CONTRACT_OWNER); + staking.setKeypers(depositors, true); + + for (uint256 i = 0; i < depositors.length; i++) { + uint256 amount = bound(amounts[i], MIN_STAKE, 5_000_000e18); + + deal(STAKING_TOKEN, depositors[i], amount); + + vm.startPrank(depositors[i]); + IERC20(STAKING_TOKEN).approve(address(staking), amount); + staking.stake(amount); + vm.stopPrank(); + } + } } From b4f74012b8fa2407878111bdfe411f64273fb0eb Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Sun, 7 Jul 2024 12:16:22 -0300 Subject: [PATCH 04/20] testForkFuzz_MultipleDepositorsStakeMinStakeSameBlock --- script/Constants.sol | 5 +- test/Staking.integration.t.sol | 113 +++++++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 15 deletions(-) diff --git a/script/Constants.sol b/script/Constants.sol index f315d11..74f24d9 100644 --- a/script/Constants.sol +++ b/script/Constants.sol @@ -1,8 +1,7 @@ -// SPDX-License-Identifier: MIT pragma solidity 0.8.26; address constant STAKING_TOKEN = 0xe485E2f1bab389C08721B291f6b59780feC83Fd7; // shutter token address constant CONTRACT_OWNER = 0x36bD3044ab68f600f6d3e081056F34f2a58432c4; // shuter multisig uint256 constant LOCK_PERIOD = 182 days; -uint256 constant MIN_STAKE = 50_000 * 1e18; -uint256 constant REWARD_RATE = 0.1e18; +uint256 constant MIN_STAKE = 50_000e18; +uint256 constant REWARD_RATE = 0.1333333333e18; diff --git a/test/Staking.integration.t.sol b/test/Staking.integration.t.sol index 01cc8f2..55b30e2 100644 --- a/test/Staking.integration.t.sol +++ b/test/Staking.integration.t.sol @@ -22,12 +22,45 @@ contract StakingIntegrationTest is Test { function setUp() public { vm.label(STAKING_TOKEN, "SHU"); - vm.createSelectFork(vm.rpcUrl("mainnet")); + vm.createSelectFork(vm.rpcUrl("mainnet"), 20254999); Deploy deployScript = new Deploy(); (staking, rewardsDistributor) = deployScript.run(); } + function _boundUnlockedTime(uint256 _time) internal view returns (uint256) { + return + bound( + _time, + vm.getBlockTimestamp() + LOCK_PERIOD, + vm.getBlockTimestamp() + 105 weeks + ); + } + + function _boundRealisticTimeAhead( + uint256 _time + ) internal pure returns (uint256) { + return bound(_time, 1, 105 weeks); // two years + } + + function _jumpAhead(uint256 _seconds) public { + vm.warp(vm.getBlockTimestamp() + _seconds); + } + + function _setRewardAndFund() public { + vm.prank(CONTRACT_OWNER); + rewardsDistributor.setRewardConfiguration( + address(staking), + REWARD_RATE + ); + + uint256 poolSize = 10_000_000e18; + deal(STAKING_TOKEN, CONTRACT_OWNER, poolSize); + + vm.prank(CONTRACT_OWNER); + IERC20(STAKING_TOKEN).transfer(address(rewardsDistributor), poolSize); + } + function testFork_DeployStakingContracts() public view { assertEq(staking.owner(), CONTRACT_OWNER); assertEq(address(staking.stakingToken()), STAKING_TOKEN); @@ -56,24 +89,80 @@ contract StakingIntegrationTest is Test { assertEq(lastUpdate, block.timestamp); } - function testForkFuzz_MultipleDepositorsStake( - address[] calldata depositors, - uint256[] calldata amounts + function testForkFuzz_MultipleDepositorsStakeMinStakeSameBlock( + uint256 _depositorsCount, + uint256 _jump ) public { - vm.assume(depositors.length == amounts.length); + _setRewardAndFund(); - vm.prank(CONTRACT_OWNER); - staking.setKeypers(depositors, true); + _depositorsCount = bound(_depositorsCount, 1, 1000); + + address[] memory depositors = new address[](_depositorsCount); for (uint256 i = 0; i < depositors.length; i++) { - uint256 amount = bound(amounts[i], MIN_STAKE, 5_000_000e18); + address depositor = address( + uint160(uint256(keccak256(abi.encodePacked(i)))) + ); + vm.prank(CONTRACT_OWNER); + staking.setKeyper(depositor, true); + + deal(STAKING_TOKEN, depositor, MIN_STAKE); + + vm.startPrank(depositor); + IERC20(STAKING_TOKEN).approve(address(staking), MIN_STAKE); + staking.stake(MIN_STAKE); + vm.stopPrank(); + } + + _jump = _boundRealisticTimeAhead(_jump); + + uint256 expectedRewardsDistributed = REWARD_RATE * _jump; + + uint256 expectedRewardPerKeyper = expectedRewardsDistributed / + depositors.length; - deal(STAKING_TOKEN, depositors[i], amount); + _jumpAhead(_jump); - vm.startPrank(depositors[i]); - IERC20(STAKING_TOKEN).approve(address(staking), amount); - staking.stake(amount); + // collect rewards + for (uint256 i = 0; i < depositors.length; i++) { + address depositor = address( + uint160(uint256(keccak256(abi.encodePacked(i)))) + ); + vm.startPrank(depositor); + uint256 rewards = staking.claimRewards(0); vm.stopPrank(); + + assertApproxEqAbs(rewards, expectedRewardPerKeyper, 0.1e18); } } + + function testFork_7PercentTotalSupplyStakedNoCompoundResultsIn20PercentAPR() + public + { + _setRewardAndFund(); + + uint256 totalSupply = IERC20(STAKING_TOKEN).totalSupply(); + uint256 staked = 0.07e18 * totalSupply; + + deal(STAKING_TOKEN, address(this), staked); + + vm.prank(CONTRACT_OWNER); + staking.setKeyper(address(this), true); + + staking.stake(staked); + + uint256 jump = 365 days; + + uint256 expectedRewardsDistributed = REWARD_RATE * jump; + + uint256 rewardsReceived = staking.claimRewards(0); + + assertApproxEqAbs(rewardsReceived, expectedRewardsDistributed, 0.1e18); + //APRi= Annualized percentage return based on how much participant i has staked versus how much they have earned in rewards over the year i. + //For example for a Keyper APRKeyper=(RKeyperSKeyper)*365t*100 + + uint256 apr = (rewardsReceived / staked) * (365 days) * 100; + + assertApproxEqAbs(apr, 20, 0.1e18); + } } From a13401c32f1c31b4ad90437588e3894427370349 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Sun, 7 Jul 2024 13:54:39 -0300 Subject: [PATCH 05/20] checkpoint --- src/RewardsDistributor.sol | 9 +++-- test/Staking.integration.t.sol | 73 ++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/RewardsDistributor.sol b/src/RewardsDistributor.sol index 78481a2..78ba1d8 100644 --- a/src/RewardsDistributor.sol +++ b/src/RewardsDistributor.sol @@ -98,10 +98,11 @@ contract RewardsDistributor is Ownable2Step, IRewardsDistributor { ) external override onlyOwner { require(receiver != address(0), ZeroAddress()); - rewardConfigurations[receiver] = RewardConfiguration( - emissionRate, - block.timestamp - ); + // only update last update if it's the first time + if (rewardConfigurations[receiver].lastUpdate == 0) { + rewardConfigurations[receiver].lastUpdate = block.timestamp; + } + rewardConfigurations[receiver].emissionRate = emissionRate; emit RewardConfigurationSet(receiver, emissionRate); } diff --git a/test/Staking.integration.t.sol b/test/Staking.integration.t.sol index 55b30e2..4ba1f88 100644 --- a/test/Staking.integration.t.sol +++ b/test/Staking.integration.t.sol @@ -20,6 +20,8 @@ contract StakingIntegrationTest is Test { Staking staking; RewardsDistributor rewardsDistributor; + uint256 constant CIRCULATION_SUPPLY = 81_000_000e18; + function setUp() public { vm.label(STAKING_TOKEN, "SHU"); vm.createSelectFork(vm.rpcUrl("mainnet"), 20254999); @@ -61,6 +63,20 @@ contract StakingIntegrationTest is Test { IERC20(STAKING_TOKEN).transfer(address(rewardsDistributor), poolSize); } + function _calculateAPR( + uint256 _rewardsReceived, + uint256 _staked, + uint256 _days + ) internal pure returns (uint256) { + // using scalar math + uint256 SCALAR = 1e18; + + uint256 aprScalar = ((_rewardsReceived * SCALAR) * 365 days * 100) / + (_staked * _days); + + return aprScalar; + } + function testFork_DeployStakingContracts() public view { assertEq(staking.owner(), CONTRACT_OWNER); assertEq(address(staking.stakingToken()), STAKING_TOKEN); @@ -89,6 +105,31 @@ contract StakingIntegrationTest is Test { assertEq(lastUpdate, block.timestamp); } + function testFork_25PercentParticipationRateGives20PercentAPR() public { + _setRewardAndFund(); + + uint256 staked = (CIRCULATION_SUPPLY * 25) / 100; + + deal(STAKING_TOKEN, address(this), staked); + + vm.prank(CONTRACT_OWNER); + staking.setKeyper(address(this), true); + + IERC20(STAKING_TOKEN).approve(address(staking), staked); + staking.stake(staked); + + uint256 jump = 86.81 days; + + _jumpAhead(jump); + + uint256 rewardsReceived = staking.claimRewards(0); + + uint256 APR = _calculateAPR(rewardsReceived, staked, jump); + + // 1% error margin + assertApproxEqAbs(APR, 20e18, 1e18); + } + function testForkFuzz_MultipleDepositorsStakeMinStakeSameBlock( uint256 _depositorsCount, uint256 _jump @@ -121,6 +162,8 @@ contract StakingIntegrationTest is Test { uint256 expectedRewardPerKeyper = expectedRewardsDistributed / depositors.length; + uint256 APR = _calculateAPR(expectedRewardPerKeyper, MIN_STAKE, _jump); + _jumpAhead(_jump); // collect rewards @@ -135,34 +178,4 @@ contract StakingIntegrationTest is Test { assertApproxEqAbs(rewards, expectedRewardPerKeyper, 0.1e18); } } - - function testFork_7PercentTotalSupplyStakedNoCompoundResultsIn20PercentAPR() - public - { - _setRewardAndFund(); - - uint256 totalSupply = IERC20(STAKING_TOKEN).totalSupply(); - uint256 staked = 0.07e18 * totalSupply; - - deal(STAKING_TOKEN, address(this), staked); - - vm.prank(CONTRACT_OWNER); - staking.setKeyper(address(this), true); - - staking.stake(staked); - - uint256 jump = 365 days; - - uint256 expectedRewardsDistributed = REWARD_RATE * jump; - - uint256 rewardsReceived = staking.claimRewards(0); - - assertApproxEqAbs(rewardsReceived, expectedRewardsDistributed, 0.1e18); - //APRi= Annualized percentage return based on how much participant i has staked versus how much they have earned in rewards over the year i. - //For example for a Keyper APRKeyper=(RKeyperSKeyper)*365t*100 - - uint256 apr = (rewardsReceived / staked) * (365 days) * 100; - - assertApproxEqAbs(apr, 20, 0.1e18); - } } From 31765c578c51d2c84cc271ae1d044fac320a2bd5 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Sun, 7 Jul 2024 18:46:59 -0300 Subject: [PATCH 06/20] testFork_FirstDepositorsAlwaysReceiveMoreRewards --- test/Staking.integration.t.sol | 66 +++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/test/Staking.integration.t.sol b/test/Staking.integration.t.sol index 4ba1f88..bc0e077 100644 --- a/test/Staking.integration.t.sol +++ b/test/Staking.integration.t.sol @@ -118,7 +118,7 @@ contract StakingIntegrationTest is Test { IERC20(STAKING_TOKEN).approve(address(staking), staked); staking.stake(staked); - uint256 jump = 86.81 days; + uint256 jump = 86 days; _jumpAhead(jump); @@ -130,6 +130,70 @@ contract StakingIntegrationTest is Test { assertApproxEqAbs(APR, 20e18, 1e18); } + function testFork_FirstDepositorsAlwaysReceiveMoreRewards() public { + uint256 depositorsCount = 400; + + _setRewardAndFund(); + + uint256 jumpBetweenStakes = 1 hours; + + uint256[] memory timeStaked = new uint256[](depositorsCount); + uint256 previousDepositorShares; + + uint256 timestampFirstStake = vm.getBlockTimestamp(); + + for (uint256 i = 1; i <= depositorsCount; i++) { + address participant = address(uint160(i)); + + deal(STAKING_TOKEN, participant, MIN_STAKE); + + vm.prank(CONTRACT_OWNER); + staking.setKeyper(participant, true); + + vm.startPrank(participant); + IERC20(STAKING_TOKEN).approve(address(staking), MIN_STAKE); + staking.stake(MIN_STAKE); + vm.stopPrank(); + + uint256 shares = staking.balanceOf(participant); + if (i > 1) { + assertGt(previousDepositorShares, shares); + } + previousDepositorShares = shares; + + timeStaked[i - 1] = vm.getBlockTimestamp(); + + _jumpAhead(jumpBetweenStakes); + } + + uint256 previousRewardsReceived; + + // collect rewards and calculate rewards + for (uint256 i = 1; i <= depositorsCount; i++) { + address participant = address(uint160(i)); + + uint256 expectedTimestamp = timeStaked[i - 1] + 365 days; + // jump the diferrence between expected and actual time + _jumpAhead(expectedTimestamp - vm.getBlockTimestamp()); + + vm.startPrank(participant); + uint256 rewardsReceived = staking.claimRewards(0); + + vm.stopPrank(); + + if (i > 1) { + assertGt(rewardsReceived, previousRewardsReceived); + } + + _jumpAhead(jumpBetweenStakes); + + uint256 assetsAfter = staking.convertToAssets( + staking.balanceOf(participant) + ); + assertApproxEqAbs(assetsAfter, MIN_STAKE, 2); + } + } + function testForkFuzz_MultipleDepositorsStakeMinStakeSameBlock( uint256 _depositorsCount, uint256 _jump From 98f4c9022139f8c5df3166c5fe6ca0be2a413cb8 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Sun, 7 Jul 2024 19:59:58 -0300 Subject: [PATCH 07/20] checkpoint --- src/RewardsDistributor.sol | 10 ++- src/Staking.sol | 18 ++--- test/Staking.integration.t.sol | 116 ++++++++++++++++++++++++++------- 3 files changed, 109 insertions(+), 35 deletions(-) diff --git a/src/RewardsDistributor.sol b/src/RewardsDistributor.sol index 78ba1d8..486c489 100644 --- a/src/RewardsDistributor.sol +++ b/src/RewardsDistributor.sol @@ -76,7 +76,15 @@ contract RewardsDistributor is Ownable2Step, IRewardsDistributor { // difference in time since last update uint256 timeDelta = block.timestamp - rewardConfiguration.lastUpdate; - if (rewardConfiguration.emissionRate != 0 && timeDelta != 0) { + // the contract must have funds to distribute + // we don't want to revert in case its zero to not block the staking contract + uint256 funds = rewardToken.balanceOf(address(this)); + + if ( + rewardConfiguration.emissionRate != 0 && + timeDelta != 0 && + funds != 0 + ) { rewards = rewardConfiguration.emissionRate * timeDelta; // update the last update timestamp diff --git a/src/Staking.sol b/src/Staking.sol index ce8a10c..8f8d15d 100644 --- a/src/Staking.sol +++ b/src/Staking.sol @@ -374,7 +374,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { /// @param _rewardsDistributor The address of the rewards distributor contract function setRewardsDistributor( address _rewardsDistributor - ) external onlyOwner updateRewards { + ) external onlyOwner { rewardsDistributor = IRewardsDistributor(_rewardsDistributor); emit NewRewardsDistributor(_rewardsDistributor); @@ -382,9 +382,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { /// @notice Set the lock period /// @param _lockPeriod The lock period in seconds - function setLockPeriod( - uint256 _lockPeriod - ) external onlyOwner updateRewards { + function setLockPeriod(uint256 _lockPeriod) external onlyOwner { lockPeriod = _lockPeriod; emit NewLockPeriod(_lockPeriod); @@ -401,10 +399,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { /// @notice Set a keyper /// @param keyper The keyper address /// @param isKeyper Whether the keyper is a keyper or not - function setKeyper( - address keyper, - bool isKeyper - ) external onlyOwner updateRewards { + function setKeyper(address keyper, bool isKeyper) external onlyOwner { _setKeyper(keyper, isKeyper); } @@ -414,7 +409,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { function setKeypers( address[] memory _keypers, bool isKeyper - ) external onlyOwner updateRewards { + ) external onlyOwner { for (uint256 i = 0; i < _keypers.length; i++) { _setKeyper(_keypers[i], isKeyper); } @@ -452,6 +447,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { /// @return The maximum amount of assets that a keyper can withdraw function maxWithdraw(address keyper) public view virtual returns (uint256) { uint256 shares = balanceOf(keyper); + require(shares > 0, KeyperHasNoShares()); uint256 assets = convertToAssets(shares); @@ -461,7 +457,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { : minStake; if (assets < compare) { - // TODO check this + // need this branch as convertToAssets rounds down return 0; } else { return assets - compare; @@ -481,7 +477,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { uint256 compare = locked >= minStake ? locked : minStake; if (assets < compare) { - // TODO check this + // need this branch as convertToAssets rounds down return 0; } else { return assets - compare; diff --git a/test/Staking.integration.t.sol b/test/Staking.integration.t.sol index bc0e077..8f43c9a 100644 --- a/test/Staking.integration.t.sol +++ b/test/Staking.integration.t.sol @@ -30,15 +30,6 @@ contract StakingIntegrationTest is Test { (staking, rewardsDistributor) = deployScript.run(); } - function _boundUnlockedTime(uint256 _time) internal view returns (uint256) { - return - bound( - _time, - vm.getBlockTimestamp() + LOCK_PERIOD, - vm.getBlockTimestamp() + 105 weeks - ); - } - function _boundRealisticTimeAhead( uint256 _time ) internal pure returns (uint256) { @@ -63,7 +54,7 @@ contract StakingIntegrationTest is Test { IERC20(STAKING_TOKEN).transfer(address(rewardsDistributor), poolSize); } - function _calculateAPR( + function _calculateReturnOverPrincipal( uint256 _rewardsReceived, uint256 _staked, uint256 _days @@ -124,24 +115,28 @@ contract StakingIntegrationTest is Test { uint256 rewardsReceived = staking.claimRewards(0); - uint256 APR = _calculateAPR(rewardsReceived, staked, jump); + uint256 APR = _calculateReturnOverPrincipal( + rewardsReceived, + staked, + jump + ); // 1% error margin assertApproxEqAbs(APR, 20e18, 1e18); } - function testFork_FirstDepositorsAlwaysReceiveMoreRewards() public { + function testForkFuzz_MultipleDepositorsStakeMinAmountDifferentTimestamp( + uint256 _jump + ) public { uint256 depositorsCount = 400; _setRewardAndFund(); - uint256 jumpBetweenStakes = 1 hours; + _jump = bound(_jump, 1 minutes, 12 hours); uint256[] memory timeStaked = new uint256[](depositorsCount); uint256 previousDepositorShares; - uint256 timestampFirstStake = vm.getBlockTimestamp(); - for (uint256 i = 1; i <= depositorsCount; i++) { address participant = address(uint160(i)); @@ -163,12 +158,11 @@ contract StakingIntegrationTest is Test { timeStaked[i - 1] = vm.getBlockTimestamp(); - _jumpAhead(jumpBetweenStakes); + _jumpAhead(_jump); } uint256 previousRewardsReceived; - // collect rewards and calculate rewards for (uint256 i = 1; i <= depositorsCount; i++) { address participant = address(uint160(i)); @@ -185,16 +179,87 @@ contract StakingIntegrationTest is Test { assertGt(rewardsReceived, previousRewardsReceived); } - _jumpAhead(jumpBetweenStakes); - uint256 assetsAfter = staking.convertToAssets( staking.balanceOf(participant) ); - assertApproxEqAbs(assetsAfter, MIN_STAKE, 2); + assertApproxEqAbs(assetsAfter, MIN_STAKE, 1e18); + } + } + + function testFork_ClaimRewardsAtTheEndOfSemester() public { + _setRewardAndFund(); + + uint256 staked = (CIRCULATION_SUPPLY * 25) / 100; + + deal(STAKING_TOKEN, address(this), staked); + + vm.prank(CONTRACT_OWNER); + staking.setKeyper(address(this), true); + + IERC20(STAKING_TOKEN).approve(address(staking), staked); + staking.stake(staked); + + uint256 jump = 86 days; + + _jumpAhead(jump); + + vm.prank(CONTRACT_OWNER); + staking.setKeyper(address(1), true); + + uint256 rewardsReceived = staking.claimRewards(0); + + uint256 APR = _calculateReturnOverPrincipal( + rewardsReceived, + staked, + jump + ); + + // 1% error margin + assertApproxEqAbs(APR, 20e18, 1e18); + } + + function testFork_ClaimRewardsEveryDayAndReestakeUntilEndSemester() public { + _setRewardAndFund(); + + uint256 staked = (CIRCULATION_SUPPLY * 25) / 100; + + deal(STAKING_TOKEN, address(this), staked); + + vm.prank(CONTRACT_OWNER); + staking.setKeyper(address(this), true); + + IERC20(STAKING_TOKEN).approve(address(staking), staked); + staking.stake(staked); + + uint256 previousTimestamp = vm.getBlockTimestamp(); + + for (uint256 i = 1; i < 2064; i++) { + _jumpAhead(1 hours); + + previousTimestamp = vm.getBlockTimestamp(); + uint256 rewardsReceived = staking.claimRewards(0); + + IERC20(STAKING_TOKEN).approve(address(staking), rewardsReceived); + staking.stake(rewardsReceived); } + + _jumpAhead(1 hours); + + uint256 assets = staking.convertToAssets( + staking.balanceOf(address(this)) + ); + + uint256 APR = _calculateReturnOverPrincipal( + assets - staked, + staked, + 86 days + ); + + // 1% error margin + assertApproxEqAbs(APR, 20e18, 1e18); } - function testForkFuzz_MultipleDepositorsStakeMinStakeSameBlock( + function testForkFuzz_MultipleDepositorsStakeMinStakeSameTimestamp( uint256 _depositorsCount, uint256 _jump ) public { @@ -226,8 +291,6 @@ contract StakingIntegrationTest is Test { uint256 expectedRewardPerKeyper = expectedRewardsDistributed / depositors.length; - uint256 APR = _calculateAPR(expectedRewardPerKeyper, MIN_STAKE, _jump); - _jumpAhead(_jump); // collect rewards @@ -240,6 +303,13 @@ contract StakingIntegrationTest is Test { vm.stopPrank(); assertApproxEqAbs(rewards, expectedRewardPerKeyper, 0.1e18); + + uint256 APR = _calculateReturnOverPrincipal( + rewards, + MIN_STAKE, + _jump + ); + assertApproxEqAbs(APR, 20e18, 1e18); } } } From 2b612b504eb736010d85c55445299f85ff842bb6 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Sun, 7 Jul 2024 20:11:20 -0300 Subject: [PATCH 08/20] update fuzzing --- test/Staking.integration.t.sol | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/test/Staking.integration.t.sol b/test/Staking.integration.t.sol index 8f43c9a..6849872 100644 --- a/test/Staking.integration.t.sol +++ b/test/Staking.integration.t.sol @@ -122,7 +122,7 @@ contract StakingIntegrationTest is Test { ); // 1% error margin - assertApproxEqAbs(APR, 20e18, 1e18); + assertApproxEqAbs(APR, 21e18, 1e18); } function testForkFuzz_MultipleDepositorsStakeMinAmountDifferentTimestamp( @@ -215,7 +215,7 @@ contract StakingIntegrationTest is Test { ); // 1% error margin - assertApproxEqAbs(APR, 20e18, 1e18); + assertApproxEqAbs(APR, 21e18, 1e18); } function testFork_ClaimRewardsEveryDayAndReestakeUntilEndSemester() public { @@ -255,21 +255,20 @@ contract StakingIntegrationTest is Test { 86 days ); - // 1% error margin - assertApproxEqAbs(APR, 20e18, 1e18); + assertApproxEqAbs(APR, 21e18, 1e18); } function testForkFuzz_MultipleDepositorsStakeMinStakeSameTimestamp( uint256 _depositorsCount, uint256 _jump ) public { - _setRewardAndFund(); - _depositorsCount = bound(_depositorsCount, 1, 1000); - address[] memory depositors = new address[](_depositorsCount); + _jump = _boundRealisticTimeAhead(_jump); + + _setRewardAndFund(); - for (uint256 i = 0; i < depositors.length; i++) { + for (uint256 i = 0; i < _depositorsCount; i++) { address depositor = address( uint160(uint256(keccak256(abi.encodePacked(i)))) ); @@ -284,17 +283,14 @@ contract StakingIntegrationTest is Test { vm.stopPrank(); } - _jump = _boundRealisticTimeAhead(_jump); - uint256 expectedRewardsDistributed = REWARD_RATE * _jump; uint256 expectedRewardPerKeyper = expectedRewardsDistributed / - depositors.length; + _depositorsCount; _jumpAhead(_jump); - // collect rewards - for (uint256 i = 0; i < depositors.length; i++) { + for (uint256 i = 0; i < _depositorsCount; i++) { address depositor = address( uint160(uint256(keccak256(abi.encodePacked(i)))) ); @@ -303,13 +299,6 @@ contract StakingIntegrationTest is Test { vm.stopPrank(); assertApproxEqAbs(rewards, expectedRewardPerKeyper, 0.1e18); - - uint256 APR = _calculateReturnOverPrincipal( - rewards, - MIN_STAKE, - _jump - ); - assertApproxEqAbs(APR, 20e18, 1e18); } } } From 3d9da999a15edb3f0f5bad48f44ae49760516935 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Sun, 7 Jul 2024 20:15:21 -0300 Subject: [PATCH 09/20] add erc20 name and symbol --- src/Staking.sol | 3 +-- test/Staking.t.sol | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Staking.sol b/src/Staking.sol index 8f8d15d..067f754 100644 --- a/src/Staking.sol +++ b/src/Staking.sol @@ -177,7 +177,7 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { uint256 _lockPeriod, uint256 _minStake ) public initializer { - // TODO set name and symbol + __ERC20_init("Staked SHU", "sSHU"); // Transfer ownership to the DAO contract _transferOwnership(newOwner); @@ -198,7 +198,6 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { /// - Only keypers can stake /// @param amount The amount of SHU to stake /// @return stakeId The index of the stake - /// TODO slippage protection function stake( uint256 amount ) external onlyKeyper updateRewards returns (uint256 stakeId) { diff --git a/test/Staking.t.sol b/test/Staking.t.sol index 5da3c4b..89179fc 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.26; import "@forge-std/Test.sol"; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol"; import {TransparentUpgradeableProxy, ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; @@ -138,6 +139,8 @@ contract StakingTest is Test { contract Initializer is StakingTest { function test_Initialize() public view { + assertEq(IERC20Metadata(address(staking)).name(), "Staked SHU"); + assertEq(IERC20Metadata(address(staking)).symbol(), "sSHU"); assertEq(staking.owner(), address(this), "Wrong owner"); assertEq( address(staking.stakingToken()), From c35c0e6dbfdad7254ebb5832b7513f580ae21376 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 08:37:19 -0300 Subject: [PATCH 10/20] fix test_RevertIf_UnstakeResultsInBalanceLowerThanMinStaked --- src/Staking.sol | 2 -- test/Staking.t.sol | 19 ++++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Staking.sol b/src/Staking.sol index 067f754..641b357 100644 --- a/src/Staking.sol +++ b/src/Staking.sol @@ -10,7 +10,6 @@ import {FixedPointMathLib} from "@solmate/utils/FixedPointMathLib.sol"; import {IRewardsDistributor} from "./interfaces/IRewardsDistributor.sol"; // TODO is this vulnerable to first deposit attack? -// TODO check calculations contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { /*////////////////////////////////////////////////////////////// LIBRARIES @@ -262,7 +261,6 @@ contract Staking is ERC20VotesUpgradeable, Ownable2StepUpgradeable { /// @param stakeId The stake index /// @param amount The amount /// TODO check for reentrancy - /// TODO slippage protection function unstake( address keyper, uint256 stakeId, diff --git a/test/Staking.t.sol b/test/Staking.t.sol index 89179fc..e3e001c 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -1273,12 +1273,9 @@ contract Unstake is StakingTest { staking.unstake(_depositor, stakeId, 0); } - function testFuzz_RevertIf_UnstakeResultsInBalanceLowerThanMinStaked( - address _depositor - ) public { - vm.assume( - _depositor != address(0) && - _depositor != ProxyUtils.getAdminAddress(address(staking)) + function test_RevertIf_UnstakeResultsInBalanceLowerThanMinStaked() public { + address depositor = address( + uint160(uint256(keccak256(abi.encodePacked(1234)))) ); // create multiple users staking to make the rewards amount accumulated @@ -1292,16 +1289,16 @@ contract Unstake is StakingTest { _stake(user, MIN_STAKE); } - _mintGovToken(_depositor, MIN_STAKE); - _setKeyper(_depositor, true); + _mintGovToken(depositor, MIN_STAKE); + _setKeyper(depositor, true); - uint256 stakeId = _stake(_depositor, MIN_STAKE); + uint256 stakeId = _stake(depositor, MIN_STAKE); _jumpAhead(vm.getBlockTimestamp() + LOCK_PERIOD); - vm.prank(_depositor); + vm.prank(depositor); vm.expectRevert(Staking.WithdrawAmountTooHigh.selector); - staking.unstake(_depositor, stakeId, MIN_STAKE); + staking.unstake(depositor, stakeId, MIN_STAKE); } function testFuzz_RevertIf_StakeDoesNotBelongToKeyper( From 1f83e76d105b9dce63caf4d7332225817ad47fbb Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 08:40:53 -0300 Subject: [PATCH 11/20] compile --- test/Staking.t.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/Staking.t.sol b/test/Staking.t.sol index e3e001c..b7082da 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -1274,9 +1274,7 @@ contract Unstake is StakingTest { } function test_RevertIf_UnstakeResultsInBalanceLowerThanMinStaked() public { - address depositor = address( - uint160(uint256(keccak256(abi.encodePacked(1234)))) - ); + address depositor = address(uint160(123)); // create multiple users staking to make the rewards amount accumulated // for _depositor not greater enough to withdraw the min stake From 573d5c48073b24acd37d44599a77a6f1dcdad85b Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 09:04:56 -0300 Subject: [PATCH 12/20] fix nonOwner tests --- test/Staking.t.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/Staking.t.sol b/test/Staking.t.sol index b7082da..ce70533 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -1539,7 +1539,8 @@ contract OwnableFunctions is StakingTest { ) public { vm.assume( _nonOwner != address(0) && - _nonOwner != ProxyUtils.getAdminAddress(address(staking)) + _nonOwner != ProxyUtils.getAdminAddress(address(staking)) && + _nonOwner != address(this) ); vm.prank(_nonOwner); @@ -1559,7 +1560,8 @@ contract OwnableFunctions is StakingTest { ) public { vm.assume( _nonOwner != address(0) && - _nonOwner != ProxyUtils.getAdminAddress(address(staking)) + _nonOwner != ProxyUtils.getAdminAddress(address(staking)) && + _nonOwner != address(this) ); vm.prank(_nonOwner); From 7d9b16ead97692c94f346af0f2f4e16d16c94ef8 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 09:09:16 -0300 Subject: [PATCH 13/20] non owners --- test/Staking.t.sol | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/Staking.t.sol b/test/Staking.t.sol index ce70533..6b81f39 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -1461,14 +1461,10 @@ contract OwnableFunctions is StakingTest { "Wrong balance" ); - // get keyper stake ids - uint256[] memory keyperStakeIds = staking.getKeyperStakeIds(_keyper); assertEq(keyperStakeIds.length, 0, "Wrong stake ids"); } - // TEST CASES FOR NON OWNERS - function testFuzz_RevertIf_NonOwnerSetRewardsDistributor( address _newRewardsDistributor, address _nonOwner @@ -1481,7 +1477,8 @@ contract OwnableFunctions is StakingTest { vm.assume( _nonOwner != address(0) && - _nonOwner != ProxyUtils.getAdminAddress(address(staking)) + _nonOwner != ProxyUtils.getAdminAddress(address(staking)) && + _nonOwner != address(this) ); vm.prank(_nonOwner); @@ -1500,7 +1497,8 @@ contract OwnableFunctions is StakingTest { ) public { vm.assume( _nonOwner != address(0) && - _nonOwner != ProxyUtils.getAdminAddress(address(staking)) + _nonOwner != ProxyUtils.getAdminAddress(address(staking)) && + _nonOwner != address(this) ); vm.prank(_nonOwner); @@ -1519,7 +1517,8 @@ contract OwnableFunctions is StakingTest { ) public { vm.assume( _nonOwner != address(0) && - _nonOwner != ProxyUtils.getAdminAddress(address(staking)) + _nonOwner != ProxyUtils.getAdminAddress(address(staking)) && + _nonOwner != address(this) ); vm.prank(_nonOwner); From 1583e3d21030a3e5ad45cc50ba2c9b04d19fa4bf Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 09:10:52 -0300 Subject: [PATCH 14/20] testFuzz_CollectRewardsTransferTokens --- test/RewardsDistributor.t.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/RewardsDistributor.t.sol b/test/RewardsDistributor.t.sol index 071d4ad..e925cf0 100644 --- a/test/RewardsDistributor.t.sol +++ b/test/RewardsDistributor.t.sol @@ -266,7 +266,9 @@ contract CollectRewards is RewardsDistributorTest { uint256 _jump, uint256 _emissionRate ) public { - vm.assume(address(_receiver) != address(0)); + vm.assume( + _receiver != address(0) && _receiver != address(rewardsDistributor) + ); _emissionRate = bound(_emissionRate, 1, 1e18); rewardsDistributor.setRewardConfiguration(_receiver, _emissionRate); From 7ca7b2fa96dd2f4db8d598ccae0b220bf079aa85 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 09:14:52 -0300 Subject: [PATCH 15/20] test_RevertIf_UnstakeResultsInBalanceLowerThanMinStaked --- test/Staking.t.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Staking.t.sol b/test/Staking.t.sol index 6b81f39..92c610f 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -1287,7 +1287,8 @@ contract Unstake is StakingTest { _stake(user, MIN_STAKE); } - _mintGovToken(depositor, MIN_STAKE); + govToken.mint(depositor, MIN_STAKE); + _setKeyper(depositor, true); uint256 stakeId = _stake(depositor, MIN_STAKE); From d13273e81e42fb4bc9fb7e2216ee6d4764901917 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 09:16:45 -0300 Subject: [PATCH 16/20] skip integration from CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a464789..5147860 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: )" >> $GITHUB_ENV - name: Run tests - run: forge test + run: forge test -no-match-contract Integration coverage: runs-on: ubuntu-latest From 618049465593aede0f2bbd6161bf329b953b159f Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 09:18:30 -0300 Subject: [PATCH 17/20] typo --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5147860..d7f7e14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: )" >> $GITHUB_ENV - name: Run tests - run: forge test -no-match-contract Integration + run: forge test --no-match-contract Integration coverage: runs-on: ubuntu-latest From 112a6b44722c949165093100ae8ecc3d21865676 Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 09:19:17 -0300 Subject: [PATCH 18/20] skip Integration from coverage --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7f7e14..a55b3b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: )" >> $GITHUB_ENV - name: Run coverage - run: forge coverage --report summary --report lcov --ir-minimum + run: forge coverage --report summary --report lcov --ir-minimum --no-match-contract Integration # To ignore coverage for certain directories modify the paths in this step as needed. The # below default ignores coverage results for the test and script directories. Alternatively, From 5bc4cf555cab4e743febda6020beafe1969a452a Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 09:23:39 -0300 Subject: [PATCH 19/20] fuzz assumptions --- test/Staking.t.sol | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/Staking.t.sol b/test/Staking.t.sol index 92c610f..af7b4ff 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -1282,16 +1282,20 @@ contract Unstake is StakingTest { address user = address( uint160(uint256(keccak256(abi.encodePacked(i)))) ); - _mintGovToken(user, MIN_STAKE); + govToken.mint(user, MIN_STAKE); _setKeyper(user, true); - _stake(user, MIN_STAKE); + vm.startPrank(user); + govToken.approve(address(staking), MIN_STAKE); + staking.stake(MIN_STAKE); + vm.stopPrank(); } - govToken.mint(depositor, MIN_STAKE); - _setKeyper(depositor, true); - uint256 stakeId = _stake(depositor, MIN_STAKE); + vm.startPrank(depositor); + govToken.approve(address(staking), MIN_STAKE); + uint256 stakeId = staking.stake(MIN_STAKE); + vm.stopPrank(); _jumpAhead(vm.getBlockTimestamp() + LOCK_PERIOD); From b21101adadc7bebad1dccd144abeecc9bb05518f Mon Sep 17 00:00:00 2001 From: Ana Julia Date: Mon, 8 Jul 2024 09:26:42 -0300 Subject: [PATCH 20/20] test_RevertIf_UnstakeResultsInBalanceLowerThanMinStaked --- test/Staking.t.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Staking.t.sol b/test/Staking.t.sol index af7b4ff..68a6190 100644 --- a/test/Staking.t.sol +++ b/test/Staking.t.sol @@ -1293,6 +1293,7 @@ contract Unstake is StakingTest { _setKeyper(depositor, true); vm.startPrank(depositor); + govToken.mint(depositor, MIN_STAKE); govToken.approve(address(staking), MIN_STAKE); uint256 stakeId = staking.stake(MIN_STAKE); vm.stopPrank();