DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: low
Valid

Incorrect calculation of available margin

Summary

The function is using the initialMarginUsdX18 to calculate the available margin, when it should be using the maintenanceMarginUsdX18. This is incorrect.

Vulnerability Details

function getAccountMarginBreakdown(uint128 tradingAccountId)
external
view
returns (
SD59x18 marginBalanceUsdX18,
UD60x18 initialMarginUsdX18,
UD60x18 maintenanceMarginUsdX18,
SD59x18 availableMarginUsdX18
)
{
TradingAccount.Data storage tradingAccount = TradingAccount.loadExisting(tradingAccountId);
SD59x18 activePositionsUnrealizedPnlUsdX18 = tradingAccount.getAccountUnrealizedPnlUsd();
marginBalanceUsdX18 = tradingAccount.getMarginBalanceUsd(activePositionsUnrealizedPnlUsdX18);
for (uint256 i; i < tradingAccount.activeMarketsIds.length(); i++) {
uint128 marketId = tradingAccount.activeMarketsIds.at(i).toUint128();
PerpMarket.Data storage perpMarket = PerpMarket.load(marketId);
Position.Data storage position = Position.load(tradingAccountId, marketId);
UD60x18 indexPrice = perpMarket.getIndexPrice();
UD60x18 markPrice = perpMarket.getMarkPrice(unary(sd59x18(position.size)), indexPrice);
UD60x18 notionalValueX18 = position.getNotionalValue(markPrice);
(UD60x18 positionInitialMarginUsdX18, UD60x18 positionMaintenanceMarginUsdX18) = Position
.getMarginRequirement(
notionalValueX18,
ud60x18(perpMarket.configuration.initialMarginRateX18),
ud60x18(perpMarket.configuration.maintenanceMarginRateX18)
);
initialMarginUsdX18 = initialMarginUsdX18.add(positionInitialMarginUsdX18);
maintenanceMarginUsdX18 = maintenanceMarginUsdX18.add(positionMaintenanceMarginUsdX18);
}
availableMarginUsdX18 = marginBalanceUsdX18.sub((initialMarginUsdX18).intoSD59x18());
}
availableMarginUsdX18 = marginBalanceUsdX18.sub((initialMarginUsdX18).intoSD59x18());

The getAccountMarginBreakdown function is using the initialMarginUsdX18 to calculate the available margin, when it should be using the maintenanceMarginUsdX18. This is incorrect because:

  1. Available margin is typically calculated as the difference between the total margin balance and the maintenance margin, not the initial margin.

  2. Using initial margin for this calculation can lead to an underestimation of the available margin, as initial margin requirements are generally higher than maintenance margin requirements.

  3. This error could potentially prevent users from withdrawing funds that should be available to them, as it's using a more conservative (higher) margin requirement for the calculation.

  4. The @dev comment mentions that "If the account's maintenance margin rate rises to 100% or above (MMR >= 1e18), the liquidation engine will be triggered." This is correct, but the available balance calculation doesn't align with this liquidation threshold, creating a disconnect between the user's perceived available funds and the actual risk of liquidation.

Impact

Users might see less available margin than they should, potentially leading to unnecessary margin calls or preventing them from opening new positions when they actually have sufficient funds.

Tools Used

Manual Review

Recommendations

availableMarginUsdX18 = marginBalanceUsdX18.sub((maintenanceMarginUsdX18).intoSD59x18());

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

`getAccountMarginBreakdown` is using the `initialMarginUsdX18` to calculate the available margin, when it should be using the `maintenanceMarginUsdX18`

Support

FAQs

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