diff --git a/.gas-snapshot b/.gas-snapshot index 5ea6a96..44ffc75 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,77 +1,74 @@ 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, μ: 67190289, ~: 76237020) -QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 66054084, ~: 76001988) -QueryProcessorTest:testFindNearestSample_NotInitialized() (gas: 8937393461068805977) +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, μ: 68389660, ~: 68389600) -QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 256, μ: 27025, ~: 27087) -QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 256, μ: 70813025, ~: 79026739) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 68828792, ~: 79254030) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 68858620, ~: 79285630) -QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 256, μ: 66027680, ~: 75973166) -QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 256, μ: 66061751, ~: 76007996) -QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 256, μ: 66019273, ~: 75964877) -QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 256, μ: 66030758, ~: 75974761) -QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 256, μ: 105658725, ~: 115790545) +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) QueryProcessorTest:testGetTimeWeightedAverage_BadSecs() (gas: 10995) -ReservoirPriceOracleTest:testClearRoute() (gas: 52160) -ReservoirPriceOracleTest:testClearRoute_AllWordsCleared() (gas: 155162) -ReservoirPriceOracleTest:testDesignatePair() (gas: 29119) -ReservoirPriceOracleTest:testDesignatePair_IncorrectPair() (gas: 21206) +ReservoirPriceOracleTest:testClearRoute() (gas: 52085) +ReservoirPriceOracleTest:testClearRoute_AllWordsCleared() (gas: 155034) +ReservoirPriceOracleTest:testDesignatePair() (gas: 29091) +ReservoirPriceOracleTest:testDesignatePair_IncorrectPair() (gas: 21111) ReservoirPriceOracleTest:testDesignatePair_NotOwner() (gas: 17531) -ReservoirPriceOracleTest:testDesignatePair_TokenOrderReversed() (gas: 30802) -ReservoirPriceOracleTest:testGasBountyAvailable(uint256) (runs: 256, μ: 9885, ~: 9881) -ReservoirPriceOracleTest:testGasBountyAvailable_Zero() (gas: 8939) -ReservoirPriceOracleTest:testGetLargestSafeQueryWindow() (gas: 8390) -ReservoirPriceOracleTest:testGetLatest(uint32) (runs: 256, μ: 92809, ~: 92737) -ReservoirPriceOracleTest:testGetLatest_Inverted() (gas: 96792) -ReservoirPriceOracleTest:testGetPastAccumulators() (gas: 196359) -ReservoirPriceOracleTest:testGetPastAccumulators_Inverted() (gas: 156800) -ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 256, μ: 35812, ~: 35927) -ReservoirPriceOracleTest:testGetQuote_AmountInTooLarge() (gas: 13030) -ReservoirPriceOracleTest:testGetQuote_BaseIsVault(uint256) (runs: 256, μ: 418324, ~: 418079) -ReservoirPriceOracleTest:testGetQuote_ComplicatedDecimals() (gas: 10353271) -ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 256, μ: 37974, ~: 38148) -ReservoirPriceOracleTest:testGetQuote_MultipleHops() (gas: 114566) -ReservoirPriceOracleTest:testGetQuote_MultipleHops_Inverse() (gas: 114799) -ReservoirPriceOracleTest:testGetQuote_MultipleHops_PriceZero() (gas: 127407) -ReservoirPriceOracleTest:testGetQuote_NoFallbackOracle() (gas: 21074) -ReservoirPriceOracleTest:testGetQuote_PriceZero() (gas: 16564) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 256, μ: 5328089, ~: 5328201) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 256, μ: 10493966, ~: 10493970) -ReservoirPriceOracleTest:testGetQuote_SameBaseQuote(uint256,address) (runs: 256, μ: 9030, ~: 9030) -ReservoirPriceOracleTest:testGetQuote_UseFallback() (gas: 38776) -ReservoirPriceOracleTest:testGetQuote_ZeroIn() (gas: 39390) -ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 256, μ: 33323, ~: 33438) -ReservoirPriceOracleTest:testGetTimeWeightedAverage() (gas: 141789) -ReservoirPriceOracleTest:testGetTimeWeightedAverage_Inverted() (gas: 120964) -ReservoirPriceOracleTest:testSetFallbackOracle_NotOwner() (gas: 11003) -ReservoirPriceOracleTest:testSetRoute() (gas: 58848) -ReservoirPriceOracleTest:testSetRoute_InvalidRoute() (gas: 18005) -ReservoirPriceOracleTest:testSetRoute_InvalidRouteLength() (gas: 17611) -ReservoirPriceOracleTest:testSetRoute_MultipleHops() (gas: 196135) +ReservoirPriceOracleTest:testDesignatePair_TokenOrderReversed() (gas: 30729) +ReservoirPriceOracleTest:testGasBountyAvailable(uint256) (runs: 256, μ: 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: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: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: 162578) -ReservoirPriceOracleTest:testSetRoute_SameToken() (gas: 12048) -ReservoirPriceOracleTest:testUndesignatePair() (gas: 30357) -ReservoirPriceOracleTest:testUndesignatePair_NotOwner() (gas: 15332) -ReservoirPriceOracleTest:testUpdatePriceDeviationThreshold(uint256) (runs: 256, μ: 21342, ~: 21085) -ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold() (gas: 214700) -ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold_InsufficientReward(uint256) (runs: 256, μ: 203743, ~: 203946) -ReservoirPriceOracleTest:testUpdatePrice_BeyondThreshold_ZeroRecipient() (gas: 196326) -ReservoirPriceOracleTest:testUpdatePrice_FirstUpdate() (gas: 204010) -ReservoirPriceOracleTest:testUpdatePrice_IntermediateRoutes() (gas: 15869194) -ReservoirPriceOracleTest:testUpdatePrice_PriceOutOfRange() (gas: 5351368) -ReservoirPriceOracleTest:testUpdatePrice_WithinThreshold() (gas: 204855) -ReservoirPriceOracleTest:testUpdateRewardGasAmount() (gas: 19055) +ReservoirPriceOracleTest:testSetRoute_OverwriteExisting() (gas: 162438) +ReservoirPriceOracleTest:testSetRoute_SameToken() (gas: 12070) +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:testUpdateRewardGasAmount() (gas: 19033) ReservoirPriceOracleTest:testUpdateRewardGasAmount_NotOwner() (gas: 10984) -ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 256, μ: 21785, ~: 21870) -ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 256, μ: 17874, ~: 18164) -ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 256, μ: 30022, ~: 29777) +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) diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index 791e4a1..90256be 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -8,11 +8,10 @@ import { OracleErrors } from "src/libraries/OracleErrors.sol"; import { IReservoirPriceOracle, OracleAverageQuery, - OracleLatestQuery, - OracleAccumulatorQuery + OracleLatestQuery } from "src/interfaces/IReservoirPriceOracle.sol"; import { IPriceOracle } from "src/interfaces/IPriceOracle.sol"; -import { QueryProcessor, ReservoirPair, Buffer, PriceType } from "src/libraries/QueryProcessor.sol"; +import { QueryProcessor, ReservoirPair, PriceType } from "src/libraries/QueryProcessor.sol"; import { Utils } from "src/libraries/Utils.sol"; import { Owned } from "lib/amm-core/lib/solmate/src/auth/Owned.sol"; import { ReentrancyGuard } from "lib/amm-core/lib/solmate/src/utils/ReentrancyGuard.sol"; @@ -26,7 +25,6 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. using LibSort for address[]; using FlagsLib for *; using QueryProcessor for ReservoirPair; - using Utils for *; /////////////////////////////////////////////////////////////////////////////////////////////// // EVENTS // @@ -136,13 +134,13 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. /// @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) public nonReentrant { - (address lToken0, address lToken1) = aTokenA.sortTokens(aTokenB); + (address lToken0, address lToken1) = Utils.sortTokens(aTokenA, aTokenB); (address[] memory lRoute,, uint256 lPrevPrice) = _getRouteDecimalDifferencePrice(lToken0, lToken1); if (lRoute.length == 0) revert OracleErrors.NoPath(); for (uint256 i = 0; i < lRoute.length - 1; ++i) { - (lToken0, lToken1) = lRoute[i].sortTokens(lRoute[i + 1]); + (lToken0, lToken1) = Utils.sortTokens(lRoute[i], lRoute[i + 1]); uint256 lNewPrice = _getTimeWeightedAverageSingle( OracleAverageQuery( @@ -190,31 +188,6 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. return lResult; } - /// @inheritdoc IReservoirPriceOracle - function getPastAccumulators(OracleAccumulatorQuery[] memory aQueries) - external - view - returns (int256[] memory rResults) - { - rResults = new int256[](aQueries.length); - - OracleAccumulatorQuery memory lQuery; - for (uint256 i = 0; i < aQueries.length; ++i) { - lQuery = aQueries[i]; - ReservoirPair lPair = pairs[lQuery.base][lQuery.quote]; - _validatePair(lPair); - - (,,, uint16 lIndex) = lPair.getReserves(); - int256 lAcc = lPair.getPastAccumulator(lQuery.priceType, lIndex, lQuery.ago); - rResults[i] = lAcc; - } - } - - /// @inheritdoc IReservoirPriceOracle - function getLargestSafeQueryWindow() external pure returns (uint256) { - return Buffer.SIZE; - } - /////////////////////////////////////////////////////////////////////////////////////////////// // INTERNAL FUNCTIONS // /////////////////////////////////////////////////////////////////////////////////////////////// @@ -249,10 +222,17 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. if (aRecipient == address(0)) return; // N.B. Revisit this whenever deployment on a new chain is needed - // we use `block.basefee` instead of `ArbGasInfo::getMinimumGasPrice()` on ARB because the latter will always return - // the demand insensitive base fee, while the former can return real higher fees during times of congestion - // safety: this mul will not overflow even in extreme cases of `block.basefee` - uint256 lPayoutAmt = block.basefee * rewardGasAmount; + // + // we use `block.basefee` instead of `ArbGasInfo::getMinimumGasPrice()` + // on ARB because the latter will always return the demand insensitive + // base fee, while the former can return higher fees during times of + // congestion + + // SAFETY: this mul will not overflow even in extreme cases of `block.basefee`. + uint256 lPayoutAmt; + unchecked { + lPayoutAmt = block.basefee * rewardGasAmount; + } if (lPayoutAmt <= address(this).balance) { payable(aRecipient).transfer(lPayoutAmt); @@ -269,7 +249,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. returns (address[] memory rRoute, int256 rDecimalDiff, uint256 rPrice) { address[] memory lResults = new address[](Constants.MAX_ROUTE_LENGTH); - bytes32 lSlot = aToken0.calculateSlot(aToken1); + bytes32 lSlot = Utils.calculateSlot(aToken0, aToken1); bytes32 lFirstWord; uint256 lRouteLength; @@ -297,7 +277,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. assembly { lSecondWord := sload(add(lSlot, 1)) } - address lThirdToken = lFirstWord.getThirdToken(lSecondWord); + address lThirdToken = lSecondWord.getThirdToken(); lResults[2] = lThirdToken; lResults[3] = aToken1; @@ -321,9 +301,9 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. /// 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) = aToken0.sortTokens(aToken1); + (address lLowerToken, address lHigherToken) = Utils.sortTokens(aToken0, aToken1); - bytes32 lSlot = lLowerToken.calculateSlot(lHigherToken); + bytes32 lSlot = Utils.calculateSlot(lLowerToken, lHigherToken); bytes32 lData; assembly { lData := sload(lSlot) @@ -342,7 +322,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. view returns (uint256 rPrice, int256 rDecimalDiff) { - bytes32 lSlot = aToken0.calculateSlot(aToken1); + bytes32 lSlot = Utils.calculateSlot(aToken0, aToken1); bytes32 lData; assembly { @@ -357,7 +337,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. function _writePriceCache(address aToken0, address aToken1, uint256 aNewPrice) internal { if (aNewPrice == 0 || aNewPrice > Constants.MAX_SUPPORTED_PRICE) revert OracleErrors.PriceOutOfRange(aNewPrice); - bytes32 lSlot = aToken0.calculateSlot(aToken1); + bytes32 lSlot = Utils.calculateSlot(aToken0, aToken1); bytes32 lData; assembly { lData := sload(lSlot) @@ -380,7 +360,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. if (aBase == aQuote) return (aAmount, aAmount); if (aAmount > Constants.MAX_AMOUNT_IN) revert OracleErrors.AmountInTooLarge(); - (address lToken0, address lToken1) = aBase.sortTokens(aQuote); + (address lToken0, address lToken1) = Utils.sortTokens(aBase, aQuote); (address[] memory lRoute, int256 lDecimalDiff, uint256 lPrice) = _getRouteDecimalDifferencePrice(lToken0, lToken1); @@ -410,7 +390,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. assert(lRoute[0] == aBase); for (uint256 i = 0; i < lRoute.length - 1; ++i) { - (address lLowerToken, address lHigherToken) = lRoute[i].sortTokens(lRoute[i + 1]); + (address lLowerToken, address lHigherToken) = Utils.sortTokens(lRoute[i], lRoute[i + 1]); // it is assumed that intermediate routes defined here are simple routes and not composite routes (lPrice, lDecimalDiff) = _priceCache(lLowerToken, lHigherToken); @@ -495,7 +475,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. /// @notice Sets the pair to serve as price feed for a given route. function designatePair(address aTokenA, address aTokenB, ReservoirPair aPair) external onlyOwner { - (aTokenA, aTokenB) = aTokenA.sortTokens(aTokenB); + (aTokenA, aTokenB) = Utils.sortTokens(aTokenA, aTokenB); if (aTokenA != address(aPair.token0()) || aTokenB != address(aPair.token1())) { revert OracleErrors.IncorrectTokensDesignatePair(); } @@ -505,7 +485,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. } function undesignatePair(address aToken0, address aToken1) external onlyOwner { - (aToken0, aToken1) = aToken0.sortTokens(aToken1); + (aToken0, aToken1) = Utils.sortTokens(aToken0, aToken1); delete pairs[aToken0][aToken1]; emit DesignatePair(aToken0, aToken1, ReservoirPair(address(0))); @@ -523,7 +503,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. if (lRouteLength > Constants.MAX_ROUTE_LENGTH || lRouteLength < 2) revert OracleErrors.InvalidRouteLength(); if (aRoute[0] != aToken0 || aRoute[lRouteLength - 1] != aToken1) revert OracleErrors.InvalidRoute(); - bytes32 lSlot = aToken0.calculateSlot(aToken1); + bytes32 lSlot = Utils.calculateSlot(aToken0, aToken1); // simple route if (lRouteLength == 2) { @@ -571,7 +551,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg. (address[] memory lRoute,,) = _getRouteDecimalDifferencePrice(aToken0, aToken1); - bytes32 lSlot = aToken0.calculateSlot(aToken1); + bytes32 lSlot = Utils.calculateSlot(aToken0, aToken1); // clear the storage slot that the route has written to previously assembly { diff --git a/src/interfaces/IReservoirPriceOracle.sol b/src/interfaces/IReservoirPriceOracle.sol index 16e92ce..22d3b8a 100644 --- a/src/interfaces/IReservoirPriceOracle.sol +++ b/src/interfaces/IReservoirPriceOracle.sol @@ -14,7 +14,7 @@ pragma solidity ^0.8.0; -import { OracleAverageQuery, OracleLatestQuery, OracleAccumulatorQuery } from "src/Structs.sol"; +import { OracleAverageQuery, OracleLatestQuery } from "src/Structs.sol"; /** * @dev Interface for querying historical data from a Pool that can be used as a Price Oracle. @@ -40,24 +40,4 @@ interface IReservoirPriceOracle { * @dev Returns latest sample of `priceType`. Prices are represented as 18 decimal fixed point values. */ function getLatest(OracleLatestQuery calldata priceType) external view returns (uint256); - - /** - * @dev Returns largest time window that can be safely queried, where 'safely' means the Oracle is guaranteed to be - * able to produce a result and not revert. - * - * If a query has a non-zero `ago` value, then `secs + ago` (the oldest point in time) must be smaller than this - * value for 'safe' queries. - * - * Since ReservoirPair's oracle writes every second, the largest safe query window is the number of seconds - * same as the size of the buffer. - */ - function getLargestSafeQueryWindow() external view returns (uint256); - - /** - * @dev Returns the accumulators corresponding to each of `queries`. - */ - function getPastAccumulators(OracleAccumulatorQuery[] memory queries) - external - view - returns (int256[] memory results); } diff --git a/src/libraries/FlagsLib.sol b/src/libraries/FlagsLib.sol index a955e85..3d7ad57 100644 --- a/src/libraries/FlagsLib.sol +++ b/src/libraries/FlagsLib.sol @@ -47,12 +47,8 @@ library FlagsLib { pure returns (bytes32 rFirstWord, bytes32 rSecondWord) { - bytes32 lThirdTokenTop10Bytes = bytes32(bytes20(aThirdToken)) >> 176; - // Trim away the first 10 bytes since we only want the last 10 bytes. - bytes32 lThirdTokenBottom10Bytes = bytes32(bytes20(aThirdToken) << 80); - - rFirstWord = FLAG_3_HOP_ROUTE | bytes32(bytes20(aSecondToken)) >> 8 | lThirdTokenTop10Bytes; - rSecondWord = lThirdTokenBottom10Bytes; + rFirstWord = FLAG_3_HOP_ROUTE | bytes32(bytes20(aSecondToken)) >> 8; + rSecondWord = bytes20(aThirdToken); } function getPrice(bytes32 aData) internal pure returns (uint256 rPrice) { @@ -64,9 +60,7 @@ library FlagsLib { address(uint160(uint256(aData & 0x00ffffffffffffffffffffffffffffffffffffffff0000000000000000000000) >> 88)); } - function getThirdToken(bytes32 aFirstWord, bytes32 aSecondWord) internal pure returns (address rToken) { - bytes32 lTop10Bytes = (aFirstWord & 0x00000000000000000000000000000000000000000000ffffffffffffffffffff) << 80; - bytes32 lBottom10Bytes = aSecondWord >> 176; - rToken = address(uint160(uint256(lTop10Bytes | lBottom10Bytes))); + function getThirdToken(bytes32 aSecondWord) internal pure returns (address rToken) { + rToken = address(bytes20(aSecondWord)); } } diff --git a/src/libraries/Utils.sol b/src/libraries/Utils.sol index 880db57..2e8dae8 100644 --- a/src/libraries/Utils.sol +++ b/src/libraries/Utils.sol @@ -2,11 +2,6 @@ pragma solidity ^0.8.0; library Utils { - /// @dev Square of 1e18 (WAD) - uint256 internal constant WAD_SQUARED = 1e36; - - error OutOfRange(uint256 value); - // returns the lower address followed by the higher address function sortTokens(address tokenA, address tokenB) internal pure returns (address, address) { return tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); @@ -16,10 +11,4 @@ library Utils { function calculateSlot(address aToken0, address aToken1) internal pure returns (bytes32) { return keccak256(abi.encode(aToken0, aToken1)); } - - function invertWad(uint256 x) internal pure returns (uint256) { - if (x == 0 || x > WAD_SQUARED) revert OutOfRange(x); - - return WAD_SQUARED / x; - } } diff --git a/test/unit/ReservoirPriceOracle.t.sol b/test/unit/ReservoirPriceOracle.t.sol index 9b2dced..00fa54d 100644 --- a/test/unit/ReservoirPriceOracle.t.sol +++ b/test/unit/ReservoirPriceOracle.t.sol @@ -5,12 +5,10 @@ import { BaseTest, console2, ReservoirPair, MintableERC20 } from "test/__fixture import { Utils } from "src/libraries/Utils.sol"; import { - Buffer, FixedPointMathLib, PriceType, OracleErrors, OracleLatestQuery, - OracleAccumulatorQuery, OracleAverageQuery, ReservoirPriceOracle, IERC20, @@ -808,37 +806,6 @@ contract ReservoirPriceOracleTest is BaseTest { assertEq(lLatestPrice, 98_918_868_099_219_913_512); } - function testGetPastAccumulators() external { - // arrange - skip(1 hours); - _pair.sync(); - skip(1 hours); - _pair.sync(); - skip(1 hours); - _pair.sync(); - _oracle.designatePair(address(_tokenA), address(_tokenB), _pair); - OracleAccumulatorQuery[] memory lQueries = new OracleAccumulatorQuery[](3); - lQueries[0] = OracleAccumulatorQuery(PriceType.RAW_PRICE, address(_tokenA), address(_tokenB), 0); - lQueries[1] = OracleAccumulatorQuery(PriceType.RAW_PRICE, address(_tokenA), address(_tokenB), 1 hours); - lQueries[2] = OracleAccumulatorQuery(PriceType.RAW_PRICE, address(_tokenA), address(_tokenB), 2 hours); - - // act - int256[] memory lResults = _oracle.getPastAccumulators(lQueries); - - // assert - assertEq(lResults.length, lQueries.length); - vm.startPrank(address(_oracle)); - assertEq(lResults[0], _pair.observation(2).logAccRawPrice); - assertEq(lResults[1], _pair.observation(1).logAccRawPrice); - assertEq(lResults[2], _pair.observation(0).logAccRawPrice); - vm.stopPrank(); - } - - function testGetLargestSafeQueryWindow() external view { - // assert - assertEq(_oracle.getLargestSafeQueryWindow(), Buffer.SIZE); - } - function testDesignatePair() external { // act vm.expectEmit(false, false, false, true); @@ -884,25 +851,6 @@ contract ReservoirPriceOracleTest is BaseTest { _oracle.getLatest(OracleLatestQuery(PriceType.RAW_PRICE, address(_tokenB), address(_tokenA))); } - function testGetPastAccumulators_Inverted() external { - // arrange - skip(1 hours); - _pair.sync(); - skip(1 hours); - _pair.sync(); - skip(1 hours); - _pair.sync(); - _oracle.designatePair(address(_tokenA), address(_tokenB), _pair); - OracleAccumulatorQuery[] memory lQueries = new OracleAccumulatorQuery[](3); - lQueries[0] = OracleAccumulatorQuery(PriceType.RAW_PRICE, address(_tokenB), address(_tokenA), 0); - lQueries[1] = OracleAccumulatorQuery(PriceType.RAW_PRICE, address(_tokenB), address(_tokenA), 1 hours); - lQueries[2] = OracleAccumulatorQuery(PriceType.RAW_PRICE, address(_tokenB), address(_tokenA), 2 hours); - - // act & assert - vm.expectRevert(OracleErrors.NoDesignatedPair.selector); - _oracle.getPastAccumulators(lQueries); - } - function testGetTimeWeightedAverage_Inverted() external { // arrange skip(60);