Chainlink aggregators have a built-in circuit breaker if the price of an asset goes outside of a predetermined price band.
The result is that if an asset experiences a huge drop in value(i.e.LUNA crash) the price of the oracle will continue to return the minPrice
instead of the actual price of the asset.
This would allow users to continue borrowing with the asset but at the wrong price. This is exactly what happened to Venus on BSC when LUNAcrashed.
function baseOracleCircuitBreaker(
uint256 protocolPrice,
uint80 roundId,
int256 chainlinkPrice,
uint256 timeStamp,
uint256 chainlinkPriceInEth
) private view returns (uint256 _protocolPrice) {
bool invalidFetchData = roundId == 0 || timeStamp == 0
|| timeStamp > block.timestamp || chainlinkPrice <= 0
|| block.timestamp > 2 hours + timeStamp;
uint256 chainlinkDiff = chainlinkPriceInEth > protocolPrice
? chainlinkPriceInEth - protocolPrice
: protocolPrice - chainlinkPriceInEth;
bool priceDeviation =
protocolPrice > 0 && chainlinkDiff.div(protocolPrice) > 0.5 ether;
if (invalidFetchData || priceDeviation) {
uint256 twapPrice = IDiamond(payable(address(this))).estimateWETHInUSDC(
Constants.UNISWAP_WETH_BASE_AMT, 30 minutes
);
uint256 twapPriceInEther = (twapPrice / Constants.DECIMAL_USDC) * 1 ether;
uint256 twapPriceInv = twapPriceInEther.inv();
if (twapPriceInEther == 0) {
revert Errors.InvalidTwapPrice();
}
if (invalidFetchData) {
return twapPriceInv;
} else {
uint256 twapDiff = twapPriceInv > protocolPrice
? twapPriceInv - protocolPrice
: protocolPrice - twapPriceInv;
if (chainlinkDiff <= twapDiff) {
return chainlinkPriceInEth;
}
IERC20 weth = IERC20(Constants.WETH);
uint256 wethBal = weth.balanceOf(Constants.USDC_WETH);
if (wethBal < 100 ether) revert Errors.InsufficientEthInLiquidityPool();
return twapPriceInv;
}
} else {
return chainlinkPriceInEth;
}
}
function oracleCircuitBreaker(
uint80 roundId,
uint80 baseRoundId,
int256 chainlinkPrice,
int256 baseChainlinkPrice,
uint256 timeStamp,
uint256 baseTimeStamp
) private view {
bool invalidFetchData = roundId == 0 || timeStamp == 0
|| timeStamp > block.timestamp || chainlinkPrice <= 0 || baseRoundId == 0
|| baseTimeStamp == 0 || baseTimeStamp > block.timestamp
|| baseChainlinkPrice <= 0;
if (invalidFetchData) revert Errors.InvalidPrice();
}
You should check if the price of the token has reached a certain minValue/maxValue you define and then revert accordingly.