Omniscia Euler Finance Audit
ChronicleOracle Manual Review Findings
ChronicleOracle Manual Review Findings
COL-01M: Inexistent Registration of Chronicle Subscriber
Type | Severity | Location |
---|---|---|
Standard Conformity | ChronicleOracle.sol:L43 |
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:
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
Type | Severity | Location |
---|---|---|
Standard Conformity | ChronicleOracle.sol:L41, L42 |
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:
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.