Omniscia Euler Finance Audit

ChronicleOracle Manual Review Findings

ChronicleOracle Manual Review Findings

COL-01M: Inexistent Registration of Chronicle Subscriber

Description:

The Chronicle oracle system implements an access control mechanism for its oracles permitting only those who have properly registered themselves as consumers to interact with them.

This can be confirmed on the presently-live oracles on the Ethereum mainnet, such as the Chronicle_GNO_USD_2 oracle located here.

Example:

src/adapter/chronicle/ChronicleOracle.sol
27/// @notice Deploy a ChronicleOracle.
28/// @param _base The address of the base asset corresponding to the feed.
29/// @param _quote The address of the quote asset corresponding to the feed.
30/// @param _feed The address of the Chronicle price feed.
31/// @param _maxStaleness The maximum allowed age of the price.
32/// @dev Base and quote are not required to correspond to the feed assets.
33/// For example, the ETH/USD feed can be used to price WETH/USDC.
34constructor(address _base, address _quote, address _feed, uint256 _maxStaleness) {
35 base = _base;
36 quote = _quote;
37 feed = _feed;
38 maxStaleness = _maxStaleness;
39
40 // The scale factor is used to correctly convert decimals.
41 uint8 baseDecimals = IERC20(base).decimals();
42 uint8 quoteDecimals = IERC20(quote).decimals();
43 uint8 feedDecimals = IChronicle(feed).decimals();
44 scale = ScaleUtils.calcScale(baseDecimals, quoteDecimals, feedDecimals);
45}
46
47/// @notice Get the quote from the Chronicle feed.
48/// @param inAmount The amount of `base` to convert.
49/// @param _base The token that is being priced.
50/// @param _quote The token that is the unit of account.
51/// @return The converted amount using the Chronicle feed.
52function _getQuote(uint256 inAmount, address _base, address _quote) internal view override returns (uint256) {
53 bool inverse = ScaleUtils.getDirectionOrRevert(_base, base, _quote, quote);
54
55 (uint256 price, uint256 age) = IChronicle(feed).readWithAge();
56 if (price == 0) revert Errors.PriceOracle_InvalidAnswer();
57
58 uint256 staleness = block.timestamp - age;
59 if (staleness > maxStaleness) revert Errors.PriceOracle_TooStale(staleness, maxStaleness);
60
61 return ScaleUtils.calcOutAmount(inAmount, price, scale, inverse);
62}

Recommendation:

The Chronicle oracle system presently appears immature, and access control is imposed but no on-chain mechanism appears to be present in the Ethereum mainnet to acquire access.

Development examples make use of a SelfKisser instance that permits a Chronicle oracle user to authenticate themselves via the ISelfKisser::selfKiss function, however, this approach is incorrect for the Ethereum mainnet as no such contract is present there (at least publicly) and the SelfKisser repository specifies it will not be deployed in production.

Due to these reasons, we advise the ChronicleOracle implementation to be considered incomplete until the Chronicle project matures. If authentication is expected to be manually provided by the Chronicle team directly, we advise this to be denoted in the codebase as the ChronicleOracle is not a permissionless contract that can be deployed by anyone in such a case.

Alleviation:

The Euler Finance team reached out to the Chronicle team for clarifications and confirmed that explicit authorization must be bestowed to the ChronicleOracle before it can fetch price measurements from a Chronicle oracle.

Based on this information and our recommendation, the documentation of the ChronicleOracle contract was updated to outline this restriction which we consider as sufficient warning for potential deployers of the ChronicleOracle contract.

COL-02M: Potentially Unsupported Function Signature

Description:

The code of the ChronicleOracle::constructor will invoke the IERC20::decimals function as exposed by the forge-std library, however, the IERC20::decimals function is not actually part of the EIP-20 specification.

Impact:

Most EIP-20 assets do implement the IERC20::decimals function signature, however, it is not mandated by the standard and as such a small subset of EIP-20 tokens is incompatible with the ChronicleOracle presently.

Example:

src/adapter/chronicle/ChronicleOracle.sol
27/// @notice Deploy a ChronicleOracle.
28/// @param _base The address of the base asset corresponding to the feed.
29/// @param _quote The address of the quote asset corresponding to the feed.
30/// @param _feed The address of the Chronicle price feed.
31/// @param _maxStaleness The maximum allowed age of the price.
32/// @dev Base and quote are not required to correspond to the feed assets.
33/// For example, the ETH/USD feed can be used to price WETH/USDC.
34constructor(address _base, address _quote, address _feed, uint256 _maxStaleness) {
35 base = _base;
36 quote = _quote;
37 feed = _feed;
38 maxStaleness = _maxStaleness;
39
40 // The scale factor is used to correctly convert decimals.
41 uint8 baseDecimals = IERC20(base).decimals();
42 uint8 quoteDecimals = IERC20(quote).decimals();
43 uint8 feedDecimals = IChronicle(feed).decimals();
44 scale = ScaleUtils.calcScale(baseDecimals, quoteDecimals, feedDecimals);
45}

Recommendation:

In case all EIP-20 assets are expected to be supported, we advise decimals to either be opportunistically queried or for decimals to be supplied as input arguments thus permitting any token to have a ChronicleOracle deployed.

Alleviation:

A common BaseAdapter::_getDecimals implementation has been introduced in the BaseAdapter upstream contract that will attempt to fetch the IERC20::decimals of an asset and default to 32 if they cannot be fetched.

As such, we consider this exhibit fully alleviated.