Summary
There is a division before multiplication PerpMarket::getMarkPrice
which will cause the protocol to use an incorrect price, due to a precision loss.
Vulnerability Details
When calling PerpMarket::getMarkPrice
, priceImpactBeforeDelta and priceImpactAfterDelta
calculation is done with division operation like below.
SD59x18 priceImpactBeforeDelta = skew.div(skewScale);
SD59x18 newSkew = skew\.add(skewDelta);
SD59x18 priceImpactAfterDelta = newSkew\.div(skewScale);
Then the same values are taken and multiplied in the calculation of priceBeforeDelta
and priceAfterDelta
to get the final mark price.
UD60x18 priceBeforeDelta =
cachedIndexPriceX18.add(cachedIndexPriceX18.mul(priceImpactBeforeDelta)).intoUD60x18();
UD60x18 priceAfterDelta =
cachedIndexPriceX18.add(cachedIndexPriceX18.mul(priceImpactAfterDelta)).intoUD60x18();
markPrice = priceBeforeDelta.add(priceAfterDelta).div(ud60x18Convert(2));
Impact
This precision loss has an impact on all functions using the mark price.
Tools Used
Manual review
Recommendations
function getMarkPrice(
Data storage self,
SD59x18 skewDelta,
UD60x18 indexPriceX18
)
internal
view
returns (UD60x18 markPrice)
{
SD59x18 skewScale = sd59x18(self.configuration.skewScale.toInt256());
SD59x18 skew = sd59x18(self.skew);
SD59x18 priceImpactBeforeDelta = skew.div(skewScale);
SD59x18 newSkew = skew.add(skewDelta);
SD59x18 priceImpactAfterDelta = newSkew.div(skewScale);
SD59x18 cachedIndexPriceX18 = indexPriceX18.intoSD59x18();
- UD60x18 priceBeforeDelta = cachedIndexPriceX18.add(cachedIndexPriceX18.mul(priceImpactBeforeDelta)).intoUD60x18();
- UD60x18 priceAfterDelta = cachedIndexPriceX18.add(cachedIndexPriceX18.mul(priceImpactAfterDelta)).intoUD60x18();
+ UD60x18 priceBeforeDelta = cachedIndexPriceX18.add(cachedIndexPriceX18.mul(skew).div(skewscale)).intoUD60x18();
+ UD60x18 priceAfterDelta = cachedIndexPriceX18.add(cachedIndexPriceX18.mul(newSkew).div(skewscale)).intoUD60x18();
markPrice = priceBeforeDelta.add(priceAfterDelta).div(ud60x18Convert(2));
}