Because of an extreme case that pnl/profit can be positive and the account can be liquidatable in this state the contract will not receive the PNL and required margin of the user because the pnl has not yet been credited to the users margin balance hence, apart from the liquidation fee and some dust amounts the pnl and required margins are not recovered from the user.
There is no check or settlement for pnl that are greater than 0.
@audit>> if (ctx.pnlUsdX18.gt(SD59x18_ZERO)) {
IMarketMakingEngine marketMakingEngine = IMarketMakingEngine(perpsEngineConfiguration.marketMakingEngine);
ctx.marginToAddX18 =
marketMakingEngine.getAdjustedProfitForMarketId(marketId, ctx.pnlUsdX18.intoUD60x18().intoUint256());
tradingAccount.deposit(perpsEngineConfiguration.usdToken, ctx.marginToAddX18);
marketMakingEngine.withdrawUsdTokenFromMarket(marketId, ctx.marginToAddX18.intoUint256());
}
ctx.marketIds = new uint256[](1);
ctx.marketIds[0] = marketId;
tradingAccount.deductAccountMargin(
TradingAccount.DeductAccountMarginParams({
feeRecipients: FeeRecipients.Data({
marginCollateralRecipient: perpsEngineConfiguration.marginCollateralRecipient,
orderFeeRecipient: perpsEngineConfiguration.orderFeeRecipient,
settlementFeeRecipient: perpsEngineConfiguration.settlementFeeRecipient
}),
pnlUsdX18: ctx.pnlUsdX18.lt(SD59x18_ZERO) ? ctx.pnlUsdX18.abs().intoUD60x18() : UD60x18_ZERO,
orderFeeUsdX18: ctx.orderFeeUsdX18,
settlementFeeUsdX18: ctx.settlementFeeUsdX18,
marketIds: ctx.marketIds,
accountPositionsNotionalValueX18: new UD60x18[](1)
})
@audit>> ctx.liquidatedCollateralUsdX18 = tradingAccount.deductAccountMargin(
TradingAccount.DeductAccountMarginParams({
feeRecipients: FeeRecipients.Data({
marginCollateralRecipient: perpsEngineConfiguration.marginCollateralRecipient,
orderFeeRecipient: address(0),
@audit>> settlementFeeRecipient: perpsEngineConfiguration.liquidationFeeRecipient
}),
@audit>> pnlUsdX18: ctx.accountTotalUnrealizedPnlUsdX18.abs().intoUD60x18().add(
ctx.requiredMaintenanceMarginUsdX18
),
orderFeeUsdX18: UD60x18_ZERO,
@audit>> settlementFeeUsdX18: ctx.liquidationFeeUsdX18,
marketIds: ctx.activeMarketsIds,
accountPositionsNotionalValueX18: ctx.accountPositionsNotionalValueX18
})
);
function deductAccountMargin(
Data storage self,
DeductAccountMarginParams memory params
)
internal
returns (UD60x18 marginDeductedUsdX18)
{
DeductAccountMarginContext memory ctx;
PerpsEngineConfiguration.Data storage perpsEngineConfiguration = PerpsEngineConfiguration.load();
IMarketMakingEngine marketMakingEngine = IMarketMakingEngine(perpsEngineConfiguration.marketMakingEngine);
uint256 cachedCollateralLiquidationPriorityLength =
perpsEngineConfiguration.collateralLiquidationPriority.length();
for (uint256 i; i < cachedCollateralLiquidationPriorityLength; i++) {
address collateralType = perpsEngineConfiguration.collateralLiquidationPriority.at(i);
MarginCollateralConfiguration.Data storage marginCollateralConfiguration =
MarginCollateralConfiguration.load(collateralType);
ctx.marginCollateralBalanceX18 = getMarginCollateralBalance(self, collateralType);
@audit>>>> if (ctx.marginCollateralBalanceX18.isZero()) continue;
ctx.marginCollateralPriceUsdX18 = marginCollateralConfiguration.getPrice();
if (
params.settlementFeeUsdX18.gt(UD60x18_ZERO)
&& ctx.settlementFeeDeductedUsdX18.lt(params.settlementFeeUsdX18)
) {
(ctx.withdrawnMarginUsdX18, ctx.isMissingMargin,) = withdrawMarginUsd(
self,
collateralType,
ctx.marginCollateralPriceUsdX18,
params.settlementFeeUsdX18.sub(ctx.settlementFeeDeductedUsdX18),
params.feeRecipients.settlementFeeRecipient
);
ctx.settlementFeeDeductedUsdX18 = ctx.settlementFeeDeductedUsdX18.add(ctx.withdrawnMarginUsdX18);
}
if (params.orderFeeUsdX18.gt(UD60x18_ZERO) && ctx.orderFeeDeductedUsdX18.lt(params.orderFeeUsdX18)) {
(ctx.withdrawnMarginUsdX18, ctx.isMissingMargin,) = withdrawMarginUsd(
self,
collateralType,
ctx.marginCollateralPriceUsdX18,
params.orderFeeUsdX18.sub(ctx.orderFeeDeductedUsdX18),
params.feeRecipients.orderFeeRecipient
);
ctx.orderFeeDeductedUsdX18 = ctx.orderFeeDeductedUsdX18.add(ctx.withdrawnMarginUsdX18);
}
@audit>>> if (params.pnlUsdX18.gt(UD60x18_ZERO) && ctx.pnlDeductedUsdX18.lt(params.pnlUsdX18)) {
@audit>>> (ctx.withdrawnMarginUsdX18, ctx.isMissingMargin, ctx.assetAmount) = withdrawMarginUsd(
self,
collateralType,
ctx.marginCollateralPriceUsdX18,
params.pnlUsdX18.sub(ctx.pnlDeductedUsdX18),
address(this)
);
@audit>>>
@audit>>> if (ctx.assetAmount > 0) {
UD60x18 sumOfAllPositionsUsdX18;
uint256 cachedMarketIdsLength = params.marketIds.length;
if (cachedMarketIdsLength > 1) {
for (uint256 j; j < params.accountPositionsNotionalValueX18.length; j++) {
sumOfAllPositionsUsdX18 =
sumOfAllPositionsUsdX18.add(params.accountPositionsNotionalValueX18[j]);
}
}
for (uint256 j; j < cachedMarketIdsLength; j++) {
UD60x18 collateralAmountX18 =
marginCollateralConfiguration.convertTokenAmountToUd60x18(ctx.assetAmount);
if (cachedMarketIdsLength > 1) {
UD60x18 percentDeductForThisMarketX18 =
params.accountPositionsNotionalValueX18[j].div(sumOfAllPositionsUsdX18);
collateralAmountX18 = collateralAmountX18.mul(percentDeductForThisMarketX18);
}
@audit>>> marketMakingEngine.depositCreditForMarket(
uint128(params.marketIds[j]),
collateralType,
marginCollateralConfiguration.convertUd60x18ToTokenAmount(collateralAmountX18)
);
}
}
ctx.pnlDeductedUsdX18 = ctx.pnlDeductedUsdX18.add(ctx.withdrawnMarginUsdX18);
}
@audit>>
@audit>>
@audit>> if (!ctx.isMissingMargin) {
@audit>> break;
}
}
@audit>> marginDeductedUsdX18 =
ctx.settlementFeeDeductedUsdX18.add(ctx.orderFeeDeductedUsdX18).add(ctx.pnlDeductedUsdX18);
}
Because we do not want to revert any liquidation we do not revert or call to ensure there is any strict check.
From the above code with a test it will be confirmed that the pnl is not handled at all also there will be no update in the marketmaking engine that we just incorporated.
The new update will not work in this scenario and the Pnl is lost, no one is claiming it. not the user and not the margin recipient and also no update in the Marketmaking engine will be possible.
Settle all positive pnl as done in the settlement branch in the liquidation branch before calling the deduct margin