The idea of setting slippage is to protect the user from getting less tokens than they should due to high volatility and stop them from being exploited.
The RenzoOracleL2.sol contract specially the getMintRate() function using the aggregator v3 to get/call the latestRoundData(). The function should check for the min and max price returned according Chainlink Documentation.
However, in RenzoOracleL2::getMintRate(), the function only checks for the minPrice set at 1 ether but fails to check for the maxPrice.
The function makes an internal call to _deposit() in its return statement.
// Fetch price and timestamp of ezETH from the configured price feed
(uint256 _lastPrice, uint256 _lastPriceTimestamp) = getMintRate();
// Verify the price is not stale
if (block.timestamp > _lastPriceTimestamp + 1 days) {
revert OraclePriceExpired();
}
// Calculate the amount of xezETH to mint
uint256 xezETHAmount = (1e18 * amountOut) / _lastPrice;
getMintRate() is used here to fetch the price and timestamp of ezETH from the configured price feed. The price retrieved, _lastPrice, is then used in the calculation to determine the amount of xezETH to mint.
The absence of a maximum price check in getMintRate() means that if the price of ezETH fetched is unexpectedly high due to an error or manipulation, the contract will mint a smaller amount of xezETH than it should. This is because the minted amount is inversely proportional to the price:
A higher _lastPrice results in less xezETH minted for the depositor, potentially leading to losses for users if the price is incorrect.
Tools Used
Manual Review
Recommended Mitigation Steps
A circuit breaker should be implemented on the oracle so that when the price edges close to maxPrice it starts reverting, to avoid consuming very high prices.
Define the maxPrice then refactor the check as follows:
Lines of code
https://github.com/code-423n4/2024-04-renzo/blob/main/contracts%2FBridge%2FL2%2FOracle%2FRenzoOracleL2.sol#L55
Vulnerability details
Summary
The idea of setting
slippage
is to protect the user from getting less tokens than they should due to high volatility and stop them from being exploited.The
RenzoOracleL2.sol
contract specially thegetMintRate()
function using theaggregator v3
to get/call thelatestRoundData()
. The function should check for themin and max price
returned according Chainlink Documentation.However, in
RenzoOracleL2::getMintRate()
, the function only checks for theminPrice
set at1 ether
but fails to check for themaxPrice
.Proof of Concept
The
getMintRate()
pulls the price ofezETH
. This determines the number of tokes received upon minting.Here is the application:
getMintRate()
is used here to fetch theprice
andtimestamp
ofezETH
from the configured price feed. The price retrieved,_lastPrice
, is then used in the calculation to determine the amount ofxezETH
to mint.The absence of a
maximum price
check ingetMintRate()
means that if the price ofezETH
fetched isunexpectedly high
due to an error or manipulation,the contract will mint a smaller amount of xezETH than it should
. This is because the minted amount is inversely proportional to the price:Impact
A higher
_lastPrice
results in lessxezETH
minted for the depositor, potentially leading to losses for users if the price is incorrect.Tools Used
Manual Review
Recommended Mitigation Steps
A
circuit breaker
should be implemented on the oracle so that when the price edges close tomaxPrice
it starts reverting, to avoid consuming very high prices.Define the
maxPrice
then refactor the check as follows:Assessed type
Oracle