Skip to content

Latest commit

 

History

History
84 lines (54 loc) · 3.7 KB

088.md

File metadata and controls

84 lines (54 loc) · 3.7 KB

peanuts

high

getPriceUSD() in StableOracleDAI.sol does not calculate DAI price in terms of USD properly

Summary

The price of DAI in terms of USD is not calculated properly.

Vulnerability Detail

This is the current calculation of DAI in terms of USD. Note that the return value has to be in 18 decimal places in order to integrate well with other contracts in the protocol.

        return
            (wethPriceUSD * 1e18) /
            ((DAIWethPrice + uint256(price) * 1e10) / 2);
    }

The pricing formula comprises 3 types of price. wethPriceUSD, DAIWethPrice and the price variable from priceFeedDAIETH.

  1. wethPriceUSD is derived from ethOracle price, presumably the ETH/USD Chainlink oracle, and returns the ETH price in USD.
  2. DAIWethPrice is derived from the on-chain Uniswap oracle, and calculates the WETH price in terms of DAI.
  3. price is derived from priceFeedDAIETH oracle, which is the DAI/ETH Chainlink oracle,
        uint256 wethPriceUSD = ethOracle.getPriceUSD();
        uint256 DAIWethPrice = DAIEthOracle.quoteSpecificPoolsWithTimePeriod(
        (, int256 price, , , ) = priceFeedDAIETH.latestRoundData();

Assuming all integrations work well, this is how the calculation will go:

(wethPriceUSD * 1e18) / ((DAIWethPrice + uint256(price) * 1e10) / 2);

wethPriceUSD = 181759000000 (8 decimal places) DAIWethPrice = 1817331962824889681175 (18 decimal places) price = 551720649183640 ( 18 decimal places)

(181759000000 * 1e18) / ((1817331962824889681175 + 551720649183640 * 1e10) / 2) = 65866.3581832, which is not the correct DAI/USD price.

Here is why it is different:

  1. price is returned in 18 decimals already. There is no need to scale by another 1e10
  2. price returns DAI price in ETH, means it returns 0.0005517206, and not ETH price in DAI. The intention of adding DAIWethPrice and price and then dividing by 2 is to get the average of ETH price in terms of DAI using an onChain oracle (uniswap) and an off chain oracle (chainlink). However, DAIWethPrice returns ETH price in DAI (1 ETH is worth 1817 DAI), and price returns DAI price in ETH (1 DAI is worth 0.00055 ETH), so the addition does not make sense.

References:

  1. price (priceFeedDAIETH) DAI/ETH Chainlink oracle: https://data.chain.link/ethereum/mainnet/stablecoins/dai-eth Etherscan: https://etherscan.io/address/0x773616e4d11a78f511299002da57a0a94577f1f4#readContract

  2. wethPriceUSD (ethOracle.getPriceUSD()) ETH/USD Chainlink oracle: https://data.chain.link/ethereum/mainnet/crypto-usd/eth-usd Etherscan: https://etherscan.io/address/0x5f4ec3df9cbd43714fe2740f5e3616155c5b8419#readContract

  3. DAIEthOracle (DAIWethPrice) quoteSpecificPoolsWithTimePeriod: https://etherscan.io/address/0xB210CE856631EeEB767eFa666EC7C1C57738d438#readContract

(Calculation will differ due to fluctuation in prices)

Impact

Wrong pricing of DAI will break protocol especially since DAI is integral to the protocol. (rebalance, collateral etc.)

Code Snippet

https://github.com/sherlock-audit/2023-05-USSD/blob/main/ussd-contracts/contracts/oracles/StableOracleDAI.sol#L50-L53

Tool used

Manual Review

Recommendation

Recommend using the DAI/USD chainlink oracle for simplicity: 0xaed0c38402a5d19df6e4c03f4e2dced6e29c1ee9. https://data.chain.link/ethereum/mainnet/stablecoins/dai-usd

If intent is to calculate DAI-ETH and then ETH-USD, make sure that the decimal calculations are correct. wethPriceUSD returns 8 decimals so have to scale up by 10. Afterwards, scale by 1e18 since other contracts in the protocol assumes oracle returns decimals in 1e18 format.

((wethPriceUSD * 1e10) / DAIWethPrice) * 1e18 ((181759000000 * 1e10) / (1817331962824889681175) ) * 1e18 = 1.000142e+18

1 DAI = 1.000142e18 USD