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