From d67540ce2119548ff30bf87fef8b7bdabae90043 Mon Sep 17 00:00:00 2001 From: OliverNChalk <11343499+OliverNChalk@users.noreply.github.com> Date: Sun, 7 Jul 2024 15:03:32 -0500 Subject: [PATCH 1/9] don't use associated functions when the operation is not associated --- src/ReservoirPriceOracle.sol | 8 ++++---- src/libraries/FlagsLib.sol | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index 90256be..2567501 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -346,7 +346,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. int256 lDiff = lData.getDecimalDifference(); - lData = lDiff.packSimplePrice(aNewPrice); + lData = FlagsLib.packSimplePrice(lDiff, aNewPrice); assembly { sstore(lSlot, lData) } @@ -513,7 +513,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. int256 lDiff = int256(lToken1Decimals) - int256(lToken0Decimals); - bytes32 lData = lDiff.packSimplePrice(0); + bytes32 lData = FlagsLib.packSimplePrice(lDiff, 0); assembly { // Write data to storage. sstore(lSlot, lData) @@ -525,12 +525,12 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. address lThirdToken = aRoute[2]; if (lRouteLength == 3) { - bytes32 lData = lSecondToken.pack2HopRoute(); + bytes32 lData = FlagsLib.pack2HopRoute(lSecondToken); assembly { sstore(lSlot, lData) } } else if (lRouteLength == 4) { - (bytes32 lFirstWord, bytes32 lSecondWord) = lSecondToken.pack3HopRoute(lThirdToken); + (bytes32 lFirstWord, bytes32 lSecondWord) = FlagsLib.pack3HopRoute(lSecondToken, lThirdToken); // Write two words to storage. assembly { diff --git a/src/libraries/FlagsLib.sol b/src/libraries/FlagsLib.sol index 3d7ad57..63f3419 100644 --- a/src/libraries/FlagsLib.sol +++ b/src/libraries/FlagsLib.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; +// REVIEW: Rename `FlagsLib` -> `RoutesLib` as all operations are routes related? library FlagsLib { bytes32 public constant FLAG_UNINITIALIZED = bytes32(hex"00"); bytes32 public constant FLAG_SIMPLE_PRICE = bytes32(hex"01"); From 1871c1879eb5fab25be76a49893102bcd3feeda6 Mon Sep 17 00:00:00 2001 From: OliverNChalk <11343499+OliverNChalk@users.noreply.github.com> Date: Sun, 7 Jul 2024 15:09:47 -0500 Subject: [PATCH 2/9] factor out _validateTokens --- src/ReservoirPriceOracle.sol | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index 2567501..75fc344 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -192,10 +192,16 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. // INTERNAL FUNCTIONS // /////////////////////////////////////////////////////////////////////////////////////////////// - function _validatePair(ReservoirPair aPair) internal pure { + function _validatePair(ReservoirPair aPair) private pure { if (address(aPair) == address(0)) revert OracleErrors.NoDesignatedPair(); } + function _validateTokens(address aToken0, address aToken1) private pure { + // REVIEW: Can be collapsed into `if (aToken1 <= aToken1) revert`. + if (aToken0 == aToken1) revert OracleErrors.SameToken(); + if (aToken1 < aToken0) revert OracleErrors.TokensUnsorted(); + } + function _getTimeWeightedAverageSingle(OracleAverageQuery memory aQuery) internal view returns (uint256 rResult) { ReservoirPair lPair = pairs[aQuery.base][aQuery.quote]; _validatePair(lPair); @@ -498,8 +504,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. function setRoute(address aToken0, address aToken1, address[] memory aRoute) public onlyOwner { uint256 lRouteLength = aRoute.length; - if (aToken0 == aToken1) revert OracleErrors.SameToken(); - if (aToken1 < aToken0) revert OracleErrors.TokensUnsorted(); + _validateTokens(aToken0, aToken1); if (lRouteLength > Constants.MAX_ROUTE_LENGTH || lRouteLength < 2) revert OracleErrors.InvalidRouteLength(); if (aRoute[0] != aToken0 || aRoute[lRouteLength - 1] != aToken1) revert OracleErrors.InvalidRoute(); @@ -546,8 +551,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. } function clearRoute(address aToken0, address aToken1) external onlyOwner { - if (aToken0 == aToken1) revert OracleErrors.SameToken(); - if (aToken1 < aToken0) revert OracleErrors.TokensUnsorted(); + _validateTokens(aToken0, aToken1); (address[] memory lRoute,,) = _getRouteDecimalDifferencePrice(aToken0, aToken1); From 5a84c54beb9b040829d61678d58629cc7abee738 Mon Sep 17 00:00:00 2001 From: OliverNChalk <11343499+OliverNChalk@users.noreply.github.com> Date: Sun, 7 Jul 2024 15:18:08 -0500 Subject: [PATCH 3/9] use A/B when unsorted; 0/1 when sorted --- src/ReservoirPriceOracle.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index 75fc344..0a19e6b 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -278,6 +278,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. lResults[0] = aToken0; lResults[1] = lSecondToken; + // REVIEW: Is it more logical to handle `is2HopRoute` then fallback to `assert(is3HopRoute)`? if (lFirstWord.is3HopRoute()) { bytes32 lSecondWord; assembly { @@ -305,20 +306,19 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. /// Calculate the storage slot for this intermediate segment and read it to see if there is an existing /// route. If there isn't an existing route, we write it as well. - /// @dev assumed that aToken0 and aToken1 are not necessarily sorted - function _checkAndPopulateIntermediateRoute(address aToken0, address aToken1) internal { - (address lLowerToken, address lHigherToken) = Utils.sortTokens(aToken0, aToken1); + function _checkAndPopulateIntermediateRoute(address aTokenA, address aTokenB) private { + (address lToken0, address lToken1) = Utils.sortTokens(aTokenA, aTokenB); - bytes32 lSlot = Utils.calculateSlot(lLowerToken, lHigherToken); + bytes32 lSlot = Utils.calculateSlot(lToken0, lToken1); bytes32 lData; assembly { lData := sload(lSlot) } if (lData == bytes32(0)) { address[] memory lIntermediateRoute = new address[](2); - lIntermediateRoute[0] = lLowerToken; - lIntermediateRoute[1] = lHigherToken; - setRoute(lLowerToken, lHigherToken, lIntermediateRoute); + lIntermediateRoute[0] = lToken0; + lIntermediateRoute[1] = lToken1; + setRoute(lToken0, lToken1, lIntermediateRoute); } } From 56657be6cd1bff152b94c605af898c060c0ffabc Mon Sep 17 00:00:00 2001 From: OliverNChalk <11343499+OliverNChalk@users.noreply.github.com> Date: Sun, 7 Jul 2024 15:21:59 -0500 Subject: [PATCH 4/9] avoid unnecessary temporary array --- src/ReservoirPriceOracle.sol | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index 0a19e6b..0d76121 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -254,20 +254,18 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. view returns (address[] memory rRoute, int256 rDecimalDiff, uint256 rPrice) { - address[] memory lResults = new address[](Constants.MAX_ROUTE_LENGTH); bytes32 lSlot = Utils.calculateSlot(aToken0, aToken1); bytes32 lFirstWord; - uint256 lRouteLength; assembly { lFirstWord := sload(lSlot) } // simple route if (lFirstWord.isSimplePrice()) { - lResults[0] = aToken0; - lResults[1] = aToken1; - lRouteLength = 2; + rRoute = new address[](2); + rRoute[0] = aToken0; + rRoute[1] = aToken1; rDecimalDiff = lFirstWord.getDecimalDifference(); rPrice = lFirstWord.getPrice(); } @@ -275,9 +273,6 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. else if (lFirstWord.isCompositeRoute()) { address lSecondToken = lFirstWord.getTokenFirstWord(); - lResults[0] = aToken0; - lResults[1] = lSecondToken; - // REVIEW: Is it more logical to handle `is2HopRoute` then fallback to `assert(is3HopRoute)`? if (lFirstWord.is3HopRoute()) { bytes32 lSecondWord; @@ -286,21 +281,21 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. } address lThirdToken = lSecondWord.getThirdToken(); - lResults[2] = lThirdToken; - lResults[3] = aToken1; - lRouteLength = 4; + rRoute = new address[](4); + rRoute[2] = lThirdToken; + rRoute[3] = aToken1; } else { - lResults[2] = aToken1; - lRouteLength = 3; + rRoute = new address[](3); + rRoute[2] = aToken1; } + + rRoute[0] = aToken0; + rRoute[1] = lSecondToken; } // no route // solhint-disable-next-line no-empty-blocks - else if (lFirstWord.isUninitialized()) { } - - rRoute = new address[](lRouteLength); - for (uint256 i = 0; i < lRouteLength; ++i) { - rRoute[i] = lResults[i]; + else if (lFirstWord.isUninitialized()) { + rRoute = new address[](0); } } From 821c0faafda8c501606b9b1136c09b893157633a Mon Sep 17 00:00:00 2001 From: OliverNChalk <11343499+OliverNChalk@users.noreply.github.com> Date: Sun, 7 Jul 2024 15:23:52 -0500 Subject: [PATCH 5/9] update gas snapshot --- .gas-snapshot | 122 +++++++++++++++++++++++++------------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 44ffc75..ca3359e 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,75 +1,75 @@ FlagsLibTest:testGetDecimalDifference() (gas: 3974) FlagsLibTest:testIsCompositeRoute() (gas: 4341) -FlagsLibTest:testPackSimplePrice(int8,uint256) (runs: 256, μ: 7791, ~: 7555) -QueryProcessorTest:testFindNearestSample_CanFindExactValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 67735642, ~: 76032605) -QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 65799154, ~: 75921271) -QueryProcessorTest:testFindNearestSample_NotInitialized() (gas: 1056945756) -QueryProcessorTest:testFindNearestSample_OneSample(uint256) (runs: 256, μ: 80323, ~: 80360) -QueryProcessorTest:testGetInstantValue() (gas: 124248) -QueryProcessorTest:testGetInstantValue_NotInitialized(uint256) (runs: 256, μ: 19397, ~: 19397) -QueryProcessorTest:testGetInstantValue_NotInitialized_BeyondBufferSize(uint8,uint16) (runs: 256, μ: 68389656, ~: 68389600) -QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 256, μ: 27031, ~: 27087) -QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 256, μ: 69753539, ~: 80758108) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 69259342, ~: 79254030) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 69289298, ~: 79285630) -QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 256, μ: 65772814, ~: 75892463) -QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 256, μ: 65806837, ~: 75927279) -QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 256, μ: 65764408, ~: 75884174) -QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 256, μ: 65775927, ~: 75894057) -QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 256, μ: 107971505, ~: 117069798) +FlagsLibTest:testPackSimplePrice(int8,uint256) (runs: 259, μ: 7799, ~: 7555) +QueryProcessorTest:testFindNearestSample_CanFindExactValue(uint32,uint256,uint256,uint256) (runs: 259, μ: 67557567, ~: 76401709) +QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 259, μ: 67033233, ~: 76116668) +QueryProcessorTest:testFindNearestSample_NotInitialized() (gas: 8937393461068805977) +QueryProcessorTest:testFindNearestSample_OneSample(uint256) (runs: 259, μ: 80945, ~: 80974) +QueryProcessorTest:testGetInstantValue() (gas: 126197) +QueryProcessorTest:testGetInstantValue_NotInitialized(uint256) (runs: 259, μ: 19397, ~: 19397) +QueryProcessorTest:testGetInstantValue_NotInitialized_BeyondBufferSize(uint8,uint16) (runs: 259, μ: 69647140, ~: 69647072) +QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 259, μ: 27034, ~: 27087) +QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 259, μ: 72319369, ~: 82729762) +QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 259, μ: 71628429, ~: 81068158) +QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 259, μ: 71658441, ~: 81099758) +QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 259, μ: 67006873, ~: 76087503) +QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 259, μ: 67040913, ~: 76122676) +QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 259, μ: 66998495, ~: 76079900) +QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 259, μ: 67009985, ~: 76089440) +QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 259, μ: 111263283, ~: 119811834) QueryProcessorTest:testGetTimeWeightedAverage_BadSecs() (gas: 10995) -ReservoirPriceOracleTest:testClearRoute() (gas: 52085) -ReservoirPriceOracleTest:testClearRoute_AllWordsCleared() (gas: 155034) +ReservoirPriceOracleTest:testClearRoute() (gas: 51595) +ReservoirPriceOracleTest:testClearRoute_AllWordsCleared() (gas: 152969) ReservoirPriceOracleTest:testDesignatePair() (gas: 29091) ReservoirPriceOracleTest:testDesignatePair_IncorrectPair() (gas: 21111) ReservoirPriceOracleTest:testDesignatePair_NotOwner() (gas: 17531) ReservoirPriceOracleTest:testDesignatePair_TokenOrderReversed() (gas: 30729) -ReservoirPriceOracleTest:testGasBountyAvailable(uint256) (runs: 256, μ: 9929, ~: 9925) +ReservoirPriceOracleTest:testGasBountyAvailable(uint256) (runs: 259, μ: 9929, ~: 9925) ReservoirPriceOracleTest:testGasBountyAvailable_Zero() (gas: 8961) -ReservoirPriceOracleTest:testGetLatest(uint32) (runs: 256, μ: 92679, ~: 92614) -ReservoirPriceOracleTest:testGetLatest_Inverted() (gas: 96786) -ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 256, μ: 35782, ~: 35886) +ReservoirPriceOracleTest:testGetLatest(uint32) (runs: 259, μ: 93296, ~: 93228) +ReservoirPriceOracleTest:testGetLatest_Inverted() (gas: 97400) +ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 259, μ: 35879, ~: 36002) ReservoirPriceOracleTest:testGetQuote_AmountInTooLarge() (gas: 12963) -ReservoirPriceOracleTest:testGetQuote_BaseIsVault(uint256) (runs: 256, μ: 418221, ~: 417982) -ReservoirPriceOracleTest:testGetQuote_ComplicatedDecimals() (gas: 10352967) -ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 256, μ: 37892, ~: 38058) -ReservoirPriceOracleTest:testGetQuote_MultipleHops() (gas: 114257) -ReservoirPriceOracleTest:testGetQuote_MultipleHops_Inverse() (gas: 114512) -ReservoirPriceOracleTest:testGetQuote_MultipleHops_PriceZero() (gas: 126984) -ReservoirPriceOracleTest:testGetQuote_NoFallbackOracle() (gas: 21084) -ReservoirPriceOracleTest:testGetQuote_PriceZero() (gas: 16486) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 256, μ: 5327988, ~: 5328070) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 256, μ: 10493634, ~: 10493649) -ReservoirPriceOracleTest:testGetQuote_SameBaseQuote(uint256,address) (runs: 256, μ: 8963, ~: 8963) -ReservoirPriceOracleTest:testGetQuote_UseFallback() (gas: 38730) -ReservoirPriceOracleTest:testGetQuote_ZeroIn() (gas: 39315) -ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 256, μ: 33271, ~: 33375) -ReservoirPriceOracleTest:testGetTimeWeightedAverage() (gas: 141765) -ReservoirPriceOracleTest:testGetTimeWeightedAverage_Inverted() (gas: 120958) +ReservoirPriceOracleTest:testGetQuote_BaseIsVault(uint256) (runs: 259, μ: 417780, ~: 417538) +ReservoirPriceOracleTest:testGetQuote_ComplicatedDecimals() (gas: 10449568) +ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 259, μ: 37987, ~: 38050) +ReservoirPriceOracleTest:testGetQuote_MultipleHops() (gas: 114390) +ReservoirPriceOracleTest:testGetQuote_MultipleHops_Inverse() (gas: 114645) +ReservoirPriceOracleTest:testGetQuote_MultipleHops_PriceZero() (gas: 126519) +ReservoirPriceOracleTest:testGetQuote_NoFallbackOracle() (gas: 20920) +ReservoirPriceOracleTest:testGetQuote_PriceZero() (gas: 15902) +ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 258, μ: 5390914, ~: 5390998) +ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 258, μ: 10591778, ~: 10591794) +ReservoirPriceOracleTest:testGetQuote_SameBaseQuote(uint256,address) (runs: 259, μ: 8963, ~: 8963) +ReservoirPriceOracleTest:testGetQuote_UseFallback() (gas: 38402) +ReservoirPriceOracleTest:testGetQuote_ZeroIn() (gas: 38847) +ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 259, μ: 33018, ~: 33141) +ReservoirPriceOracleTest:testGetTimeWeightedAverage() (gas: 142993) +ReservoirPriceOracleTest:testGetTimeWeightedAverage_Inverted() (gas: 122186) ReservoirPriceOracleTest:testSetFallbackOracle_NotOwner() (gas: 10938) -ReservoirPriceOracleTest:testSetRoute() (gas: 58807) -ReservoirPriceOracleTest:testSetRoute_InvalidRoute() (gas: 18004) -ReservoirPriceOracleTest:testSetRoute_InvalidRouteLength() (gas: 17633) -ReservoirPriceOracleTest:testSetRoute_MultipleHops() (gas: 196034) -ReservoirPriceOracleTest:testSetRoute_NotSorted() (gas: 12095) -ReservoirPriceOracleTest:testSetRoute_OverwriteExisting() (gas: 162438) -ReservoirPriceOracleTest:testSetRoute_SameToken() (gas: 12070) +ReservoirPriceOracleTest:testSetRoute() (gas: 58954) +ReservoirPriceOracleTest:testSetRoute_InvalidRoute() (gas: 18066) +ReservoirPriceOracleTest:testSetRoute_InvalidRouteLength() (gas: 17695) +ReservoirPriceOracleTest:testSetRoute_MultipleHops() (gas: 194508) +ReservoirPriceOracleTest:testSetRoute_NotSorted() (gas: 12113) +ReservoirPriceOracleTest:testSetRoute_OverwriteExisting() (gas: 162430) +ReservoirPriceOracleTest:testSetRoute_SameToken() (gas: 12088) ReservoirPriceOracleTest:testUndesignatePair() (gas: 30257) ReservoirPriceOracleTest:testUndesignatePair_NotOwner() (gas: 15288) -ReservoirPriceOracleTest:testUpdatePriceDeviationThreshold(uint256) (runs: 256, μ: 21331, ~: 21085) -ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold() (gas: 214357) -ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold_InsufficientReward(uint256) (runs: 256, μ: 203578, ~: 203794) -ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold_ZeroRecipient() (gas: 196179) -ReservoirPriceOracleTest:testUpdatePrice_FirstUpdate() (gas: 203806) -ReservoirPriceOracleTest:testUpdatePrice_IntermediateRoutes() (gas: 15868736) -ReservoirPriceOracleTest:testUpdatePrice_PriceOutOfRange() (gas: 5351311) -ReservoirPriceOracleTest:testUpdatePrice_WithinThreshold() (gas: 204695) +ReservoirPriceOracleTest:testUpdatePriceDeviationThreshold(uint256) (runs: 259, μ: 21307, ~: 21085) +ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold() (gas: 216683) +ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold_InsufficientReward(uint256) (runs: 259, μ: 205898, ~: 205839) +ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold_ZeroRecipient() (gas: 198506) +ReservoirPriceOracleTest:testUpdatePrice_FirstUpdate() (gas: 205783) +ReservoirPriceOracleTest:testUpdatePrice_IntermediateRoutes() (gas: 15879018) +ReservoirPriceOracleTest:testUpdatePrice_PriceOutOfRange() (gas: 5354517) +ReservoirPriceOracleTest:testUpdatePrice_WithinThreshold() (gas: 207022) ReservoirPriceOracleTest:testUpdateRewardGasAmount() (gas: 19033) ReservoirPriceOracleTest:testUpdateRewardGasAmount_NotOwner() (gas: 10984) -ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 256, μ: 21743, ~: 21828) -ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 256, μ: 17863, ~: 18164) -ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 256, μ: 29936, ~: 29697) -SamplesTest:testAccumulator() (gas: 3959) -SamplesTest:testAccumulator_BadVariableRequest() (gas: 3523) -SamplesTest:testInstant() (gas: 3909) -SamplesTest:testInstant_BadVariableRequest() (gas: 3566) \ No newline at end of file +ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 259, μ: 21733, ~: 21828) +ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 259, μ: 17867, ~: 18164) +ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 259, μ: 30289, ~: 30047) +SamplesTest:testAccumulator() (gas: 4580) +SamplesTest:testAccumulator_BadVariableRequest() (gas: 4045) +SamplesTest:testInstant() (gas: 4536) +SamplesTest:testInstant_BadVariableRequest() (gas: 4088) \ No newline at end of file From c58587e0da55aedaf1087f524264a359740fb042 Mon Sep 17 00:00:00 2001 From: "A.L." Date: Sun, 7 Jul 2024 22:45:30 +0200 Subject: [PATCH 6/9] fix: collapse validation logic --- src/ReservoirPriceOracle.sol | 16 +++++++--------- src/libraries/OracleErrors.sol | 3 +-- src/libraries/{FlagsLib.sol => RoutesLib.sol} | 3 +-- test/unit/ReservoirPriceOracle.t.sol | 8 ++++---- test/unit/libraries/FlagsLib.t.sol | 18 +++++++++--------- 5 files changed, 22 insertions(+), 26 deletions(-) rename src/libraries/{FlagsLib.sol => RoutesLib.sol} (96%) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index 0d76121..81a3549 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -18,12 +18,12 @@ import { ReentrancyGuard } from "lib/amm-core/lib/solmate/src/utils/ReentrancyGu import { FixedPointMathLib } from "lib/amm-core/lib/solady/src/utils/FixedPointMathLib.sol"; import { LibSort } from "lib/solady/src/utils/LibSort.sol"; import { Constants } from "src/libraries/Constants.sol"; -import { FlagsLib } from "src/libraries/FlagsLib.sol"; +import { RoutesLib } from "src/libraries/RoutesLib.sol"; contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg.sender), ReentrancyGuard { using FixedPointMathLib for uint256; using LibSort for address[]; - using FlagsLib for *; + using RoutesLib for bytes32; using QueryProcessor for ReservoirPair; /////////////////////////////////////////////////////////////////////////////////////////////// @@ -197,9 +197,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. } function _validateTokens(address aToken0, address aToken1) private pure { - // REVIEW: Can be collapsed into `if (aToken1 <= aToken1) revert`. - if (aToken0 == aToken1) revert OracleErrors.SameToken(); - if (aToken1 < aToken0) revert OracleErrors.TokensUnsorted(); + if (aToken1 <= aToken0) revert OracleErrors.InvalidTokensProvided(); } function _getTimeWeightedAverageSingle(OracleAverageQuery memory aQuery) internal view returns (uint256 rResult) { @@ -347,7 +345,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. int256 lDiff = lData.getDecimalDifference(); - lData = FlagsLib.packSimplePrice(lDiff, aNewPrice); + lData = RoutesLib.packSimplePrice(lDiff, aNewPrice); assembly { sstore(lSlot, lData) } @@ -513,7 +511,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. int256 lDiff = int256(lToken1Decimals) - int256(lToken0Decimals); - bytes32 lData = FlagsLib.packSimplePrice(lDiff, 0); + bytes32 lData = RoutesLib.packSimplePrice(lDiff, 0); assembly { // Write data to storage. sstore(lSlot, lData) @@ -525,12 +523,12 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. address lThirdToken = aRoute[2]; if (lRouteLength == 3) { - bytes32 lData = FlagsLib.pack2HopRoute(lSecondToken); + bytes32 lData = RoutesLib.pack2HopRoute(lSecondToken); assembly { sstore(lSlot, lData) } } else if (lRouteLength == 4) { - (bytes32 lFirstWord, bytes32 lSecondWord) = FlagsLib.pack3HopRoute(lSecondToken, lThirdToken); + (bytes32 lFirstWord, bytes32 lSecondWord) = RoutesLib.pack3HopRoute(lSecondToken, lThirdToken); // Write two words to storage. assembly { diff --git a/src/libraries/OracleErrors.sol b/src/libraries/OracleErrors.sol index 3a70260..310f313 100644 --- a/src/libraries/OracleErrors.sol +++ b/src/libraries/OracleErrors.sol @@ -7,11 +7,10 @@ library OracleErrors { error IncorrectTokensDesignatePair(); error InvalidRoute(); error InvalidRouteLength(); + error InvalidTokensProvided(); error InvalidTwapPeriod(); error NoDesignatedPair(); error PriceDeviationThresholdTooHigh(); - error SameToken(); - error TokensUnsorted(); error UnsupportedTokenDecimals(); // query errors diff --git a/src/libraries/FlagsLib.sol b/src/libraries/RoutesLib.sol similarity index 96% rename from src/libraries/FlagsLib.sol rename to src/libraries/RoutesLib.sol index 63f3419..0bc207c 100644 --- a/src/libraries/FlagsLib.sol +++ b/src/libraries/RoutesLib.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; -// REVIEW: Rename `FlagsLib` -> `RoutesLib` as all operations are routes related? -library FlagsLib { +library RoutesLib { bytes32 public constant FLAG_UNINITIALIZED = bytes32(hex"00"); bytes32 public constant FLAG_SIMPLE_PRICE = bytes32(hex"01"); bytes32 public constant FLAG_2_HOP_ROUTE = bytes32(hex"02"); diff --git a/test/unit/ReservoirPriceOracle.t.sol b/test/unit/ReservoirPriceOracle.t.sol index 00fa54d..6082106 100644 --- a/test/unit/ReservoirPriceOracle.t.sol +++ b/test/unit/ReservoirPriceOracle.t.sol @@ -13,7 +13,7 @@ import { ReservoirPriceOracle, IERC20, IPriceOracle, - FlagsLib + RoutesLib } from "src/ReservoirPriceOracle.sol"; import { Bytes32Lib } from "amm-core/libraries/Bytes32.sol"; import { EnumerableSetLib } from "lib/solady/src/utils/EnumerableSetLib.sol"; @@ -23,7 +23,7 @@ import { StubERC4626 } from "test/mock/StubERC4626.sol"; contract ReservoirPriceOracleTest is BaseTest { using Utils for *; - using FlagsLib for *; + using RoutesLib for *; using Bytes32Lib for *; using EnumerableSetLib for EnumerableSetLib.AddressSet; using FixedPointMathLib for uint256; @@ -941,7 +941,7 @@ contract ReservoirPriceOracleTest is BaseTest { lRoute[1] = lToken1; // act & assert - vm.expectRevert(OracleErrors.SameToken.selector); + vm.expectRevert(OracleErrors.InvalidTokensProvided.selector); _oracle.setRoute(lToken0, lToken1, lRoute); } @@ -954,7 +954,7 @@ contract ReservoirPriceOracleTest is BaseTest { lRoute[1] = lToken1; // act & assert - vm.expectRevert(OracleErrors.TokensUnsorted.selector); + vm.expectRevert(OracleErrors.InvalidTokensProvided.selector); _oracle.setRoute(lToken0, lToken1, lRoute); } diff --git a/test/unit/libraries/FlagsLib.t.sol b/test/unit/libraries/FlagsLib.t.sol index e2f1021..3b1db8d 100644 --- a/test/unit/libraries/FlagsLib.t.sol +++ b/test/unit/libraries/FlagsLib.t.sol @@ -3,18 +3,18 @@ pragma solidity ^0.8.0; import { Test, console2, stdError } from "forge-std/Test.sol"; -import { FlagsLib } from "src/libraries/FlagsLib.sol"; +import { RoutesLib } from "src/libraries/RoutesLib.sol"; -contract FlagsLibTest is Test { - using FlagsLib for bytes32; - using FlagsLib for int256; +contract RoutesLibTest is Test { + using RoutesLib for bytes32; + using RoutesLib for int256; function testIsCompositeRoute() external pure { // arrange - bytes32 lUninitialized = FlagsLib.FLAG_UNINITIALIZED; - bytes32 l1HopRoute = FlagsLib.FLAG_SIMPLE_PRICE; - bytes32 l2HopRoute = FlagsLib.FLAG_2_HOP_ROUTE; - bytes32 l3HopRoute = FlagsLib.FLAG_3_HOP_ROUTE; + bytes32 lUninitialized = RoutesLib.FLAG_UNINITIALIZED; + bytes32 l1HopRoute = RoutesLib.FLAG_SIMPLE_PRICE; + bytes32 l2HopRoute = RoutesLib.FLAG_2_HOP_ROUTE; + bytes32 l3HopRoute = RoutesLib.FLAG_3_HOP_ROUTE; // act & assert assertTrue(l2HopRoute.isCompositeRoute()); @@ -43,7 +43,7 @@ contract FlagsLibTest is Test { bytes32 lResult = int256(aDiff).packSimplePrice(lPrice); // assert - assertEq(lResult[0], FlagsLib.FLAG_SIMPLE_PRICE); + assertEq(lResult[0], RoutesLib.FLAG_SIMPLE_PRICE); assertEq(lResult[1], bytes1(uint8(aDiff))); assertEq(lResult.getPrice(), lPrice); } From 29107aadf6bbb0832e1d696d710186452b5522ab Mon Sep 17 00:00:00 2001 From: "A.L." Date: Sun, 7 Jul 2024 22:46:42 +0200 Subject: [PATCH 7/9] refactor: rename FlagsLib into RoutesLib --- test/unit/libraries/{FlagsLib.t.sol => RoutesLib.t.sol} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/unit/libraries/{FlagsLib.t.sol => RoutesLib.t.sol} (100%) diff --git a/test/unit/libraries/FlagsLib.t.sol b/test/unit/libraries/RoutesLib.t.sol similarity index 100% rename from test/unit/libraries/FlagsLib.t.sol rename to test/unit/libraries/RoutesLib.t.sol From 81cdb3d6da0dce9d390eb574a46f9610bfd631a2 Mon Sep 17 00:00:00 2001 From: "A.L." Date: Sun, 7 Jul 2024 22:58:52 +0200 Subject: [PATCH 8/9] fix: process 2 hop route before 3 hop route --- src/ReservoirPriceOracle.sol | 11 +++++------ src/libraries/RoutesLib.sol | 4 ++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index 81a3549..5e9fe8d 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -271,8 +271,11 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. else if (lFirstWord.isCompositeRoute()) { address lSecondToken = lFirstWord.getTokenFirstWord(); - // REVIEW: Is it more logical to handle `is2HopRoute` then fallback to `assert(is3HopRoute)`? - if (lFirstWord.is3HopRoute()) { + if (lFirstWord.is2HopRoute()) { + rRoute = new address[](3); + rRoute[2] = aToken1; + } else { + assert(lFirstWord.is3HopRoute()); bytes32 lSecondWord; assembly { lSecondWord := sload(add(lSlot, 1)) @@ -282,16 +285,12 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. rRoute = new address[](4); rRoute[2] = lThirdToken; rRoute[3] = aToken1; - } else { - rRoute = new address[](3); - rRoute[2] = aToken1; } rRoute[0] = aToken0; rRoute[1] = lSecondToken; } // no route - // solhint-disable-next-line no-empty-blocks else if (lFirstWord.isUninitialized()) { rRoute = new address[](0); } diff --git a/src/libraries/RoutesLib.sol b/src/libraries/RoutesLib.sol index 0bc207c..72b4a83 100644 --- a/src/libraries/RoutesLib.sol +++ b/src/libraries/RoutesLib.sol @@ -19,6 +19,10 @@ library RoutesLib { return aData[0] & hex"02" > 0; } + function is2HopRoute(bytes32 aData) internal pure returns (bool) { + return aData[0] == FLAG_2_HOP_ROUTE; + } + function is3HopRoute(bytes32 aData) internal pure returns (bool) { return aData[0] == FLAG_3_HOP_ROUTE; } From 5f5ede4f6da4ddb2d74b4ca95a7b72ac5c9217f9 Mon Sep 17 00:00:00 2001 From: "A.L." Date: Sun, 7 Jul 2024 23:02:38 +0200 Subject: [PATCH 9/9] gas: update snapshot --- .gas-snapshot | 124 +++++++++++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index ca3359e..39f09f6 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,75 +1,75 @@ -FlagsLibTest:testGetDecimalDifference() (gas: 3974) -FlagsLibTest:testIsCompositeRoute() (gas: 4341) -FlagsLibTest:testPackSimplePrice(int8,uint256) (runs: 259, μ: 7799, ~: 7555) -QueryProcessorTest:testFindNearestSample_CanFindExactValue(uint32,uint256,uint256,uint256) (runs: 259, μ: 67557567, ~: 76401709) -QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 259, μ: 67033233, ~: 76116668) -QueryProcessorTest:testFindNearestSample_NotInitialized() (gas: 8937393461068805977) -QueryProcessorTest:testFindNearestSample_OneSample(uint256) (runs: 259, μ: 80945, ~: 80974) -QueryProcessorTest:testGetInstantValue() (gas: 126197) -QueryProcessorTest:testGetInstantValue_NotInitialized(uint256) (runs: 259, μ: 19397, ~: 19397) -QueryProcessorTest:testGetInstantValue_NotInitialized_BeyondBufferSize(uint8,uint16) (runs: 259, μ: 69647140, ~: 69647072) -QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 259, μ: 27034, ~: 27087) -QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 259, μ: 72319369, ~: 82729762) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 259, μ: 71628429, ~: 81068158) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 259, μ: 71658441, ~: 81099758) -QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 259, μ: 67006873, ~: 76087503) -QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 259, μ: 67040913, ~: 76122676) -QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 259, μ: 66998495, ~: 76079900) -QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 259, μ: 67009985, ~: 76089440) -QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 259, μ: 111263283, ~: 119811834) +QueryProcessorTest:testFindNearestSample_CanFindExactValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 67585746, ~: 76007063) +QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 67051023, ~: 76087612) +QueryProcessorTest:testFindNearestSample_NotInitialized() (gas: 1056945756) +QueryProcessorTest:testFindNearestSample_OneSample(uint256) (runs: 256, μ: 80325, ~: 80360) +QueryProcessorTest:testGetInstantValue() (gas: 124248) +QueryProcessorTest:testGetInstantValue_NotInitialized(uint256) (runs: 256, μ: 19397, ~: 19397) +QueryProcessorTest:testGetInstantValue_NotInitialized_BeyondBufferSize(uint8,uint16) (runs: 256, μ: 68389647, ~: 68389600) +QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 256, μ: 27017, ~: 27087) +QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 256, μ: 69289018, ~: 79307245) +QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 70900090, ~: 79224986) +QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 70930212, ~: 79256586) +QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 256, μ: 67024575, ~: 76058923) +QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 256, μ: 67058609, ~: 76093620) +QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 256, μ: 67016184, ~: 76050634) +QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 256, μ: 67027596, ~: 76060517) +QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 256, μ: 101761121, ~: 108597954) QueryProcessorTest:testGetTimeWeightedAverage_BadSecs() (gas: 10995) -ReservoirPriceOracleTest:testClearRoute() (gas: 51595) -ReservoirPriceOracleTest:testClearRoute_AllWordsCleared() (gas: 152969) +ReservoirPriceOracleTest:testClearRoute() (gas: 50974) +ReservoirPriceOracleTest:testClearRoute_AllWordsCleared() (gas: 151907) ReservoirPriceOracleTest:testDesignatePair() (gas: 29091) ReservoirPriceOracleTest:testDesignatePair_IncorrectPair() (gas: 21111) ReservoirPriceOracleTest:testDesignatePair_NotOwner() (gas: 17531) ReservoirPriceOracleTest:testDesignatePair_TokenOrderReversed() (gas: 30729) -ReservoirPriceOracleTest:testGasBountyAvailable(uint256) (runs: 259, μ: 9929, ~: 9925) +ReservoirPriceOracleTest:testGasBountyAvailable(uint256) (runs: 256, μ: 9929, ~: 9925) ReservoirPriceOracleTest:testGasBountyAvailable_Zero() (gas: 8961) -ReservoirPriceOracleTest:testGetLatest(uint32) (runs: 259, μ: 93296, ~: 93228) -ReservoirPriceOracleTest:testGetLatest_Inverted() (gas: 97400) -ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 259, μ: 35879, ~: 36002) +ReservoirPriceOracleTest:testGetLatest(uint32) (runs: 256, μ: 92685, ~: 92614) +ReservoirPriceOracleTest:testGetLatest_Inverted() (gas: 96786) +ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 256, μ: 35198, ~: 35302) ReservoirPriceOracleTest:testGetQuote_AmountInTooLarge() (gas: 12963) -ReservoirPriceOracleTest:testGetQuote_BaseIsVault(uint256) (runs: 259, μ: 417780, ~: 417538) -ReservoirPriceOracleTest:testGetQuote_ComplicatedDecimals() (gas: 10449568) -ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 259, μ: 37987, ~: 38050) -ReservoirPriceOracleTest:testGetQuote_MultipleHops() (gas: 114390) -ReservoirPriceOracleTest:testGetQuote_MultipleHops_Inverse() (gas: 114645) -ReservoirPriceOracleTest:testGetQuote_MultipleHops_PriceZero() (gas: 126519) -ReservoirPriceOracleTest:testGetQuote_NoFallbackOracle() (gas: 20920) +ReservoirPriceOracleTest:testGetQuote_BaseIsVault(uint256) (runs: 256, μ: 417430, ~: 417188) +ReservoirPriceOracleTest:testGetQuote_ComplicatedDecimals() (gas: 10350840) +ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 256, μ: 37311, ~: 37474) +ReservoirPriceOracleTest:testGetQuote_MultipleHops() (gas: 113391) +ReservoirPriceOracleTest:testGetQuote_MultipleHops_Inverse() (gas: 113646) +ReservoirPriceOracleTest:testGetQuote_MultipleHops_PriceZero() (gas: 125259) +ReservoirPriceOracleTest:testGetQuote_NoFallbackOracle() (gas: 20875) ReservoirPriceOracleTest:testGetQuote_PriceZero() (gas: 15902) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 258, μ: 5390914, ~: 5390998) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 258, μ: 10591778, ~: 10591794) -ReservoirPriceOracleTest:testGetQuote_SameBaseQuote(uint256,address) (runs: 259, μ: 8963, ~: 8963) -ReservoirPriceOracleTest:testGetQuote_UseFallback() (gas: 38402) -ReservoirPriceOracleTest:testGetQuote_ZeroIn() (gas: 38847) -ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 259, μ: 33018, ~: 33141) -ReservoirPriceOracleTest:testGetTimeWeightedAverage() (gas: 142993) -ReservoirPriceOracleTest:testGetTimeWeightedAverage_Inverted() (gas: 122186) +ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 256, μ: 5327381, ~: 5327411) +ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 256, μ: 10492723, ~: 10492862) +ReservoirPriceOracleTest:testGetQuote_SameBaseQuote(uint256,address) (runs: 256, μ: 8963, ~: 8963) +ReservoirPriceOracleTest:testGetQuote_UseFallback() (gas: 38312) +ReservoirPriceOracleTest:testGetQuote_ZeroIn() (gas: 38147) +ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 256, μ: 32687, ~: 32791) +ReservoirPriceOracleTest:testGetTimeWeightedAverage() (gas: 141765) +ReservoirPriceOracleTest:testGetTimeWeightedAverage_Inverted() (gas: 120958) ReservoirPriceOracleTest:testSetFallbackOracle_NotOwner() (gas: 10938) -ReservoirPriceOracleTest:testSetRoute() (gas: 58954) -ReservoirPriceOracleTest:testSetRoute_InvalidRoute() (gas: 18066) -ReservoirPriceOracleTest:testSetRoute_InvalidRouteLength() (gas: 17695) -ReservoirPriceOracleTest:testSetRoute_MultipleHops() (gas: 194508) -ReservoirPriceOracleTest:testSetRoute_NotSorted() (gas: 12113) -ReservoirPriceOracleTest:testSetRoute_OverwriteExisting() (gas: 162430) -ReservoirPriceOracleTest:testSetRoute_SameToken() (gas: 12088) +ReservoirPriceOracleTest:testSetRoute() (gas: 58216) +ReservoirPriceOracleTest:testSetRoute_InvalidRoute() (gas: 17990) +ReservoirPriceOracleTest:testSetRoute_InvalidRouteLength() (gas: 17619) +ReservoirPriceOracleTest:testSetRoute_MultipleHops() (gas: 193395) +ReservoirPriceOracleTest:testSetRoute_NotSorted() (gas: 12102) +ReservoirPriceOracleTest:testSetRoute_OverwriteExisting() (gas: 160967) +ReservoirPriceOracleTest:testSetRoute_SameToken() (gas: 12115) ReservoirPriceOracleTest:testUndesignatePair() (gas: 30257) ReservoirPriceOracleTest:testUndesignatePair_NotOwner() (gas: 15288) -ReservoirPriceOracleTest:testUpdatePriceDeviationThreshold(uint256) (runs: 259, μ: 21307, ~: 21085) -ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold() (gas: 216683) -ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold_InsufficientReward(uint256) (runs: 259, μ: 205898, ~: 205839) -ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold_ZeroRecipient() (gas: 198506) -ReservoirPriceOracleTest:testUpdatePrice_FirstUpdate() (gas: 205783) -ReservoirPriceOracleTest:testUpdatePrice_IntermediateRoutes() (gas: 15879018) -ReservoirPriceOracleTest:testUpdatePrice_PriceOutOfRange() (gas: 5354517) -ReservoirPriceOracleTest:testUpdatePrice_WithinThreshold() (gas: 207022) +ReservoirPriceOracleTest:testUpdatePriceDeviationThreshold(uint256) (runs: 256, μ: 21328, ~: 21085) +ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold() (gas: 213770) +ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold_InsufficientReward(uint256) (runs: 256, μ: 202989, ~: 203067) +ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold_ZeroRecipient() (gas: 195593) +ReservoirPriceOracleTest:testUpdatePrice_FirstUpdate() (gas: 203220) +ReservoirPriceOracleTest:testUpdatePrice_IntermediateRoutes() (gas: 15867847) +ReservoirPriceOracleTest:testUpdatePrice_PriceOutOfRange() (gas: 5350717) +ReservoirPriceOracleTest:testUpdatePrice_WithinThreshold() (gas: 204109) ReservoirPriceOracleTest:testUpdateRewardGasAmount() (gas: 19033) ReservoirPriceOracleTest:testUpdateRewardGasAmount_NotOwner() (gas: 10984) -ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 259, μ: 21733, ~: 21828) -ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 259, μ: 17867, ~: 18164) -ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 259, μ: 30289, ~: 30047) -SamplesTest:testAccumulator() (gas: 4580) -SamplesTest:testAccumulator_BadVariableRequest() (gas: 4045) -SamplesTest:testInstant() (gas: 4536) -SamplesTest:testInstant_BadVariableRequest() (gas: 4088) \ No newline at end of file +ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 256, μ: 21745, ~: 21828) +ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 256, μ: 17861, ~: 18164) +ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 256, μ: 29939, ~: 29697) +RoutesLibTest:testGetDecimalDifference() (gas: 3974) +RoutesLibTest:testIsCompositeRoute() (gas: 4341) +RoutesLibTest:testPackSimplePrice(int8,uint256) (runs: 256, μ: 7786, ~: 7555) +SamplesTest:testAccumulator() (gas: 3959) +SamplesTest:testAccumulator_BadVariableRequest() (gas: 3523) +SamplesTest:testInstant() (gas: 3909) +SamplesTest:testInstant_BadVariableRequest() (gas: 3566) \ No newline at end of file