Fast Khaki Raccoon
Medium
Even if oracles are marked as "bad" borrowing will still be possible, due to both highExchangeRate
and lowExchangeRate
being the same, and passing the bellow check as _deviation
would be 0
uint256 _deviation = (
DEVIATION_PRECISION * (_exchangeRateInfo.highExchangeRate - _exchangeRateInfo.lowExchangeRate)
) / _exchangeRateInfo.highExchangeRate;
if (_deviation <= _exchangeRateInfo.maxOracleDeviation) {
_isBorrowAllowed = true;
}
When bad data is returned both _priceLow
and _priceHigh
are set to the same price one
function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) {
address[] memory _pools = new address[](1);
_pools[0] = UNI_V3_PAIR_ADDRESS;
uint256 _price1 = IStaticOracle(0xB210CE856631EeEB767eFa666EC7C1C57738d438).quoteSpecificPoolsWithTimePeriod(
ORACLE_PRECISION, BASE_TOKEN, QUOTE_TOKEN, _pools, TWAP_DURATION
);
uint256 _price2;
(_isBadData, _price2) = _getChainlinkPrice();
// If bad data return price1 for both, else set high to higher price and low to lower price
_priceLow = _isBadData || _price1 < _price2 ? _price1 : _price2;
_priceHigh = _isBadData || _price1 > _price2 ? _price1 : _price2;
}
This can be if one of the oracles reports late:
if (CHAINLINK_MULTIPLY_ADDRESS != address(0)) {
(, int256 _answer,, uint256 _updatedAt,) =
AggregatorV3Interface(CHAINLINK_MULTIPLY_ADDRESS).latestRoundData();
// If data is stale or negative, set bad data to true and return
if (_answer <= 0 || (block.timestamp - _updatedAt > maxOracleDelay)) {
_isBadData = true;
return (_isBadData, _price);
}
_price = _price * uint256(_answer);
}
if (CHAINLINK_DIVIDE_ADDRESS != address(0)) {
(, int256 _answer,, uint256 _updatedAt,) = AggregatorV3Interface(CHAINLINK_DIVIDE_ADDRESS).latestRoundData();
// If data is stale or negative, set bad data to true and return
if (_answer <= 0 || (block.timestamp - _updatedAt > maxOracleDelay)) {
_isBadData = true;
return (_isBadData, _price);
}
_price = _price / uint256(_answer);
}
Where since both highExchangeRate
and lowExchangeRate
are gonna be the same - TWAP there won't be any deviation, meaning borrowing is allowed:
uint256 _deviation = (
DEVIATION_PRECISION * (_exchangeRateInfo.highExchangeRate - _exchangeRateInfo.lowExchangeRate)
) / _exchangeRateInfo.highExchangeRate;
if (_deviation <= _exchangeRateInfo.maxOracleDeviation) {
_isBorrowAllowed = true;
}
No response
No response
The system will not work in general and the attack can happen without the intention or knowledge on the side of the borrower
- Price decreases by 20% in the span of 3min due to a sudden crash/hack/etc...
- Chainlink is stopped for some reason - happened in the luna crash or with any rugged/hacked tokens when their prices drop 90% or more
- User borrows against that token
- TWAP is set to 20min, so price is barely impacted
- This causes bad debt or insolvency
Borrowing is allowed when it shouldn't be as there is no available comparison to calculate _deviation
.
Even worse, a there is a "bad" oracle it may mean that the price is unstable and or rapidly decreasing, making the TWAP dangerous in this scenario as if the price drops 20% in 5 min (pretty common in crypto) a TWAP of 10min would record a slight drop and a one with 20 or 30min will be barely impacted.
No response
Allow liquidations and repayments, but make sure to disable borrowing when the oracle is bad.
- if (_deviation <= _exchangeRateInfo.maxOracleDeviation) {
+ if (_deviation > 0 && _deviation <= _exchangeRateInfo.maxOracleDeviation) {
_isBorrowAllowed = true;
}