Description
In DSCEngine
there are 2 functions that retrieve the price from the Chainlink oracle and both have the same operations with regards to determining and rounding to 18 decimals of the feed returned price.
getUsdValue
function getUsdValue(address token, uint256 amount) public view returns (uint256) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
(, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
return ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION;
}
getTokenAmountFromUsd
function getTokenAmountFromUsd(address token, uint256 usdAmountInWei) public view returns (uint256) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
(, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
return (usdAmountInWei * PRECISION) / (uint256(price) * ADDITIONAL_FEED_PRECISION);
}
Recommend Mitigation
Move the price determination function to the OracleLib and implement it while taking into consideration decimals.
Example implementation in libraries/OracleLib.sol
:
@@ -17,6 +17,7 @@ library OracleLib {
error OracleLib__StalePrice();
uint256 private constant TIMEOUT = 3 hours; // 3 * 60 * 60 = 10800 seconds
+ uint256 private constant ADDITIONAL_FEED_PRECISION = 1e10;
function staleCheckLatestRoundData(AggregatorV3Interface priceFeed)
public
@@ -32,6 +33,16 @@ library OracleLib {
return (roundId, answer, startedAt, updatedAt, answeredInRound);
}
+ function getPrice(AggregatorV3Interface priceFeed)
+ public
+ view
+ returns (uint256)
+ {
+ (, int256 price,,,) = staleCheckLatestRoundData(priceFeed);
+
+ return uint256(price) * ADDITIONAL_FEED_PRECISION;
+ }
+
function getTimeout(AggregatorV3Interface /* chainlinkFeed */ ) public pure returns (uint256) {
return TIMEOUT;
}
and also reimplement the DSCEngine::getUsdValue
and DSCEngine::getTokenAmountFromUsd
as to use it, as such:
@@ -342,9 +342,9 @@ contract DSCEngine is ReentrancyGuard {
// $/ETH ETH ??
// $2000 / ETH. $1000 = 0.5 ETH
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
- (, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
- // ($10e18 * 1e18) / ($2000e8 * 1e10)
- return (usdAmountInWei * PRECISION) / (uint256(price) * ADDITIONAL_FEED_PRECISION);
+ uint256 price = priceFeed.getPrice();
+ // ($10e18 * 1e18) / $2000e18
+ return (usdAmountInWei * PRECISION) / price;
}
function getAccountCollateralValue(address user) public view returns (uint256 totalCollateralValueInUsd) {
@@ -360,10 +360,10 @@ contract DSCEngine is ReentrancyGuard {
function getUsdValue(address token, uint256 amount) public view returns (uint256) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
- (, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
+ uint256 price = priceFeed.getPrice();
// 1 ETH = $1000
- // The returned value from CL will be 1000 * 1e8
- return ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION;
+ // The returned value from CL will be 1000 * 1e18
+ return price * amount / PRECISION;
}
function getAccountInformation(address user)