Skip to content

Commit

Permalink
🐛 rewards: fix released calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
santichez authored and cruzdanilo committed Jun 3, 2024
1 parent bb38a54 commit afd8de1
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 79 deletions.
5 changes: 5 additions & 0 deletions .changeset/moody-beds-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/protocol": patch
---

🐛 rewards: fix released calculation
143 changes: 72 additions & 71 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ PreviewerTest:testAccountsWithAccountOnlyDeposit() (gas: 862391)
PreviewerTest:testAccountsWithAccountThatHasBalances() (gas: 2265026)
PreviewerTest:testAccountsWithEmptyAccount() (gas: 690558)
PreviewerTest:testAccountsWithIntermediateOperationsReturningAccurateAmounts() (gas: 17617503)
PreviewerTest:testActualTimeBeforeStartDistributionRewards() (gas: 7753427)
PreviewerTest:testEmptyExactly() (gas: 5645469)
PreviewerTest:testActualTimeBeforeStartDistributionRewards() (gas: 7774796)
PreviewerTest:testEmptyExactly() (gas: 5666767)
PreviewerTest:testExactlyReturningInterestRateModelData() (gas: 688149)
PreviewerTest:testFixedAvailableLiquidityProjectingNewFloatingDebt() (gas: 13301596)
PreviewerTest:testFixedPoolsA() (gas: 19318341)
Expand All @@ -305,7 +305,7 @@ PreviewerTest:testFlexibleAvailableLiquidity() (gas: 17242437)
PreviewerTest:testFlexibleBorrowSharesAndAssets() (gas: 4401038)
PreviewerTest:testFloatingAvailableLiquidityProjectingNewFloatingDebt() (gas: 12554334)
PreviewerTest:testFloatingRateAndUtilization() (gas: 1128246)
PreviewerTest:testJustUpdatedRewardRatesShouldStillReturnRate() (gas: 7174821)
PreviewerTest:testJustUpdatedRewardRatesShouldStillReturnRate() (gas: 7196163)
PreviewerTest:testMaxBorrowAssetsCapacity() (gas: 2469700)
PreviewerTest:testMaxBorrowAssetsCapacityForAccountWithShortfall() (gas: 10991846)
PreviewerTest:testMaxBorrowAssetsCapacityPerMarket() (gas: 13181978)
Expand Down Expand Up @@ -352,12 +352,12 @@ PreviewerTest:testPreviewWithdrawAtMaturityWithOneUnit() (gas: 251718)
PreviewerTest:testPreviewWithdrawAtMaturityWithSameTimestamp() (gas: 233406)
PreviewerTest:testPreviewWithdrawAtMaturityWithZeroAmount() (gas: 251675)
PreviewerTest:testReserveFactor() (gas: 707280)
PreviewerTest:testReturnRewardAssetUsdPrice() (gas: 6697322)
PreviewerTest:testRewardsRateAfterDistributionEnd() (gas: 7481122)
PreviewerTest:testRewardsRateOnlyWithFixedBorrows() (gas: 6788297)
PreviewerTest:testRewardsRateWithDifferentRewardLengths() (gas: 19241268)
PreviewerTest:testRewardsRateWithMarketWithDifferentDecimals() (gas: 18372292)
PreviewerTest:testRewardsRateX() (gas: 8127662)
PreviewerTest:testReturnRewardAssetUsdPrice() (gas: 6718669)
PreviewerTest:testRewardsRateAfterDistributionEnd() (gas: 7502464)
PreviewerTest:testRewardsRateOnlyWithFixedBorrows() (gas: 6809617)
PreviewerTest:testRewardsRateWithDifferentRewardLengths() (gas: 19262635)
PreviewerTest:testRewardsRateWithMarketWithDifferentDecimals() (gas: 18393705)
PreviewerTest:testRewardsRateX() (gas: 8149075)
PriceFeedDoubleTest:testPriceFeedDoubleReturningAccurateDecimals() (gas: 597567)
PriceFeedDoubleTest:testPriceFeedDoubleReturningPrice() (gas: 53190)
PriceFeedDoubleTest:testPriceFeedDoubleWithActualOnChainValues() (gas: 76310)
Expand All @@ -374,69 +374,70 @@ PriceFeedWrapperTest:testPriceFeedWrapperReturningPriceAfterRebase() (gas: 48989
PriceFeedWrapperTest:testPriceFeedWrapperWithActualOnChainValues() (gas: 75210)
PriceFeedWrapperTest:testPriceFeedWrapperWithNegativePriceShouldRevert() (gas: 164216)
PriceFeedWrapperTest:testPriceFeedWrapperWithUsdPriceFeed() (gas: 1243191)
RewardsControllerTest:testAccrueRewardsForWholeDistributionPeriod() (gas: 1245256)
RewardsControllerTest:testAccrueRewardsWithBadDebtClearingOfFixedBorrow() (gas: 3338579)
RewardsControllerTest:testAccrueRewardsWithRepayOfBorrowBalance() (gas: 1602527)
RewardsControllerTest:testAccrueRewardsWithRepayOfFixedBorrowBalance() (gas: 1791601)
RewardsControllerTest:testAccrueRewardsWithSeizeOfAllDepositShares() (gas: 1991092)
RewardsControllerTest:testAfterDistributionPeriodEnd() (gas: 1818279)
RewardsControllerTest:testAllClaimableUSDCWithAnotherAccountInPool() (gas: 2273354)
RewardsControllerTest:testAllClaimableUSDCWithDeposit() (gas: 1627706)
RewardsControllerTest:testAllClaimableUSDCWithFloatingBorrow() (gas: 1560044)
RewardsControllerTest:testAllClaimableUSDCWithFloatingRefund() (gas: 1667236)
RewardsControllerTest:testAllClaimableUSDCWithFloatingRepay() (gas: 1673750)
RewardsControllerTest:testAllClaimableUSDCWithMint() (gas: 1284296)
RewardsControllerTest:testAllClaimableUSDCWithRedeem() (gas: 1640399)
RewardsControllerTest:testAllClaimableUSDCWithTransfer() (gas: 2220308)
RewardsControllerTest:testAllClaimableUSDCWithTransferFrom() (gas: 2135247)
RewardsControllerTest:testAllClaimableUSDCWithWithdraw() (gas: 1641434)
RewardsControllerTest:testAllClaimableWETH() (gas: 1247789)
RewardsControllerTest:testAllClaimableWithMaturedFixedPool() (gas: 1127875)
RewardsControllerTest:testAllClaimableWithTimeElapsedZero() (gas: 1624639)
RewardsControllerTest:testClaim() (gas: 1192161)
RewardsControllerTest:testClaimAll() (gas: 2188738)
RewardsControllerTest:testClaimMarketWithoutRewards() (gas: 1240983)
RewardsControllerTest:testClaimWithNotEnabledRewardAsset() (gas: 1222637)
RewardsControllerTest:testConfigSettingNewStartWithOnGoingDistributionShouldNotUpdate() (gas: 430182)
RewardsControllerTest:testConfigWithDistributionNotYetStartedShouldNotFail() (gas: 613404)
RewardsControllerTest:testConfigWithTransitionFactorHigherOrEqThanCap() (gas: 107189)
RewardsControllerTest:testConfigWithZeroDepositAllocationWeightFactorShouldRevert() (gas: 71542)
RewardsControllerTest:testDifferentDistributionTimeForDifferentRewards() (gas: 2025649)
RewardsControllerTest:testEmitAccrue() (gas: 1317916)
RewardsControllerTest:testEmitClaimRewards() (gas: 1112375)
RewardsControllerTest:testEmitConfigUpdate() (gas: 439567)
RewardsControllerTest:testEmitIndexUpdate() (gas: 1445530)
RewardsControllerTest:testLastUndistributed() (gas: 2189661)
RewardsControllerTest:testOperationAfterDistributionEnded() (gas: 722976)
RewardsControllerTest:testOperationsBeforeDistributionStart() (gas: 1674576)
RewardsControllerTest:testPermitClaim() (gas: 1275282)
RewardsControllerTest:testSetDistributionConfigWithDifferentDecimals() (gas: 11445059)
RewardsControllerTest:testSetDistributionOperationShouldUpdateIndex() (gas: 136200)
RewardsControllerTest:testSetDistributionWithOnGoingMarketOperations() (gas: 1202358)
RewardsControllerTest:testSetHigherTotalDistribution() (gas: 1831201)
RewardsControllerTest:testSetLowerAndEqualDistributionPeriodThanCurrentTimestampShouldRevert() (gas: 1274696)
RewardsControllerTest:testSetLowerAndEqualTotalDistributionThanReleasedShouldRevert() (gas: 1267721)
RewardsControllerTest:testSetLowerDistributionPeriod() (gas: 2284176)
RewardsControllerTest:testSetLowerDistributionPeriodAndLowerTotalDistribution() (gas: 2286901)
RewardsControllerTest:testSetLowerTotalDistribution() (gas: 1831114)
RewardsControllerTest:testSetNewDistributionPeriod() (gas: 3143972)
RewardsControllerTest:testSetNewDistributionPeriodAfterDistributionEnds() (gas: 1406666)
RewardsControllerTest:testSetNewTargetDebt() (gas: 1671525)
RewardsControllerTest:testSetNewTargetDebtAfterDistributionEnds() (gas: 1734966)
RewardsControllerTest:testSetNewTargetDebtWithClaimOnlyAtEnd() (gas: 1389661)
RewardsControllerTest:testSetNewTreasuryFeeShouldImpactAllocation() (gas: 658882)
RewardsControllerTest:testSetTargetDebtMultipleTimes() (gas: 2719324)
RewardsControllerTest:testSetTargetDebtMultipleTimesAfterEnd() (gas: 2756094)
RewardsControllerTest:testSetTotalDistributionMultipleTimes() (gas: 1838222)
RewardsControllerTest:testTriggerHandleBorrowHookBeforeUpdatingFloatingDebt() (gas: 1879339)
RewardsControllerTest:testUpdateConfig() (gas: 1328855)
RewardsControllerTest:testUpdateIndexesWithUtilizationEqualToOne() (gas: 1257771)
RewardsControllerTest:testUpdateIndexesWithUtilizationHigherThanOne() (gas: 1352512)
RewardsControllerTest:testUpdateWithTotalDebtZeroShouldUpdateLastUndistributed() (gas: 575506)
RewardsControllerTest:testUtilizationEqualZero() (gas: 921863)
RewardsControllerTest:testWithTwelveFixedPools() (gas: 8055024)
RewardsControllerTest:testAccrueRewardsForWholeDistributionPeriod() (gas: 1245322)
RewardsControllerTest:testAccrueRewardsWithBadDebtClearingOfFixedBorrow() (gas: 3338667)
RewardsControllerTest:testAccrueRewardsWithRepayOfBorrowBalance() (gas: 1602615)
RewardsControllerTest:testAccrueRewardsWithRepayOfFixedBorrowBalance() (gas: 1791645)
RewardsControllerTest:testAccrueRewardsWithSeizeOfAllDepositShares() (gas: 1991114)
RewardsControllerTest:testAfterDistributionPeriodEnd() (gas: 1818322)
RewardsControllerTest:testAllClaimableUSDCWithAnotherAccountInPool() (gas: 2274252)
RewardsControllerTest:testAllClaimableUSDCWithDeposit() (gas: 1628756)
RewardsControllerTest:testAllClaimableUSDCWithFloatingBorrow() (gas: 1560744)
RewardsControllerTest:testAllClaimableUSDCWithFloatingRefund() (gas: 1667674)
RewardsControllerTest:testAllClaimableUSDCWithFloatingRepay() (gas: 1674188)
RewardsControllerTest:testAllClaimableUSDCWithMint() (gas: 1284996)
RewardsControllerTest:testAllClaimableUSDCWithRedeem() (gas: 1640815)
RewardsControllerTest:testAllClaimableUSDCWithTransfer() (gas: 2221471)
RewardsControllerTest:testAllClaimableUSDCWithTransferFrom() (gas: 2136365)
RewardsControllerTest:testAllClaimableUSDCWithWithdraw() (gas: 1641850)
RewardsControllerTest:testAllClaimableWETH() (gas: 1248402)
RewardsControllerTest:testAllClaimableWithMaturedFixedPool() (gas: 1128007)
RewardsControllerTest:testAllClaimableWithTimeElapsedZero() (gas: 1624747)
RewardsControllerTest:testClaim() (gas: 1192249)
RewardsControllerTest:testClaimAll() (gas: 2188892)
RewardsControllerTest:testClaimMarketWithoutRewards() (gas: 1241017)
RewardsControllerTest:testClaimWithNotEnabledRewardAsset() (gas: 1222725)
RewardsControllerTest:testConfigSettingNewStartWithOnGoingDistributionShouldNotUpdate() (gas: 430252)
RewardsControllerTest:testConfigWithDistributionNotYetStartedShouldNotFail() (gas: 613248)
RewardsControllerTest:testConfigWithTransitionFactorHigherOrEqThanCap() (gas: 107077)
RewardsControllerTest:testConfigWithZeroDepositAllocationWeightFactorShouldRevert() (gas: 71475)
RewardsControllerTest:testDifferentDistributionTimeForDifferentRewards() (gas: 2025778)
RewardsControllerTest:testEmitAccrue() (gas: 1317982)
RewardsControllerTest:testEmitClaimRewards() (gas: 1112419)
RewardsControllerTest:testEmitConfigUpdate() (gas: 439411)
RewardsControllerTest:testEmitIndexUpdate() (gas: 1445640)
RewardsControllerTest:testLastUndistributed() (gas: 2189926)
RewardsControllerTest:testOperationAfterDistributionEnded() (gas: 723020)
RewardsControllerTest:testOperationsBeforeDistributionStart() (gas: 1674553)
RewardsControllerTest:testPermitClaim() (gas: 1275326)
RewardsControllerTest:testSetDistributionConfigWithDifferentDecimals() (gas: 11445190)
RewardsControllerTest:testSetDistributionOperationShouldUpdateIndex() (gas: 136066)
RewardsControllerTest:testSetDistributionWithOnGoingMarketOperations() (gas: 1202335)
RewardsControllerTest:testSetHigherTotalDistribution() (gas: 1831337)
RewardsControllerTest:testSetLowerAndEqualDistributionPeriodThanCurrentTimestampShouldRevert() (gas: 1274818)
RewardsControllerTest:testSetLowerAndEqualTotalDistributionThanReleasedShouldRevert() (gas: 1267821)
RewardsControllerTest:testSetLowerDistributionPeriod() (gas: 2284312)
RewardsControllerTest:testSetLowerDistributionPeriodAndLowerTotalDistribution() (gas: 2287059)
RewardsControllerTest:testSetLowerTotalDistribution() (gas: 1831250)
RewardsControllerTest:testSetNewDistributionPeriod() (gas: 3144152)
RewardsControllerTest:testSetNewDistributionPeriodAfterDistributionEnds() (gas: 1406553)
RewardsControllerTest:testSetNewTargetDebt() (gas: 1671639)
RewardsControllerTest:testSetNewTargetDebtAfterDistributionEnds() (gas: 1735009)
RewardsControllerTest:testSetNewTargetDebtWithClaimOnlyAtEnd() (gas: 1389753)
RewardsControllerTest:testSetNewTreasuryFeeShouldImpactAllocation() (gas: 658948)
RewardsControllerTest:testSetTargetDebtMultipleTimes() (gas: 2719552)
RewardsControllerTest:testSetTargetDebtMultipleTimesAfterEnd() (gas: 2756047)
RewardsControllerTest:testSetTotalDistributionMultipleTimes() (gas: 1838384)
RewardsControllerTest:testTriggerHandleBorrowHookBeforeUpdatingFloatingDebt() (gas: 1879494)
RewardsControllerTest:testUpdateConfig() (gas: 1328946)
RewardsControllerTest:testUpdateConfigIncreaseRewardDistribution() (gas: 405501)
RewardsControllerTest:testUpdateIndexesWithUtilizationEqualToOne() (gas: 1257815)
RewardsControllerTest:testUpdateIndexesWithUtilizationHigherThanOne() (gas: 1352623)
RewardsControllerTest:testUpdateWithTotalDebtZeroShouldUpdateLastUndistributed() (gas: 575527)
RewardsControllerTest:testUtilizationEqualZero() (gas: 921840)
RewardsControllerTest:testWithTwelveFixedPools() (gas: 8055310)
RewardsControllerTest:testWithdrawAllRewardBalance() (gas: 71913)
RewardsControllerTest:testWithdrawOnlyAdminRole() (gas: 122309)
RewardsControllerTest:testWithdrawOnlyAdminRole() (gas: 122331)
SwapperTest:testSwapBasic() (gas: 216831)
SwapperTest:testSwapWithAllowance() (gas: 481530)
SwapperTest:testSwapWithInaccurateSlippageSendsETHToAccount() (gas: 297968)
Expand Down
24 changes: 16 additions & 8 deletions contracts/RewardsController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,14 @@ contract RewardsController is Initializable, AccessControlUpgradeable {
return (rewardData.start, rewardData.end, rewardData.lastUpdate);
}

/// @notice Gets the release reward rate of a given market and reward.
/// @param market The market to get the release rate.
/// @param reward The reward asset.
/// @return The release reward rate.
function releaseRate(Market market, ERC20 reward) external view returns (uint256) {

Check warning on line 234 in contracts/RewardsController.sol

View check run for this annotation

Codecov / codecov/patch

contracts/RewardsController.sol#L234

Added line #L234 was not covered by tests
return distribution[market].rewards[reward].releaseRate;
}

/// @notice Retrieves all rewards addresses.
function allRewards() external view returns (ERC20[] memory) {
return rewardList;
Expand Down Expand Up @@ -501,7 +509,7 @@ contract RewardsController is Initializable, AccessControlUpgradeable {
}
uint256 rewards;
{
uint256 releaseRate = rewardData.releaseRate;
uint256 rate = rewardData.releaseRate;
uint256 lastUndistributed = rewardData.lastUndistributed;
t.period = t.end - t.start;
uint256 distributionFactor = t.period > 0
Expand All @@ -512,11 +520,11 @@ contract RewardsController is Initializable, AccessControlUpgradeable {
uint256 exponential = uint256((-int256(distributionFactor * deltaTime)).expWad());
newUndistributed =
lastUndistributed.mulWadDown(exponential) +
releaseRate.mulDivDown(1e18 - target, distributionFactor).mulWadUp(1e18 - exponential);
rate.mulDivDown(1e18 - target, distributionFactor).mulWadUp(1e18 - exponential);
} else {
newUndistributed = lastUndistributed + releaseRate.mulWadDown(1e18 - target) * deltaTime;
newUndistributed = lastUndistributed + rate.mulWadDown(1e18 - target) * deltaTime;
}
rewards = uint256(int256(releaseRate * deltaTime) - (int256(newUndistributed) - int256(lastUndistributed)));
rewards = uint256(int256(rate * deltaTime) - (int256(newUndistributed) - int256(lastUndistributed)));
} else if (rewardData.lastUpdate > t.end) {
newUndistributed =
lastUndistributed -
Expand All @@ -529,13 +537,13 @@ contract RewardsController is Initializable, AccessControlUpgradeable {
exponential = uint256((-int256(distributionFactor * deltaTime)).expWad());
newUndistributed =
lastUndistributed.mulWadDown(exponential) +
releaseRate.mulDivDown(1e18 - target, distributionFactor).mulWadUp(1e18 - exponential);
rate.mulDivDown(1e18 - target, distributionFactor).mulWadUp(1e18 - exponential);
} else {
newUndistributed = lastUndistributed + releaseRate.mulWadDown(1e18 - target) * deltaTime;
newUndistributed = lastUndistributed + rate.mulWadDown(1e18 - target) * deltaTime;
}
exponential = uint256((-int256(distributionFactor * (block.timestamp - t.end))).expWad());
newUndistributed = newUndistributed - newUndistributed.mulWadUp(1e18 - exponential);
rewards = uint256(int256(releaseRate * deltaTime) - (int256(newUndistributed) - int256(lastUndistributed)));
rewards = uint256(int256(rate * deltaTime) - (int256(newUndistributed) - int256(lastUndistributed)));
}
if (rewards == 0) return (rewardData.borrowIndex, rewardData.depositIndex, newUndistributed);
}
Expand Down Expand Up @@ -678,7 +686,7 @@ contract RewardsController is Initializable, AccessControlUpgradeable {
released =
rewardData.lastConfigReleased +
rewardData.releaseRate *
(block.timestamp - rewardData.lastConfig);
(block.timestamp - Math.max(rewardData.lastConfig, start));
elapsed = block.timestamp - start;
if (configs[i].totalDistribution <= released || configs[i].distributionPeriod <= elapsed) {
revert InvalidConfig();
Expand Down
53 changes: 53 additions & 0 deletions test/RewardsController.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,59 @@ contract RewardsControllerTest is Test {
assertEq(config.start, 0);
}

function testUpdateConfigIncreaseRewardDistribution() external {
vm.warp(20 weeks);

opRewardAsset.mint(address(rewardsController), 4_000 ether);
RewardsController.Config[] memory configs = new RewardsController.Config[](1);
configs[0] = RewardsController.Config({
market: marketUSDC,
reward: opRewardAsset,
priceFeed: MockPriceFeed(address(0)),
targetDebt: 40_000e6,
totalDistribution: 4_000 ether,
start: uint32(40 weeks),
distributionPeriod: 10 weeks,
undistributedFactor: 0.5e18,
flipSpeed: 2e18,
compensationFactor: 0.85e18,
transitionFactor: 0.64e18,
borrowAllocationWeightFactor: 0,
depositAllocationWeightAddend: 0.02e18,
depositAllocationWeightFactor: 0.01e18
});
rewardsController.config(configs);

uint256 oldReleaseRate = rewardsController.releaseRate(marketUSDC, opRewardAsset);

vm.warp(42 weeks);
opRewardAsset.mint(address(rewardsController), 5_000 ether);
configs[0] = RewardsController.Config({
market: marketUSDC,
reward: opRewardAsset,
priceFeed: MockPriceFeed(address(0)),
targetDebt: 40_000e6,
totalDistribution: 9_000 ether,
start: uint32(40 weeks),
distributionPeriod: 10 weeks,
undistributedFactor: 0.5e18,
flipSpeed: 2e18,
compensationFactor: 0.85e18,
transitionFactor: 0.64e18,
borrowAllocationWeightFactor: 0,
depositAllocationWeightAddend: 0.02e18,
depositAllocationWeightFactor: 0.01e18
});
rewardsController.config(configs);

uint256 newReleaseRate = rewardsController.releaseRate(marketUSDC, opRewardAsset);

assertApproxEqAbs(oldReleaseRate * 10 weeks, 4_000 ether, 1e9);
assertApproxEqAbs(oldReleaseRate * 2 weeks, 800 ether, 1e9);
assertApproxEqAbs(newReleaseRate * 8 weeks, 8_200 ether, 1e9);
assertApproxEqAbs(oldReleaseRate * 2 weeks + newReleaseRate * 8 weeks, 9_000 ether, 1e9);
}

function testConfigWithDistributionNotYetStartedShouldNotFail() external {
RewardsController.Config[] memory configs = new RewardsController.Config[](1);
configs[0] = RewardsController.Config({
Expand Down

0 comments on commit afd8de1

Please sign in to comment.