Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed drift check from Oracle Router and gas optimizations #1943

Open
wants to merge 5 commits into
base: nicka/oeth-oracle
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions contracts/contracts/mocks/MockOracleRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ contract MockOracleRouter is OracleRouterBase {
struct FeedMetadata {
address feedAddress;
uint256 maxStaleness;
uint256 decimals;
}

mapping(address => FeedMetadata) public assetToFeedMetadata;
Expand All @@ -25,19 +26,14 @@ contract MockOracleRouter is OracleRouterBase {
function setFeed(
address _asset,
address _feed,
uint256 _maxStaleness
uint256 _maxStaleness,
uint256 _decimals
) external {
assetToFeedMetadata[_asset] = FeedMetadata(_feed, _maxStaleness);
}

/*
* The dev version of the Oracle doesn't need to gas optimize and cache the decimals
*/
function getDecimals(address _feed) internal view override returns (uint8) {
require(_feed != address(0), "Asset not available");
require(_feed != FIXED_PRICE, "Fixed price feeds not supported");

return AggregatorV3Interface(_feed).decimals();
assetToFeedMetadata[_asset] = FeedMetadata(
_feed,
_maxStaleness,
_decimals
);
}

/**
Expand All @@ -51,10 +47,15 @@ contract MockOracleRouter is OracleRouterBase {
internal
view
override
returns (address feedAddress, uint256 maxStaleness)
returns (
address feedAddress,
uint256 maxStaleness,
uint256 decimals
)
{
FeedMetadata storage fm = assetToFeedMetadata[asset];
feedAddress = fm.feedAddress;
maxStaleness = fm.maxStaleness;
decimals = fm.decimals;
}
}
16 changes: 12 additions & 4 deletions contracts/contracts/mocks/MockOracleRouterNoStale.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ contract MockOracleRouterNoStale is OracleRouter {
pure
virtual
override
returns (address feedAddress, uint256 maxStaleness)
returns (
address feedAddress,
uint256 maxStaleness,
uint256 decimals
)
{
(feedAddress, ) = super.feedMetadata(asset);
(feedAddress, , decimals) = super.feedMetadata(asset);
maxStaleness = 365 days;
}
}
Expand All @@ -29,9 +33,13 @@ contract MockOETHOracleRouterNoStale is OETHOracleRouter {
pure
virtual
override
returns (address feedAddress, uint256 maxStaleness)
returns (
address feedAddress,
uint256 maxStaleness,
uint256 decimals
)
{
(feedAddress, ) = super.feedMetadata(asset);
(feedAddress, , decimals) = super.feedMetadata(asset);
maxStaleness = 365 days;
}
}
105 changes: 56 additions & 49 deletions contracts/contracts/oracle/OracleRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ abstract contract OracleRouterBase is IOracle {
using StableMath for uint256;
using SafeCast for int256;

uint256 internal constant MIN_DRIFT = 0.7e18;
uint256 internal constant MAX_DRIFT = 1.3e18;
address internal constant FIXED_PRICE =
0x0000000000000000000000000000000000000001;
// Maximum allowed staleness buffer above normal Oracle maximum staleness
uint256 internal constant STALENESS_BUFFER = 1 days;
mapping(address => uint8) internal decimalsCache;
mapping(address => uint8) internal _deprecated_decimalsCache;

/**
* @dev The price feed contract to use for a particular asset along with
Expand All @@ -31,21 +29,27 @@ abstract contract OracleRouterBase is IOracle {
internal
view
virtual
returns (address feedAddress, uint256 maxStaleness);
returns (
address feedAddress,
uint256 maxStaleness,
uint256 decimals
);

/**
* @notice Returns the total price in 18 digit unit for a given asset.
* @param asset address of the asset
* @return uint256 unit price for 1 asset unit, in 18 decimal fixed
* @return _price unit price for 1 asset unit, in 18 decimal fixed
*/
function price(address asset)
external
view
virtual
override
returns (uint256)
returns (uint256 _price)
{
(address _feed, uint256 maxStaleness) = feedMetadata(asset);
(address _feed, uint256 maxStaleness, uint256 decimals) = feedMetadata(
asset
);
require(_feed != address(0), "Asset not available");
require(_feed != FIXED_PRICE, "Fixed price feeds not supported");

Expand All @@ -57,45 +61,20 @@ abstract contract OracleRouterBase is IOracle {
"Oracle price too old"
);

uint8 decimals = getDecimals(_feed);

uint256 _price = _iprice.toUint256().scaleBy(18, decimals);
if (shouldBePegged(asset)) {
require(_price <= MAX_DRIFT, "Oracle: Price exceeds max");
require(_price >= MIN_DRIFT, "Oracle: Price under min");
}
return _price;
}

function getDecimals(address _feed) internal view virtual returns (uint8) {
uint8 decimals = decimalsCache[_feed];
require(decimals > 0, "Oracle: Decimals not cached");
return decimals;
_price = _iprice.toUint256().scaleBy(18, decimals);
}

/**
* @notice Before an asset/feed price is fetches for the first time the
* decimals need to be cached. This is a gas optimization
* @param asset address of the asset
* @return uint8 corresponding asset decimals
* @notice Checks if the configured decimals matches what's in the aggregated Oracle feed.
* @param asset address of the asset token. eg DAI, stETH or CRV
*/
function cacheDecimals(address asset) external returns (uint8) {
(address _feed, ) = feedMetadata(asset);
require(_feed != address(0), "Asset not available");
require(_feed != FIXED_PRICE, "Fixed price feeds not supported");
function isDecimalsValid(address asset) external view returns (bool) {
(address _feed, , uint256 decimalsConfig) = feedMetadata(asset);
if (_feed == FIXED_PRICE) return true;

uint8 decimals = AggregatorV3Interface(_feed).decimals();
decimalsCache[_feed] = decimals;
return decimals;
}
uint256 decimalsActual = AggregatorV3Interface(_feed).decimals();

function shouldBePegged(address _asset) internal view returns (bool) {
string memory symbol = Helpers.getSymbol(_asset);
bytes32 symbolHash = keccak256(abi.encodePacked(symbol));
return
symbolHash == keccak256(abi.encodePacked("DAI")) ||
symbolHash == keccak256(abi.encodePacked("USDC")) ||
symbolHash == keccak256(abi.encodePacked("USDT"));
return decimalsConfig == decimalsActual;
}
}

Expand All @@ -107,13 +86,20 @@ contract OracleRouter is OracleRouterBase {
* @param asset address of the asset
* @return feedAddress address of the price feed for the asset
* @return maxStaleness maximum acceptable data staleness duration
* @return decimals number of decimals of the aggregated price.
* This is different to the decimals of the asset itself.
* For example, DAI and USDC are both 8 decimals
*/
function feedMetadata(address asset)
internal
pure
virtual
override
returns (address feedAddress, uint256 maxStaleness)
returns (
address feedAddress,
uint256 maxStaleness,
uint256 decimals
)
{
/* + STALENESS_BUFFER is added in case Oracle for some reason doesn't
* update on heartbeat and we add a generous buffer amount.
Expand All @@ -123,60 +109,69 @@ contract OracleRouter is OracleRouterBase {
// Chainlink: DAI/USD
feedAddress = 0xAed0c38402a5d19df6E4c03F4E2DceD6e29c1ee9;
maxStaleness = 1 hours + STALENESS_BUFFER;
decimals = 8;
} else if (asset == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) {
// https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd
// Chainlink: USDC/USD
feedAddress = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;
maxStaleness = 1 days + STALENESS_BUFFER;
decimals = 8;
} else if (asset == 0xdAC17F958D2ee523a2206206994597C13D831ec7) {
// https://data.chain.link/ethereum/mainnet/stablecoins/usdt-usd
// Chainlink: USDT/USD
feedAddress = 0x3E7d1eAB13ad0104d2750B8863b489D65364e32D;
maxStaleness = 1 days + STALENESS_BUFFER;
decimals = 8;
} else if (asset == 0xc00e94Cb662C3520282E6f5717214004A7f26888) {
// https://data.chain.link/ethereum/mainnet/crypto-usd/comp-usd
// Chainlink: COMP/USD
feedAddress = 0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5;
maxStaleness = 1 hours + STALENESS_BUFFER;
decimals = 8;
} else if (asset == 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9) {
// https://data.chain.link/ethereum/mainnet/crypto-usd/aave-usd
// Chainlink: AAVE/USD
feedAddress = 0x547a514d5e3769680Ce22B2361c10Ea13619e8a9;
maxStaleness = 1 hours + STALENESS_BUFFER;
decimals = 8;
} else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {
// https://data.chain.link/ethereum/mainnet/crypto-usd/crv-usd
// Chainlink: CRV/USD
feedAddress = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f;
maxStaleness = 1 days + STALENESS_BUFFER;
decimals = 18;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {
// Chainlink: CVX/USD
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cvx/USD is also 8 decimals:
https://etherscan.io/address/0xd962fc30a72a84ce50161031391756bf2876af5d#readContract

can you also add this link into the comment for easier verification (like in the above feeds):
https://data.chain.link/ethereum/mainnet/crypto-usd/cvx-usd

feedAddress = 0xd962fC30A72A84cE50161031391756Bf2876Af5D;
maxStaleness = 1 days + STALENESS_BUFFER;
decimals = 18;
} else {
revert("Asset not available");
}
}
}

// @notice Oracle Router that denominates all prices in ETH
contract OETHOracleRouter is OracleRouter {
contract OETHOracleRouter is OracleRouterBase {
using StableMath for uint256;

/**
* @notice Returns the total price in 18 digit units for a given asset.
* This implementation does not (!) do range checks as the
* parent OracleRouter does.
* @param asset address of the asset
* @return uint256 unit price for 1 asset unit, in 18 decimal fixed
* @return _price unit price for 1 asset unit, in 18 decimal fixed
*/
function price(address asset)
external
view
virtual
override
returns (uint256)
returns (uint256 _price)
{
(address _feed, uint256 maxStaleness) = feedMetadata(asset);
(address _feed, uint256 maxStaleness, uint256 decimals) = feedMetadata(
asset
);
if (_feed == FIXED_PRICE) {
return 1e18;
}
Expand All @@ -190,9 +185,7 @@ contract OETHOracleRouter is OracleRouter {
"Oracle price too old"
);

uint8 decimals = getDecimals(_feed);
uint256 _price = uint256(_iprice).scaleBy(18, decimals);
return _price;
_price = uint256(_iprice).scaleBy(18, decimals);
}

/**
Expand All @@ -201,47 +194,61 @@ contract OETHOracleRouter is OracleRouter {
* @param asset address of the asset
* @return feedAddress address of the price feed for the asset
* @return maxStaleness maximum acceptable data staleness duration
* @return decimals number of decimals of the aggregated price.
* This is different to the decimals of the asset itself.
* For example, CRV and CVX are both 8 decimals.
*/
function feedMetadata(address asset)
internal
pure
virtual
override
returns (address feedAddress, uint256 maxStaleness)
returns (
address feedAddress,
uint256 maxStaleness,
uint256 decimals
)
{
if (asset == 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) {
// FIXED_PRICE: WETH/ETH
feedAddress = FIXED_PRICE;
maxStaleness = 0;
decimals = 18;
} else if (asset == 0x5E8422345238F34275888049021821E8E08CAa1f) {
// frxETH/ETH
feedAddress = 0xC58F3385FBc1C8AD2c0C9a061D7c13b141D7A5Df;
maxStaleness = 18 hours + STALENESS_BUFFER;
decimals = 18;
} else if (asset == 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/steth-eth
// Chainlink: stETH/ETH
feedAddress = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812;
maxStaleness = 1 days + STALENESS_BUFFER;
decimals = 18;
} else if (asset == 0xae78736Cd615f374D3085123A210448E74Fc6393) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/reth-eth
// Chainlink: rETH/ETH
feedAddress = 0x536218f9E9Eb48863970252233c8F271f554C2d0;
maxStaleness = 1 days + STALENESS_BUFFER;
decimals = 18;
} else if (asset == 0xD533a949740bb3306d119CC777fa900bA034cd52) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/crv-eth
// Chainlink: CRV/ETH
feedAddress = 0x8a12Be339B0cD1829b91Adc01977caa5E9ac121e;
maxStaleness = 1 days + STALENESS_BUFFER;
decimals = 18;
} else if (asset == 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/cvx-eth
// Chainlink: CVX/ETH
feedAddress = 0xC9CbF687f43176B302F03f5e58470b77D07c61c6;
maxStaleness = 1 days + STALENESS_BUFFER;
decimals = 18;
} else if (asset == 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704) {
// https://data.chain.link/ethereum/mainnet/crypto-eth/cbeth-eth
// Chainlink: cbETH/ETH
feedAddress = 0xF017fcB346A1885194689bA23Eff2fE6fA5C483b;
maxStaleness = 1 days + STALENESS_BUFFER;
decimals = 18;
} else {
revert("Asset not available");
}
Expand Down
Loading
Loading