From 209564e742b4133f9a7ad5dd4858dd75c025d72b Mon Sep 17 00:00:00 2001 From: Santiago Sanchez Avalos Date: Tue, 18 Jun 2024 13:40:53 -0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20rewards:=20support=20claim=20on=20b?= =?UTF-8?q?ehalf=20of=20account?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/silver-poets-raise.md | 5 + .gas-snapshot | 217 +++++++++++++------------- contracts/RewardsController.sol | 55 +++++-- test/RewardsController.t.sol | 53 ++++++- test/hardhat/19_rewards_controller.ts | 7 + 5 files changed, 217 insertions(+), 120 deletions(-) create mode 100644 .changeset/silver-poets-raise.md diff --git a/.changeset/silver-poets-raise.md b/.changeset/silver-poets-raise.md new file mode 100644 index 00000000..c906fe39 --- /dev/null +++ b/.changeset/silver-poets-raise.md @@ -0,0 +1,5 @@ +--- +"@exactly/protocol": minor +--- + +✨ rewards: support claim on behalf of account diff --git a/.gas-snapshot b/.gas-snapshot index 2f4b42dc..c5370a94 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -21,7 +21,7 @@ DebtManagerTest:testAllowanceSurplus() (gas: 965332) DebtManagerTest:testApproveMaliciousMarket() (gas: 50529) DebtManagerTest:testApproveMarket() (gas: 83345) DebtManagerTest:testBalancerFlashloanCallFromDifferentOrigin() (gas: 98619) -DebtManagerTest:testCallReceiveFlashLoanFromAnyAddress() (gas: 61271) +DebtManagerTest:testCallReceiveFlashLoanFromAnyAddress() (gas: 63771) DebtManagerTest:testDeleverage() (gas: 963660) DebtManagerTest:testDeleverageHalfPosition() (gas: 980627) DebtManagerTest:testDeleverageIncremental() (gas: 1661563) @@ -32,37 +32,37 @@ DebtManagerTest:testFakeMarketDeleverage() (gas: 1237823) DebtManagerTest:testFakeMarketLeverage() (gas: 1276166) DebtManagerTest:testFakeMarketRollFixed() (gas: 1966338) DebtManagerTest:testFixedRoll() (gas: 1180518) -DebtManagerTest:testFixedRollSameMaturityWithThreeLoops() (gas: 989775) -DebtManagerTest:testFixedRollWithAccurateBorrowSlippage() (gas: 1736390) -DebtManagerTest:testFixedRollWithAccurateBorrowSlippageWithThreeLoops() (gas: 2742970) -DebtManagerTest:testFixedRollWithAccurateRepaySlippage() (gas: 1736348) -DebtManagerTest:testFixedRollWithAccurateRepaySlippageWithThreeLoops() (gas: 2736967) +DebtManagerTest:testFixedRollSameMaturityWithThreeLoops() (gas: 994775) +DebtManagerTest:testFixedRollWithAccurateBorrowSlippage() (gas: 1743390) +DebtManagerTest:testFixedRollWithAccurateBorrowSlippageWithThreeLoops() (gas: 2754970) +DebtManagerTest:testFixedRollWithAccurateRepaySlippage() (gas: 1743348) +DebtManagerTest:testFixedRollWithAccurateRepaySlippageWithThreeLoops() (gas: 2743967) DebtManagerTest:testFixedToFloatingRoll() (gas: 1141582) DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidity() (gas: 1245428) -DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidityWithSlippage() (gas: 1924654) -DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidityWithSlippageWithThreeLoops() (gas: 2612074) -DebtManagerTest:testFixedToFloatingRollWithAccurateSlippage() (gas: 1653099) +DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidityWithSlippage() (gas: 1939154) +DebtManagerTest:testFixedToFloatingRollHigherThanAvailableLiquidityWithSlippageWithThreeLoops() (gas: 2626574) +DebtManagerTest:testFixedToFloatingRollWithAccurateSlippage() (gas: 1660099) DebtManagerTest:testFlashloanFeeGreaterThanZero() (gas: 782399) DebtManagerTest:testFloatingToFixedRoll() (gas: 1122257) DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidity() (gas: 1290029) -DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidityWithSlippage() (gas: 2025850) -DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidityWithSlippageWithThreePools() (gas: 2787984) -DebtManagerTest:testFloatingToFixedRollWithAccurateSlippage() (gas: 1687218) -DebtManagerTest:testFloatingToFixedRollWithAccurateSlippageWithPreviousPosition() (gas: 1924341) -DebtManagerTest:testFuzzRolls(uint8[4],uint8[4],uint256[4],uint40[4],uint8[4]) (runs: 256, μ: 6504217, ~: 6529787) +DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidityWithSlippage() (gas: 2037850) +DebtManagerTest:testFloatingToFixedRollHigherThanAvailableLiquidityWithSlippageWithThreePools() (gas: 2794984) +DebtManagerTest:testFloatingToFixedRollWithAccurateSlippage() (gas: 1699218) +DebtManagerTest:testFloatingToFixedRollWithAccurateSlippageWithPreviousPosition() (gas: 1931341) +DebtManagerTest:testFuzzRolls(uint8[4],uint8[4],uint256[4],uint40[4],uint8[4]) (runs: 256, μ: 6567002, ~: 6703643) DebtManagerTest:testLateFixedRoll() (gas: 1310831) DebtManagerTest:testLateFixedRollWithThreeLoops() (gas: 1897545) DebtManagerTest:testLateFixedToFloatingRoll() (gas: 1273784) DebtManagerTest:testLateFixedToFloatingRollWithThreeLoops() (gas: 1834425) DebtManagerTest:testLeverage() (gas: 624861) DebtManagerTest:testLeverageIncremental() (gas: 1334229) -DebtManagerTest:testLeverageShouldFailWhenHealthFactorNearOne() (gas: 1328819) +DebtManagerTest:testLeverageShouldFailWhenHealthFactorNearOne() (gas: 1395319) DebtManagerTest:testLeverageWithAlreadyDepositedAmount() (gas: 812467) DebtManagerTest:testLeverageWithInvalidBalancerVault() (gas: 3909346) DebtManagerTest:testLeverageWithMoreThanBalancerAvailableLiquidity() (gas: 1000405) DebtManagerTest:testLeverageWithNegativePrincipal() (gas: 1255085) DebtManagerTest:testLeverageWithPartialNegativePrincipal() (gas: 1424159) -DebtManagerTest:testMockBalancerVault() (gas: 6109482) +DebtManagerTest:testMockBalancerVault() (gas: 6116982) DebtManagerTest:testPartialDeleverageWithWithdrawAndNewRatio() (gas: 1031475) DebtManagerTest:testPartialDeleverageWithWithdrawKeepingRatio() (gas: 1031481) DebtManagerTest:testPartialFixedRoll() (gas: 1200784) @@ -81,7 +81,7 @@ DebtPreviewerTest:testLeverageRatesWithNativeBorrow() (gas: 416300) DebtPreviewerTest:testLeverageRatesWithNegativeNativeResult() (gas: 416382) DebtPreviewerTest:testLeverageRatesZeroPrincipalCrossAsset() (gas: 712846) DebtPreviewerTest:testLeverageRatesZeroPrincipalSameAsset() (gas: 419463) -DebtPreviewerTest:testPreviewDeleverageSameAsset() (gas: 2305503) +DebtPreviewerTest:testPreviewDeleverageSameAsset() (gas: 2323003) DebtPreviewerTest:testPreviewEmptyLeverage() (gas: 317803) DebtPreviewerTest:testPreviewLeverage() (gas: 865236) DebtPreviewerTest:testPreviewLeverageBalancerAvailableLiquidity() (gas: 321707) @@ -94,13 +94,13 @@ DebtPreviewerTest:testPreviewLeverageSameWETHAssetMaxRatioMultipleCollateralAndD DebtPreviewerTest:testPreviewLeverageSameWETHAssetMultipleCollateralAndDebtWithMinHealthFactor() (gas: 2400935) DebtPreviewerTest:testPreviewMaxRatioWithdrawWithSameAssetLeverage() (gas: 976876) DebtPreviewerTest:testPreviewSameAssetInvalidLeverageShouldCapRatio() (gas: 1052001) -EscrowedEXATest:testCancelExternalStreams() (gas: 368824) -EscrowedEXATest:testCancelExternalStreamsWithesEXACancel() (gas: 1104987) +EscrowedEXATest:testCancelExternalStreams() (gas: 371324) +EscrowedEXATest:testCancelExternalStreamsWithesEXACancel() (gas: 1107487) EscrowedEXATest:testCancelShouldDeleteReserves() (gas: 694424) EscrowedEXATest:testCancelShouldGiveReservesBack() (gas: 1039449) EscrowedEXATest:testCancelTwiceShouldRevert() (gas: 703568) EscrowedEXATest:testCancelWithInvalidAccount() (gas: 511059) -EscrowedEXATest:testFakeTokenWithesEXARecipient() (gas: 1173516) +EscrowedEXATest:testFakeTokenWithesEXARecipient() (gas: 1176016) EscrowedEXATest:testGrantTransferrerRoleAsAdmin() (gas: 75138) EscrowedEXATest:testMint() (gas: 181476) EscrowedEXATest:testMintMoreThanBalance() (gas: 41605) @@ -141,11 +141,11 @@ InterestRateModelTest:testFixedBorrowRate() (gas: 2052054) InterestRateModelTest:testFixedRateRevertAlreadyMatured() (gas: 2046236) InterestRateModelTest:testFixedRateRevertUtilizationExceeded() (gas: 2053399) InterestRateModelTest:testFloatingBorrowRate() (gas: 2045540) -InterestRateModelTest:testFuzzFixedRateGrowth(uint256,uint256,uint256,uint256) (runs: 256, μ: 2067148, ~: 2063904) -InterestRateModelTest:testFuzzFixedRateTimeSensitivity(uint256,uint256,uint256) (runs: 256, μ: 2073165, ~: 2073210) -InterestRateModelTest:testFuzzReferenceLegacyRateFixed(uint32,uint256,uint256[2],uint256[2],uint256,uint256,uint256) (runs: 256, μ: 9997383, ~: 10168015) -InterestRateModelTest:testFuzzReferenceRateFixed(uint256,uint256,uint256,uint256,uint256,uint256,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,int256,uint256,uint256)) (runs: 256, μ: 2337320, ~: 2339917) -InterestRateModelTest:testFuzzReferenceRateFloating(uint256,uint256,(uint256,uint256,uint256,uint256,uint256,uint256,uint256)) (runs: 256, μ: 2275592, ~: 2276418) +InterestRateModelTest:testFuzzFixedRateGrowth(uint256,uint256,uint256,uint256) (runs: 256, μ: 2066394, ~: 2063637) +InterestRateModelTest:testFuzzFixedRateTimeSensitivity(uint256,uint256,uint256) (runs: 256, μ: 2073176, ~: 2073210) +InterestRateModelTest:testFuzzReferenceLegacyRateFixed(uint32,uint256,uint256[2],uint256[2],uint256,uint256,uint256) (runs: 256, μ: 10048826, ~: 10168016) +InterestRateModelTest:testFuzzReferenceRateFixed(uint256,uint256,uint256,uint256,uint256,uint256,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,int256,uint256,uint256)) (runs: 256, μ: 2337296, ~: 2339311) +InterestRateModelTest:testFuzzReferenceRateFloating(uint256,uint256,(uint256,uint256,uint256,uint256,uint256,uint256,uint256)) (runs: 256, μ: 2275548, ~: 2276258) InterestRateModelTest:testMinTimeToMaturity() (gas: 2063554) InterestRateModelTest:testRevertMaxUtilizationLowerThanWad() (gas: 266682) MarketTest:testAccountLiquidityAdjustedDebt() (gas: 499501) @@ -193,7 +193,7 @@ MarketTest:testDistributionOfLossesShouldReduceFromFloatingBackupBorrowedAccordi MarketTest:testEarlyRepayLiquidationUnassignedEarnings() (gas: 2116887) MarketTest:testEarlyRepaymentWithExcessiveAmountOfFees() (gas: 3423650) MarketTest:testEarlyWithdrawFromFreeLunchShouldNotRevertWithFloatingFullUtilization() (gas: 1050103) -MarketTest:testEmergencyAdminRole() (gas: 317263) +MarketTest:testEmergencyAdminRole() (gas: 322263) MarketTest:testEmitFrozen() (gas: 91047) MarketTest:testFixedBorrowFailingWhenFlexibleBorrowAccruesDebt() (gas: 1530894) MarketTest:testFixedBorrowRateToMaturity() (gas: 562527) @@ -208,7 +208,7 @@ MarketTest:testFlexibleBorrowFromAnotherUserSubtractsAllowance() (gas: 469265) MarketTest:testFlexibleBorrowFromAnotherUserWithAllowance() (gas: 458681) MarketTest:testFlexibleBorrowFromAnotherUserWithoutAllowance() (gas: 252381) MarketTest:testFrontRunSmartPoolEarningsDistributionWithBigPenaltyRepayment() (gas: 1370302) -MarketTest:testFullPause() (gas: 5954954) +MarketTest:testFullPause() (gas: 5959954) MarketTest:testInitiallyUnfrozen() (gas: 15592) MarketTest:testInsufficientProtocolLiquidity() (gas: 1940418) MarketTest:testLiquidateAndChargeIncentiveForLenders() (gas: 2433524) @@ -220,7 +220,7 @@ MarketTest:testLiquidateFlexibleAndFixedBorrowPositionsInSingleCall() (gas: 2638 MarketTest:testLiquidateFlexibleBorrow() (gas: 2160641) MarketTest:testLiquidateFlexibleBorrowChargeLendersAssetsToLiquidator() (gas: 1152387) MarketTest:testLiquidateFlexibleBorrowConsideringDebtOverTime() (gas: 1167683) -MarketTest:testLiquidateLeavingDustAsCollateral() (gas: 3617882) +MarketTest:testLiquidateLeavingDustAsCollateral() (gas: 3640382) MarketTest:testLiquidateTransferRepayAssetsBeforeSeize() (gas: 1045359) MarketTest:testLiquidateUpdateFloatingDebt() (gas: 1903239) MarketTest:testLiquidateWhenFrozen() (gas: 1232974) @@ -234,7 +234,7 @@ MarketTest:testMultipleDepositsToSmartPool() (gas: 915573) MarketTest:testMultipleFixedBorrowsRepays() (gas: 1348420) MarketTest:testMultipleLiquidationSameUser() (gas: 2940723) MarketTest:testNotEnteredMarketShouldNotBeSeized() (gas: 8571427) -MarketTest:testOnlyAdminCanFreezeUnfreeze() (gas: 207703) +MarketTest:testOnlyAdminCanFreezeUnfreeze() (gas: 212203) MarketTest:testOperationsShouldUpdateFloatingAssetsAverage() (gas: 1445531) MarketTest:testOperationsWithBtcWbtcRate() (gas: 8243018) MarketTest:testOperationsWithStEthAsset() (gas: 8130470) @@ -285,7 +285,7 @@ PoolLibTest:testAtomicDepositBorrowRepayWithdraw() (gas: 46018) PoolLibTest:testBackupBorrow() (gas: 33676) PoolLibTest:testEarningsAccrual() (gas: 38999) PoolLibTest:testEarningsDistribution() (gas: 32640) -PoolLibTest:testFuzzAddRemoveAll(uint8[12]) (runs: 256, μ: 55186, ~: 54164) +PoolLibTest:testFuzzAddRemoveAll(uint8[12]) (runs: 256, μ: 54558, ~: 54037) PoolLibTest:testMaturityRangeLimit() (gas: 7873) PoolLibTest:testMaturityRangeTooWide() (gas: 6831) PreviewerTest:testAccountsReturningAccurateAmounts() (gas: 1393737) @@ -294,22 +294,22 @@ PreviewerTest:testAccountsWithAccountOnlyDeposit() (gas: 862391) PreviewerTest:testAccountsWithAccountThatHasBalances() (gas: 2265104) PreviewerTest:testAccountsWithEmptyAccount() (gas: 690558) PreviewerTest:testAccountsWithIntermediateOperationsReturningAccurateAmounts() (gas: 17628100) -PreviewerTest:testActualTimeBeforeStartDistributionRewards() (gas: 7768578) -PreviewerTest:testEmptyExactly() (gas: 5660590) +PreviewerTest:testActualTimeBeforeStartDistributionRewards() (gas: 7828218) +PreviewerTest:testEmptyExactly() (gas: 5720098) PreviewerTest:testExactlyReturningInterestRateModelData() (gas: 688149) PreviewerTest:testFixedAvailableLiquidityProjectingNewFloatingDebt() (gas: 13312154) -PreviewerTest:testFixedPoolsA() (gas: 19328899) +PreviewerTest:testFixedPoolsA() (gas: 19333899) PreviewerTest:testFixedPoolsChangingMaturityInTime() (gas: 1627253) PreviewerTest:testFixedPoolsRatesAndUtilizations() (gas: 14825642) PreviewerTest:testFixedPoolsWithFloatingAssetsAverage() (gas: 15648827) -PreviewerTest:testFlexibleAvailableLiquidity() (gas: 17252956) +PreviewerTest:testFlexibleAvailableLiquidity() (gas: 17257956) PreviewerTest:testFlexibleBorrowSharesAndAssets() (gas: 4401038) PreviewerTest:testFloatingAvailableLiquidityProjectingNewFloatingDebt() (gas: 12564814) PreviewerTest:testFloatingRateAndUtilization() (gas: 1128246) -PreviewerTest:testJustUpdatedRewardRatesShouldStillReturnRate() (gas: 7190152) -PreviewerTest:testMaxBorrowAssetsCapacity() (gas: 2469700) +PreviewerTest:testJustUpdatedRewardRatesShouldStillReturnRate() (gas: 7249748) +PreviewerTest:testMaxBorrowAssetsCapacity() (gas: 2482200) PreviewerTest:testMaxBorrowAssetsCapacityForAccountWithShortfall() (gas: 11002333) -PreviewerTest:testMaxBorrowAssetsCapacityPerMarket() (gas: 13192458) +PreviewerTest:testMaxBorrowAssetsCapacityPerMarket() (gas: 13214958) PreviewerTest:testOraclePriceReturningAccurateValues() (gas: 10144173) PreviewerTest:testPreviewBorrowAtAllMaturitiesReturningAccurateAmount() (gas: 4242626) PreviewerTest:testPreviewBorrowAtMaturityReturningAccurateAmount() (gas: 623428) @@ -353,12 +353,12 @@ PreviewerTest:testPreviewWithdrawAtMaturityWithOneUnit() (gas: 251718) PreviewerTest:testPreviewWithdrawAtMaturityWithSameTimestamp() (gas: 233406) PreviewerTest:testPreviewWithdrawAtMaturityWithZeroAmount() (gas: 251675) PreviewerTest:testReserveFactor() (gas: 707280) -PreviewerTest:testReturnRewardAssetUsdPrice() (gas: 6712433) -PreviewerTest:testRewardsRateAfterDistributionEnd() (gas: 7496473) -PreviewerTest:testRewardsRateOnlyWithFixedBorrows() (gas: 6803647) -PreviewerTest:testRewardsRateWithDifferentRewardLengths() (gas: 19266979) -PreviewerTest:testRewardsRateWithMarketWithDifferentDecimals() (gas: 18398132) -PreviewerTest:testRewardsRateX() (gas: 8143022) +PreviewerTest:testReturnRewardAssetUsdPrice() (gas: 6772029) +PreviewerTest:testRewardsRateAfterDistributionEnd() (gas: 7556069) +PreviewerTest:testRewardsRateOnlyWithFixedBorrows() (gas: 6863199) +PreviewerTest:testRewardsRateWithDifferentRewardLengths() (gas: 19326707) +PreviewerTest:testRewardsRateWithMarketWithDifferentDecimals() (gas: 18457860) +PreviewerTest:testRewardsRateX() (gas: 8202750) PriceFeedDoubleTest:testPriceFeedDoubleReturningAccurateDecimals() (gas: 597567) PriceFeedDoubleTest:testPriceFeedDoubleReturningPrice() (gas: 53190) PriceFeedDoubleTest:testPriceFeedDoubleWithActualOnChainValues() (gas: 76310) @@ -375,71 +375,74 @@ PriceFeedWrapperTest:testPriceFeedWrapperReturningPriceAfterRebase() (gas: 48989 PriceFeedWrapperTest:testPriceFeedWrapperWithActualOnChainValues() (gas: 75210) PriceFeedWrapperTest:testPriceFeedWrapperWithNegativePriceShouldRevert() (gas: 164216) PriceFeedWrapperTest:testPriceFeedWrapperWithUsdPriceFeed() (gas: 1243191) -RewardsControllerTest:testAccrueRewardsForWholeDistributionPeriod() (gas: 1245824) -RewardsControllerTest:testAccrueRewardsWithBadDebtClearingOfFixedBorrow() (gas: 3340160) -RewardsControllerTest:testAccrueRewardsWithRepayOfBorrowBalance() (gas: 1603649) -RewardsControllerTest:testAccrueRewardsWithRepayOfFixedBorrowBalance() (gas: 1793044) -RewardsControllerTest:testAccrueRewardsWithSeizeOfAllDepositShares() (gas: 1992236) -RewardsControllerTest:testAfterDistributionPeriodEnd() (gas: 1818847) -RewardsControllerTest:testAllClaimableUSDCWithAnotherAccountInPool() (gas: 2273836) -RewardsControllerTest:testAllClaimableUSDCWithDeposit() (gas: 1627808) -RewardsControllerTest:testAllClaimableUSDCWithFloatingBorrow() (gas: 1560146) -RewardsControllerTest:testAllClaimableUSDCWithFloatingRefund() (gas: 1667569) -RewardsControllerTest:testAllClaimableUSDCWithFloatingRepay() (gas: 1674192) -RewardsControllerTest:testAllClaimableUSDCWithMint() (gas: 1284289) -RewardsControllerTest:testAllClaimableUSDCWithRedeem() (gas: 1640881) -RewardsControllerTest:testAllClaimableUSDCWithTransfer() (gas: 2220830) -RewardsControllerTest:testAllClaimableUSDCWithTransferFrom() (gas: 2135769) -RewardsControllerTest:testAllClaimableUSDCWithWithdraw() (gas: 1641894) -RewardsControllerTest:testAllClaimableWETH() (gas: 1247851) -RewardsControllerTest:testAllClaimableWithMaturedFixedPool() (gas: 1128294) -RewardsControllerTest:testAllClaimableWithTimeElapsedZero() (gas: 1625327) -RewardsControllerTest:testAllRewards() (gas: 216315) -RewardsControllerTest:testClaim() (gas: 1192577) -RewardsControllerTest:testClaimAll() (gas: 2189364) -RewardsControllerTest:testClaimMarketWithoutRewards() (gas: 1241247) -RewardsControllerTest:testClaimWithNotEnabledRewardAsset() (gas: 1223075) -RewardsControllerTest:testConfigSettingNewStartWithOnGoingDistributionShouldNotUpdate() (gas: 430465) -RewardsControllerTest:testConfigWithDistributionNotYetStartedShouldNotFail() (gas: 613446) -RewardsControllerTest:testConfigWithTransitionFactorHigherOrEqThanCap() (gas: 107189) -RewardsControllerTest:testConfigWithZeroDepositAllocationWeightFactorShouldRevert() (gas: 71475) -RewardsControllerTest:testDifferentDistributionTimeForDifferentRewards() (gas: 2026279) -RewardsControllerTest:testEmitAccrue() (gas: 1318654) -RewardsControllerTest:testEmitClaimRewards() (gas: 1112943) -RewardsControllerTest:testEmitConfigUpdate() (gas: 439500) -RewardsControllerTest:testEmitIndexUpdate() (gas: 1446604) -RewardsControllerTest:testLastUndistributed() (gas: 2191515) -RewardsControllerTest:testLastUpdateAfterDistributionPeriodEnd() (gas: 1848042) -RewardsControllerTest:testOperationAfterDistributionEnded() (gas: 723078) -RewardsControllerTest:testOperationsBeforeDistributionStart() (gas: 1675436) -RewardsControllerTest:testPermitClaim() (gas: 1275720) -RewardsControllerTest:testSetDistributionConfigWithDifferentDecimals() (gas: 11456364) -RewardsControllerTest:testSetDistributionOperationShouldUpdateIndex() (gas: 136200) -RewardsControllerTest:testSetDistributionWithOnGoingMarketOperations() (gas: 1202546) -RewardsControllerTest:testSetHigherTotalDistribution() (gas: 1833017) -RewardsControllerTest:testSetLowerAndEqualDistributionPeriodThanCurrentTimestampShouldRevert() (gas: 1275675) -RewardsControllerTest:testSetLowerAndEqualTotalDistributionThanReleasedShouldRevert() (gas: 1268722) -RewardsControllerTest:testSetLowerDistributionPeriod() (gas: 2286644) -RewardsControllerTest:testSetLowerDistributionPeriodAndLowerTotalDistribution() (gas: 2289457) -RewardsControllerTest:testSetLowerTotalDistribution() (gas: 1832952) -RewardsControllerTest:testSetNewDistributionPeriod() (gas: 3147502) -RewardsControllerTest:testSetNewDistributionPeriodAfterDistributionEnds() (gas: 1407532) -RewardsControllerTest:testSetNewTargetDebt() (gas: 1672829) -RewardsControllerTest:testSetNewTargetDebtAfterDistributionEnds() (gas: 1736539) -RewardsControllerTest:testSetNewTargetDebtWithClaimOnlyAtEnd() (gas: 1390642) -RewardsControllerTest:testSetNewTreasuryFeeShouldImpactAllocation() (gas: 658922) -RewardsControllerTest:testSetTargetDebtMultipleTimes() (gas: 2721660) -RewardsControllerTest:testSetTargetDebtMultipleTimesAfterEnd() (gas: 2758550) -RewardsControllerTest:testSetTotalDistributionMultipleTimes() (gas: 1839593) -RewardsControllerTest:testTriggerHandleBorrowHookBeforeUpdatingFloatingDebt() (gas: 1879760) -RewardsControllerTest:testUpdateConfig() (gas: 1329664) -RewardsControllerTest:testUpdateIndexesWithUtilizationEqualToOne() (gas: 1258467) -RewardsControllerTest:testUpdateIndexesWithUtilizationHigherThanOne() (gas: 1353230) +RewardsControllerTest:testAccountKeeperClaimOnBehalfOf() (gas: 1294500) +RewardsControllerTest:testAccrueRewardsForWholeDistributionPeriod() (gas: 1246234) +RewardsControllerTest:testAccrueRewardsWithBadDebtClearingOfFixedBorrow() (gas: 3340292) +RewardsControllerTest:testAccrueRewardsWithRepayOfBorrowBalance() (gas: 1603825) +RewardsControllerTest:testAccrueRewardsWithRepayOfFixedBorrowBalance() (gas: 1793176) +RewardsControllerTest:testAccrueRewardsWithSeizeOfAllDepositShares() (gas: 1992324) +RewardsControllerTest:testAfterDistributionPeriodEnd() (gas: 1819345) +RewardsControllerTest:testAllClaimableUSDCWithAnotherAccountInPool() (gas: 2274936) +RewardsControllerTest:testAllClaimableUSDCWithDeposit() (gas: 1629040) +RewardsControllerTest:testAllClaimableUSDCWithFloatingBorrow() (gas: 1560982) +RewardsControllerTest:testAllClaimableUSDCWithFloatingRefund() (gas: 1668141) +RewardsControllerTest:testAllClaimableUSDCWithFloatingRepay() (gas: 1674742) +RewardsControllerTest:testAllClaimableUSDCWithMint() (gas: 1285212) +RewardsControllerTest:testAllClaimableUSDCWithRedeem() (gas: 1641409) +RewardsControllerTest:testAllClaimableUSDCWithTransfer() (gas: 2221996) +RewardsControllerTest:testAllClaimableUSDCWithTransferFrom() (gas: 2136869) +RewardsControllerTest:testAllClaimableUSDCWithWithdraw() (gas: 1642422) +RewardsControllerTest:testAllClaimableWETH() (gas: 1248621) +RewardsControllerTest:testAllClaimableWithMaturedFixedPool() (gas: 1128558) +RewardsControllerTest:testAllClaimableWithTimeElapsedZero() (gas: 1625825) +RewardsControllerTest:testAllRewards() (gas: 216448) +RewardsControllerTest:testClaim() (gas: 1193011) +RewardsControllerTest:testClaimAll() (gas: 2189950) +RewardsControllerTest:testClaimMarketWithoutRewards() (gas: 1241426) +RewardsControllerTest:testClaimWithNotEnabledRewardAsset() (gas: 1223553) +RewardsControllerTest:testConfigSettingNewStartWithOnGoingDistributionShouldNotUpdate() (gas: 430420) +RewardsControllerTest:testConfigWithDistributionNotYetStartedShouldNotFail() (gas: 613268) +RewardsControllerTest:testConfigWithTransitionFactorHigherOrEqThanCap() (gas: 107077) +RewardsControllerTest:testConfigWithZeroDepositAllocationWeightFactorShouldRevert() (gas: 71430) +RewardsControllerTest:testDifferentDistributionTimeForDifferentRewards() (gas: 2026432) +RewardsControllerTest:testEmitAccrue() (gas: 1319086) +RewardsControllerTest:testEmitClaimRewards() (gas: 1113375) +RewardsControllerTest:testEmitConfigUpdate() (gas: 439433) +RewardsControllerTest:testEmitIndexUpdate() (gas: 1447380) +RewardsControllerTest:testLastUndistributed() (gas: 2192591) +RewardsControllerTest:testLastUpdateAfterDistributionPeriodEnd() (gas: 1849096) +RewardsControllerTest:testNotKeeperClaimOnBehalfOf() (gas: 185650) +RewardsControllerTest:testOperationAfterDistributionEnded() (gas: 723254) +RewardsControllerTest:testOperationsBeforeDistributionStart() (gas: 1675913) +RewardsControllerTest:testPermitClaim() (gas: 1276089) +RewardsControllerTest:testSetDistributionConfigWithDifferentDecimals() (gas: 11457021) +RewardsControllerTest:testSetDistributionOperationShouldUpdateIndex() (gas: 136133) +RewardsControllerTest:testSetDistributionWithOnGoingMarketOperations() (gas: 1202681) +RewardsControllerTest:testSetHigherTotalDistribution() (gas: 1833704) +RewardsControllerTest:testSetKeeperOnlyAdminRole() (gas: 118463) +RewardsControllerTest:testSetLowerAndEqualDistributionPeriodThanCurrentTimestampShouldRevert() (gas: 1275540) +RewardsControllerTest:testSetLowerAndEqualTotalDistributionThanReleasedShouldRevert() (gas: 1268587) +RewardsControllerTest:testSetLowerDistributionPeriod() (gas: 2287763) +RewardsControllerTest:testSetLowerDistributionPeriodAndLowerTotalDistribution() (gas: 2290488) +RewardsControllerTest:testSetLowerTotalDistribution() (gas: 1833639) +RewardsControllerTest:testSetNewDistributionPeriod() (gas: 3148877) +RewardsControllerTest:testSetNewDistributionPeriodAfterDistributionEnds() (gas: 1407875) +RewardsControllerTest:testSetNewTargetDebt() (gas: 1673494) +RewardsControllerTest:testSetNewTargetDebtAfterDistributionEnds() (gas: 1737248) +RewardsControllerTest:testSetNewTargetDebtWithClaimOnlyAtEnd() (gas: 1390985) +RewardsControllerTest:testSetNewTreasuryFeeShouldImpactAllocation() (gas: 659010) +RewardsControllerTest:testSetTargetDebtMultipleTimes() (gas: 2722279) +RewardsControllerTest:testSetTargetDebtMultipleTimesAfterEnd() (gas: 2759191) +RewardsControllerTest:testSetTotalDistributionMultipleTimes() (gas: 1839846) +RewardsControllerTest:testTriggerHandleBorrowHookBeforeUpdatingFloatingDebt() (gas: 1879936) +RewardsControllerTest:testUpdateConfig() (gas: 1330051) +RewardsControllerTest:testUpdateIndexesWithUtilizationEqualToOne() (gas: 1258855) +RewardsControllerTest:testUpdateIndexesWithUtilizationHigherThanOne() (gas: 1353685) RewardsControllerTest:testUpdateWithTotalDebtZeroShouldUpdateLastUndistributed() (gas: 575908) -RewardsControllerTest:testUtilizationEqualZero() (gas: 922133) -RewardsControllerTest:testWithTwelveFixedPools() (gas: 8060108) -RewardsControllerTest:testWithdrawAllRewardBalance() (gas: 71935) -RewardsControllerTest:testWithdrawOnlyAdminRole() (gas: 122220) +RewardsControllerTest:testUtilizationEqualZero() (gas: 922132) +RewardsControllerTest:testWithTwelveFixedPools() (gas: 8061113) +RewardsControllerTest:testWithdrawAllRewardBalance() (gas: 71913) +RewardsControllerTest:testWithdrawOnlyAdminRole() (gas: 122264) SwapperTest:testSwapBasic() (gas: 216831) SwapperTest:testSwapWithAllowance() (gas: 481530) SwapperTest:testSwapWithInaccurateSlippageSendsETHToAccount() (gas: 297968) diff --git a/contracts/RewardsController.sol b/contracts/RewardsController.sol index 0319170c..9d3332fe 100644 --- a/contracts/RewardsController.sol +++ b/contracts/RewardsController.sol @@ -27,6 +27,8 @@ contract RewardsController is Initializable, AccessControlUpgradeable { ERC20[] public rewardList; /// @notice Stores Markets with distributions set. Market[] public marketList; + /// @notice Tracks the allowed `keeper` to claim on behalf of `account`. + mapping(address account => address keeper) public accountKeepers; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -82,6 +84,21 @@ contract RewardsController is Initializable, AccessControlUpgradeable { } } + /// @notice Claims `account` rewards for the given operations and reward assets. + /// @param marketOps The operations to claim rewards for. + /// @param account The account to claim the rewards for. + /// @param rewardsList The list of rewards assets to claim. + /// @return rewardsList The list of rewards assets. + /// @return claimedAmounts The list of claimed amounts. + function claimOnBehalfOf( + MarketOperation[] memory marketOps, + address account, + ERC20[] memory rewardsList + ) external returns (ERC20[] memory, uint256[] memory) { + if (accountKeepers[account] != msg.sender) revert NotKeeper(); + return claim(marketOps, account, account, rewardsList); + } + /// @notice Claims all `msg.sender` rewards to the given account. /// @param to The address to send the rewards to. /// @return rewardsList The list of rewards assets. @@ -100,23 +117,29 @@ contract RewardsController is Initializable, AccessControlUpgradeable { MarketOperation[] memory marketOps, address to, ERC20[] memory rewardsList - ) public claimSender returns (ERC20[] memory, uint256[] memory claimedAmounts) { - uint256 rewardsCount = rewardsList.length; - claimedAmounts = new uint256[](rewardsCount); - address sender = _claimSender; + ) public claimSender returns (ERC20[] memory, uint256[] memory) { + return claim(marketOps, _claimSender, to, rewardsList); + } + + function claim( + MarketOperation[] memory marketOps, + address account, + address to, + ERC20[] memory rewardsList + ) internal returns (ERC20[] memory, uint256[] memory claimedAmounts) { + claimedAmounts = new uint256[](rewardsList.length); for (uint256 i = 0; i < marketOps.length; ) { MarketOperation memory marketOperation = marketOps[i]; Distribution storage dist = distribution[marketOperation.market]; - uint256 availableRewards = dist.availableRewardsCount; - for (uint128 r = 0; r < availableRewards; ) { + for (uint128 r = 0; r < dist.availableRewardsCount; ) { update( - sender, + account, marketOperation.market, dist.availableRewards[r], accountBalanceOperations( marketOperation.market, marketOperation.operations, - sender, + account, dist.rewards[dist.availableRewards[r]].start ) ); @@ -124,13 +147,13 @@ contract RewardsController is Initializable, AccessControlUpgradeable { ++r; } } - for (uint256 r = 0; r < rewardsCount; ) { + for (uint256 r = 0; r < rewardsList.length; ) { RewardData storage rewardData = dist.rewards[rewardsList[r]]; for (uint256 o = 0; o < marketOperation.operations.length; ) { - uint256 rewardAmount = rewardData.accounts[sender][marketOperation.operations[o]].accrued; + uint256 rewardAmount = rewardData.accounts[account][marketOperation.operations[o]].accrued; if (rewardAmount != 0) { claimedAmounts[r] += rewardAmount; - rewardData.accounts[sender][marketOperation.operations[o]].accrued = 0; + rewardData.accounts[account][marketOperation.operations[o]].accrued = 0; } unchecked { ++o; @@ -148,7 +171,7 @@ contract RewardsController is Initializable, AccessControlUpgradeable { uint256 claimedAmount = claimedAmounts[r]; if (claimedAmount > 0) { rewardsList[r].safeTransfer(to, claimedAmount); - emit Claim(sender, rewardsList[r], to, claimedAmount); + emit Claim(account, rewardsList[r], to, claimedAmount); } unchecked { ++r; @@ -622,6 +645,13 @@ contract RewardsController is Initializable, AccessControlUpgradeable { } } + /// @notice Sets the `keeper` address for the given `account`. + /// @param account The account to set the `keeper` for. + /// @param keeper The address to set as the `keeper`. + function setKeeper(address account, address keeper) external onlyRole(DEFAULT_ADMIN_ROLE) { + accountKeepers[account] = keeper; + } + /// @notice Withdraws the contract's balance of the given asset to the given address. /// @param asset The asset to withdraw. /// @param to The address to withdraw the asset to. @@ -927,6 +957,7 @@ contract RewardsController is Initializable, AccessControlUpgradeable { error IndexOverflow(); error InvalidConfig(); +error NotKeeper(); struct ClaimPermit { address owner; diff --git a/test/RewardsController.t.sol b/test/RewardsController.t.sol index c6d5ca5a..4383a3b2 100644 --- a/test/RewardsController.t.sol +++ b/test/RewardsController.t.sol @@ -10,7 +10,7 @@ import { InterestRateModel } from "../contracts/InterestRateModel.sol"; import { Auditor, IPriceFeed } from "../contracts/Auditor.sol"; import { Market } from "../contracts/Market.sol"; import { MockPriceFeed } from "../contracts/mocks/MockPriceFeed.sol"; -import { ERC20, RewardsController, ClaimPermit, InvalidConfig } from "../contracts/RewardsController.sol"; +import { ERC20, RewardsController, ClaimPermit, InvalidConfig, NotKeeper } from "../contracts/RewardsController.sol"; import { FixedLib } from "../contracts/utils/FixedLib.sol"; contract RewardsControllerTest is Test { @@ -1559,6 +1559,57 @@ contract RewardsControllerTest is Test { assertApproxEqAbs(opRewardAsset.balanceOf(address(this)) + lastUndistributed, releaseRate * 12 weeks, 1e6); } + function testAccountKeeperClaimOnBehalfOf() external { + vm.startPrank(BOB); + marketUSDC.deposit(10_000e6, BOB); + marketUSDC.borrow(3_000e6, BOB, BOB); + vm.stopPrank(); + + vm.warp(6 weeks); + uint256 bobRewards = rewardsController.allClaimable(BOB, opRewardAsset); + assertGt(bobRewards, 0); + + rewardsController.setKeeper(BOB, address(this)); + + RewardsController.MarketOperation[] memory marketOps = new RewardsController.MarketOperation[](1); + bool[] memory ops = new bool[](2); + ops[0] = true; + ops[1] = false; + marketOps[0] = RewardsController.MarketOperation({ market: marketUSDC, operations: ops }); + ERC20[] memory rewardList = new ERC20[](2); + rewardList[0] = opRewardAsset; + rewardsController.claimOnBehalfOf(marketOps, BOB, rewardList); + assertEq(bobRewards, opRewardAsset.balanceOf(BOB)); + bobRewards = rewardsController.allClaimable(BOB, opRewardAsset); + assertEq(bobRewards, 0); + + rewardsController.setKeeper(BOB, address(0)); + } + + function testNotKeeperClaimOnBehalfOf() external { + RewardsController.MarketOperation[] memory marketOps; + ERC20[] memory rewardList; + + vm.expectRevert(NotKeeper.selector); + rewardsController.claimOnBehalfOf(marketOps, BOB, rewardList); + + rewardsController.setKeeper(BOB, address(this)); + rewardsController.claimOnBehalfOf(marketOps, BOB, rewardList); + + rewardsController.setKeeper(BOB, address(0)); + vm.expectRevert(NotKeeper.selector); + rewardsController.claimOnBehalfOf(marketOps, BOB, rewardList); + } + + function testSetKeeperOnlyAdminRole() external { + vm.expectRevert(bytes("")); + vm.prank(BOB); + rewardsController.setKeeper(BOB, address(this)); + + // setKeeper call from contract should not revert + rewardsController.setKeeper(BOB, address(this)); + } + function testSetDistributionConfigWithDifferentDecimals() external { MockERC20 rewardAsset = new MockERC20("Reward", "RWD", 10); MockERC20 asset = new MockERC20("Asset", "AST", 6); diff --git a/test/hardhat/19_rewards_controller.ts b/test/hardhat/19_rewards_controller.ts index e64b2d7c..d50df20f 100644 --- a/test/hardhat/19_rewards_controller.ts +++ b/test/hardhat/19_rewards_controller.ts @@ -75,6 +75,13 @@ describe("RewardsController", function () { rewardsController.allClaimable(alice.address, alice.address, { gasLimit: 1_000_000 }), ).to.be.revertedWithoutReason(); }); + + it("AND setting a keeper enables claiming on behalf of an account", async () => { + await timelockExecute(multisig, rewardsController, "setKeeper", [alice.address, alice.address]); + + const marketOps = [{ market: marketUSDC.target, operations: [false, true] }]; + await rewardsController.connect(alice).claimOnBehalfOf(marketOps, alice.address, [op.target]); + }); }); describe("GIVEN a zero utilization level", () => {