Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
xenide committed Jun 7, 2024
2 parents 796b060 + 3d403d4 commit 44e3cde
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 10 deletions.
34 changes: 26 additions & 8 deletions src/ReservoirPriceOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg.
///////////////////////////////////////////////////////////////////////////////////////////////

event DesignatePair(address token0, address token1, ReservoirPair pair);
event FallbackOracleSet(address fallbackOracle);
event PriceDeviationThreshold(uint256 newThreshold);
event RewardGasAmount(uint256 newAmount);
event Route(address token0, address token1, address[] route);
Expand All @@ -43,6 +44,10 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg.
// STORAGE //
///////////////////////////////////////////////////////////////////////////////////////////////

/// @notice The PriceOracle to call if this router is not configured for base/quote.
/// @dev If `address(0)` then there is no fallback.
address public fallbackOracle;

/// @dev the following 4 storage variables take up 1 storage slot

/// @notice percentage change greater than which, a price update may result in a reward payout of native tokens,
Expand Down Expand Up @@ -90,7 +95,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg.

/// @inheritdoc IPriceOracle
function getQuote(uint256 aAmount, address aBase, address aQuote) external view returns (uint256 rOut) {
rOut = _getQuote(aAmount, aBase, aQuote);
(rOut,) = _getQuotes(aAmount, aBase, aQuote, false);
}

/// @inheritdoc IPriceOracle
Expand All @@ -99,8 +104,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg.
view
returns (uint256 rBidOut, uint256 rAskOut)
{
uint256 lResult = _getQuote(aAmount, aBase, aQuote);
(rBidOut, rAskOut) = (lResult, lResult);
(rBidOut, rAskOut) = _getQuotes(aAmount, aBase, aQuote, true);
}

// price update related functions
Expand Down Expand Up @@ -377,19 +381,28 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg.
}
}

function _getQuote(uint256 aAmount, address aBase, address aQuote) internal view returns (uint256 rOut) {
if (aBase == aQuote) return aAmount;
function _getQuotes(uint256 aAmount, address aBase, address aQuote, bool isGetQuotes)
internal
view
returns (uint256 rBidOut, uint256 rAskOut)
{
if (aBase == aQuote) return (aAmount, aAmount);
if (aAmount > Constants.MAX_AMOUNT_IN) revert OracleErrors.AmountInTooLarge();

(address lToken0, address lToken1) = aBase.sortTokens(aQuote);
(address[] memory lRoute, int256 lDecimalDiff, uint256 lPrice) =
_getRouteDecimalDifferencePrice(lToken0, lToken1);

// route does not exist on our oracle, attempt querying the fallback
if (lRoute.length == 0) {
revert OracleErrors.NoPath();
if (fallbackOracle == address(0)) revert OracleErrors.NoPath();

// We do not catch errors here so the fallback oracle will revert if it doesn't support the query.
if (isGetQuotes) (rBidOut, rAskOut) = IPriceOracle(fallbackOracle).getQuotes(aAmount, aBase, aQuote);
else rBidOut = rAskOut = IPriceOracle(fallbackOracle).getQuote(aAmount, aBase, aQuote);
} else if (lRoute.length == 2) {
if (lPrice == 0) revert OracleErrors.PriceZero();
rOut = _calcAmtOut(aAmount, lPrice, lDecimalDiff, lRoute[0] != aBase);
rBidOut = rAskOut = _calcAmtOut(aAmount, lPrice, lDecimalDiff, lRoute[0] != aBase);
}
// for composite route, read simple prices to derive composite price
else {
Expand All @@ -407,7 +420,7 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg.
if (lPrice == 0) revert OracleErrors.PriceZero();
lIntermediateAmount = _calcAmtOut(lIntermediateAmount, lPrice, lDecimalDiff, lRoute[i] != lLowerToken);
}
rOut = lIntermediateAmount;
rBidOut = rAskOut = lIntermediateAmount;
}
}

Expand Down Expand Up @@ -444,6 +457,11 @@ contract ReservoirPriceOracle is IPriceOracle, IReservoirPriceOracle, Owned(msg.
// ADMIN FUNCTIONS //
///////////////////////////////////////////////////////////////////////////////////////////////

function setFallbackOracle(address aFallbackOracle) public onlyOwner {
fallbackOracle = aFallbackOracle;
emit FallbackOracleSet(aFallbackOracle);
}

function updatePriceDeviationThreshold(uint64 aNewThreshold) public onlyOwner {
if (aNewThreshold > Constants.MAX_DEVIATION_THRESHOLD) {
revert OracleErrors.PriceDeviationThresholdTooHigh();
Expand Down
22 changes: 22 additions & 0 deletions test/mock/MockFallbackOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import { IPriceOracle } from "src/interfaces/IPriceOracle.sol";

contract MockFallbackOracle is IPriceOracle {
function name() external view returns (string memory) {
return "MOCK";
}

function getQuote(uint256 amount, address, address) external view returns (uint256 out) {
out = amount;
}

function getQuotes(uint256 amount, address, address)
external
view
returns (uint256 bidOut, uint256 askOut)
{
(bidOut, askOut) = (amount, amount);
}
}
27 changes: 25 additions & 2 deletions test/unit/ReservoirPriceOracle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { Bytes32Lib } from "amm-core/libraries/Bytes32.sol";
import { EnumerableSetLib } from "lib/solady/src/utils/EnumerableSetLib.sol";
import { Constants } from "src/libraries/Constants.sol";
import { MockFallbackOracle } from "test/mock/MockFallbackOracle.sol";

contract ReservoirPriceOracleTest is BaseTest {
using Utils for *;
Expand All @@ -33,10 +34,12 @@ contract ReservoirPriceOracleTest is BaseTest {

uint256 private constant WAD = 1e18;

address internal constant ADDRESS_THRESHOLD = address(0x1000);

// to keep track of addresses to ensure no clash for fuzz tests
EnumerableSetLib.AddressSet internal _addressSet;

address internal constant ADDRESS_THRESHOLD = address(0x1000);
MockFallbackOracle internal _fallbackOracle = new MockFallbackOracle();

// writes the cached prices, for easy testing
function _writePriceCache(address aToken0, address aToken1, uint256 aPrice) internal {
Expand Down Expand Up @@ -469,6 +472,20 @@ contract ReservoirPriceOracleTest is BaseTest {
assertEq(lAmtOut, aAmtIn);
}

function testGetQuote_UseFallback() external {
// arrange
_oracle.setFallbackOracle(address(_fallbackOracle));

// act
uint256 lAmountOut = _oracle.getQuote(1, address(_tokenC), address(_tokenD));
(uint256 lBidOut, uint256 lAskOut) = _oracle.getQuotes(1, address(_tokenC), address(_tokenD));

// assert
assertGt(lAmountOut, 0);
assertGt(lBidOut, 0);
assertGt(lAskOut, 0);
}

function testUpdatePriceDeviationThreshold(uint256 aNewThreshold) external {
// assume
uint64 lNewThreshold = uint64(bound(aNewThreshold, 0, 0.1e18));
Expand Down Expand Up @@ -963,6 +980,12 @@ contract ReservoirPriceOracleTest is BaseTest {
_oracle.getTimeWeightedAverage(lQueries);
}

function testSetFallbackOracle_NotOwner() external {
vm.prank(address(123));
vm.expectRevert("UNAUTHORIZED");
_oracle.setFallbackOracle(address(456));
}

function testDesignatePair_IncorrectPair() external {
// act & assert
vm.expectRevert();
Expand Down Expand Up @@ -1105,7 +1128,7 @@ contract ReservoirPriceOracleTest is BaseTest {
_oracle.updateRewardGasAmount(111);
}

function testGetQuote_NoPath() external {
function testGetQuote_NoFallbackOracle() external {
// act & assert
vm.expectRevert(OracleErrors.NoPath.selector);
_oracle.getQuote(123, address(123), address(456));
Expand Down

0 comments on commit 44e3cde

Please sign in to comment.