sherlock-audit / 2024-06-leveraged-vaults-judging

11 stars 8 forks source link

nirohgo - Precision calculation error in PendlePTOracle::_calculateBaseToQuote causing erroneous price reports #91

Closed sherlock-admin4 closed 4 months ago

sherlock-admin4 commented 4 months ago

nirohgo

High

Precision calculation error in PendlePTOracle::_calculateBaseToQuote causing erroneous price reports

Summary

PendlePTOracle is expected to provide a result in rateDecimals (10^18) but instead provides the result in different decimals (depending on PTToken decimals) due to a calculation error. As a result, reported pricing can diverge from real price by orders of magnitude.

Vulnerability Detail

In PendlePTOracle::_calculateBaseToQuote the calculation providing the answer is this:

int256 ptRate = expiry <= block.timestamp ? ptDecimals : _getPTRate();

answer = (ptRate * baseToUSD * rateDecimals) /
    (baseToUSDDecimals * ptDecimals);
function _getPTRate() internal view returns (int256) {
    uint256 ptRate = useSyOracleRate ? 
        Deployments.PENDLE_ORACLE.getPtToSyRate(pendleMarket, twapDuration) :
        Deployments.PENDLE_ORACLE.getPtToAssetRate(pendleMarket, twapDuration);
    return ptRate.toInt();
}

This calculation assumes that the ptRate returned from PENDLE_ORACLE is given in ptDecimals (which is why the formula divides by ptDecimals). However this is incorrect. In fact the Pendle oracle returns ptRate in fixed 10^18 decimals (regardless of the PT Token decimals): This can be seen in Pendle's oracle code here, note that when getPtToAssetRateRaw returns a value of 1 (in the case that expiry time has passed) PMath.ONE (which is 10^18) is used. An example can be seen in the aUSDC pendle market on Arbitrum where the PT token decimals are 6 but when calling the Pendle Oracle getPtToAssetRate function with the aUSDC market (0x875F154f4eC93255bEAEA9367c3AdF71Cdcb4Cc0) the result is given in 10^18 precision.

The result is that for any pendle market where the PT token decimals are different than 10^18 the oracle returns a result that is orders of magnitude different than the true result (which is expected to be in 10^18 decimals). The distorted price will have an effect system wide, whenever the ptOracle is consulted for price for the purpose of calculations.

root cause

Dividing by ptDecimals in the answer formula instead of by the pendle oracle precision of 10^18.

Impact

The resulting price deviation (in the example of aUSDC the result will be in 10^30 decimals instead of 10^18) will cause price reporting to be orders of magnitude different than the expected result. The result is used in TradingMudule::getOraclePrice causing it to potentially report prices that are way off.
Impact includes:
A. Erroneous health checks and collateralization rate calculations leading to increased bad debt risk (over-evaluation) or unnecessary liquidations causing loss to users (under-evaluation).
B. Trade module malfunctioning due to wrong calculation of trade limits, causing trades to either fail, or conversely performing trades that should have been reverted due to excessive slippage.

Code Snippet

https://github.com/sherlock-audit/2024-06-leveraged-vaults/blob/14d3eaf0445c251c52c86ce88a84a3f5b9dfad94/leveraged-vaults-private/contracts/oracles/PendlePTOracle.sol#L117

Tool used

Manual Review

Recommendation

Correct the formula by dividing by the Pendle Oracle fixed precision (10^18) instead of the pt token decimals.

Duplicate of #66