Omniscia Euler Finance Audit
EVK Price Oracles Security Audit
Audit Report Revisions
Commit Hash | Date | Audit Report Hash |
---|---|---|
c91093256f | April 1st 2024 | 1d801106a2 |
eeb1847df7 | May 3rd 2024 | 46fcf8f104 |
eeb1847df7 | May 10th 2024 | a300a47bfd |
Audit Overview
We were tasked with performing an audit of the Euler Finance codebase and in particular their EVK Price Oracles module.
High-Level Description
The EVK Price Oracles repository is meant to represent multiple oracle implementations that target specific protocols as well as a top-level router implementation that will detect which oracle it should route a quoting call to.
The security review focused on answering all the Security Questions
that were present in the repository's README.md
file as well as validating that the integrations of each protocol have been securely performed.
Euler Router
This module is responsible for properly routing any quoting calls to the correct oracle implementations, as well as wrapping and unwrapping the input and output assets if they represent EIP-4626 vaults.
An important error was identified in the conversion mechanism's recursion when the output asset (quote
) is a wrapped variant, causing it to produce invalid results when either of the two following conditions is met:
- The sequence of wrapped assets (i.e.
USDC
->wUSDC
->wwUSDC
->w...USDC
) expands and then shrinks the amount of the input asset (i.e.1 USDC = 10 wUSDC = 100 wwUSDC = ... = 1w...USDC
), thereby resulting in a truncation that would not exist normally - The EIP-4626 conversion mechanism (i.e.
IERC4626::convertToShares
) is non-commutative (i.e. forf(x) = wUSDC::convertToShares
andg(x) = wwUSDC::convertToShares
,f(g(x)) != g(f(x))
)
The first condition is effectively a subset of the second condition due to Solidity arithmetic operations being non-commutative, however, it is outlined for simplicity.
Chainlink Oracle
The Chainlink oracle represents a straightforward implementation that queries the latest price by a Chainlink oracle, applies a basic staleness check on the latest report, and yields the post-conversion amount by performing the calculation in the correct direction.
From a documentational perspective, we highlighted that stablecoin asset classes are not meant to be considered equivalent and this finding expands to the README.md
file and specifically the following statement:
Note that we consider pricing "by analogy" a valid use case, i.e. using a ETH/USD feed for pricing WETH/GUSD.
The above statement is incorrect as stablecoins are never on-par with their stable asset (i.e. 1 USDC != 1 USDT != 1 USD
). We advise such terminology to be removed from the codebase, avoiding insecure deployment of oracles which would result in exploitable arbitrage opportunities.
From a security perspective, we denoted two best practices that are also recommended by the Chainlink specification and are not presently adhered to by the oracle.
Chronicle Oracle
The Chronicle oracle represents an implementation meant to interface with the Chronicle Protocol, however, we observed a potential issue with the integrated project.
The Chronicle Protocol oracles appear to enforce access control on all price-querying functions via a Toll
system, yet the ChronicleOracle
does not appear to register itself as an authorized invoker.
The integration code available in the Chronicle Protocol documentation highlights that a SelfKisser
implementation needs to be integrated with, however, the production deployment of the Chronicle Protocol most likely does not have an instance of it deployed.
Based on the aforementioned data, we advise the Chronicle Oracle implementation to be set aside as a future implementation once the Chronicle Protocol matures.
Alternatively, we advise the self-registration (or manual registration) mechanism that the Euler Finance team may have directly agreed with the Chronicle Protocol to be detailed.
Lido Oracle
The Lido Oracle is meant to utilize the stETH
asset directly to convert bidirectionally in the stETH-wstETH
quotation path.
The contract is securely integrated, and direct integration with stETH
instead of wstETH
is performed as a matter of optimization as the relevant wstETH
functions would simply forward calls to the stETH
implementation (i.e. wstETH::getWstETHByStETH
simply forwards the call to stETH::getSharesByPooledEth
).
A systemic risk similar to the Rocket Pool can be observed whereby users can be privy to upcoming rebase operations and thus arbitrage the instantaneous changes in conversion rates.
Rebase operations occur only once per 24-hour intervals, and in case the rebase oracles of Lido Finance do not achieve a consensus, the rebase may be skipped entirely and thus compound to the next epoch.
So as to avoid this systemic risk, we advise the code to integrate with the Lido AccountingOracle
to impose a "staleness" check based on when consensus was last reached.
MakerDAO Oracle (sDAI)
The MakerDAO Oracle is meant to integrate with the savings DAI
Pot to convert between the sDAI
and DAI
tokens.
A flaw was identified in the conversion mechanism whereby the conversion rate utilized could be considered stale as the latest cumulative interest rate is not calculated.
Pyth Oracle
The Pyth Oracle provides a way to integrate with any oracle supported by the Pyth Network, utilizing a custom execution path for ScaleUtils
that supports positive exponents.
As outlined in findings within the report, positive exponents are not considered to be valid values by the Pyth Network itself, and thus the code can be simplified similarly to other oracles.
Redstone Oracle
The Redstone Oracle exposes a contract that integrates with Redstone's "on-demand" price measurements by exposing a separate function via which price measurements are maintained in the contract.
A flaw in the staleness check was identified whereby the actual staleness of the data point consumed would be higher than the actual timestamp the price was reported at by the Redstone oracle.
Rocket Pool Oracle
The Rocket Pool Oracle is meant to utilize the rETH
asset directly to convert bidirectionally in the ETH-rETH
quotation path.
Similarly to the Lido Oracle, the Rocket Pool Oracle suffers from the same systemic risk of delayed rebase reports that occur once per 24 hours and may be delayed for a longer duration.
To impose similar staleness checks to the Lido Oracle, we advise the RocketNetworkBalances
contract deployment to be integrated with that reports the last block balances were recorded at.
The delta between the current block.number
and the value reported by the IRocketNetworkBalances::getBalancesBlock
function can be utilized to calculate the time that has elapsed since the last update, permitting a staleness check to be imposed.
Uniswap V3 Oracle
The Uniswap V3 oracle implementation implements a TWAP based mechanism to calculate a time-weighted average price for a particular Uniswap V3 AMM pair.
In this implementation, we observed that the cardinality of the AMM pair interacted with is not increased, and that certain mathematical operations were incorrectly performed using checked arithmetics.
Overview Conclusion
Systemic risks are inherent to any off-chain data source that is utilized as an on-chain price measurement mechanism.
Within the audit report, we describe that the Rocket Pool and Lido oracle implementations are especially susceptible to arbitrage due to their delayed reporting mechanism.
The actual level of arbitrage that would be exposed by these mechanisms is minimal, and at the time of the production of this report the following percentage adjustments were observed:
- Lido:
0.008%
per day - Rocket Pool:
0.0075%
per day
The influence of these deviations may be considered negligible for the use-case of the Euler Finance team, and as such the complex staleness checks may be considered unnecessary.
As an additional point, the ETH-rETH
oracle in particular can be utilized in conjunction with an AMM pair (i.e. Curve) with a deviation threshold between the two rates permitted which would provide a more resistant evaluation of the assets' exchange rate.
We advise the Euler Finance team to closely evaluate all minor-and-above findings identified in the report and promptly remediate them as well as consider all optimizational exhibits identified in the report.
Post-Audit Conclusion
The Euler Finance team iterated through all findings within the report and provided us with a revised commit hash to evaluate all exhibits on.
We evaluated all alleviations performed by Euler Finance and have identified that all exhibits have either been adequately dealt with, have been safely acknowledged, or have been properly contested.
We consider all outputs of the audit report properly consumed by the Euler Finance team rendering this audit engagement to be concluded.
Audit Synopsis
Severity | Identified | Alleviated | Partially Alleviated | Acknowledged |
---|---|---|---|---|
![]() | 1 | 1 | 0 | 0 |
![]() | 31 | 24 | 0 | 7 |
![]() | 7 | 7 | 0 | 0 |
![]() | 2 | 2 | 0 | 0 |
![]() | 0 | 0 | 0 | 0 |
During the audit, we filtered and validated a total of 4 findings utilizing static analysis tools as well as identified a total of 37 findings during the manual review of the codebase. We strongly recommend that any minor severity or higher findings are dealt with promptly prior to the project's launch as they can introduce potential misbehaviours of the system as well as exploits.
Total Alleviations
The list below covers each segment of the audit in depth and links to the respective chapter of the report: