diff --git a/.gas-snapshot b/.gas-snapshot index e4f9362..712c3c7 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,68 +1,75 @@ -QueryProcessorTest:testFindNearestSample_CanFindExactValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 69038209, ~: 78939846) -QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 68311518, ~: 79140121) +QueryProcessorTest:testFindNearestSample_CanFindExactValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 69293044, ~: 77954902) +QueryProcessorTest:testFindNearestSample_CanFindIntermediateValue(uint32,uint256,uint256,uint256) (runs: 256, μ: 68012389, ~: 77774179) QueryProcessorTest:testFindNearestSample_NotInitialized() (gas: 1056944146) -QueryProcessorTest:testFindNearestSample_OneSample(uint256) (runs: 256, μ: 80622, ~: 80652) +QueryProcessorTest:testFindNearestSample_OneSample(uint256) (runs: 256, μ: 80623, ~: 80652) QueryProcessorTest:testGetInstantValue() (gas: 124418) QueryProcessorTest:testGetInstantValue_NotInitialized(uint256) (runs: 256, μ: 19400, ~: 19400) -QueryProcessorTest:testGetInstantValue_NotInitialized_BeyondBufferSize(uint8,uint16) (runs: 256, μ: 69204608, ~: 69204518) -QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 256, μ: 26930, ~: 26984) -QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 256, μ: 73842560, ~: 83474413) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 68671249, ~: 78734958) -QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 68701118, ~: 78766725) -QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 256, μ: 68285121, ~: 79111756) -QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 256, μ: 68319459, ~: 79146413) -QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 256, μ: 68276708, ~: 79102843) -QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 256, μ: 68288213, ~: 79113100) -QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 256, μ: 103845966, ~: 112262763) +QueryProcessorTest:testGetInstantValue_NotInitialized_BeyondBufferSize(uint8,uint16) (runs: 256, μ: 69204583, ~: 69204518) +QueryProcessorTest:testGetPastAccumulator_BufferEmpty(uint8) (runs: 256, μ: 26911, ~: 26984) +QueryProcessorTest:testGetPastAccumulator_ExactMatch(uint32,uint256,uint256,uint16) (runs: 256, μ: 72113529, ~: 81319251) +QueryProcessorTest:testGetPastAccumulator_ExactMatch_LatestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 69463960, ~: 78425849) +QueryProcessorTest:testGetPastAccumulator_ExactMatch_OldestAccumulator(uint32,uint256,uint256) (runs: 256, μ: 69493894, ~: 78457616) +QueryProcessorTest:testGetPastAccumulator_ExtrapolatesBeyondLatest(uint32,uint256,uint256,uint256) (runs: 256, μ: 67986036, ~: 77745180) +QueryProcessorTest:testGetPastAccumulator_InterpolatesBetweenPastAccumulators(uint32,uint256,uint256,uint256) (runs: 256, μ: 68020346, ~: 77780471) +QueryProcessorTest:testGetPastAccumulator_InvalidAgo(uint32,uint256,uint256,uint256) (runs: 256, μ: 67977605, ~: 77737553) +QueryProcessorTest:testGetPastAccumulator_QueryTooOld(uint32,uint256,uint256,uint256) (runs: 256, μ: 67989141, ~: 77747128) +QueryProcessorTest:testGetTimeWeightedAverage(uint32,uint256,uint256,uint256,uint256) (runs: 256, μ: 103407330, ~: 109858182) QueryProcessorTest:testGetTimeWeightedAverage_BadSecs() (gas: 10981) ReservoirPriceOracleTest:testClearRoute() (gas: 52209) -ReservoirPriceOracleTest:testClearRoute_AllWordsCleared() (gas: 159631) -ReservoirPriceOracleTest:testDesignatePair() (gas: 29056) -ReservoirPriceOracleTest:testDesignatePair_IncorrectPair() (gas: 21148) -ReservoirPriceOracleTest:testDesignatePair_NotOwner() (gas: 17541) -ReservoirPriceOracleTest:testDesignatePair_TokenOrderReversed() (gas: 30605) -ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 256, μ: 33696, ~: 33805) -ReservoirPriceOracleTest:testGetQuote_AmountInTooLarge() (gas: 12950) +ReservoirPriceOracleTest:testClearRoute_AllWordsCleared() (gas: 159642) +ReservoirPriceOracleTest:testDesignatePair() (gas: 29089) +ReservoirPriceOracleTest:testDesignatePair_IncorrectPair() (gas: 21129) +ReservoirPriceOracleTest:testDesignatePair_NotOwner() (gas: 17515) +ReservoirPriceOracleTest:testDesignatePair_TokenOrderReversed() (gas: 30670) +ReservoirPriceOracleTest:testGetQuote(uint256,uint256) (runs: 256, μ: 33680, ~: 33783) +ReservoirPriceOracleTest:testGetQuote_AmountInTooLarge() (gas: 13019) ReservoirPriceOracleTest:testGetQuote_BaseIsVault(uint256) (runs: 256, μ: 401362, ~: 401124) -ReservoirPriceOracleTest:testGetQuote_ComplicatedDecimals() (gas: 10361834) -ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 256, μ: 35820, ~: 35987) -ReservoirPriceOracleTest:testGetQuote_MultipleHops() (gas: 111582) -ReservoirPriceOracleTest:testGetQuote_MultipleHops_Inverse() (gas: 111900) -ReservoirPriceOracleTest:testGetQuote_MultipleHops_PriceZero() (gas: 122208) -ReservoirPriceOracleTest:testGetQuote_NoFallbackOracle() (gas: 20776) -ReservoirPriceOracleTest:testGetQuote_PriceZero() (gas: 15930) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 256, μ: 5316035, ~: 5316010) -ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 256, μ: 10503497, ~: 10503613) -ReservoirPriceOracleTest:testGetQuote_SameBaseQuote(uint256,address) (runs: 256, μ: 8927, ~: 8927) -ReservoirPriceOracleTest:testGetQuote_UseFallback() (gas: 38244) -ReservoirPriceOracleTest:testGetQuote_ZeroIn() (gas: 36635) -ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 256, μ: 26240, ~: 26349) -ReservoirPriceOracleTest:testPriceCache_Inverted() (gas: 21949) -ReservoirPriceOracleTest:testSetFallbackOracle_NotOwner() (gas: 10928) -ReservoirPriceOracleTest:testSetRoute() (gas: 61014) -ReservoirPriceOracleTest:testSetRoute_InvalidRewardThreshold() (gas: 37278) -ReservoirPriceOracleTest:testSetRoute_InvalidRoute() (gas: 20106) -ReservoirPriceOracleTest:testSetRoute_InvalidRouteLength() (gas: 19253) -ReservoirPriceOracleTest:testSetRoute_MultipleHops() (gas: 201105) -ReservoirPriceOracleTest:testSetRoute_NotSorted() (gas: 13006) -ReservoirPriceOracleTest:testSetRoute_OverwriteExisting() (gas: 169482) -ReservoirPriceOracleTest:testSetRoute_SameToken() (gas: 13019) -ReservoirPriceOracleTest:testUndesignatePair() (gas: 30237) -ReservoirPriceOracleTest:testUndesignatePair_NotOwner() (gas: 15344) -ReservoirPriceOracleTest:testUpdatePrice_AboveThresholdBelowMaxReward(uint256) (runs: 256, μ: 164717, ~: 164737) -ReservoirPriceOracleTest:testUpdatePrice_BelowThreshold(uint256) (runs: 256, μ: 149562, ~: 149238) -ReservoirPriceOracleTest:testUpdatePrice_BeyondMaxReward(uint256) (runs: 256, μ: 162212, ~: 162240) -ReservoirPriceOracleTest:testUpdatePrice_FirstUpdate() (gas: 153342) -ReservoirPriceOracleTest:testUpdatePrice_IntermediateRoutes() (gas: 16283142) -ReservoirPriceOracleTest:testUpdatePrice_PriceOutOfRange() (gas: 5408263) -ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_ContractNoReceive() (gas: 152501) -ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_InsufficientReward(uint256) (runs: 256, μ: 210795, ~: 211013) -ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_ZeroRecipient() (gas: 146149) +ReservoirPriceOracleTest:testGetQuote_ComplicatedDecimals() (gas: 10361941) +ReservoirPriceOracleTest:testGetQuote_ERC4626AssetFails() (gas: 21434) +ReservoirPriceOracleTest:testGetQuote_Inverse(uint256,uint256) (runs: 256, μ: 35858, ~: 36022) +ReservoirPriceOracleTest:testGetQuote_MultipleHops() (gas: 111597) +ReservoirPriceOracleTest:testGetQuote_MultipleHops_Inverse() (gas: 111891) +ReservoirPriceOracleTest:testGetQuote_MultipleHops_PriceZero() (gas: 122213) +ReservoirPriceOracleTest:testGetQuote_NoFallbackOracle() (gas: 20830) +ReservoirPriceOracleTest:testGetQuote_PriceZero() (gas: 15942) +ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_1HopRoute(uint256,uint256,address,address,uint8,uint8) (runs: 256, μ: 5316105, ~: 5316096) +ReservoirPriceOracleTest:testGetQuote_RandomizeAllParam_2HopRoute(uint256,uint256,uint256,address,address,address,uint8,uint8,uint8) (runs: 256, μ: 10503666, ~: 10503746) +ReservoirPriceOracleTest:testGetQuote_SameBaseQuote(uint256,address) (runs: 256, μ: 8972, ~: 8972) +ReservoirPriceOracleTest:testGetQuote_UseFallback() (gas: 38232) +ReservoirPriceOracleTest:testGetQuote_ZeroIn() (gas: 36638) +ReservoirPriceOracleTest:testGetQuotes(uint256,uint256) (runs: 256, μ: 26236, ~: 26339) +ReservoirPriceOracleTest:testName() (gas: 9355) +ReservoirPriceOracleTest:testPriceCache_Inverted() (gas: 22028) +ReservoirPriceOracleTest:testSetFallbackOracle_NotOwner() (gas: 10993) +ReservoirPriceOracleTest:testSetRoute() (gas: 60992) +ReservoirPriceOracleTest:testSetRoute_InvalidDecimals() (gas: 761313) +ReservoirPriceOracleTest:testSetRoute_InvalidRewardThreshold() (gas: 37234) +ReservoirPriceOracleTest:testSetRoute_InvalidRewardThresholdLength() (gas: 18050) +ReservoirPriceOracleTest:testSetRoute_InvalidRoute() (gas: 20151) +ReservoirPriceOracleTest:testSetRoute_InvalidRouteLength() (gas: 19209) +ReservoirPriceOracleTest:testSetRoute_MultipleHops() (gas: 201150) +ReservoirPriceOracleTest:testSetRoute_NotSorted() (gas: 13029) +ReservoirPriceOracleTest:testSetRoute_OverwriteExisting() (gas: 169526) +ReservoirPriceOracleTest:testSetRoute_SameToken() (gas: 12975) +ReservoirPriceOracleTest:testUndesignatePair() (gas: 30263) +ReservoirPriceOracleTest:testUndesignatePair_NotOwner() (gas: 15315) +ReservoirPriceOracleTest:testUpdatePrice_AboveThresholdBelowMaxReward(uint256) (runs: 256, μ: 164770, ~: 164790) +ReservoirPriceOracleTest:testUpdatePrice_BelowThreshold(uint256) (runs: 256, μ: 149645, ~: 149316) +ReservoirPriceOracleTest:testUpdatePrice_BeyondMaxReward(uint256) (runs: 256, μ: 162201, ~: 162226) +ReservoirPriceOracleTest:testUpdatePrice_FirstUpdate() (gas: 153384) +ReservoirPriceOracleTest:testUpdatePrice_IntermediateRoutes() (gas: 11080585) +ReservoirPriceOracleTest:testUpdatePrice_NoPath() (gas: 15986) +ReservoirPriceOracleTest:testUpdatePrice_PriceOutOfRange() (gas: 5374104) +ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_ContractNoReceive() (gas: 152500) +ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_InsufficientReward(uint256) (runs: 256, μ: 210828, ~: 211040) +ReservoirPriceOracleTest:testUpdatePrice_RewardEligible_ZeroRecipient() (gas: 146179) +ReservoirPriceOracleTest:testUpdatePrice_WriteToNonSimpleRoute() (gas: 498183) ReservoirPriceOracleTest:testUpdateRewardGasAmount() (gas: 19038) -ReservoirPriceOracleTest:testUpdateRewardGasAmount_NotOwner() (gas: 10952) -ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 256, μ: 21576, ~: 21667) -ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 256, μ: 17701, ~: 17994) -ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 256, μ: 30057, ~: 29819) +ReservoirPriceOracleTest:testUpdateRewardGasAmount_NotOwner() (gas: 10953) +ReservoirPriceOracleTest:testUpdateTwapPeriod(uint256) (runs: 256, μ: 21613, ~: 21687) +ReservoirPriceOracleTest:testUpdateTwapPeriod_InvalidTwapPeriod(uint256) (runs: 256, μ: 17688, ~: 17994) +ReservoirPriceOracleTest:testValidatePair_NoDesignatedPair() (gas: 119204) +ReservoirPriceOracleTest:testWritePriceCache(uint256) (runs: 256, μ: 30001, ~: 29763) RoutesLibTest:testGetDecimalDifference() (gas: 3966) RoutesLibTest:testIsCompositeRoute() (gas: 4332) RoutesLibTest:testPackSimplePrice(int8,uint256) (runs: 256, μ: 8083, ~: 7862) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcc449d..1d36f9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,3 +68,23 @@ jobs: - run: forge snapshot --check env: FOUNDRY_PROFILE: default + + coverage: + needs: [test-unit] + if: false # skip this job for now as it always makes the github CI timeout + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - run: forge coverage --report lcov + + - uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: true + files: ./lcov.info diff --git a/foundry.toml b/foundry.toml index 35b800e..6e4272e 100644 --- a/foundry.toml +++ b/foundry.toml @@ -16,6 +16,9 @@ fs_permissions = [ ignored_error_codes = [] skip = ["test/large/*.sol"] +[profile.default.fuzz] +seed = "0xe9cdc8fb2a2c0d1c046034d74d6498826ae3f1d9877353cf40d8a6a7aedfe875" + [profile.large-test] via_ir = true match_path = "test/large/*.sol" diff --git a/src/ReservoirPriceOracle.sol b/src/ReservoirPriceOracle.sol index d405366..2289b1a 100644 --- a/src/ReservoirPriceOracle.sol +++ b/src/ReservoirPriceOracle.sol @@ -492,7 +492,7 @@ contract ReservoirPriceOracle is IPriceOracle, Owned(msg.sender), ReentrancyGuar _validateTokens(aToken0, aToken1); require(lRouteLength > 1 && lRouteLength <= Constants.MAX_ROUTE_LENGTH, OracleErrors.InvalidRouteLength()); require(aRoute[0] == aToken0 && aRoute[lRouteLength - 1] == aToken1, OracleErrors.InvalidRoute()); - require(aRewardThresholds.length == lRouteLength - 1, OracleErrors.InvalidArrayLengthRewardThresholds()); + require(aRewardThresholds.length == lRouteLength - 1, OracleErrors.InvalidRewardThresholdsLength()); bytes32 lSlot = Utils.calculateSlot(aToken0, aToken1); diff --git a/src/libraries/OracleErrors.sol b/src/libraries/OracleErrors.sol index 2a14a8d..95b3ca3 100644 --- a/src/libraries/OracleErrors.sol +++ b/src/libraries/OracleErrors.sol @@ -6,7 +6,7 @@ library OracleErrors { // config errors error IncorrectTokensDesignatePair(); error InvalidRewardThreshold(); - error InvalidArrayLengthRewardThresholds(); + error InvalidRewardThresholdsLength(); error InvalidRoute(); error InvalidRouteLength(); error InvalidTokensProvided(); diff --git a/test/__fixtures/BaseTest.t.sol b/test/__fixtures/BaseTest.t.sol index c3ca5ad..8ab1e97 100644 --- a/test/__fixtures/BaseTest.t.sol +++ b/test/__fixtures/BaseTest.t.sol @@ -21,6 +21,8 @@ contract BaseTest is Test { GenericFactory internal _factory = new GenericFactory(); ReservoirPair internal _pair; + ReservoirPair internal _pairBC; + ReservoirPair internal _pairCD; ReservoirPriceOracle internal _oracle = new ReservoirPriceOracle(DEFAULT_TWAP_PERIOD, DEFAULT_REWARD_GAS_AMOUNT, PriceType.CLAMPED_PRICE); @@ -54,6 +56,16 @@ contract BaseTest is Test { _tokenA.mint(address(_pair), 103e6); _tokenB.mint(address(_pair), 10_189e18); _pair.mint(address(this)); + + _pairBC = ReservoirPair(_createPair(address(_tokenB), address(_tokenC), 1)); + _tokenB.mint(address(_pairBC), 102_303e18); + _tokenC.mint(address(_pairBC), 292e10); + _pairBC.mint(address(this)); + + _pairCD = ReservoirPair(_createPair(address(_tokenC), address(_tokenD), 0)); + _tokenC.mint(address(_pairCD), 100 * 10 ** _tokenC.decimals()); + _tokenD.mint(address(_pairCD), 200 * 10 ** _tokenD.decimals()); + _pairCD.mint(address(this)); } function _createPair(address aTokenA, address aTokenB, uint256 aCurveId) internal returns (address rPair) { diff --git a/test/unit/ReservoirPriceOracle.t.sol b/test/unit/ReservoirPriceOracle.t.sol index e636e5d..74b7eb2 100644 --- a/test/unit/ReservoirPriceOracle.t.sol +++ b/test/unit/ReservoirPriceOracle.t.sol @@ -11,6 +11,7 @@ import { OracleAverageQuery, ReservoirPriceOracle, IERC20, + IERC4626, IPriceOracle, RoutesLib } from "src/ReservoirPriceOracle.sol"; @@ -19,6 +20,7 @@ import { EnumerableSetLib } from "lib/solady/src/utils/EnumerableSetLib.sol"; import { Constants } from "src/libraries/Constants.sol"; import { MockFallbackOracle } from "test/mock/MockFallbackOracle.sol"; import { StubERC4626 } from "test/mock/StubERC4626.sol"; +import {Errors} from "../../lib/amm-core/test/integration/AaveErrors.sol"; contract ReservoirPriceOracleTest is BaseTest { using Utils for *; @@ -92,9 +94,16 @@ contract ReservoirPriceOracleTest is BaseTest { lRewardThreshold[0] = 200; // 2% _oracle.designatePair(address(_tokenB), address(_tokenA), _pair); + _oracle.designatePair(address(_tokenB), address(_tokenC), _pairBC); + _oracle.designatePair(address(_tokenD), address(_tokenC), _pairCD); _oracle.setRoute(address(_tokenA), address(_tokenB), lRoute, lRewardThreshold); } + function testName() external { + // act & assert + assertEq(_oracle.name(), "RESERVOIR PRICE ORACLE"); + } + function testWritePriceCache(uint256 aPrice) external { // arrange uint256 lPrice = bound(aPrice, 1, 1e36); @@ -649,29 +658,23 @@ contract ReservoirPriceOracleTest is BaseTest { _oracle.setRoute(lStart, lEnd, lRoute, lRewardThreshold); ReservoirPair lAC = ReservoirPair(_createPair(address(_tokenA), address(_tokenC), 0)); - ReservoirPair lCD = ReservoirPair(_createPair(address(_tokenC), address(_tokenD), 0)); ReservoirPair lBD = ReservoirPair(_createPair(address(_tokenB), address(_tokenD), 0)); _tokenA.mint(address(lAC), 200 * 10 ** _tokenA.decimals()); _tokenC.mint(address(lAC), 100 * 10 ** _tokenC.decimals()); lAC.mint(address(this)); - _tokenC.mint(address(lCD), 100 * 10 ** _tokenC.decimals()); - _tokenD.mint(address(lCD), 200 * 10 ** _tokenD.decimals()); - lCD.mint(address(this)); - _tokenB.mint(address(lBD), 100 * 10 ** _tokenB.decimals()); _tokenD.mint(address(lBD), 200 * 10 ** _tokenD.decimals()); lBD.mint(address(this)); _oracle.designatePair(lStart, lIntermediate1, lAC); - _oracle.designatePair(lIntermediate2, lIntermediate1, lCD); _oracle.designatePair(lIntermediate2, lEnd, lBD); skip(1); _pair.sync(); lAC.sync(); - lCD.sync(); + _pairCD.sync(); lBD.sync(); skip(_oracle.twapPeriod()); @@ -697,8 +700,8 @@ contract ReservoirPriceOracleTest is BaseTest { _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), ""); + _tokenC.mint(address(_pairCD), lSwapAmt * 10 ** _tokenC.decimals()); + _pairCD.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), ""); @@ -976,6 +979,43 @@ contract ReservoirPriceOracleTest is BaseTest { _oracle.updatePrice(address(_tokenB), address(_tokenC), address(0)); } + function testUpdatePrice_WriteToNonSimpleRoute() external { + // arrange + uint16[] memory lRewardThresholds = new uint16[](2); + lRewardThresholds[0] = lRewardThresholds[1] = 1; + address[] memory lRoute = new address[](3); + lRoute[0] = address(_tokenA); + lRoute[1] = address(_tokenB); + lRoute[2] = address(_tokenC); + + _oracle.setRoute(address(_tokenA), address(_tokenC), lRoute, lRewardThresholds); + + // then we change the original B->C route with B->D->C + address[] memory lModifiedRoute = new address[](3); + lModifiedRoute[0] = address(_tokenB); + lModifiedRoute[1] = address(_tokenD); + lModifiedRoute[2] = address(_tokenC); + _oracle.setRoute(address(_tokenB), address(_tokenC), lModifiedRoute, lRewardThresholds); + + skip(10); + _pair.sync(); + _pairBC.sync(); + _pairCD.sync(); + skip(_oracle.twapPeriod()); + _pair.sync(); + _pairBC.sync(); + _pairCD.sync(); + + // act & assert + vm.expectRevert(OracleErrors.WriteToNonSimpleRoute.selector); + _oracle.updatePrice(address(_tokenA), address(_tokenC), address(0)); + } + + function testUpdatePrice_NoPath() external { + vm.expectRevert(OracleErrors.NoPath.selector); + _oracle.updatePrice(address(_tokenD), address(_tokenC), address(0)); + } + function testSetRoute_SameToken() external { // arrange address lToken0 = address(0x1); @@ -1071,6 +1111,37 @@ contract ReservoirPriceOracleTest is BaseTest { _oracle.setRoute(lRoute[0], lRoute[1], lRoute, lInvalidRewardThreshold); } + function testSetRoute_InvalidRewardThresholdLength() external { + address[] memory lRoute = new address[](2); + uint16[] memory lRewardThreshold = new uint16[](2); + lRewardThreshold[0] = Constants.BP_SCALE; + lRoute[0] = address(_tokenC); + lRoute[1] = address(_tokenD); + + // act & assert + vm.expectRevert(OracleErrors.InvalidRewardThresholdsLength.selector); + _oracle.setRoute(address(_tokenC), address(_tokenD), lRoute, lRewardThreshold); + } + + function testSetRoute_InvalidDecimals() external { + // arrange + MintableERC20 lToken = new MintableERC20("AA", "AA", 21); + address[] memory lRoute = new address[](2); + lRoute[0] = address(lToken) < address(_tokenA) ? address(lToken) : address(_tokenA); + lRoute[1] = address(lToken) < address(_tokenA) ? address(_tokenA) : address(lToken); + uint16[] memory lRewardThreshold = new uint16[](1); + lRewardThreshold[0] = Constants.BP_SCALE; + + // act & assert + vm.expectRevert(OracleErrors.UnsupportedTokenDecimals.selector); + _oracle.setRoute( + address(lToken) < address(_tokenA) ? address(lToken) : address(_tokenA), + address(lToken) < address(_tokenA) ? address(_tokenA) : address(lToken), + lRoute, + lRewardThreshold + ); + } + function testUpdateRewardGasAmount_NotOwner() external { // act & assert vm.prank(address(123)); @@ -1108,4 +1179,28 @@ contract ReservoirPriceOracleTest is BaseTest { vm.expectRevert(OracleErrors.AmountInTooLarge.selector); _oracle.getQuote(lAmtIn, address(_tokenA), address(_tokenB)); } + + function testGetQuote_ERC4626AssetFails() external { + // arrange + address lTargetContract = address(_tokenA); // just any address that doesn't impl the `asset()` function + + // act & assert - the target should be called but should not fail despite not having the function. It should only fail when attempting to query the fallback + vm.expectCall(lTargetContract, abi.encodeCall(IERC4626.asset, ())); + vm.expectRevert(OracleErrors.NoPath.selector); + _oracle.getQuote(123, lTargetContract, address(_tokenD)); + } + + function testValidatePair_NoDesignatedPair() external { + // arrange + skip(1); + _pair.sync(); + skip(_oracle.twapPeriod()); + _pair.sync(); + + _oracle.undesignatePair(address(_tokenA), address(_tokenB)); + + // act & assert + vm.expectRevert(OracleErrors.NoDesignatedPair.selector); + _oracle.updatePrice(address(_tokenA), address(_tokenB), address(this)); + } }