codehawks-2023-07-dsc-m04
[M-04] All of the USD pair price feeds doesn’t have 8 decimals
Summary
Chainlink 가격 정보가 소수점 8자리를 이용한다고 가정해 하드코딩했다. 하지만 소수점 8자리가 아닌 피드도 있으며, 그 경우 가격을 잘못 계산하여 자금 손실이 발생할 수 있다.
Keyword
chainlink price oracle, token decimals, arithmetic error
Vulnerability
이 프로젝트에서는 Chainlink의 가격 피드(USD 페어의 가격)를 이용하고, 가격 정보는 소수점 8자리를 사용한다고 가정했다.
getTokenAmountFromUsd 함수나 getUsdValue 함수에서 가격 피드가 소수점 8자리를 이용한다 가정하고 ADDITIONAL_FEED_PRECISION 를 곱해 보정한다.
function getTokenAmountFromUsd(address token, uint256 usdAmountInWei) public view returns (uint256) {
// price of ETH (token)
// $/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);
}
function getUsdValue(address token, uint256 amount) public view returns (uint256) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
(, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
// 1 ETH = $1000
// The returned value from CL will be 1000 * 1e8
return ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION;
}하지만 Chainlink의 USD 페어 가격 피드에서는 다른 소수점 자리수를 이용할 수도 있다. 예를 들어 AMPL / USD 피드 에서는 소수점 18자리를 이용한다.
Impact
가격 계산에 오류가 생길 수 있으므로 자금 손실이 생길 수 있다.
Mitigation
소수점 정보를 8자리로 가정하지 말고, Chainlink 컨트랙트에서 가져와 사용한다.
function getTokenAmountFromUsd(address token, uint256 usdAmountInWei) public view returns (uint256) {
// price of ETH (token)
// $/ETH ETH ??
// $2000 / ETH. $1000 = 0.5 ETH
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
(, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
+ uint8 decimals = priceFeed.decimals();
+ uint256 priceWithDecimals = (uint256(price) * 1e18) / (10 ** decimals);
- return (usdAmountInWei * PRECISION) / (uint256(price) * ADDITIONAL_FEED_PRECISION);
+ return (usdAmountInWei * PRECISION) / priceWithDecimals;
}
function getUsdValue(address token, uint256 amount) public view returns (uint256) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
(, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
// 1 ETH = $1000
// The returned value from CL will be 1000 * 1e8
+ uint8 decimals = priceFeed.decimals();
+ uint256 priceWithDecimals = (uint256(price) * 1e18) / (10 ** decimals);
- return ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION;
+ return (priceWithDecimals * amount) / PRECISION;
}tags: bughunting, codehawks, smart contract, solidity, chainlink, price oracle, arithmetic error, token decimals, severity medium