Summary
When a user's order is being filled the new funding fee is calculated in order to account it in the unrealizedPnl. The logic used for calculating funding fee is based on the skew of the market. The problem lies in the fact that the code is using the old skew instead of the new one which accounts for the sizeDelta of the order which results in wrong calculations of the funding fee.
Vulnerability Details
In SettlementBranch::_fillOdrer
the new funding fee is calculated and updated for the market in order to be used for the current trade:
ctx.fundingRateX18 = perpMarket.getCurrentFundingRate();
ctx.fundingFeePerUnitX18 = perpMarket.getNextFundingFeePerUnit(ctx.fundingRateX18, fillPriceX18);
perpMarket.updateFunding(ctx.fundingRateX18, ctx.fundingFeePerUnitX18);
If we see how fundingRateX18
is calculated by going in PerpMarket::getCurrentFundingRate
we can see that it calls PerpMarket::getCurrentFundingVelocity
where the skew of the market is used:
function getCurrentFundingRate(Data storage self) internal view returns (SD59x18) {
return sd59x18(self.lastFundingRate).add(
getCurrentFundingVelocity(self).mul(getProportionalElapsedSinceLastFunding(self).intoSD59x18())
);
}
function getCurrentFundingVelocity(Data storage self) internal view returns (SD59x18) {
SD59x18 maxFundingVelocity = sd59x18(uint256(self.configuration.maxFundingVelocity).toInt256());
SD59x18 skewScale = sd59x18(uint256(self.configuration.skewScale).toInt256());
SD59x18 skew = sd59x18(self.skew);
if (skewScale.isZero()) {
return SD59x18_ZERO;
}
SD59x18 proportionalSkew = skew.div(skewScale);
SD59x18 proportionalSkewBounded = Math.min(Math.max(unary(SD_UNIT), proportionalSkew), SD_UNIT);
return proportionalSkewBounded.mul(maxFundingVelocity);
}
But the skew of the market is updated only later on in the SettlementBranch::_fillOrder
function, meaning that the current funding fee is outdated and will not be the correct value:
ctx.fundingRateX18 = perpMarket.getCurrentFundingRate();
ctx.fundingFeePerUnitX18 = perpMarket.getNextFundingFeePerUnit(ctx.fundingRateX18, fillPriceX18);
perpMarket.updateFunding(ctx.fundingRateX18, ctx.fundingFeePerUnitX18);
ctx.orderFeeUsdX18 = perpMarket.getOrderFeeUsd(sizeDeltaX18, fillPriceX18);
ctx.settlementFeeUsdX18 = ud60x18(uint256(settlementConfiguration.fee));
...
@> perpMarket.updateOpenInterest(ctx.newOpenInterestX18, ctx.newSkewX18);
Impact
Incorrect calculation of funding fee which results in traders receiving more/less funds than they are supposed to.
Tools Used
Manual Review
VS Code
Recommendations
Use the updated skew when calculating the funding fee