Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: medium
Invalid

SettlementBranch._fillOrder: dust PnLs might skip orderFee and settlementFee distribution

Summary

Vulnerability report #699 has not been fixed in current code base.

Vulnerability Details

Vulnerability details and root cause are well explained in https://codehawks.cyfrin.io/c/2024-07-zaros/s/699

Impact

  • For dust PnLs, orderFees and settlementFees are not delivered to corresponding recipients

As the root cause is already described in the original report, let's focus how dust PnL is likely to happen:

First, how small is dust?

Dust should be small enough to rounded down to zero when divided by collateralPriceX18:

UD60x18 requiredMarginInCollateralX18 = amountUsdX18.div(marginCollateralPriceUsdX18);

Considering BTC price is 100k these days, we can assume marginCollateralPriceUsdX18 = 1e23

So if amountUsd < 1e5, requiredMarginInCollateralX18 will be rounded down to zero.

Second, where do we get this tiny difference?

PnL is calculated as follows:

// pnl = position.size * priceShift
function getUnrealizedPnl(Data storage self, UD60x18 price) internal view returns (SD59x18 unrealizedPnlUsdX18) {
SD59x18 priceShift = price.intoSD59x18().sub(ud60x18(self.lastInteractionPrice).intoSD59x18());
unrealizedPnlUsdX18 = sd59x18(self.size).mul(priceShift);
}

Price shift is calculated as currentMarkPrice - lastMarkPriceand markPrice is calculated as follows:

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();
markPrice = priceBeforeDelta.add(priceAfterDelta).div(ud60x18Convert(2));
}

And division by skewScaleis where we can get decimal fractions which will be lower than 1e5

This is definitely possible for markets with decimal skew scale. For example, DOGE_USD_SKEW_SCALE = 2_415_071_153_532e18

Tools Used

Manual checking

Recommendations

Use divUpinstead of div

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.