Deep Sepia Gazelle
Medium
OracleUtils
contract doesn't have fallback mechanism to handle the case when Chainlink oracle price is stale
The OracleUtils
contract provides ETH to token and token to ETH conversions using Chainlink price feeds. But the contract doesn't implement fallback solutions for the price feed oracle. That means if the Chainlink returns stale price, the functionality of the whole protocol will be disrupted.
In the OracleUtils
contract there are several functions that call Chainlink Oracle to retrieve a given price. These functions are: ethToToken
, ethToTokenRoundUp
, tokenToEth
and tokenToEthRoundUp
.
function ethToToken(
uint256 _ethAmount,
address _pricefeed,
uint128 _chainlink_heartbeat,
uint256 _decimals
) public view checkSequencerActive returns (uint256 tokenAmount) {
(
uint80 roundID,
int256 price,
,
uint256 timeStamp,
uint80 answeredInRound
@> ) = AggregatorV3Interface(_pricefeed).latestRoundData();
// heartbeat check
@> require(
timeStamp >= block.timestamp - _chainlink_heartbeat,
"Stale pricefeed"
);
// minAnswer/maxAnswer check
IChainlinkAggregator aggregator = IChainlinkAggregator(
IChainlinkPriceFeed(_pricefeed).aggregator()
);
@> require(
((price > int256(aggregator.minAnswer())) &&
(price < int256(aggregator.maxAnswer()))),
"min/max reached"
);
@> require(answeredInRound >= roundID, "Answer given before round");
.
.
.
}
function ethToTokenRoundUp(
uint256 _ethAmount,
address _pricefeed,
uint128 _chainlink_heartbeat,
uint256 _decimals
) public view checkSequencerActive returns (uint256 tokenAmount) {
(
uint80 roundID,
int256 price,
,
uint256 timeStamp,
uint80 answeredInRound
@> ) = AggregatorV3Interface(_pricefeed).latestRoundData();
// heartbeat check
@> require(
timeStamp >= block.timestamp - _chainlink_heartbeat,
"Stale pricefeed"
);
// minAnswer/maxAnswer check
IChainlinkAggregator aggregator = IChainlinkAggregator(
IChainlinkPriceFeed(_pricefeed).aggregator()
);
@> require(
((price > int256(aggregator.minAnswer())) &&
(price < int256(aggregator.maxAnswer()))),
"min/max reached"
);
@> require(answeredInRound >= roundID, "Answer given before round");
.
.
.
}
function tokenToEth(
uint256 _amount,
address _pricefeed,
uint128 _chainlink_heartbeat,
uint256 _decimals
) public view checkSequencerActive returns (uint256 EthValue) {
(
uint80 roundID,
int256 price,
,
uint256 timeStamp,
uint80 answeredInRound
@> ) = AggregatorV3Interface(_pricefeed).latestRoundData();
// heartbeat check
@> require(
timeStamp >= block.timestamp - _chainlink_heartbeat,
"Stale pricefeed"
);
// minAnswer/maxAnswer check
IChainlinkAggregator aggregator = IChainlinkAggregator(
IChainlinkPriceFeed(_pricefeed).aggregator()
);
@> require(
((price > int256(aggregator.minAnswer())) &&
(price < int256(aggregator.maxAnswer()))),
"min/max reached"
);
@> require(answeredInRound >= roundID, "Answer given before round");
.
.
.
}
function tokenToEthRoundUp(
uint256 _amount,
address _pricefeed,
uint128 _chainlink_heartbeat,
uint256 _decimals
) public view checkSequencerActive returns (uint256 EthValue) {
(
uint80 roundID,
int256 price,
,
uint256 timeStamp,
uint80 answeredInRound
@> ) = AggregatorV3Interface(_pricefeed).latestRoundData();
// heartbeat check
@> require(
timeStamp >= block.timestamp - _chainlink_heartbeat,
"Stale pricefeed"
);
// minAnswer/maxAnswer check
IChainlinkAggregator aggregator = IChainlinkAggregator(
IChainlinkPriceFeed(_pricefeed).aggregator()
);
@> require(
((price > int256(aggregator.minAnswer())) &&
(price < int256(aggregator.maxAnswer()))),
"min/max reached"
);
@> require(answeredInRound >= roundID, "Answer given before round");
.
.
.
}
After retrieving the Chainlink's price, these functions perform a check to ensure that the price is not stale. The problem is that if one of this checks fail, the whole transaction will revert and this will dirupt the functionality of the protocol.
No response
No response
No response
If the Chainlink's aggregator fails to update the price feed, the Chainlink will return stale price, the transaction will revert and this will break the correct functionality of the protocol. The protocol will be unable to operate.
No response
Implement a fallback mechanism to prevent the unability of the protocol to work, if the Chainlink returns stale price. You can use other off-chain oracle providers or onchain Uniswap's TWAP, for feeding price data.