DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: medium
Invalid

fillOrder() checking validateMarginRequirement() is not accurate

Summary

in fillOrder()
Use tradingAccount.validateMarginRequirement(totalFees=orderFee+ settlementFee) to check for sufficient collateral
But when calculating the collateral value, it is multiplied by marginCollateralConfiguration.loanToValue
So the actual collateral value deducted will be smaller than totalFees=orderFee+ settlementFee

Vulnerability Details

in fillOrder()
We need to check if the collateral is sufficient

function _fillOrder(
uint128 tradingAccountId,
uint128 marketId,
uint128 settlementConfigurationId,
SD59x18 sizeDeltaX18,
UD60x18 fillPriceX18
)
...
(
UD60x18 requiredInitialMarginUsdX18,
UD60x18 requiredMaintenanceMarginUsdX18,
SD59x18 accountTotalUnrealizedPnlUsdX18
) = tradingAccount.getAccountMarginRequirementUsdAndUnrealizedPnlUsd(marketId, sizeDeltaX18);
// check maintenance margin if:
// 1) position is not increasing AND
// 2) existing position is being decreased in size
//
// when a position is under the higher initial margin requirement but over the
// lower maintenance margin requirement, we want to allow the trader to decrease
// their losing position size before they become subject to liquidation
//
// but if the trader is opening a new position or increasing the size
// of their existing position we want to ensure they satisfy the higher
// initial margin requirement
ctx.shouldUseMaintenanceMargin = !ctx.isIncreasing && !ctx.oldPositionSizeX18.isZero();
ctx.requiredMarginUsdX18 =
ctx.shouldUseMaintenanceMargin ? requiredMaintenanceMarginUsdX18 : requiredInitialMarginUsdX18;
tradingAccount.validateMarginRequirement(
ctx.requiredMarginUsdX18,
tradingAccount.getMarginBalanceUsd(accountTotalUnrealizedPnlUsdX18),
@> ctx.orderFeeUsdX18.add(ctx.settlementFeeUsdX18)
);
}
@> tradingAccount.deductAccountMargin({
feeRecipients: FeeRecipients.Data({
marginCollateralRecipient: globalConfiguration.marginCollateralRecipient,
orderFeeRecipient: globalConfiguration.orderFeeRecipient,
settlementFeeRecipient: globalConfiguration.settlementFeeRecipient
}),
pnlUsdX18: ctx.pnlUsdX18.lt(SD59x18_ZERO) ? ctx.pnlUsdX18.abs().intoUD60x18() : UD60x18_ZERO,
orderFeeUsdX18: ctx.orderFeeUsdX18,
settlementFeeUsdX18: ctx.settlementFeeUsdX18
});

First: tradingAccount.validateMarginRequirement() directly subtracts orderFee+ settlementFee
And finally: using tradingAccount.deductAccountMargin(orderFee+ settlementFee)
But when we value the collateral getMarginBalanceUsd(), we multiply by marginCollateralConfiguration.loanToValue
adjustedBalanceUsdX18 = marginCollateralConfiguration.getPrice().mul(ud60x18(balance)).mul(ud60x18(marginCollateralConfiguration. loanToValue)

So tradingAccount.deductAccountMargin(orderFee+ settlementFee) actually reduces the valuation by = (orderFee+ settlementFee) * loanToValue

Impact

Due to the existence of marginCollateralConfiguration.loanToValue, using validateMarginRequirement() to determine if there is enough collateral is inaccurate and may result in failure to execute the order properly

Tools Used

Recommendations

Check for sufficient collateral at the end of the fillOrder() method.

function _fillOrder(
uint128 tradingAccountId,
uint128 marketId,
uint128 settlementConfigurationId,
SD59x18 sizeDeltaX18,
UD60x18 fillPriceX18
)
....
ctx.shouldUseMaintenanceMargin = !ctx.isIncreasing && !ctx.oldPositionSizeX18.isZero();
ctx.requiredMarginUsdX18 =
ctx.shouldUseMaintenanceMargin ? requiredMaintenanceMarginUsdX18 : requiredInitialMarginUsdX18;
// reverts if the trader can't satisfy the appropriate margin requirement
- tradingAccount.validateMarginRequirement(
- ctx.requiredMarginUsdX18,
- tradingAccount.getMarginBalanceUsd(accountTotalUnrealizedPnlUsdX18),
- ctx.orderFeeUsdX18.add(ctx.settlementFeeUsdX18)
- );
}
...
tradingAccount.deductAccountMargin({
feeRecipients: FeeRecipients.Data({
marginCollateralRecipient: globalConfiguration.marginCollateralRecipient,
orderFeeRecipient: globalConfiguration.orderFeeRecipient,
settlementFeeRecipient: globalConfiguration.settlementFeeRecipient
}),
pnlUsdX18: ctx.pnlUsdX18.lt(SD59x18_ZERO) ? ctx.pnlUsdX18.abs().intoUD60x18() : UD60x18_ZERO,
orderFeeUsdX18: ctx.orderFeeUsdX18,
settlementFeeUsdX18: ctx.settlementFeeUsdX18
});
+ tradingAccount.validateMarginRequirement(
+ ctx.requiredMarginUsdX18,
+ tradingAccount.getMarginBalanceUsd(SD59x18_ZERO), // will load the latest collateral value
+ SD59x18_ZERO
+ );
emit LogFillOrder(
msg.sender,
tradingAccountId,
marketId,
sizeDeltaX18.intoInt256(),
fillPriceX18.intoUint256(),
ctx.orderFeeUsdX18.intoUint256(),
ctx.settlementFeeUsdX18.intoUint256(),
ctx.pnlUsdX18.intoInt256(),
ctx.fundingFeePerUnitX18.intoInt256()
);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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