Skip to content

Commit

Permalink
👔 staking: disable provider when distribution finishes
Browse files Browse the repository at this point in the history
  • Loading branch information
itofarina committed Aug 8, 2024
1 parent bcb8f93 commit f2444a6
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 48 deletions.
82 changes: 41 additions & 41 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -461,93 +461,93 @@ StakedEXATest:invariantNoDuplicatedReward() (runs: 10, calls: 5000, reverts: 0)
StakedEXATest:invariantRewardsUpOnly() (runs: 10, calls: 5000, reverts: 0)
StakedEXATest:invariantShareValueIsOne() (runs: 10, calls: 5000, reverts: 0)
StakedEXATest:testAlreadyListedError() (gas: 43862)
StakedEXATest:testAvgIndex(uint256[3],uint256[2]) (runs: 256, μ: 1247332, ~: 1305416)
StakedEXATest:testAvgStartTime(uint256[3],uint256[2]) (runs: 256, μ: 1233419, ~: 1291503)
StakedEXATest:testBalanceOfDeposit(uint80) (runs: 256, μ: 343891, ~: 350615)
StakedEXATest:testAvgIndex(uint256[3],uint256[2]) (runs: 256, μ: 1246078, ~: 1280202)
StakedEXATest:testAvgStartTime(uint256[3],uint256[2]) (runs: 256, μ: 1232165, ~: 1266289)
StakedEXATest:testBalanceOfDeposit(uint80) (runs: 256, μ: 343956, ~: 350680)
StakedEXATest:testBalanceOfWithdraw(uint256) (runs: 256, μ: 60559, ~: 60566)
StakedEXATest:testCanChangeRewardsDurationWhenDisabled() (gas: 168895)
StakedEXATest:testCanChangeRewardsDurationWhenDisabled() (gas: 174277)
StakedEXATest:testClaimAfterHarvest() (gas: 854153)
StakedEXATest:testClaimAndUnstake() (gas: 1579631)
StakedEXATest:testClaimAndWithdrawAfterRefTime() (gas: 1079773)
StakedEXATest:testClaimBeforeFirstHarvest() (gas: 528009)
StakedEXATest:testDepositClaimsRewardsToReceiver() (gas: 1111861)
StakedEXATest:testDepositEvent(uint256) (runs: 256, μ: 352568, ~: 352286)
StakedEXATest:testDepositShouldClaim(uint256[2],uint32) (runs: 256, μ: 836904, ~: 758252)
StakedEXATest:testDepositClaimsRewardsToReceiver() (gas: 1111839)
StakedEXATest:testDepositEvent(uint256) (runs: 256, μ: 352573, ~: 352286)
StakedEXATest:testDepositShouldClaim(uint256[2],uint32) (runs: 256, μ: 831538, ~: 758192)
StakedEXATest:testDepositToAnotherWithAllowance() (gas: 397264)
StakedEXATest:testDepositToAnotherWithoutAllowanceShouldFail() (gas: 122813)
StakedEXATest:testDepositWithdrawAvgStartTimeAndIndex(uint256[3],uint256,uint256[5]) (runs: 256, μ: 1783788, ~: 1842268)
StakedEXATest:testEarnedWithTime(uint256) (runs: 256, μ: 35410, ~: 35690)
StakedEXATest:testDepositWithdrawAvgStartTimeAndIndex(uint256[3],uint256,uint256[5]) (runs: 256, μ: 1778484, ~: 1842223)
StakedEXATest:testEarnedWithTime(uint256) (runs: 256, μ: 35418, ~: 35690)
StakedEXATest:testEmergencyAdminCanPauseNotUnpause() (gas: 159237)
StakedEXATest:testFinishDistributionEmitEvent() (gas: 389881)
StakedEXATest:testFinishDistributionLetsClaimUnclaimed() (gas: 1584790)
StakedEXATest:testFinishDistributionStopsEmission() (gas: 1564575)
StakedEXATest:testFinishDistributionThatAlreadyFinished() (gas: 424222)
StakedEXATest:testFinishDistributionTransfersRemainingToSavings() (gas: 112135)
StakedEXATest:testFinishDistributionEmitEvent() (gas: 392671)
StakedEXATest:testFinishDistributionLetsClaimUnclaimed() (gas: 1590217)
StakedEXATest:testFinishDistributionStopsEmission() (gas: 1569957)
StakedEXATest:testFinishDistributionThatAlreadyFinished() (gas: 429610)
StakedEXATest:testFinishDistributionTransfersRemainingToSavings() (gas: 117517)
StakedEXATest:testGrantRevokeEmergencyAdmin() (gas: 107506)
StakedEXATest:testGrantRevokePauser() (gas: 107235)
StakedEXATest:testHandlerClaim(uint8) (runs: 256, μ: 302617, ~: 302617)
StakedEXATest:testHandlerDeposit(uint80) (runs: 256, μ: 811983, ~: 823294)
StakedEXATest:testHandlerHarvest(uint64) (runs: 256, μ: 331432, ~: 329775)
StakedEXATest:testHandlerNotifyRewardAmount(uint64) (runs: 256, μ: 127473, ~: 123299)
StakedEXATest:testHandlerSetDuration(uint32) (runs: 256, μ: 149216, ~: 164431)
StakedEXATest:testHandlerDeposit(uint80) (runs: 256, μ: 811316, ~: 823222)
StakedEXATest:testHandlerHarvest(uint64) (runs: 256, μ: 332326, ~: 329795)
StakedEXATest:testHandlerNotifyRewardAmount(uint64) (runs: 256, μ: 127838, ~: 123277)
StakedEXATest:testHandlerSetDuration(uint32) (runs: 256, μ: 152223, ~: 169813)
StakedEXATest:testHandlerSetMarket() (gas: 124946)
StakedEXATest:testHandlerWithdraw(uint256) (runs: 256, μ: 70130, ~: 70137)
StakedEXATest:testHandlerWithdraw(uint256) (runs: 256, μ: 70108, ~: 70115)
StakedEXATest:testHarvest() (gas: 185142)
StakedEXATest:testHarvestAmountWithReducedAllowance() (gas: 202998)
StakedEXATest:testHarvestEffectOnRewardData() (gas: 178166)
StakedEXATest:testHarvestEffectOnRewardData() (gas: 178144)
StakedEXATest:testHarvestEmitsRewardAmountNotified() (gas: 176185)
StakedEXATest:testHarvestFailDoesntDoSDeposits() (gas: 360543)
StakedEXATest:testHarvestWhenFinished() (gas: 308477)
StakedEXATest:testHarvestZero() (gas: 255193)
StakedEXATest:testInitialValues() (gas: 89248)
StakedEXATest:testInsufficientBalanceError(uint256) (runs: 256, μ: 64179, ~: 64309)
StakedEXATest:testInsufficientBalanceError(uint256) (runs: 256, μ: 64173, ~: 64309)
StakedEXATest:testMaxRewardsGasConsumption() (gas: 138537892)
StakedEXATest:testMultipleClaimsVsOne() (gas: 25444683)
StakedEXATest:testMultipleClaimsVsOne() (gas: 25444749)
StakedEXATest:testMultipleHarvests() (gas: 436412)
StakedEXATest:testNoRewardsAfterPeriod(uint256) (runs: 256, μ: 1581196, ~: 1587799)
StakedEXATest:testNoRewardsAfterPeriod(uint256) (runs: 256, μ: 1581090, ~: 1587799)
StakedEXATest:testNotPausingRoleError() (gas: 39589)
StakedEXATest:testNotifyRewardAmount(uint256,uint256) (runs: 256, μ: 129951, ~: 129896)
StakedEXATest:testNotifyRewardAmount(uint256,uint256) (runs: 256, μ: 129954, ~: 129896)
StakedEXATest:testNotifyRewardWithUnderlyingAsset() (gas: 485760)
StakedEXATest:testOnlyAdminEnableReward() (gas: 1199012)
StakedEXATest:testOnlyAdminFinishDistribution() (gas: 190976)
StakedEXATest:testOnlyAdminEnableReward() (gas: 1198990)
StakedEXATest:testOnlyAdminFinishDistribution() (gas: 196358)
StakedEXATest:testOnlyAdminNotifyRewardAmount() (gas: 203387)
StakedEXATest:testOnlyAdminSetProvider() (gas: 143637)
StakedEXATest:testOnlyAdminSetProvider() (gas: 143602)
StakedEXATest:testOnlyAdminSetProviderRatio() (gas: 143295)
StakedEXATest:testOnlyAdminSetRewardsDuration() (gas: 152933)
StakedEXATest:testOnlyAdminSetSavings() (gas: 141076)
StakedEXATest:testPausable() (gas: 1168567)
StakedEXATest:testPausableClaim() (gas: 627593)
StakedEXATest:testPausableHarvest() (gas: 331866)
StakedEXATest:testPauserCanPauseUnpause() (gas: 157897)
StakedEXATest:testPausableHarvest() (gas: 331844)
StakedEXATest:testPauserCanPauseUnpause() (gas: 157875)
StakedEXATest:testPenaltyGrowthRange() (gas: 67233)
StakedEXATest:testPenaltyThresholdRange() (gas: 37134)
StakedEXATest:testPermitAndDeposit() (gas: 361227)
StakedEXATest:testRemoveDepositAllowance() (gas: 489733)
StakedEXATest:testResetDepositAfterRefTime(uint256) (runs: 256, μ: 1027838, ~: 1027558)
StakedEXATest:testResetDepositAfterRefTime(uint256) (runs: 256, μ: 1027844, ~: 1027558)
StakedEXATest:testResetStake() (gas: 1017606)
StakedEXATest:testRewardAmountNotifiedEvent(uint256) (runs: 256, μ: 104616, ~: 105458)
StakedEXATest:testRewardAmountNotifiedEvent(uint256) (runs: 256, μ: 104653, ~: 105458)
StakedEXATest:testRewardNotListedError() (gas: 1109543)
StakedEXATest:testRewardPaidEvent(uint256,uint256) (runs: 256, μ: 800832, ~: 742909)
StakedEXATest:testRewardsAmounts(uint256) (runs: 256, μ: 1588693, ~: 1588333)
StakedEXATest:testRewardPaidEvent(uint256,uint256) (runs: 256, μ: 799853, ~: 741748)
StakedEXATest:testRewardsAmounts(uint256) (runs: 256, μ: 1588702, ~: 1588333)
StakedEXATest:testRewardsDurationSetEvent(uint40) (runs: 256, μ: 52028, ~: 52012)
StakedEXATest:testSetDuration(uint256,uint40) (runs: 256, μ: 58997, ~: 59226)
StakedEXATest:testSetDuration(uint256,uint40) (runs: 256, μ: 58976, ~: 59226)
StakedEXATest:testSetMarketAddressZero() (gas: 37102)
StakedEXATest:testSetMarketOnlyAdmin() (gas: 1272469)
StakedEXATest:testSetMaxRewardsTokensExceeded() (gas: 104944694)
StakedEXATest:testSetMinTime() (gas: 82116)
StakedEXATest:testSetPenaltyGrowth() (gas: 82126)
StakedEXATest:testSetPenaltyThreshold() (gas: 81992)
StakedEXATest:testSetProviderRatioOverOneError() (gas: 37157)
StakedEXATest:testSetProviderZeroAddressError() (gas: 37087)
StakedEXATest:testSetSavingsZeroAddressError() (gas: 37240)
StakedEXATest:testTotalSupplyDeposit(uint80) (runs: 256, μ: 343493, ~: 350217)
StakedEXATest:testSetSavingsZeroAddressError() (gas: 37218)
StakedEXATest:testTotalSupplyDeposit(uint80) (runs: 256, μ: 343471, ~: 350195)
StakedEXATest:testTotalSupplyWithdraw(uint256) (runs: 256, μ: 62073, ~: 62080)
StakedEXATest:testUntransferable(uint80) (runs: 256, μ: 363143, ~: 370769)
StakedEXATest:testWithdrawEvent(uint256) (runs: 256, μ: 508606, ~: 508325)
StakedEXATest:testUntransferable(uint80) (runs: 256, μ: 363144, ~: 370769)
StakedEXATest:testWithdrawEvent(uint256) (runs: 256, μ: 508611, ~: 508325)
StakedEXATest:testWithdrawRewardUnderlyingAsset() (gas: 466609)
StakedEXATest:testWithdrawRewardsOnlyAdmin() (gas: 227940)
StakedEXATest:testWithdrawRewardsOnlyReward() (gas: 1109495)
StakedEXATest:testWithdrawSameAmountRewardsShouldEqual(uint256,uint256) (runs: 256, μ: 1063400, ~: 1131612)
StakedEXATest:testWithdrawWithRewards(uint256) (runs: 256, μ: 867913, ~: 867632)
StakedEXATest:testWithdrawSameAmountRewardsShouldEqual(uint256,uint256) (runs: 256, μ: 1062717, ~: 1131564)
StakedEXATest:testWithdrawWithRewards(uint256) (runs: 256, μ: 867896, ~: 867610)
StakedEXATest:testZeroRateError() (gas: 58048)
StakingPreviewerTest:testAllClaimable() (gas: 452427)
StakingPreviewerTest:testAllClaimed() (gas: 655179)
Expand Down
3 changes: 2 additions & 1 deletion contracts/StakedEXA.sol
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,8 @@ contract StakedEXA is
reward.safeTransfer(savings, (finishAt - block.timestamp) * rewards[reward].rate);
}

if (reward == IERC20(address(market.asset()))) setProvider(address(0));

emit DistributionFinished(reward, msg.sender);
}

Expand Down Expand Up @@ -482,7 +484,6 @@ contract StakedEXA is
/// @notice Sets the provider address.
/// @param provider_ The new provider address.
function setProvider(address provider_) public onlyRole(DEFAULT_ADMIN_ROLE) {
if (provider_ == address(0)) revert ZeroAddress();
provider = provider_;
emit ProviderSet(provider_, msg.sender);
}
Expand Down
29 changes: 23 additions & 6 deletions test/StakedEXA.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.so
import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import { ERC20, ERC4626, IERC4626 } from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
import { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";

import {
Expand Down Expand Up @@ -426,7 +427,7 @@ contract StakedEXATest is Test {
uint256 savings = stEXA.market().maxWithdraw(SAVINGS);
try stEXA.harvest() {} catch {} // solhint-disable-line no-empty-blocks
(uint256 rDuration, , , , ) = stEXA.rewards(asset);
if (rDuration != 0 && assets.mulWadDown(providerRatio) >= rDuration) {
if (rDuration != 0 && assets.mulWadDown(providerRatio) >= rDuration && stEXA.provider() != address(0)) {
assertEq(stEXA.market().balanceOf(PROVIDER), 0, "assets left");
assertEq(
stEXA.market().maxWithdraw(SAVINGS),
Expand Down Expand Up @@ -1410,11 +1411,6 @@ contract StakedEXATest is Test {
assertEq(stEXA.savings(), newSavings);
}

function testSetProviderZeroAddressError() external {
vm.expectRevert(ZeroAddress.selector);
stEXA.setProvider(address(0));
}

function testSetSavingsZeroAddressError() external {
vm.expectRevert(ZeroAddress.selector);
stEXA.setSavings(address(0));
Expand Down Expand Up @@ -1845,6 +1841,27 @@ contract StakedEXATest is Test {
assertEq(exa.balanceOf(SAVINGS), initialAmount);
}

function testHarvestWhenFinished() external {
skip(duration / 2);
stEXA.harvest();

uint256 timestamp = block.timestamp;

(, uint256 finishAt, , , ) = stEXA.rewards(providerAsset);
assertNotEq(finishAt, timestamp);

stEXA.finishDistribution(providerAsset);

(, finishAt, , , ) = stEXA.rewards(providerAsset);
assertEq(finishAt, timestamp, "finishAt != timestamp");
assertEq(stEXA.provider(), address(0));

vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InvalidApprover.selector, address(0)));
stEXA.harvest();
(, finishAt, , , ) = stEXA.rewards(providerAsset);
assertEq(finishAt, timestamp);
}

function minMaxWithdrawAllowance() internal view returns (uint256) {
return Math.min(market.convertToAssets(market.allowance(PROVIDER, address(stEXA))), market.maxWithdraw(PROVIDER));
}
Expand Down

0 comments on commit f2444a6

Please sign in to comment.