From e4268af5d2d9f9dc9707c3370d892e6c974126ba Mon Sep 17 00:00:00 2001 From: "A.L." Date: Fri, 13 Sep 2024 15:06:30 +0100 Subject: [PATCH 1/7] feat: impl return value for `updatePrice` --- src/ReservoirPriceOracle.sol | 34 ++++++++++++++++++---------- test/unit/ReservoirPriceOracle.t.sol | 6 ++--- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index adfcd59..b6d0a17 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -132,7 +132,13 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar /// @param aTokenA Address of one of the tokens for the price update. Does not have to be less than address of aTokenB /// @param aTokenB Address of one of the tokens for the price update. Does not have to be greater than address of aTokenA /// @param aRewardRecipient The beneficiary of the reward. Must be able to receive ether. Set to address(0) if not seeking a reward - function updatePrice(address aTokenA, address aTokenB, address aRewardRecipient) external nonReentrant { + /// @return rTotalReward The total amount of ETH reward if the price was updated in the same call. Mainly used by keepers and MEV bots to simulate offchain if a price update is worth doing. + /// Does not take into account if there is sufficient ETH for rewards in the contract. Oracle could have insufficient ETH resulting in no rewards even if called. + function updatePrice(address aTokenA, address aTokenB, address aRewardRecipient) + external + nonReentrant + returns (uint256 rTotalReward) + { (address lToken0, address lToken1) = Utils.sortTokens(aTokenA, aTokenB); (address[] memory lRoute,, uint256 lPrevPrice, uint256 lRewardThreshold) = @@ -158,8 +164,9 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar } _writePriceCache(lToken0, lToken1, lNewPrice); - _rewardUpdater(lPrevPrice, lNewPrice, aRewardRecipient, lRewardThreshold); + rTotalReward += _calculateReward(lPrevPrice, lNewPrice, lRewardThreshold); } + _rewardUpdater(aRewardRecipient, rTotalReward); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -182,11 +189,9 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar rResult = lPair.getTimeWeightedAverage(aQuery.priceType, aQuery.secs, aQuery.ago, lIndex); } - function _rewardUpdater(uint256 aPrevPrice, uint256 aNewPrice, address aRecipient, uint256 aRewardThreshold) - private + function _calculateReward(uint256 aPrevPrice, uint256 aNewPrice, uint256 aRewardThreshold) + private returns (uint256 rReward) { - if (aRecipient == address(0)) return; - // SAFETY: this mul will not overflow as 0 < `aRewardThreshold` <= `Constants.BP_SCALE`, as checked by `setRoute` uint256 lRewardThresholdWAD; unchecked { @@ -194,12 +199,11 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar } uint256 lPercentDiff = aPrevPrice.calcPercentageDiff(aNewPrice); - uint256 lPayoutAmt; // SAFETY: this mul will not overflow even in extreme cases of `block.basefee`. unchecked { if (lPercentDiff < lRewardThresholdWAD) { - return; + return 0; } // payout max reward else if (lPercentDiff >= lRewardThresholdWAD * MAX_REWARD_MULTIPLIER) { @@ -209,18 +213,22 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar // on ARB because the latter will always return the demand insensitive // base fee, while the former can return higher fees during times of // congestion - lPayoutAmt = block.basefee * rewardGasAmount * MAX_REWARD_MULTIPLIER; + rReward = block.basefee * rewardGasAmount * MAX_REWARD_MULTIPLIER; } else { assert( lPercentDiff >= lRewardThresholdWAD && lPercentDiff < lRewardThresholdWAD * MAX_REWARD_MULTIPLIER ); - lPayoutAmt = block.basefee * rewardGasAmount * lPercentDiff / lRewardThresholdWAD; // denominator is never 0 + rReward = block.basefee * rewardGasAmount * lPercentDiff / lRewardThresholdWAD; // denominator is never 0 } } + } + + function _rewardUpdater(address aRecipient, uint256 aReward) private { + if (aRecipient == address(0)) return; // does not revert under any circumstance assembly ("memory-safe") { - pop(call(gas(), aRecipient, lPayoutAmt, codesize(), 0x00, codesize(), 0x00)) + pop(call(gas(), aRecipient, aReward, codesize(), 0x00, codesize(), 0x00)) } } @@ -490,7 +498,9 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar int256 lDiff = int256(lToken1Decimals) - int256(lToken0Decimals); uint256 lRewardThreshold = aRewardThresholds[0]; - if (lRewardThreshold > Constants.BP_SCALE || lRewardThreshold == 0) revert OracleErrors.InvalidRewardThreshold(); + if (lRewardThreshold > Constants.BP_SCALE || lRewardThreshold == 0) { + revert OracleErrors.InvalidRewardThreshold(); + } bytes32 lData = RoutesLib.packSimplePrice(lDiff, 0, lRewardThreshold); assembly ("memory-safe") { diff --git a/test/unit/ReservoirPriceOracle.t.sol b/test/unit/ReservoirPriceOracle.t.sol index 3d6ed42..7395a22 100644 --- a/test/unit/ReservoirPriceOracle.t.sol +++ b/test/unit/ReservoirPriceOracle.t.sol @@ -529,8 +529,7 @@ contract ReservoirPriceOracleTest is BaseTest { // assert (uint256 lPrice,,) = _oracle.priceCache(address(_tokenA), address(_tokenB)); assertEq(lPrice, lCurrentPrice); - uint256 lExpectedRewardReceived = - block.basefee * _oracle.rewardGasAmount() * lPercentDiff / lRewardThresholdWAD; + uint256 lExpectedRewardReceived = block.basefee * _oracle.rewardGasAmount() * lPercentDiff / lRewardThresholdWAD; assertGe(lExpectedRewardReceived, block.basefee * _oracle.rewardGasAmount()); assertLe(lExpectedRewardReceived, block.basefee * _oracle.rewardGasAmount() * _oracle.MAX_REWARD_MULTIPLIER()); assertEq(address(this).balance, lExpectedRewardReceived); // some reward received but is less than max possible reward @@ -568,7 +567,8 @@ contract ReservoirPriceOracleTest is BaseTest { function testUpdatePrice_RewardEligible_InsufficientReward(uint256 aRewardAvailable) external { // assume - uint256 lRewardAvailable = bound(aRewardAvailable, 1, block.basefee * _oracle.rewardGasAmount() * _oracle.MAX_REWARD_MULTIPLIER() - 1); + uint256 lRewardAvailable = + bound(aRewardAvailable, 1, block.basefee * _oracle.rewardGasAmount() * _oracle.MAX_REWARD_MULTIPLIER() - 1); // arrange deal(address(_oracle), lRewardAvailable); From 6cd2f06aef370c89668b8091d9af607c7547ffef Mon Sep 17 00:00:00 2001 From: "A.L." Date: Fri, 13 Sep 2024 15:12:52 +0100 Subject: [PATCH 2/7] test: modify existing tests to test for new property --- test/unit/ReservoirPriceOracle.t.sol | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test/unit/ReservoirPriceOracle.t.sol b/test/unit/ReservoirPriceOracle.t.sol index 7395a22..c1b4918 100644 --- a/test/unit/ReservoirPriceOracle.t.sol +++ b/test/unit/ReservoirPriceOracle.t.sol @@ -469,7 +469,7 @@ contract ReservoirPriceOracleTest is BaseTest { _pair.sync(); // act - _oracle.updatePrice(address(_tokenB), address(_tokenA), address(this)); + uint256 lReward = _oracle.updatePrice(address(_tokenB), address(_tokenA), address(this)); // assert (lPrice,,) = _oracle.priceCache(address(_tokenA), address(_tokenB)); @@ -477,6 +477,7 @@ contract ReservoirPriceOracleTest is BaseTest { (lPrice,,) = _oracle.priceCache(address(_tokenB), address(_tokenA)); assertEq(lPrice, 0); assertEq(address(this).balance, 0); // there should be no reward for the first price update + assertEq(lReward, 0); } function testUpdatePrice_BelowThreshold(uint256 aPercentDiff) external { @@ -495,12 +496,13 @@ contract ReservoirPriceOracleTest is BaseTest { _pair.sync(); // act - _oracle.updatePrice(address(_tokenA), address(_tokenB), address(this)); + uint256 lReward = _oracle.updatePrice(address(_tokenA), address(_tokenB), address(this)); // assert (uint256 lPrice,,) = _oracle.priceCache(address(_tokenA), address(_tokenB)); assertEq(lPrice, lCurrentPrice); assertEq(address(this).balance, 0); // no reward as the price did not move sufficiently + assertEq(lReward, 0); } function testUpdatePrice_AboveThresholdBelowMaxReward(uint256 aPercentDiff) external { @@ -524,7 +526,7 @@ contract ReservoirPriceOracleTest is BaseTest { _pair.sync(); // act - _oracle.updatePrice(address(_tokenB), address(_tokenA), address(this)); + uint256 lReward = _oracle.updatePrice(address(_tokenB), address(_tokenA), address(this)); // assert (uint256 lPrice,,) = _oracle.priceCache(address(_tokenA), address(_tokenB)); @@ -533,6 +535,7 @@ contract ReservoirPriceOracleTest is BaseTest { assertGe(lExpectedRewardReceived, block.basefee * _oracle.rewardGasAmount()); assertLe(lExpectedRewardReceived, block.basefee * _oracle.rewardGasAmount() * _oracle.MAX_REWARD_MULTIPLIER()); assertEq(address(this).balance, lExpectedRewardReceived); // some reward received but is less than max possible reward + assertEq(lExpectedRewardReceived, lReward); } function testUpdatePrice_BeyondMaxReward(uint256 aPercentDiff) external { @@ -555,7 +558,7 @@ contract ReservoirPriceOracleTest is BaseTest { _pair.sync(); // act - _oracle.updatePrice(address(_tokenB), address(_tokenA), address(this)); + uint256 lReward = _oracle.updatePrice(address(_tokenB), address(_tokenA), address(this)); // assert (uint256 lPrice,,) = _oracle.priceCache(address(_tokenA), address(_tokenB)); @@ -563,6 +566,7 @@ contract ReservoirPriceOracleTest is BaseTest { uint256 lExpectedRewardReceived = block.basefee * _oracle.rewardGasAmount() * _oracle.MAX_REWARD_MULTIPLIER(); assertEq(address(this).balance, lExpectedRewardReceived); assertEq(address(_oracle).balance, ORACLE_STARTING_BALANCE - lExpectedRewardReceived); + assertEq(lExpectedRewardReceived, lReward); } function testUpdatePrice_RewardEligible_InsufficientReward(uint256 aRewardAvailable) external { @@ -581,12 +585,13 @@ contract ReservoirPriceOracleTest is BaseTest { _pair.swap(2e18, true, address(this), ""); // act - _oracle.updatePrice(address(_tokenA), address(_tokenB), address(this)); + uint256 lReward = _oracle.updatePrice(address(_tokenA), address(_tokenB), address(this)); // assert - no reward as there's insufficient ether in the contract, but price cache updated nonetheless (uint256 lPrice,,) = _oracle.priceCache(address(_tokenA), address(_tokenB)); assertNotEq(lPrice, 5e18); assertEq(address(this).balance, 0); + assertGt(lReward, 0); } function testUpdatePrice_RewardEligible_ZeroRecipient() external { @@ -600,12 +605,13 @@ contract ReservoirPriceOracleTest is BaseTest { _pair.sync(); // act - _oracle.updatePrice(address(_tokenA), address(_tokenB), address(0)); + uint256 lReward = _oracle.updatePrice(address(_tokenA), address(_tokenB), address(0)); // assert - no change to balance, but price cache updated nonetheless (uint256 lPrice,,) = _oracle.priceCache(address(_tokenA), address(_tokenB)); assertNotEq(lPrice, 5e18); assertEq(address(_oracle).balance, lOracleBalanceStart); + assertGt(lReward, 0); } function testUpdatePrice_RewardEligible_ContractNoReceive() external { From 2debe47b5d66cbdf1d38f029228aa8232df037aa Mon Sep 17 00:00:00 2001 From: "A.L." Date: Fri, 13 Sep 2024 17:06:12 +0100 Subject: [PATCH 3/7] test: modify existing tests to test for new property --- src/ReservoirPriceOracle.sol | 9 ++++++--- test/unit/ReservoirPriceOracle.t.sol | 26 +++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index b6d0a17..a6178b3 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -160,11 +160,14 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar // if it's a simple route, we avoid loading the price again from storage if (lRoute.length != 2) { - (lPrevPrice,,) = _priceCache(lToken0, lToken1); + (lPrevPrice,, lRewardThreshold) = _priceCache(lToken0, lToken1); } _writePriceCache(lToken0, lToken1, lNewPrice); - rTotalReward += _calculateReward(lPrevPrice, lNewPrice, lRewardThreshold); + // SAFETY: This will not overflow for, and hops are limited by `MAX_ROUTE_LENGTH` + unchecked { + rTotalReward += _calculateReward(lPrevPrice, lNewPrice, lRewardThreshold); + } } _rewardUpdater(aRewardRecipient, rTotalReward); } @@ -224,7 +227,7 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar } function _rewardUpdater(address aRecipient, uint256 aReward) private { - if (aRecipient == address(0)) return; + if (aRecipient == address(0) || aReward == 0) return; // does not revert under any circumstance assembly ("memory-safe") { diff --git a/test/unit/ReservoirPriceOracle.t.sol b/test/unit/ReservoirPriceOracle.t.sol index c1b4918..a9ad44c 100644 --- a/test/unit/ReservoirPriceOracle.t.sol +++ b/test/unit/ReservoirPriceOracle.t.sol @@ -640,7 +640,7 @@ contract ReservoirPriceOracleTest is BaseTest { address lEnd = address(_tokenB); address[] memory lRoute = new address[](4); uint16[] memory lRewardThreshold = new uint16[](3); - lRewardThreshold[0] = lRewardThreshold[1] = lRewardThreshold[2] = Constants.BP_SCALE; + lRewardThreshold[0] = lRewardThreshold[1] = lRewardThreshold[2] = 3; // 3bp lRoute[0] = lStart; lRoute[1] = lIntermediate1; lRoute[2] = lIntermediate2; @@ -672,10 +672,10 @@ contract ReservoirPriceOracleTest is BaseTest { lAC.sync(); lCD.sync(); lBD.sync(); - skip(_oracle.twapPeriod() * 2); + skip(_oracle.twapPeriod()); // act - _oracle.updatePrice(address(_tokenA), address(_tokenB), address(this)); + uint256 lReward = _oracle.updatePrice(address(_tokenA), address(_tokenB), address(this)); // assert (uint256 lPriceAC,,) = _oracle.priceCache(lStart, lIntermediate1); @@ -686,6 +686,26 @@ contract ReservoirPriceOracleTest is BaseTest { assertApproxEqRel(lPriceCD, 2e18, 0.0001e18); assertApproxEqRel(lPriceBD, 2e18, 0.0001e18); assertEq(lPriceAB, 0); // composite price is not stored in the cache + assertEq(lReward, 0); + + // arrange + skip(_oracle.twapPeriod()); + + // act + uint256 lSwapAmt = 1_000_000; + _tokenA.mint(address(lAC), lSwapAmt * 10 ** _tokenA.decimals()); + lAC.swap(int256(lSwapAmt * 10 ** _tokenA.decimals()), true, address(this), ""); + + _tokenC.mint(address(lCD), lSwapAmt * 10 ** _tokenC.decimals()); + lCD.swap(int256(lSwapAmt * 10 ** _tokenC.decimals()), true, address(this), ""); + + _tokenB.mint(address(lBD), lSwapAmt * 10 ** _tokenB.decimals()); + lBD.swap(int256(lSwapAmt * 10 ** _tokenB.decimals()), true, address(this), ""); + + skip(_oracle.twapPeriod()); + + lReward = _oracle.updatePrice(address(_tokenA), address(_tokenB), address(this)); + assertGt(lReward, _oracle.rewardGasAmount() * 3); // ensure that rewards have accumulated } function testSetRoute() public { From 9b2992cb335ee91ca6e64bb02b8652947646c957 Mon Sep 17 00:00:00 2001 From: "A.L." Date: Fri, 13 Sep 2024 17:17:22 +0100 Subject: [PATCH 4/7] doc: update safety comment --- src/ReservoirPriceOracle.sol | 3 ++- test/unit/ReservoirPriceOracle.t.sol | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index a6178b3..084f17f 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -164,7 +164,8 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar } _writePriceCache(lToken0, lToken1, lNewPrice); - // SAFETY: This will not overflow for, and hops are limited by `MAX_ROUTE_LENGTH` + // SAFETY: This will not overflow even if reward gas amount is set to the block gas limit (30M at time if writing), + // and hops are limited by `MAX_ROUTE_LENGTH`. unchecked { rTotalReward += _calculateReward(lPrevPrice, lNewPrice, lRewardThreshold); } diff --git a/test/unit/ReservoirPriceOracle.t.sol b/test/unit/ReservoirPriceOracle.t.sol index a9ad44c..8a2c2b6 100644 --- a/test/unit/ReservoirPriceOracle.t.sol +++ b/test/unit/ReservoirPriceOracle.t.sol @@ -705,7 +705,7 @@ contract ReservoirPriceOracleTest is BaseTest { skip(_oracle.twapPeriod()); lReward = _oracle.updatePrice(address(_tokenA), address(_tokenB), address(this)); - assertGt(lReward, _oracle.rewardGasAmount() * 3); // ensure that rewards have accumulated + assertGt(lReward, _oracle.rewardGasAmount() * 3); // ensure that rewards have been aggregated across routes } function testSetRoute() public { From 389e05f37a4566bad59d74e74bf667fe9fd8449d Mon Sep 17 00:00:00 2001 From: "A.L." Date: Fri, 13 Sep 2024 18:31:18 +0100 Subject: [PATCH 5/7] gas: update snapshot --- .gas-snapshot | 66 +++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 935e43a..6528428 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,19 +1,19 @@ -QueryProcessorTest:testFindNearestSample_CanFindExactValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 66334752, ~: 75124967) -QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 65933151, ~: 75772005) +QueryProcessorTest:testFindNearestSample_CanFindExactValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 67695526, ~: 75574250) +QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 66719885, ~: 75513678) QueryProcessorTest:testFindNearestSample_NotInitialized() (gas: 1056945756) -QueryProcessorTest:testFindNearestSample_OneSample(uint256) (runs: 256, μ: 80331, ~: 80360) +QueryProcessorTest:testFindNearestSample_OneSample(uint256) (runs: 256, μ: 80330, ~: 80360) QueryProcessorTest:testGetInstantValue() (gas: 124248) QueryProcessorTest:testGetInstantValue_NotInitialized(uint256) (runs: 256, μ: 19397, ~: 19397) -QueryProcessorTest:testGetInstantValue_NotInitialized_BeyondBufferSize(uint8,uint16) (runs: 256, μ: 68389672, ~: 68389600) -QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 256, μ: 27024, ~: 27087) -QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 256, μ: 71442338, ~: 80821196) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 67544657, ~: 77486110) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 67574540, ~: 77517710) -QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 256, μ: 65906778, ~: 75743223) -QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 256, μ: 65940850, ~: 75778013) -QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 256, μ: 65898377, ~: 75734934) -QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 256, μ: 65909900, ~: 75744817) -QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 256, μ: 105778530, ~: 115416718) +QueryProcessorTest:testGetInstantValue_NotInitialized_BeyondBufferSize(uint8,uint16) (runs: 256, μ: 68389669, ~: 68389600) +QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 256, μ: 27017, ~: 27087) +QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 256, μ: 70721679, ~: 80304635) +QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 68993182, ~: 78002710) +QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 69023139, ~: 78034310) +QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 256, μ: 66693346, ~: 75484579) +QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 256, μ: 66727455, ~: 75519686) +QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 256, μ: 66684950, ~: 75476976) +QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 256, μ: 66696341, ~: 75486516) +QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 256, μ: 103418908, ~: 114593801) QueryProcessorTest:testGetTimeWeightedAverage_BadSecs() (gas: 10995) ReservoirPriceOracleTest:testClearRoute() (gas: 52339) ReservoirPriceOracleTest:testClearRoute_AllWordsCleared() (gas: 159879) @@ -21,22 +21,22 @@ ReservoirPriceOracleTest:testDesignatePair() (gas: 29068) ReservoirPriceOracleTest:testDesignatePair_IncorrectPair() (gas: 21155) ReservoirPriceOracleTest:testDesignatePair_NotOwner() (gas: 17553) ReservoirPriceOracleTest:testDesignatePair_TokenOrderReversed() (gas: 30639) -ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 256, μ: 34016, ~: 34118) +ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 256, μ: 33998, ~: 34118) ReservoirPriceOracleTest:testGetQuote_AmountInTooLarge() (gas: 12963) -ReservoirPriceOracleTest:testGetQuote_BaseIsVault(uint256) (runs: 256, μ: 411295, ~: 411040) -ReservoirPriceOracleTest:testGetQuote_ComplicatedDecimals() (gas: 10354021) -ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 256, μ: 36154, ~: 36316) +ReservoirPriceOracleTest:testGetQuote_BaseIsVault(uint256) (runs: 256, μ: 411285, ~: 411040) +ReservoirPriceOracleTest:testGetQuote_ComplicatedDecimals() (gas: 10354017) +ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 256, μ: 36135, ~: 36193) ReservoirPriceOracleTest:testGetQuote_MultipleHops() (gas: 111841) -ReservoirPriceOracleTest:testGetQuote_MultipleHops_Inverse() (gas: 112181) +ReservoirPriceOracleTest:testGetQuote_MultipleHops_Inverse() (gas: 112163) ReservoirPriceOracleTest:testGetQuote_MultipleHops_PriceZero() (gas: 122567) ReservoirPriceOracleTest:testGetQuote_NoFallbackOracle() (gas: 20820) ReservoirPriceOracleTest:testGetQuote_PriceZero() (gas: 15958) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 256, μ: 5329116, ~: 5329104) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 256, μ: 10496298, ~: 10496410) +ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 256, μ: 5329107, ~: 5329104) +ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 256, μ: 10496279, ~: 10496330) ReservoirPriceOracleTest:testGetQuote_SameBaseQuote(uint256,address) (runs: 256, μ: 8941, ~: 8941) ReservoirPriceOracleTest:testGetQuote_UseFallback() (gas: 38334) ReservoirPriceOracleTest:testGetQuote_ZeroIn() (gas: 36975) -ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 256, μ: 26527, ~: 26629) +ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 256, μ: 26509, ~: 26629) ReservoirPriceOracleTest:testPriceCache_Inverted() (gas: 22001) ReservoirPriceOracleTest:testSetFallbackOracle_NotOwner() (gas: 10938) ReservoirPriceOracleTest:testSetRoute() (gas: 61093) @@ -49,20 +49,20 @@ ReservoirPriceOracleTest:testSetRoute_OverwriteExisting() (gas: 169666) ReservoirPriceOracleTest:testSetRoute_SameToken() (gas: 13041) ReservoirPriceOracleTest:testUndesignatePair() (gas: 30256) ReservoirPriceOracleTest:testUndesignatePair_NotOwner() (gas: 15355) -ReservoirPriceOracleTest:testUpdatePrice_AboveThresholdBelowMaxReward(uint256) (runs: 256, μ: 165388, ~: 165408) -ReservoirPriceOracleTest:testUpdatePrice_BelowThreshold(uint256) (runs: 256, μ: 150249, ~: 149915) -ReservoirPriceOracleTest:testUpdatePrice_BeyondMaxReward(uint256) (runs: 256, μ: 162890, ~: 162915) -ReservoirPriceOracleTest:testUpdatePrice_FirstUpdate() (gas: 153864) -ReservoirPriceOracleTest:testUpdatePrice_IntermediateRoutes() (gas: 15897381) -ReservoirPriceOracleTest:testUpdatePrice_PriceOutOfRange() (gas: 5353475) -ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_ContractNoReceive() (gas: 153424) -ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_InsufficientReward(uint256) (runs: 256, μ: 211585, ~: 211793) -ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_ZeroRecipient() (gas: 144295) +ReservoirPriceOracleTest:testUpdatePrice_AboveThresholdBelowMaxReward(uint256) (runs: 256, μ: 165939, ~: 165959) +ReservoirPriceOracleTest:testUpdatePrice_BelowThreshold(uint256) (runs: 256, μ: 150799, ~: 150473) +ReservoirPriceOracleTest:testUpdatePrice_BeyondMaxReward(uint256) (runs: 256, μ: 163441, ~: 163469) +ReservoirPriceOracleTest:testUpdatePrice_FirstUpdate() (gas: 154418) +ReservoirPriceOracleTest:testUpdatePrice_IntermediateRoutes() (gas: 16111065) +ReservoirPriceOracleTest:testUpdatePrice_PriceOutOfRange() (gas: 5353482) +ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_ContractNoReceive() (gas: 153582) +ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_InsufficientReward(uint256) (runs: 256, μ: 212149, ~: 212363) +ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_ZeroRecipient() (gas: 147250) ReservoirPriceOracleTest:testUpdateRewardGasAmount() (gas: 19039) ReservoirPriceOracleTest:testUpdateRewardGasAmount_NotOwner() (gas: 10940) -ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 256, μ: 21693, ~: 21778) -ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 256, μ: 17830, ~: 18120) -ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 256, μ: 30232, ~: 29977) +ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 256, μ: 21685, ~: 21778) +ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 256, μ: 17827, ~: 18120) +ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 256, μ: 30222, ~: 29977) RoutesLibTest:testGetDecimalDifference() (gas: 3974) RoutesLibTest:testIsCompositeRoute() (gas: 4341) RoutesLibTest:testPackSimplePrice(int8,uint256) (runs: 256, μ: 8200, ~: 7962) From 61798eb03d7eeee3c964654d9c05057e93554895 Mon Sep 17 00:00:00 2001 From: "A.L." Date: Wed, 18 Sep 2024 14:43:11 +0100 Subject: [PATCH 6/7] fix: add empty line Co-authored-by: OliverNChalk <11343499+OliverNChalk@users.noreply.github.com> --- src/ReservoirPriceOracle.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index 084f17f..a68a3b7 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -170,6 +170,7 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar rTotalReward += _calculateReward(lPrevPrice, lNewPrice, lRewardThreshold); } } + _rewardUpdater(aRewardRecipient, rTotalReward); } From 152c44d5cd243526837f9c5171f56cf4f4ad3855 Mon Sep 17 00:00:00 2001 From: "A.L." Date: Wed, 18 Sep 2024 14:50:18 +0100 Subject: [PATCH 7/7] gas: update snapshot --- .gas-snapshot | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 6528428..45766c3 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,19 +1,19 @@ -QueryProcessorTest:testFindNearestSample_CanFindExactValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 67695526, ~: 75574250) -QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 66719885, ~: 75513678) +QueryProcessorTest:testFindNearestSample_CanFindExactValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 65090324, ~: 74975370) +QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 64771162, ~: 74583507) QueryProcessorTest:testFindNearestSample_NotInitialized() (gas: 1056945756) -QueryProcessorTest:testFindNearestSample_OneSample(uint256) (runs: 256, μ: 80330, ~: 80360) +QueryProcessorTest:testFindNearestSample_OneSample(uint256) (runs: 256, μ: 80327, ~: 80360) QueryProcessorTest:testGetInstantValue() (gas: 124248) QueryProcessorTest:testGetInstantValue_NotInitialized(uint256) (runs: 256, μ: 19397, ~: 19397) -QueryProcessorTest:testGetInstantValue_NotInitialized_BeyondBufferSize(uint8,uint16) (runs: 256, μ: 68389669, ~: 68389600) -QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 256, μ: 27017, ~: 27087) -QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 256, μ: 70721679, ~: 80304635) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 68993182, ~: 78002710) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 69023139, ~: 78034310) -QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 256, μ: 66693346, ~: 75484579) -QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 256, μ: 66727455, ~: 75519686) -QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 256, μ: 66684950, ~: 75476976) -QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 256, μ: 66696341, ~: 75486516) -QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 256, μ: 103418908, ~: 114593801) +QueryProcessorTest:testGetInstantValue_NotInitialized_BeyondBufferSize(uint8,uint16) (runs: 256, μ: 68389670, ~: 68389600) +QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 256, μ: 27016, ~: 27087) +QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 256, μ: 70591497, ~: 80271384) +QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 69164823, ~: 77928090) +QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 69194817, ~: 77959690) +QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 256, μ: 64744868, ~: 74555386) +QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 256, μ: 64778894, ~: 74589515) +QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 256, μ: 64736471, ~: 74546552) +QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 256, μ: 64748015, ~: 74556435) +QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 256, μ: 102669133, ~: 112352230) QueryProcessorTest:testGetTimeWeightedAverage_BadSecs() (gas: 10995) ReservoirPriceOracleTest:testClearRoute() (gas: 52339) ReservoirPriceOracleTest:testClearRoute_AllWordsCleared() (gas: 159879) @@ -21,22 +21,22 @@ ReservoirPriceOracleTest:testDesignatePair() (gas: 29068) ReservoirPriceOracleTest:testDesignatePair_IncorrectPair() (gas: 21155) ReservoirPriceOracleTest:testDesignatePair_NotOwner() (gas: 17553) ReservoirPriceOracleTest:testDesignatePair_TokenOrderReversed() (gas: 30639) -ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 256, μ: 33998, ~: 34118) +ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 256, μ: 34001, ~: 34118) ReservoirPriceOracleTest:testGetQuote_AmountInTooLarge() (gas: 12963) -ReservoirPriceOracleTest:testGetQuote_BaseIsVault(uint256) (runs: 256, μ: 411285, ~: 411040) +ReservoirPriceOracleTest:testGetQuote_BaseIsVault(uint256) (runs: 256, μ: 411282, ~: 411040) ReservoirPriceOracleTest:testGetQuote_ComplicatedDecimals() (gas: 10354017) -ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 256, μ: 36135, ~: 36193) +ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 256, μ: 36138, ~: 36254) ReservoirPriceOracleTest:testGetQuote_MultipleHops() (gas: 111841) ReservoirPriceOracleTest:testGetQuote_MultipleHops_Inverse() (gas: 112163) ReservoirPriceOracleTest:testGetQuote_MultipleHops_PriceZero() (gas: 122567) ReservoirPriceOracleTest:testGetQuote_NoFallbackOracle() (gas: 20820) ReservoirPriceOracleTest:testGetQuote_PriceZero() (gas: 15958) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 256, μ: 5329107, ~: 5329104) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 256, μ: 10496279, ~: 10496330) +ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 256, μ: 5329100, ~: 5329104) +ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 256, μ: 10496290, ~: 10496408) ReservoirPriceOracleTest:testGetQuote_SameBaseQuote(uint256,address) (runs: 256, μ: 8941, ~: 8941) ReservoirPriceOracleTest:testGetQuote_UseFallback() (gas: 38334) ReservoirPriceOracleTest:testGetQuote_ZeroIn() (gas: 36975) -ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 256, μ: 26509, ~: 26629) +ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 256, μ: 26512, ~: 26629) ReservoirPriceOracleTest:testPriceCache_Inverted() (gas: 22001) ReservoirPriceOracleTest:testSetFallbackOracle_NotOwner() (gas: 10938) ReservoirPriceOracleTest:testSetRoute() (gas: 61093) @@ -50,19 +50,19 @@ ReservoirPriceOracleTest:testSetRoute_SameToken() (gas: 13041) ReservoirPriceOracleTest:testUndesignatePair() (gas: 30256) ReservoirPriceOracleTest:testUndesignatePair_NotOwner() (gas: 15355) ReservoirPriceOracleTest:testUpdatePrice_AboveThresholdBelowMaxReward(uint256) (runs: 256, μ: 165939, ~: 165959) -ReservoirPriceOracleTest:testUpdatePrice_BelowThreshold(uint256) (runs: 256, μ: 150799, ~: 150473) -ReservoirPriceOracleTest:testUpdatePrice_BeyondMaxReward(uint256) (runs: 256, μ: 163441, ~: 163469) +ReservoirPriceOracleTest:testUpdatePrice_BelowThreshold(uint256) (runs: 256, μ: 150797, ~: 150473) +ReservoirPriceOracleTest:testUpdatePrice_BeyondMaxReward(uint256) (runs: 256, μ: 163444, ~: 163469) ReservoirPriceOracleTest:testUpdatePrice_FirstUpdate() (gas: 154418) ReservoirPriceOracleTest:testUpdatePrice_IntermediateRoutes() (gas: 16111065) ReservoirPriceOracleTest:testUpdatePrice_PriceOutOfRange() (gas: 5353482) ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_ContractNoReceive() (gas: 153582) -ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_InsufficientReward(uint256) (runs: 256, μ: 212149, ~: 212363) +ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_InsufficientReward(uint256) (runs: 256, μ: 212147, ~: 212363) ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_ZeroRecipient() (gas: 147250) ReservoirPriceOracleTest:testUpdateRewardGasAmount() (gas: 19039) ReservoirPriceOracleTest:testUpdateRewardGasAmount_NotOwner() (gas: 10940) -ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 256, μ: 21685, ~: 21778) -ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 256, μ: 17827, ~: 18120) -ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 256, μ: 30222, ~: 29977) +ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 256, μ: 21687, ~: 21778) +ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 256, μ: 17825, ~: 18120) +ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 256, μ: 30219, ~: 29977) RoutesLibTest:testGetDecimalDifference() (gas: 3974) RoutesLibTest:testIsCompositeRoute() (gas: 4341) RoutesLibTest:testPackSimplePrice(int8,uint256) (runs: 256, μ: 8200, ~: 7962)