sherlock-audit / 2024-04-interest-rate-model-judging

9 stars 5 forks source link

BoRonGod - Deviation in oracle price could lead to arbitrage #254

Closed sherlock-admin3 closed 4 months ago

sherlock-admin3 commented 5 months ago

BoRonGod

medium

Deviation in oracle price could lead to arbitrage

Summary

Deviation in oracle price could lead to arbitrage

Vulnerability Detail

In Exactly, the maximum amount a user can borrow is calculated with the conversion rate between collateral and debt:

  //Auditor.sol
  function checkBorrow(Market market, address borrower) external {
    ...... 
    // verify that current liquidity is not short
    (uint256 collateral, uint256 debt) = accountLiquidity(borrower, Market(address(0)), 0);
    if (collateral < debt) revert InsufficientAccountLiquidity();
  }

And accountLiquidity is calculated with prices of each assets, which is fetched by calling the oracle's assetPrice function.

  //Auditor.sol
  function accountLiquidity(
    address account,
    Market marketToSimulate,
    uint256 withdrawAmount
  ) public view returns (uint256 sumCollateral, uint256 sumDebtPlusEffects) {
    AccountLiquidity memory vars; // holds all our calculation results

    // for each asset the account is in
    uint256 marketMap = accountMarkets[account];
    for (uint256 i = 0; marketMap != 0; marketMap >>= 1) {
      if (marketMap & 1 != 0) {
        ......
        // get the normalized price of the asset (18 decimals)
        vars.price = assetPrice(m.priceFeed);

        // sum all the collateral prices
        sumCollateral += vars.balance.mulDivDown(vars.price, baseUnit).mulWadDown(adjustFactor);

        // sum all the debt
        sumDebtPlusEffects += vars.borrowBalance.mulDivUp(vars.price, baseUnit).divWadUp(adjustFactor);
        ......
          }
        }
      }
      unchecked {
        ++i;
      }
    }
  }

However, Chainlink price oracles are susceptible to front-running as their prices tend to lag behind an asset's real-time price. More specifically, Chainlink oracles are updated after the change in price crosses a deviation threshold, which means a price feed could return a value slightly smaller/larger than an asset's actual price under normal conditions.

An attacker could exploit the difference between the price reported by an oracle and the asset's actual price to gain a profit by front-running the oracle's price update.

The likelihood of this condition becoming true is significantly increased when PriceFeedDouble.sol is used as the market's oracle with multiple Chainlink price feeds. As seen from above, the conversion rate between collateral token/loan token and USD is calculated with multiple price feeds, with each of them having their own deviation threshold. This amplifies the maximum possible price deviation returned by assetPrice(m.priceFeed).

For example:

Now on the ethereum network, all Exactly oracles are denominated in ETH:

WBTC/ETH = WBTC/BTC + BTC/ETH

To demonstrate how a such a deviation in price could lead to arbitrage:

Impact

All profit gained from arbitrage causes a loss of funds for lenders as the remaining bad debt is socialized by them.

Code Snippet

https://github.com/sherlock-audit/2024-04-interest-rate-model/blob/main/protocol/contracts/PriceFeedDouble.sol#L27-L29

Tool used

Manual Review

Recommendation

Consider implementing a borrowing fee to mitigate against arbitrage opportunities. Ideally, this fee would be larger than the oracle's maximum price deviation so that it is not possible to profit from arbitrage.

Further possible mitigations have also been explored by other protocols:

Angle Protocol: Oracles and Front-Running

Liquity: The oracle conundrum

Duplicate of #156