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

Margin Calculation In fillOrder Does Not Consider Mark Price Impact

Summary

A trader that is adding to an existing position may receive temporary profits from a premium markPrice. The temporary profits should be subtracted from marginBalanceUsd to avoid over-inflating the trader's actual margin balance.

Vulnerability Detail

If a trader adds to an existing position and worsens the skew (e.g. long trade in a long skewed market), he receives a markPrice that makes the existing position profitable.

Consider this example:

  • Bob has a +10 long at $100, market is skewed long

  • Bob creates another +10 long (total +20) and receives a 'premium' markPrice of $110 for worsening the skew

  • During _fillOrder, PnL for the old position is 100 and Bob receives $100 in USDz collateral

This 'profit' however is temporary as the markPrice is stored in the new position which would offset the temporary profit when PnL is calculated after.

However, in _fillOrder, when performing a check if the new position is liquidatable, validateMarginRequirement does not take into consideration this temporary profit obtained from the higher markPrice. This results in an over-inflated marginBalanceUsd.

See POC here:https://gist.github.com/giraffe0x/5929402e8d514366d4d6110d4e280012
Instructions for POC:

  • Add test file to folder `test/integration/settlement-branch/fillMarketOrder

  • Add these console logs to SettlementBranch.sol:L431

console.log("ENTER SettlementBranch._fillOrder");
console.log("getMarginBalanceUsd:", tradingAccount.getMarginBalanceUsd(accountTotalUnrealizedPnlUsdX18).intoUint256());
console.log("EXIT SettlementBranch._fillOrder");
tradingAccount.validateMarginRequirement();
);
  • Add console logs to LiquidationBranch.sol:L77

console.log("ENTER LiquidationBranch.checkLiquidatableAccounts");
console.log("getMarginBalanceUsd:", tradingAccount.getMarginBalanceUsd(accountTotalUnrealizedPnlUsdX18).intoUint256());
console.log("EXIT LiquidationBranch.checkLiquidatableAccounts");
SD59x18 marginBalanceUsdX18 = tradingAccount.getMarginBalanceUsd(accountTotalUnrealizedPnlUsdX18);
  • Remember to import: import { console } from "forge-std/console.sol";

  • Run forge test --mt test_POC_NeedToDiscountMargin -vv

Observe in logs below that marginBalance calculated in _fillOrder is higher than in checkLiquidatableAccounts which indicates that when filling the order, margin balance is over-inflated.

Impact

A trader can create and fill orders that would be immediately liquidatable after. The greater the skew imbalance caused by the trader, the greater the 'temporary' profits. During liquidation, if the actual margin is much lower than what is checked in _fillOrder, this would not be covered by the trader's collateral and result in bad debt to the protocol.

Code Snippet

https://github.com/Cyfrin/2024-07-zaros/blob/main/src/perpetuals/branches/SettlementBranch.sol#L435

Tool used

Manual Review

Recommendation

When calculating marginBalanceUsd in _fillOrder, consider subtracting any temporary profit obtained from a 'premium' markPrice due to skew imbalance.

Updates

Lead Judging Commences

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

Trader's actual margin balance is overinflated by the temporary profits from a premium `markPrice` obtained in case of adding to an existing position.

Appeal created

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

Trader's actual margin balance is overinflated by the temporary profits from a premium `markPrice` obtained in case of adding to an existing position.

Support

FAQs

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