diff --git a/.gas-snapshot b/.gas-snapshot index fb444ee7..b204db98 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -461,90 +461,91 @@ 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: 43884) -StakedEXATest:testAvgIndex(uint256[3],uint256[2]) (runs: 256, μ: 1216598, ~: 1249206) -StakedEXATest:testAvgStartTime(uint256[3],uint256[2]) (runs: 256, μ: 1203004, ~: 1235612) -StakedEXATest:testBalanceOfDeposit(uint80) (runs: 256, μ: 355442, ~: 362486) +StakedEXATest:testAvgIndex(uint256[3],uint256[2]) (runs: 256, μ: 1216267, ~: 1249579) +StakedEXATest:testAvgStartTime(uint256[3],uint256[2]) (runs: 256, μ: 1202673, ~: 1235985) +StakedEXATest:testBalanceOfDeposit(uint80) (runs: 256, μ: 355406, ~: 362451) StakedEXATest:testBalanceOfWithdraw(uint256) (runs: 256, μ: 60558, ~: 60565) StakedEXATest:testCanChangeRewardsDurationWhenDisabled() (gas: 168917) -StakedEXATest:testClaimAfterHarvest() (gas: 840843) -StakedEXATest:testClaimAndUnstake() (gas: 1486220) -StakedEXATest:testClaimAndWithdrawAfterRefTime() (gas: 1091230) -StakedEXATest:testClaimBeforeFirstHarvest() (gas: 539901) -StakedEXATest:testDepositEvent(uint256) (runs: 256, μ: 364348, ~: 364057) -StakedEXATest:testDepositShouldClaim(uint256[2],uint32) (runs: 256, μ: 824806, ~: 742146) -StakedEXATest:testDepositWithdrawAvgStartTimeAndIndex(uint256[3],uint256,uint256[5]) (runs: 256, μ: 1746056, ~: 1809047) -StakedEXATest:testEarnedWithTime(uint256) (runs: 256, μ: 35420, ~: 35711) +StakedEXATest:testClaimAfterHarvest() (gas: 840853) +StakedEXATest:testClaimAndUnstake() (gas: 1486466) +StakedEXATest:testClaimAndWithdrawAfterRefTime() (gas: 1091565) +StakedEXATest:testClaimBeforeFirstHarvest() (gas: 539888) +StakedEXATest:testDepositClaimsRewardsToReceiver() (gas: 1050445) +StakedEXATest:testDepositEvent(uint256) (runs: 256, μ: 364333, ~: 364044) +StakedEXATest:testDepositShouldClaim(uint256[2],uint32) (runs: 256, μ: 819344, ~: 742180) +StakedEXATest:testDepositWithdrawAvgStartTimeAndIndex(uint256[3],uint256,uint256[5]) (runs: 256, μ: 1748331, ~: 1809795) +StakedEXATest:testEarnedWithTime(uint256) (runs: 256, μ: 35436, ~: 35711) StakedEXATest:testEmergencyAdminCanPauseNotUnpause() (gas: 159039) -StakedEXATest:testFinishDistributionEmitEvent() (gas: 402739) -StakedEXATest:testFinishDistributionLetsClaimUnclaimed() (gas: 1516928) -StakedEXATest:testFinishDistributionStopsEmission() (gas: 1496514) -StakedEXATest:testFinishDistributionThatAlreadyFinished() (gas: 436027) +StakedEXATest:testFinishDistributionEmitEvent() (gas: 402683) +StakedEXATest:testFinishDistributionLetsClaimUnclaimed() (gas: 1516914) +StakedEXATest:testFinishDistributionStopsEmission() (gas: 1496500) +StakedEXATest:testFinishDistributionThatAlreadyFinished() (gas: 436014) StakedEXATest:testFinishDistributionTransfersRemainingToSavings() (gas: 112201) StakedEXATest:testGrantRevokeEmergencyAdmin() (gas: 107216) StakedEXATest:testGrantRevokePauser() (gas: 107191) -StakedEXATest:testHandlerClaim(uint8) (runs: 256, μ: 302723, ~: 302723) -StakedEXATest:testHandlerDeposit(uint80) (runs: 256, μ: 799412, ~: 810018) -StakedEXATest:testHandlerHarvest(uint64) (runs: 256, μ: 343765, ~: 342656) -StakedEXATest:testHandlerNotifyRewardAmount(uint64) (runs: 256, μ: 127301, ~: 123310) -StakedEXATest:testHandlerSetDuration(uint32) (runs: 256, μ: 145620, ~: 164475) +StakedEXATest:testHandlerClaim(uint8) (runs: 256, μ: 302903, ~: 302903) +StakedEXATest:testHandlerDeposit(uint80) (runs: 256, μ: 798727, ~: 810004) +StakedEXATest:testHandlerHarvest(uint64) (runs: 256, μ: 343431, ~: 342608) +StakedEXATest:testHandlerNotifyRewardAmount(uint64) (runs: 256, μ: 127022, ~: 123274) +StakedEXATest:testHandlerSetDuration(uint32) (runs: 256, μ: 145941, ~: 164475) StakedEXATest:testHandlerSetMarket() (gas: 157950) -StakedEXATest:testHandlerWithdraw(uint256) (runs: 256, μ: 69995, ~: 70002) -StakedEXATest:testHarvest() (gas: 197956) -StakedEXATest:testHarvestAmountWithReducedAllowance() (gas: 215375) -StakedEXATest:testHarvestEffectOnRewardData() (gas: 190979) -StakedEXATest:testHarvestEmitsRewardAmountNotified() (gas: 188932) -StakedEXATest:testHarvestZero() (gas: 236758) +StakedEXATest:testHandlerWithdraw(uint256) (runs: 256, μ: 69973, ~: 69980) +StakedEXATest:testHarvest() (gas: 197944) +StakedEXATest:testHarvestAmountWithReducedAllowance() (gas: 215363) +StakedEXATest:testHarvestEffectOnRewardData() (gas: 190945) +StakedEXATest:testHarvestEmitsRewardAmountNotified() (gas: 188920) +StakedEXATest:testHarvestZero() (gas: 236746) StakedEXATest:testInitialValues() (gas: 89269) -StakedEXATest:testInsufficientBalanceError(uint256) (runs: 256, μ: 64231, ~: 64353) -StakedEXATest:testMaxRewardsGasConsumption() (gas: 138550598) -StakedEXATest:testMultipleClaimsVsOne() (gas: 26036290) -StakedEXATest:testMultipleHarvests() (gas: 461995) -StakedEXATest:testNoRewardsAfterPeriod(uint256) (runs: 256, μ: 1569892, ~: 1576164) +StakedEXATest:testInsufficientBalanceError(uint256) (runs: 256, μ: 64211, ~: 64353) +StakedEXATest:testMaxRewardsGasConsumption() (gas: 138555933) +StakedEXATest:testMultipleClaimsVsOne() (gas: 26048842) +StakedEXATest:testMultipleHarvests() (gas: 461971) +StakedEXATest:testNoRewardsAfterPeriod(uint256) (runs: 256, μ: 1569438, ~: 1576150) StakedEXATest:testNotPausingRoleError() (gas: 39481) -StakedEXATest:testNotifyRewardAmount(uint256,uint256) (runs: 256, μ: 129980, ~: 129930) -StakedEXATest:testNotifyRewardWithUnderlyingAsset() (gas: 497622) -StakedEXATest:testOnlyAdminEnableReward() (gas: 1199188) +StakedEXATest:testNotifyRewardAmount(uint256,uint256) (runs: 256, μ: 129968, ~: 129918) +StakedEXATest:testNotifyRewardWithUnderlyingAsset() (gas: 497597) +StakedEXATest:testOnlyAdminEnableReward() (gas: 1199166) StakedEXATest:testOnlyAdminFinishDistribution() (gas: 191108) -StakedEXATest:testOnlyAdminNotifyRewardAmount() (gas: 203509) +StakedEXATest:testOnlyAdminNotifyRewardAmount() (gas: 203497) StakedEXATest:testOnlyAdminSetProvider() (gas: 143746) StakedEXATest:testOnlyAdminSetProviderRatio() (gas: 143405) StakedEXATest:testOnlyAdminSetRewardsDuration() (gas: 153043) StakedEXATest:testOnlyAdminSetSavings() (gas: 141186) -StakedEXATest:testPausable() (gas: 1154779) -StakedEXATest:testPausableClaim() (gas: 639349) -StakedEXATest:testPausableHarvest() (gas: 344638) -StakedEXATest:testPauserCanPauseUnpause() (gas: 157789) +StakedEXATest:testPausable() (gas: 1154861) +StakedEXATest:testPausableClaim() (gas: 639455) +StakedEXATest:testPausableHarvest() (gas: 344626) +StakedEXATest:testPauserCanPauseUnpause() (gas: 157877) StakedEXATest:testPenaltyGrowthRange() (gas: 67233) StakedEXATest:testPenaltyThresholdRange() (gas: 37156) -StakedEXATest:testPermitAndDeposit() (gas: 373099) -StakedEXATest:testResetDepositAfterRefTime(uint256) (runs: 256, μ: 1020703, ~: 1020403) -StakedEXATest:testRewardAmountNotifiedEvent(uint256) (runs: 256, μ: 104690, ~: 105555) +StakedEXATest:testPermitAndDeposit() (gas: 373086) +StakedEXATest:testResetDepositAfterRefTime(uint256) (runs: 256, μ: 1020687, ~: 1020389) +StakedEXATest:testRewardAmountNotifiedEvent(uint256) (runs: 256, μ: 104741, ~: 105543) StakedEXATest:testRewardNotListedError() (gas: 1109565) -StakedEXATest:testRewardPaidEvent(uint256,uint256) (runs: 256, μ: 810710, ~: 752450) -StakedEXATest:testRewardsAmounts(uint256) (runs: 256, μ: 1577010, ~: 1576599) -StakedEXATest:testRewardsDurationSetEvent(uint40) (runs: 256, μ: 52074, ~: 52056) -StakedEXATest:testSetDuration(uint256,uint40) (runs: 256, μ: 58982, ~: 59226) +StakedEXATest:testRewardPaidEvent(uint256,uint256) (runs: 256, μ: 813598, ~: 862521) +StakedEXATest:testRewardsAmounts(uint256) (runs: 256, μ: 1576995, ~: 1576585) +StakedEXATest:testRewardsDurationSetEvent(uint40) (runs: 256, μ: 52072, ~: 52056) +StakedEXATest:testSetDuration(uint256,uint40) (runs: 256, μ: 58965, ~: 59226) StakedEXATest:testSetMarketAddressZero() (gas: 37124) StakedEXATest:testSetMarketOnlyAdmin() (gas: 1300351) StakedEXATest:testSetMaxRewardsTokensExceeded() (gas: 104946678) StakedEXATest:testSetMinTime() (gas: 81960) -StakedEXATest:testSetPenaltyGrowth() (gas: 82125) +StakedEXATest:testSetPenaltyGrowth() (gas: 82148) StakedEXATest:testSetPenaltyThreshold() (gas: 81925) -StakedEXATest:testSetProviderRatioOverOneError() (gas: 37156) -StakedEXATest:testSetProviderZeroAddressError() (gas: 37175) -StakedEXATest:testSetSavingsZeroAddressError() (gas: 37284) -StakedEXATest:testTotalSupplyDeposit(uint80) (runs: 256, μ: 354935, ~: 361979) +StakedEXATest:testSetProviderRatioOverOneError() (gas: 37179) +StakedEXATest:testSetProviderZeroAddressError() (gas: 37131) +StakedEXATest:testSetSavingsZeroAddressError() (gas: 37240) +StakedEXATest:testTotalSupplyDeposit(uint80) (runs: 256, μ: 354921, ~: 361966) StakedEXATest:testTotalSupplyWithdraw(uint256) (runs: 256, μ: 62006, ~: 62013) -StakedEXATest:testUntransferable(uint80) (runs: 256, μ: 374588, ~: 382531) -StakedEXATest:testWithdrawEvent(uint256) (runs: 256, μ: 520341, ~: 520046) -StakedEXATest:testWithdrawSameAmountRewardsShouldEqual(uint256,uint256) (runs: 256, μ: 1073469, ~: 1143214) -StakedEXATest:testWithdrawWithRewards(uint256) (runs: 256, μ: 879758, ~: 879463) +StakedEXATest:testUntransferable(uint80) (runs: 256, μ: 374574, ~: 382518) +StakedEXATest:testWithdrawEvent(uint256) (runs: 256, μ: 520326, ~: 520033) +StakedEXATest:testWithdrawSameAmountRewardsShouldEqual(uint256,uint256) (runs: 256, μ: 1076739, ~: 1143249) +StakedEXATest:testWithdrawWithRewards(uint256) (runs: 256, μ: 879743, ~: 879450) StakedEXATest:testZeroRateError() (gas: 58047) -StakingPreviewerTest:testAllClaimable() (gas: 431264) -StakingPreviewerTest:testAllClaimed() (gas: 632779) -StakingPreviewerTest:testAllEarned() (gas: 316370) -StakingPreviewerTest:testAllRewards() (gas: 485771) -StakingPreviewerTest:testStaking() (gas: 528491) +StakingPreviewerTest:testAllClaimable() (gas: 431263) +StakingPreviewerTest:testAllClaimed() (gas: 633003) +StakingPreviewerTest:testAllEarned() (gas: 316369) +StakingPreviewerTest:testAllRewards() (gas: 485770) +StakingPreviewerTest:testStaking() (gas: 528490) SwapperTest:testSwapBasic() (gas: 216831) SwapperTest:testSwapWithAllowance() (gas: 481530) SwapperTest:testSwapWithInaccurateSlippageSendsETHToAccount() (gas: 297968) diff --git a/contracts/StakedEXA.sol b/contracts/StakedEXA.sol index dc1d4ed4..57659980 100644 --- a/contracts/StakedEXA.sol +++ b/contracts/StakedEXA.sol @@ -140,7 +140,7 @@ contract StakedEXA is if (balance != 0) claimWithdraw(reward, to, balance); avgIndexes[to][reward] = rewards[reward].index; } else { - if (balance != 0) claim_(reward); + if (balance != 0) claim_(reward, to); uint256 numerator = avgIndexes[to][reward] * balance + rewards[reward].index * amount; avgIndexes[to][reward] = numerator == 0 ? 0 : (numerator - 1) / total + 1; } @@ -367,44 +367,44 @@ contract StakedEXA is /// @notice Internal function to claim rewards. /// @param reward The reward token. - function claim_(IERC20 reward) internal whenNotPaused { - uint256 time = block.timestamp * 1e18 - avgStart[msg.sender]; + function claim_(IERC20 reward, address account) internal whenNotPaused { + uint256 time = block.timestamp * 1e18 - avgStart[account]; if (time <= minTime * 1e18) return; - uint256 claimedAmount = claimed[msg.sender][reward]; + uint256 claimedAmount = claimed[account][reward]; // due to excess exposure - uint256 claimableAmount = Math.max(rawClaimable(reward, msg.sender, balanceOf(msg.sender)), claimedAmount); + uint256 claimableAmount = Math.max(rawClaimable(reward, account, balanceOf(account)), claimedAmount); uint256 claimAmount = claimableAmount - claimedAmount; - if (claimAmount != 0) claimed[msg.sender][reward] = claimedAmount + claimAmount; + if (claimAmount != 0) claimed[account][reward] = claimedAmount + claimAmount; if (time > refTime * 1e18) { - uint256 rawEarned = earned(reward, msg.sender, balanceOf(msg.sender)); - uint256 savedAmount = saved[msg.sender][reward]; + uint256 rawEarned = earned(reward, account, balanceOf(account)); + uint256 savedAmount = saved[account][reward]; uint256 maxClaimed = Math.min(rawEarned, claimableAmount); uint256 saveAmount = rawEarned > maxClaimed + savedAmount ? rawEarned - maxClaimed - savedAmount : 0; if (saveAmount != 0) { - saved[msg.sender][reward] = savedAmount + saveAmount; + saved[account][reward] = savedAmount + saveAmount; reward.safeTransfer(savings, saveAmount); } } if (claimAmount != 0) { - reward.safeTransfer(msg.sender, claimAmount); - emit RewardPaid(reward, msg.sender, claimAmount); + reward.safeTransfer(account, claimAmount); + emit RewardPaid(reward, account, claimAmount); } } /// @notice Claims rewards for a specific reward token. /// @param reward The reward token. function claim(IERC20 reward) external { - claim_(reward); + claim_(reward, msg.sender); } /// @notice Claims rewards for all reward tokens. function claimAll() external { for (uint256 i = 0; i < rewardsTokens.length; ++i) { - claim_(rewardsTokens[i]); + claim_(rewardsTokens[i], msg.sender); } } diff --git a/test/StakedEXA.t.sol b/test/StakedEXA.t.sol index ae2cf3d2..850548fc 100644 --- a/test/StakedEXA.t.sol +++ b/test/StakedEXA.t.sol @@ -1711,6 +1711,26 @@ contract StakedEXATest is Test { assertLt(vm.lastCallGas().gasTotalUsed, 10_000_000); } + function testDepositClaimsRewardsToReceiver() external { + uint256 assets = 1_000e18; + exa.mint(address(this), assets); + stEXA.deposit(assets, address(this)); + + skip(minTime + 1); + + uint256 claimableAmount = claimable(rA, address(this)); + uint256 balanceBefore = rA.balanceOf(address(this)); + + exa.mint(BOB, assets); + vm.startPrank(BOB); + exa.approve(address(stEXA), assets); + stEXA.deposit(assets, address(this)); + vm.stopPrank(); + + assertEq(claimableAmount, rA.balanceOf(address(this)) - balanceBefore); + + } + function minMaxWithdrawAllowance() internal view returns (uint256) { return Math.min(market.convertToAssets(market.allowance(PROVIDER, address(stEXA))), market.maxWithdraw(PROVIDER)); }